You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ha...@apache.org on 2015/08/17 21:17:32 UTC

[01/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/policy

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master ff7f58041 -> 6eacb3c38


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/policy/src/main/java/brooklyn/policy/autoscaling/AutoScalerPolicy.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/autoscaling/AutoScalerPolicy.java b/policy/src/main/java/brooklyn/policy/autoscaling/AutoScalerPolicy.java
index 9da5e12..18480bd 100644
--- a/policy/src/main/java/brooklyn/policy/autoscaling/AutoScalerPolicy.java
+++ b/policy/src/main/java/brooklyn/policy/autoscaling/AutoScalerPolicy.java
@@ -40,6 +40,7 @@ import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.apache.brooklyn.core.util.task.Tasks;
@@ -52,7 +53,6 @@ import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.BasicNotificationSensor;
 import brooklyn.policy.autoscaling.SizeHistory.WindowSummary;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.loadbalancing.LoadBalancingPolicy;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java b/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
index dde1609..b76682c 100644
--- a/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
+++ b/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
@@ -38,12 +38,12 @@ import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.Attributes;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.followthesun.FollowTheSunPool.ContainerItemPair;
 import brooklyn.policy.loadbalancing.Movable;
 import brooklyn.util.collections.MutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/policy/src/main/java/brooklyn/policy/ha/AbstractFailureDetector.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/ha/AbstractFailureDetector.java b/policy/src/main/java/brooklyn/policy/ha/AbstractFailureDetector.java
index 0c2f4d2..9d8c58f 100644
--- a/policy/src/main/java/brooklyn/policy/ha/AbstractFailureDetector.java
+++ b/policy/src/main/java/brooklyn/policy/ha/AbstractFailureDetector.java
@@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.core.util.task.BasicTask;
 import org.apache.brooklyn.core.util.task.ScheduledTask;
@@ -38,7 +39,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.ha.HASensors.FailureDescriptor;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/policy/src/main/java/brooklyn/policy/ha/ConditionalSuspendPolicy.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/ha/ConditionalSuspendPolicy.java b/policy/src/main/java/brooklyn/policy/ha/ConditionalSuspendPolicy.java
index 9a869ae..b347949 100644
--- a/policy/src/main/java/brooklyn/policy/ha/ConditionalSuspendPolicy.java
+++ b/policy/src/main/java/brooklyn/policy/ha/ConditionalSuspendPolicy.java
@@ -23,13 +23,13 @@ import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.javalang.JavaClassNames;
 
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/policy/src/main/java/brooklyn/policy/ha/ServiceReplacer.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/ha/ServiceReplacer.java b/policy/src/main/java/brooklyn/policy/ha/ServiceReplacer.java
index 884f2df..6bea1d4 100644
--- a/policy/src/main/java/brooklyn/policy/ha/ServiceReplacer.java
+++ b/policy/src/main/java/brooklyn/policy/ha/ServiceReplacer.java
@@ -35,6 +35,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
@@ -47,7 +48,6 @@ import brooklyn.entity.group.StopFailedRuntimeException;
 import brooklyn.entity.trait.MemberReplaceable;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.BasicNotificationSensor;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.ha.HASensors.FailureDescriptor;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/policy/src/main/java/brooklyn/policy/ha/ServiceRestarter.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/ha/ServiceRestarter.java b/policy/src/main/java/brooklyn/policy/ha/ServiceRestarter.java
index d53434e..ab1359d 100644
--- a/policy/src/main/java/brooklyn/policy/ha/ServiceRestarter.java
+++ b/policy/src/main/java/brooklyn/policy/ha/ServiceRestarter.java
@@ -28,6 +28,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
@@ -39,7 +40,6 @@ import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.basic.ServiceStateLogic;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.BasicNotificationSensor;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.ha.HASensors.FailureDescriptor;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.javalang.JavaClassNames;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java b/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
index 2c50883..57b736b 100644
--- a/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
+++ b/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
@@ -35,6 +35,7 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -42,7 +43,6 @@ import org.slf4j.LoggerFactory;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.policy.autoscaling.AutoScalerPolicy;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.loadbalancing.BalanceableWorkerPool.ContainerItemPair;
 import brooklyn.util.collections.MutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/TestReferencingPolicy.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/TestReferencingPolicy.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/TestReferencingPolicy.java
index f175e0d..d3f550b 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/TestReferencingPolicy.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/TestReferencingPolicy.java
@@ -19,10 +19,10 @@
 package org.apache.brooklyn.camp.brooklyn;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.policy.basic.AbstractPolicy;
 
 public class TestReferencingPolicy extends AbstractPolicy {
     public static final ConfigKey<Entity> TEST_APPLICATION = new BasicConfigKey<Entity>(Entity.class, "test.reference.app");

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java
index 4d22b3f..e25b47a 100644
--- a/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java
+++ b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java
@@ -31,6 +31,7 @@ import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.core.util.javalang.UrlClassLoader;
 import org.reflections.Reflections;
@@ -45,7 +46,6 @@ import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.basic.AbstractApplication;
 import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.basic.SoftwareProcessImpl;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java
index 31a3b99..c5ea74a 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java
@@ -25,13 +25,11 @@ import javax.ws.rs.core.Response;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import brooklyn.policy.basic.Policies;
-
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.policy.basic.Policies;
 import org.apache.brooklyn.rest.api.PolicyApi;
 import org.apache.brooklyn.rest.domain.PolicySummary;
 import org.apache.brooklyn.rest.domain.Status;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
index a43127c..35e62d6 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
@@ -22,11 +22,11 @@ import java.net.URI;
 import java.util.Map;
 
 import brooklyn.config.ConfigKey;
-import brooklyn.policy.basic.Policies;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.policy.basic.Policies;
 import org.apache.brooklyn.rest.domain.ApplicationSummary;
 import org.apache.brooklyn.rest.domain.PolicyConfigSummary;
 import org.apache.brooklyn.rest.domain.PolicySummary;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
index d29b6e8..751eb22 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
@@ -53,6 +53,7 @@ import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
 import org.apache.brooklyn.core.management.entitlement.Entitlements.StringAndArgument;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import brooklyn.config.ConfigKey;
@@ -64,7 +65,6 @@ import brooklyn.entity.basic.BasicApplication;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.trait.Startable;
-import brooklyn.policy.basic.AbstractPolicy;
 
 import org.apache.brooklyn.rest.domain.ApplicationSpec;
 import org.apache.brooklyn.rest.domain.EntitySpec;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java
index d324507..5881990 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java
@@ -19,8 +19,7 @@
 package org.apache.brooklyn.rest.testing.mocks;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
-
-import brooklyn.policy.basic.AbstractPolicy;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 
 public class CapitalizePolicy extends AbstractPolicy {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java
index d6e2b77..f1546ff 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java
@@ -20,13 +20,13 @@ package org.apache.brooklyn.rest.testing.mocks;
 
 import java.util.Map;
 
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.policy.basic.AbstractPolicy;
 
 public class RestMockSimplePolicy extends AbstractPolicy {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java
index 484f6ab..2c5c1d6 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java
@@ -36,12 +36,12 @@ import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
 import org.apache.brooklyn.core.catalog.internal.CatalogTemplateItemDto;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 
 import brooklyn.entity.basic.AbstractApplication;
 import brooklyn.entity.basic.BasicEntity;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.proxying.EntityProxy;
-import brooklyn.policy.basic.AbstractPolicy;
 
 import org.apache.brooklyn.rest.domain.ApplicationSpec;
 import org.apache.brooklyn.rest.domain.EntitySpec;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java
----------------------------------------------------------------------
diff --git a/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java b/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java
index f70b60b..a034886 100644
--- a/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java
+++ b/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java
@@ -19,11 +19,11 @@
 package brooklyn.osgi.tests;
 
 
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.config.ConfigKey;
-import brooklyn.policy.basic.AbstractPolicy;
 
 public class SimplePolicy extends AbstractPolicy {
     @SetFromFlag("config1")

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/utils/test-bundles/more-entities-v1/src/main/java/brooklyn/osgi/tests/more/MorePolicy.java
----------------------------------------------------------------------
diff --git a/utils/test-bundles/more-entities-v1/src/main/java/brooklyn/osgi/tests/more/MorePolicy.java b/utils/test-bundles/more-entities-v1/src/main/java/brooklyn/osgi/tests/more/MorePolicy.java
index 50b5945..466d8e2 100644
--- a/utils/test-bundles/more-entities-v1/src/main/java/brooklyn/osgi/tests/more/MorePolicy.java
+++ b/utils/test-bundles/more-entities-v1/src/main/java/brooklyn/osgi/tests/more/MorePolicy.java
@@ -20,8 +20,7 @@ package brooklyn.osgi.tests.more;
 
 
 import org.apache.brooklyn.api.catalog.Catalog;
-
-import brooklyn.policy.basic.AbstractPolicy;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 
 public class MorePolicy extends AbstractPolicy {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/utils/test-bundles/more-entities-v2/src/main/java/brooklyn/osgi/tests/more/MorePolicy.java
----------------------------------------------------------------------
diff --git a/utils/test-bundles/more-entities-v2/src/main/java/brooklyn/osgi/tests/more/MorePolicy.java b/utils/test-bundles/more-entities-v2/src/main/java/brooklyn/osgi/tests/more/MorePolicy.java
index 4bfa366..43c53b5 100644
--- a/utils/test-bundles/more-entities-v2/src/main/java/brooklyn/osgi/tests/more/MorePolicy.java
+++ b/utils/test-bundles/more-entities-v2/src/main/java/brooklyn/osgi/tests/more/MorePolicy.java
@@ -20,7 +20,7 @@ package brooklyn.osgi.tests.more;
 
 
 import org.apache.brooklyn.api.catalog.Catalog;
-import brooklyn.policy.basic.AbstractPolicy;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 
 @Catalog(name="More Policy", description="Cataliog item OSGi test policy")
 public class MorePolicy extends AbstractPolicy {


[28/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/flags/FlagUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/flags/FlagUtils.java b/core/src/main/java/org/apache/brooklyn/core/util/flags/FlagUtils.java
new file mode 100644
index 0000000..6f28f9b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/flags/FlagUtils.java
@@ -0,0 +1,587 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.flags;
+
+import static brooklyn.util.GroovyJavaMethods.elvis;
+import static brooklyn.util.GroovyJavaMethods.truth;
+import static com.google.common.base.Preconditions.checkNotNull;
+import groovy.lang.Closure;
+import groovy.lang.GroovyObject;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.trait.Configurable;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.config.ConfigKey.HasConfigKey;
+import brooklyn.util.GroovyJavaMethods;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+
+/** class to help transfer values passed as named arguments to other well-known variables/fields/objects;
+ * see the test case for example usage */
+public class FlagUtils {
+
+    public static final Logger log = LoggerFactory.getLogger(FlagUtils.class);
+    
+    private FlagUtils() {}
+    
+    /** see {@link #setFieldsFromFlags(Object o, ConfigBag)} */
+    public static Map<?, ?> setPublicFieldsFromFlags(Map<?, ?> flags, Object o) {
+        return setFieldsFromFlagsInternal(o, Arrays.asList(o.getClass().getFields()), flags, null, true);
+    }
+
+    /** see {@link #setFieldsFromFlags(Object, ConfigBag)} */
+    public static Map<?, ?> setFieldsFromFlags(Map<?, ?> flags, Object o) {
+        return setFieldsFromFlagsInternal(o, getAllFields(o.getClass()), flags, null, true);
+    }
+    
+    /** sets all fields (including private and static, local and inherited) annotated {@link SetFromFlag} on the given object, 
+     * from the given flags map, returning just those flag-value pairs passed in which do not correspond to SetFromFlags fields 
+     * annotated ConfigKey and HasConfigKey fields are _configured_ (and we assume the object in that case is {@link Configurable});
+     * keys should be ConfigKey, HasConfigKey, or String;
+     * default values are also applied unless that is specified false on one of the variants of this method which takes such an argument
+     */
+    public static void setFieldsFromFlags(Object o, ConfigBag configBag) {
+        setFieldsFromFlagsInternal(o, getAllFields(o.getClass()), configBag.getAllConfig(), configBag, true);
+    }
+
+    /** as {@link #setFieldsFromFlags(Object, ConfigBag)}, but allowing control over whether default values should be set */
+    public static void setFieldsFromFlags(Object o, ConfigBag configBag, boolean setDefaultVals) {
+        setFieldsFromFlagsInternal(o, getAllFields(o.getClass()), configBag.getAllConfig(), configBag, setDefaultVals);
+    }
+
+    /** as {@link #setFieldsFromFlags(Object, ConfigBag)}, but specifying a subset of flags to use */
+    public static void setFieldsFromFlagsWithBag(Object o, Map<?,?> flags, ConfigBag configBag, boolean setDefaultVals) {
+        setFieldsFromFlagsInternal(o, getAllFields(o.getClass()), flags, configBag, setDefaultVals);
+    }
+
+    /**
+     * Sets the field with the given flag (if it exists) to the given value.
+     * Will attempt to coerce the value to the required type.
+     * Will respect "nullable" on the SetFromFlag annotation.
+     * 
+     * @throws IllegalArgumentException If fieldVal is null and the SetFromFlag annotation set nullable=false
+     */
+    public static boolean setFieldFromFlag(Object o, String flagName, Object fieldVal) {
+        return setFieldFromFlagInternal(checkNotNull(flagName, "flagName"), fieldVal, o, getAllFields(o.getClass()));
+    }
+    
+    /** get all fields (including private and static) on the given object and all supertypes, 
+     * that are annotated with SetFromFlags. 
+     */
+    public static Map<String, ?> getFieldsWithFlags(Object o) {
+        return getFieldsWithFlagsInternal(o, getAllFields(o.getClass()));
+    }
+    
+    /**
+     * Finds the {@link Field} on the given object annotated with the given name flag.
+     */
+    public static Field findFieldForFlag(String flagName, Object o) {
+        return findFieldForFlagInternal(flagName, o, getAllFields(o.getClass()));
+    }
+
+    /** get all fields (including private and static) and their values on the given object and all supertypes, 
+     * where the field is annotated with SetFromFlags. 
+     */
+    public static Map<String, Object> getFieldsWithFlagsExcludingModifiers(Object o, int excludingModifiers) {
+        List<Field> filteredFields = Lists.newArrayList();
+        for (Field contender : getAllFields(o.getClass())) {
+            if ((contender.getModifiers() & excludingModifiers) == 0) {
+                filteredFields.add(contender);
+            }
+        }
+        return getFieldsWithFlagsInternal(o, filteredFields);
+    }
+    
+    /** get all fields with the given modifiers, and their values on the given object and all supertypes, 
+     * where the field is annotated with SetFromFlags. 
+     */
+    public static Map<String, Object> getFieldsWithFlagsWithModifiers(Object o, int requiredModifiers) {
+        List<Field> filteredFields = Lists.newArrayList();
+        for (Field contender : getAllFields(o.getClass())) {
+            if ((contender.getModifiers() & requiredModifiers) == requiredModifiers) {
+                filteredFields.add(contender);
+            }
+        }
+        return getFieldsWithFlagsInternal(o, filteredFields);
+    }
+    
+    /** sets _all_ accessible _{@link ConfigKey}_ and {@link HasConfigKey} fields on the given object, 
+     * using the indicated flags/config-bag 
+     * @deprecated since 0.7.0 use {@link #setAllConfigKeys(Map, Configurable, boolean)} */
+    public static Map<String, ?> setAllConfigKeys(Map<String, ?> flagsOrConfig, Configurable instance) {
+        return setAllConfigKeys(flagsOrConfig, instance, false);
+    }
+    /** sets _all_ accessible _{@link ConfigKey}_ and {@link HasConfigKey} fields on the given object, 
+     * using the indicated flags/config-bag */
+    public static Map<String, ?> setAllConfigKeys(Map<String, ?> flagsOrConfig, Configurable instance, boolean includeFlags) {
+        ConfigBag bag = new ConfigBag().putAll(flagsOrConfig);
+        setAllConfigKeys(instance, bag, includeFlags);
+        return bag.getUnusedConfigMutable();
+    }
+    
+    /** sets _all_ accessible _{@link ConfigKey}_ and {@link HasConfigKey} fields on the given object, 
+     * using the indicated flags/config-bag 
+    * @deprecated since 0.7.0 use {@link #setAllConfigKeys(Configurable, ConfigBag, boolean)} */
+    public static void setAllConfigKeys(Configurable o, ConfigBag bag) {
+        setAllConfigKeys(o, bag, false);
+    }
+    /** sets _all_ accessible _{@link ConfigKey}_ and {@link HasConfigKey} fields on the given object, 
+     * using the indicated flags/config-bag */
+    public static void setAllConfigKeys(Configurable o, ConfigBag bag, boolean includeFlags) {
+        for (Field f: getAllFields(o.getClass())) {
+            ConfigKey<?> key = getFieldAsConfigKey(o, f);
+            if (key!=null) {
+                FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(f, key, bag);
+                if ((includeFlags && record.isValuePresent()) || record.getConfigKeyMaybeValue().isPresent()) {
+                    setField(o, f, record.getValueOrNullPreferringConfigKey(), null);
+                }
+            }
+        }
+    }
+    
+    public static class FlagConfigKeyAndValueRecord {
+        private String flagName = null;
+        private ConfigKey<?> configKey = null;
+        private Maybe<Object> flagValue = Maybe.absent();
+        private Maybe<Object> configKeyValue = Maybe.absent();
+        
+        public String getFlagName() {
+            return flagName;
+        }
+        public ConfigKey<?> getConfigKey() {
+            return configKey;
+        }
+        public Maybe<Object> getFlagMaybeValue() {
+            return flagValue;
+        }
+        public Maybe<Object> getConfigKeyMaybeValue() {
+            return configKeyValue;
+        }
+        public Object getValueOrNullPreferringConfigKey() {
+            return getConfigKeyMaybeValue().or(getFlagMaybeValue()).orNull();
+        }
+        public Object getValueOrNullPreferringFlag() {
+            return getFlagMaybeValue().or(getConfigKeyMaybeValue()).orNull();
+        }
+        /** true if value is present for either flag or config key */
+        public boolean isValuePresent() {
+            return flagValue.isPresent() || configKeyValue.isPresent();
+        }
+        
+        @Override
+        public String toString() {
+            return Objects.toStringHelper(this).omitNullValues()
+                .add("flag", flagName)
+                .add("configKey", configKey)
+                .add("flagValue", flagValue.orNull())
+                .add("configKeyValue", configKeyValue.orNull())
+                .toString();
+        }
+    }
+    
+    /** gets all the flags/keys in the given config bag which are applicable to the given type's config keys and flags */
+    public static <T> List<FlagConfigKeyAndValueRecord> findAllFlagsAndConfigKeys(T optionalInstance, Class<? extends T> type, ConfigBag input) {
+        List<FlagConfigKeyAndValueRecord> output = new ArrayList<FlagUtils.FlagConfigKeyAndValueRecord>();
+        for (Field f: getAllFields(type)) {
+            ConfigKey<?> key = getFieldAsConfigKey(optionalInstance, f);
+            FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(f, key, input);
+            if (record.isValuePresent())
+                output.add(record);
+        }
+        return output;
+    }
+
+    /** returns the flag/config-key record for the given input */
+    private static FlagConfigKeyAndValueRecord getFlagConfigKeyRecord(Field f, ConfigKey<?> key, ConfigBag input) {
+        FlagConfigKeyAndValueRecord result = new FlagConfigKeyAndValueRecord(); 
+        result.configKey = key;
+        if (key!=null && input.containsKey(key))
+            result.configKeyValue = Maybe.<Object>of(input.getStringKey(key.getName()));
+        SetFromFlag flag = f.getAnnotation(SetFromFlag.class);
+        if (flag!=null) {
+            result.flagName = flag.value();
+            if (input.containsKey(flag.value()))
+                result.flagValue = Maybe.of(input.getStringKey(flag.value()));
+        }
+        return result;
+    }
+
+    /** returns all fields on the given class, superclasses, and interfaces thereof, in that order of preference,
+     * (excluding fields on Object) */
+    public static List<Field> getAllFields(Class<?> base, Closure<Boolean> filter) {
+        return getAllFields(base, GroovyJavaMethods.<Field>predicateFromClosure(filter));
+    }
+    public static List<Field> getAllFields(Class<?> base) {
+        return getAllFields(base, Predicates.<Field>alwaysTrue());
+    }
+    public static List<Field> getAllFields(Class<?> base, Predicate<Field> filter) {
+        return getLocalFields(getAllAssignableTypes(base), filter);
+    }
+    /** returns all fields explicitly declared on the given classes */
+    public static List<Field> getLocalFields(List<Class<?>> classes) {
+        return getLocalFields(classes, Predicates.<Field>alwaysTrue());
+    }
+    public static List<Field> getLocalFields(List<Class<?>> classes, Closure<Boolean> filter) {
+        return getLocalFields(classes, GroovyJavaMethods.<Field>predicateFromClosure(filter));
+    }
+    public static List<Field> getLocalFields(List<Class<?>> classes, Predicate<Field> filter) {
+        List<Field> fields = Lists.newArrayList();
+        for (Class<?> c : classes) {
+            for (Field f : c.getDeclaredFields()) {
+                if (filter.apply(f)) fields.add(f);
+            }
+        }
+        return fields;
+    }
+    
+    /** returns base, superclasses, then interfaces */
+    public static List<Class<?>> getAllAssignableTypes(Class<?> base) {
+        return getAllAssignableTypes(base, new Predicate<Class<?>>() {
+            @Override public boolean apply(Class<?> it) {
+                return (it != Object.class) && (it != GroovyObject.class);
+            }
+        });
+    }
+    public static List<Class<?>> getAllAssignableTypes(Class<?> base, Closure<Boolean> filter) {
+        return getAllAssignableTypes(base, GroovyJavaMethods.<Class<?>>predicateFromClosure(filter));
+    }
+    public static List<Class<?>> getAllAssignableTypes(Class<?> base, Predicate<Class<?>> filter) {
+        List<Class<?>> classes = Lists.newArrayList();
+        for (Class<?> c = base; c != null; c = c.getSuperclass()) {
+            if (filter.apply(c)) classes.add(c);
+        }
+        for (int i=0; i<classes.size(); i++) {
+            for (Class<?> interf : classes.get(i).getInterfaces()) {
+                if (filter.apply(interf) && !(classes.contains(interf))) classes.add(interf);
+            }
+        }
+        return classes;
+    }
+    
+    private static Map<String, Object> getFieldsWithFlagsInternal(Object o, Collection<Field> fields) {
+        Map<String, Object> result = Maps.newLinkedHashMap();
+        for (Field f: fields) {
+            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
+            if (cf != null) {
+                String flagName = elvis(cf.value(), f.getName());
+                if (truth(flagName)) {
+                    result.put(flagName, getField(o, f));
+                } else {
+                    log.warn("Ignoring field {} of object {} as no flag name available", f, o);
+                }
+            }
+        }
+        return result;
+    }
+
+    private static Field findFieldForFlagInternal(String flagName, Object o, Collection<Field> fields) {
+        for (Field f: fields) {
+            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
+            if (cf != null) {
+                String contenderName = elvis(cf.value(), f.getName());
+                if (flagName.equals(contenderName)) {
+                    return f;
+                }
+            }
+        }
+        throw new NoSuchElementException("Field with flag "+flagName+" not found on "+o+" of type "+(o != null ? o.getClass() : null));
+    }
+
+    private static boolean setFieldFromFlagInternal(String flagName, Object fieldVal, Object o, Collection<Field> fields) {
+        for (Field f: fields) {
+            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
+            if (cf != null && flagName.equals(elvis(cf.value(), f.getName()))) {
+                setField(o, f, fieldVal, cf);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static Map<String, ?> setFieldsFromFlagsInternal(Object o, Collection<Field> fields, Map<?,?> flagsOrConfig, ConfigBag bag, boolean setDefaultVals) {
+        if (bag==null) bag = new ConfigBag().putAll(flagsOrConfig);
+        for (Field f: fields) {
+            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
+            if (cf!=null) setFieldFromConfig(o, f, bag, cf, setDefaultVals);
+        }
+        return bag.getUnusedConfigMutable();
+    }
+
+    private static void setFieldFromConfig(Object o, Field f, ConfigBag bag, SetFromFlag optionalAnnotation, boolean setDefaultVals) {
+        String flagName = optionalAnnotation==null ? null : (String)elvis(optionalAnnotation.value(), f.getName());
+        // prefer flag name, if present
+        if (truth(flagName) && bag.containsKey(flagName)) {
+            setField(o, f, bag.getStringKey(flagName), optionalAnnotation);
+            return;
+        }
+        // first check whether it is a key
+        ConfigKey<?> key = getFieldAsConfigKey(o, f);
+        if (key!=null && bag.containsKey(key)) {
+            Object uncoercedValue = bag.getStringKey(key.getName());
+            setField(o, f, uncoercedValue, optionalAnnotation);
+            return;
+        }
+        if (setDefaultVals && optionalAnnotation!=null && truth(optionalAnnotation.defaultVal())) {
+            Object oldValue;
+            try {
+                f.setAccessible(true);
+                oldValue = f.get(o);
+                if (oldValue==null || oldValue.equals(getDefaultValueForType(f.getType()))) {
+                    setField(o, f, optionalAnnotation.defaultVal(), optionalAnnotation);
+                }
+            } catch (Exception e) {
+                Exceptions.propagate(e);
+            }
+            return;
+        }
+    }
+
+    /** returns the given field as a config key, if it is an accessible config key, otherwise null */
+    private static ConfigKey<?> getFieldAsConfigKey(Object optionalInstance, Field f) {
+        if (optionalInstance==null) {
+            if ((f.getModifiers() & Modifier.STATIC)==0)
+                // non-static field on null instance, can't be set
+                return null;
+        }
+        if (ConfigKey.class.isAssignableFrom(f.getType())) {
+            return (ConfigKey<?>) getField(optionalInstance, f);
+        } else if (HasConfigKey.class.isAssignableFrom(f.getType())) {
+            return ((HasConfigKey<?>)getField(optionalInstance, f)).getConfigKey();
+        }
+        return null;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static void setConfig(Object objectOfField, ConfigKey<?> key, Object value, SetFromFlag optionalAnnotation) {
+        if (objectOfField instanceof Configurable) {
+            ((Configurable)objectOfField).setConfig((ConfigKey)key, value);
+            return;
+        } else {
+            if (optionalAnnotation==null) {
+                log.warn("Cannot set key "+key.getName()+" on "+objectOfField+": containing class is not Configurable");
+            } else if (!key.getName().equals(optionalAnnotation.value())) {
+                log.warn("Cannot set key "+key.getName()+" on "+objectOfField+" from flag "+optionalAnnotation.value()+": containing class is not Configurable");
+            } else {
+                // if key and flag are the same, then it will probably happen automatically
+                if (log.isDebugEnabled())
+                    log.debug("Cannot set key "+key.getName()+" on "+objectOfField+" from flag "+optionalAnnotation.value()+": containing class is not Configurable");
+            }
+            return;
+        }
+    }
+    
+    /** sets the field to the value, after checking whether the given value can be set 
+     * respecting the constraints of the annotation 
+     */
+    public static void setField(Object objectOfField, Field f, Object value, SetFromFlag optionalAnnotation) {
+        try {
+            ConfigKey<?> key = getFieldAsConfigKey(objectOfField, f);
+            if (key!=null) {
+                setConfig(objectOfField, key, value, optionalAnnotation);
+                return;
+            }
+            
+            if (!f.isAccessible()) f.setAccessible(true);
+            if (optionalAnnotation!=null && optionalAnnotation.immutable()) {
+                Object oldValue = f.get(objectOfField);
+                if (!Objects.equal(oldValue, getDefaultValueForType(f.getType())) && oldValue != value) {
+                    throw new IllegalStateException("Forbidden modification to immutable field "+
+                        f+" in "+objectOfField+": attempting to change to "+value+" when was already "+oldValue);
+                }
+            }
+            if (optionalAnnotation!=null && !optionalAnnotation.nullable() && value==null) {
+                throw new IllegalArgumentException("Forbidden null assignment to non-nullable field "+
+                        f+" in "+objectOfField);
+            }
+            if (optionalAnnotation!=null && (f.getModifiers() & Modifier.STATIC)==Modifier.STATIC)
+                log.warn("Setting static field "+f+" in "+objectOfField+" from flag "+optionalAnnotation.value()+": discouraged");
+
+            Object newValue;
+            try {
+                newValue = TypeCoercions.coerce(value, f.getType());
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Cannot set "+f+" in "+objectOfField+" from type "+value.getClass()+" ("+value+"): "+e, e);
+            }
+            f.set(objectOfField, newValue);
+            if (log.isTraceEnabled()) log.trace("FlagUtils for "+objectOfField+", setting field="+f.getName()+"; val="+value+"; newVal="+newValue+"; key="+key);
+
+        } catch (IllegalAccessException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    /** gets the value of the field. 
+     */
+    public static Object getField(Object objectOfField, Field f) {
+        try {
+            if (!f.isAccessible()) f.setAccessible(true);
+            return f.get(objectOfField);
+        } catch (IllegalAccessException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+    
+    /** returns the default/inital value that is assigned to fields of the givien type;
+     * if the type is not primitive this value is null;
+     * for primitive types it is obvious but not AFAIK programmatically visible
+     * (e.g. 0 for int, false for boolean)  
+     */
+    public static Object getDefaultValueForType(Class<?> t) {
+        if (!t.isPrimitive()) return null;
+        if (t==Integer.TYPE) return (int)0;
+        if (t==Long.TYPE) return (long)0;
+        if (t==Double.TYPE) return (double)0;
+        if (t==Float.TYPE) return (float)0;
+        if (t==Byte.TYPE) return (byte)0;
+        if (t==Short.TYPE) return (short)0;
+        if (t==Character.TYPE) return (char)0;
+        if (t==Boolean.TYPE) return false;
+        //should never happen
+        throw new IllegalStateException("Class "+t+" is an unknown primitive.");
+    }
+
+    /** returns a map of all fields which are annotated 'SetFromFlag', along with the annotation */
+    public static Map<Field,SetFromFlag> getAnnotatedFields(Class<?> type) {
+        Map<Field, SetFromFlag> result = Maps.newLinkedHashMap();
+        for (Field f: getAllFields(type)) {
+            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
+            if (truth(cf)) result.put(f, cf);
+        }
+        return result;
+    }
+
+    /** returns a map of all {@link ConfigKey} fields which are annotated 'SetFromFlag', along with the annotation */
+    public static Map<ConfigKey<?>,SetFromFlag> getAnnotatedConfigKeys(Class<?> type) {
+        Map<ConfigKey<?>, SetFromFlag> result = Maps.newLinkedHashMap();
+        List<Field> fields = getAllFields(type, new Predicate<Field>() {
+            @Override public boolean apply(Field f) {
+                return (f != null) && ConfigKey.class.isAssignableFrom(f.getType()) && ((f.getModifiers() & Modifier.STATIC)!=0);
+            }});
+        for (Field f: fields) {
+            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
+            if (cf != null) {
+                ConfigKey<?> key = getFieldAsConfigKey(null, f);
+                if (key != null) {
+                    result.put(key, cf);
+                }
+            }
+        }
+        return result;
+    }
+
+    /** returns a map of all fields which are annotated 'SetFromFlag' with their current values;
+     * useful if you want to clone settings from one object
+     */
+    public static Map<String,Object> getFieldsWithValues(Object o) {
+        try {
+            Map<String, Object> result = Maps.newLinkedHashMap();
+            for (Map.Entry<Field, SetFromFlag> entry : getAnnotatedFields(o.getClass()).entrySet()) {
+                Field f = entry.getKey();
+                SetFromFlag cf = entry.getValue();
+                String flagName = elvis(cf.value(), f.getName());
+                if (truth(flagName)) {
+                    if (!f.isAccessible()) f.setAccessible(true);
+                    result.put(flagName, f.get(o));
+                }
+            }
+            return result;
+        } catch (IllegalAccessException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+        
+    /**
+     * @throws an IllegalStateException if there are fields required (nullable=false) which are unset 
+     * @throws wrapped IllegalAccessException
+     */
+    public static void checkRequiredFields(Object o) {
+        try {
+            Set<String> unsetFields = Sets.newLinkedHashSet();
+            for (Map.Entry<Field, SetFromFlag> entry : getAnnotatedFields(o.getClass()).entrySet()) {
+                Field f = entry.getKey();
+                SetFromFlag cf = entry.getValue();
+                if (!cf.nullable()) {
+                    String flagName = elvis(cf.value(), f.getName());
+                    if (!f.isAccessible()) f.setAccessible(true);
+                    Object v = f.get(o);
+                    if (v==null) unsetFields.add(flagName);
+                }
+            }
+            if (truth(unsetFields)) {
+                throw new IllegalStateException("Missing required "+(unsetFields.size()>1 ? "fields" : "field")+": "+unsetFields);
+            }
+        } catch (IllegalAccessException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+//    /** sets all fields in target annotated with @SetFromFlag using the configuration in the given config bag */
+//    public static void setFieldsFromConfigFlags(Object target, ConfigBag configBag) {
+//        setFieldsFromConfigFlags(target, configBag.getAllConfig(), configBag);
+//    }
+//
+//    
+//    /** sets all fields in target annotated with @SetFromFlag using the configuration in the given configToUse,
+//     * marking used in the given configBag */
+//    public static void setFieldsFromConfigFlags(Object target, Map<?,?> configToUse, ConfigBag configBag) {
+//        for (Map.Entry<?,?> entry: configToUse.entrySet()) {
+//            setFieldFromConfigFlag(target, entry.getKey(), entry.getValue(), configBag);
+//        }
+//    }
+//
+//    public static void setFieldFromConfigFlag(Object target, Object key, Object value, ConfigBag optionalConfigBag) {
+//        String name = null;
+//        if (key instanceof String) name = (String)key;
+//        else if (key instanceof ConfigKey<?>) name = ((ConfigKey<?>)key).getName();
+//        else if (key instanceof HasConfigKey<?>) name = ((HasConfigKey<?>)key).getConfigKey().getName();
+//        else {
+//            if (key!=null) {
+//                log.warn("Invalid config type "+key.getClass().getCanonicalName()+" ("+key+") when configuring "+target+"; ignoring");
+//            }
+//            return;
+//        }
+//        if (setFieldFromFlag(name, value, target)) {
+//            if (optionalConfigBag!=null)
+//                optionalConfigBag.markUsed(name);
+//        }
+//    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/flags/MethodCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/flags/MethodCoercions.java b/core/src/main/java/org/apache/brooklyn/core/util/flags/MethodCoercions.java
new file mode 100644
index 0000000..20116cf
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/flags/MethodCoercions.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.flags;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.reflect.TypeToken;
+
+import javax.annotation.Nullable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A way of binding a loosely-specified method call into a strongly-typed Java method call.
+ */
+public class MethodCoercions {
+
+    /**
+     * Returns a predicate that matches a method with the given name, and a single parameter that
+     * {@link org.apache.brooklyn.core.util.flags.TypeCoercions#tryCoerce(Object, com.google.common.reflect.TypeToken)} can process
+     * from the given argument.
+     *
+     * @param methodName name of the method
+     * @param argument argument that is intended to be given
+     * @return a predicate that will match a compatible method
+     */
+    public static Predicate<Method> matchSingleParameterMethod(final String methodName, final Object argument) {
+        checkNotNull(methodName, "methodName");
+        checkNotNull(argument, "argument");
+
+        return new Predicate<Method>() {
+            @Override
+            public boolean apply(@Nullable Method input) {
+                if (input == null) return false;
+                if (!input.getName().equals(methodName)) return false;
+                Type[] parameterTypes = input.getGenericParameterTypes();
+                return parameterTypes.length == 1
+                        && TypeCoercions.tryCoerce(argument, TypeToken.of(parameterTypes[0])).isPresentAndNonNull();
+
+            }
+        };
+    }
+
+    /**
+     * Tries to find a single-parameter method with a parameter compatible with (can be coerced to) the argument, and
+     * invokes it.
+     *
+     * @param instance the object to invoke the method on
+     * @param methodName the name of the method to invoke
+     * @param argument the argument to the method's parameter.
+     * @return the result of the method call, or {@link brooklyn.util.guava.Maybe#absent()} if method could not be matched.
+     */
+    public static Maybe<?> tryFindAndInvokeSingleParameterMethod(final Object instance, final String methodName, final Object argument) {
+        Class<?> clazz = instance.getClass();
+        Iterable<Method> methods = Arrays.asList(clazz.getMethods());
+        Optional<Method> matchingMethod = Iterables.tryFind(methods, matchSingleParameterMethod(methodName, argument));
+        if (matchingMethod.isPresent()) {
+            Method method = matchingMethod.get();
+            try {
+                Type paramType = method.getGenericParameterTypes()[0];
+                Object coercedArgument = TypeCoercions.coerce(argument, TypeToken.of(paramType));
+                return Maybe.of(method.invoke(instance, coercedArgument));
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                throw Exceptions.propagate(e);
+            }
+        } else {
+            return Maybe.absent();
+        }
+    }
+
+    /**
+     * Returns a predicate that matches a method with the given name, and parameters that
+     * {@link org.apache.brooklyn.core.util.flags.TypeCoercions#tryCoerce(Object, com.google.common.reflect.TypeToken)} can process
+     * from the given list of arguments.
+     *
+     * @param methodName name of the method
+     * @param arguments arguments that is intended to be given
+     * @return a predicate that will match a compatible method
+     */
+    public static Predicate<Method> matchMultiParameterMethod(final String methodName, final List<?> arguments) {
+        checkNotNull(methodName, "methodName");
+        checkNotNull(arguments, "arguments");
+
+        return new Predicate<Method>() {
+            @Override
+            public boolean apply(@Nullable Method input) {
+                if (input == null) return false;
+                if (!input.getName().equals(methodName)) return false;
+                int numOptionParams = arguments.size();
+                Type[] parameterTypes = input.getGenericParameterTypes();
+                if (parameterTypes.length != numOptionParams) return false;
+
+                for (int paramCount = 0; paramCount < numOptionParams; paramCount++) {
+                    if (!TypeCoercions.tryCoerce(((List) arguments).get(paramCount),
+                            TypeToken.of(parameterTypes[paramCount])).isPresentAndNonNull()) return false;
+                }
+                return true;
+            }
+        };
+    }
+
+    /**
+     * Tries to find a multiple-parameter method with each parameter compatible with (can be coerced to) the
+     * corresponding argument, and invokes it.
+     *
+     * @param instance the object to invoke the method on
+     * @param methodName the name of the method to invoke
+     * @param argument a list of the arguments to the method's parameters.
+     * @return the result of the method call, or {@link brooklyn.util.guava.Maybe#absent()} if method could not be matched.
+     */
+    public static Maybe<?> tryFindAndInvokeMultiParameterMethod(final Object instance, final String methodName, final List<?> arguments) {
+        Class<?> clazz = instance.getClass();
+        Iterable<Method> methods = Arrays.asList(clazz.getMethods());
+        Optional<Method> matchingMethod = Iterables.tryFind(methods, matchMultiParameterMethod(methodName, arguments));
+        if (matchingMethod.isPresent()) {
+            Method method = matchingMethod.get();
+            try {
+                int numOptionParams = ((List)arguments).size();
+                Object[] coercedArguments = new Object[numOptionParams];
+                for (int paramCount = 0; paramCount < numOptionParams; paramCount++) {
+                    Object argument = arguments.get(paramCount);
+                    Type paramType = method.getGenericParameterTypes()[paramCount];
+                    coercedArguments[paramCount] = TypeCoercions.coerce(argument, TypeToken.of(paramType));
+                }
+                return Maybe.of(method.invoke(instance, coercedArguments));
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                throw Exceptions.propagate(e);
+            }
+        } else {
+            return Maybe.absent();
+        }
+    }
+
+    /**
+     * Tries to find a method with each parameter compatible with (can be coerced to) the corresponding argument, and invokes it.
+     *
+     * @param instance the object to invoke the method on
+     * @param methodName the name of the method to invoke
+     * @param argument a list of the arguments to the method's parameters, or a single argument for a single-parameter method.
+     * @return the result of the method call, or {@link brooklyn.util.guava.Maybe#absent()} if method could not be matched.
+     */
+    public static Maybe<?> tryFindAndInvokeBestMatchingMethod(final Object instance, final String methodName, final Object argument) {
+        if (argument instanceof List) {
+            List<?> arguments = (List<?>) argument;
+
+            // ambiguous case: we can't tell if the user is using the multi-parameter syntax, or the single-parameter
+            // syntax for a method which takes a List parameter. So we try one, then fall back to the other.
+
+            Maybe<?> maybe = tryFindAndInvokeMultiParameterMethod(instance, methodName, arguments);
+            if (maybe.isAbsent())
+                maybe = tryFindAndInvokeSingleParameterMethod(instance, methodName, argument);
+
+            return maybe;
+        } else {
+            return tryFindAndInvokeSingleParameterMethod(instance, methodName, argument);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/flags/SetFromFlag.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/flags/SetFromFlag.java b/core/src/main/java/org/apache/brooklyn/core/util/flags/SetFromFlag.java
new file mode 100644
index 0000000..3b69c05
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/flags/SetFromFlag.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Annotation to indicate that a variable may be set through the use of a named argument,
+ * looking for the name specified here or inferred from the annotated field/argument/object.
+ * <p>
+ * This is used to automate the processing where named arguments are passed in constructors
+ * and other methods, and the values of those named arguments should be transferred to
+ * other known fields/arguments/objects at runtime.
+ * <p>
+ * Fields on a class are typically set from values in a map with a call to
+ * {@link FlagUtils#setFieldsFromFlags(java.util.Map, Object)}.
+ * That method (and related, in the same class) will attend to the arguments here.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SetFromFlag {
+
+    /** the flag (key) which should be used to find the value; if empty defaults to field/argument/object name */
+    String value() default "";
+    
+    /** whether the object should not be changed once set; defaults to false
+     * <p>
+     * this is partially tested for in many routines, but not all;
+     * when nullable=false the testing (when done) is guaranteed.
+     * however if nullable is allowed we do not distinguish between null and unset
+     * so explicitly setting null then setting to a value is not detected as an illegal mutating.
+     */
+    boolean immutable() default false;
+    
+    /** whether the object is required & should not be set to null; defaults to true.
+     * (there is no 'required' parameter, but setting nullable false then invoking 
+     * e.g. {@link FlagUtils#checkRequiredFields(Object)} has the effect of requiring a value)
+     * <p>
+     * code should call that method explicitly to enforce nullable false;
+     * errors are not done during a call to setFieldsFromFlags 
+     * because fields may be initialised in multiple passes.) 
+     * <p>
+     * this is partially tested for in many routines, but not all
+     */
+    boolean nullable() default true;
+
+    /** The default value, if it is not explicitly set.
+     * <p>
+     * The value will be coerced from String where required, for types supported by {@link TypeCoercions}.
+     * <p>
+     * The field will be initialised with its default value on the first call to setFieldsFromFlags
+     * (or related).  (The field will not be initialised if that method is not called.) 
+     */
+    String defaultVal() default "";
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/flags/TypeCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/flags/TypeCoercions.java b/core/src/main/java/org/apache/brooklyn/core/util/flags/TypeCoercions.java
new file mode 100644
index 0000000..2c03620
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/flags/TypeCoercions.java
@@ -0,0 +1,879 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.flags;
+
+import groovy.lang.Closure;
+import groovy.time.TimeDuration;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.GuardedBy;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.api.event.Sensor;
+import org.apache.brooklyn.core.internal.BrooklynInitialization;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.ClosureEntityFactory;
+import brooklyn.entity.basic.ConfigurableEntityFactory;
+import brooklyn.entity.basic.ConfigurableEntityFactoryFromEntityFactory;
+import brooklyn.event.basic.Sensors;
+import brooklyn.util.JavaGroovyEquivalents;
+import brooklyn.util.collections.MutableSet;
+import brooklyn.util.collections.QuorumCheck;
+import brooklyn.util.collections.QuorumCheck.QuorumChecks;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.javalang.Enums;
+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 brooklyn.util.time.Time;
+import brooklyn.util.yaml.Yamls;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+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.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+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);
+    
+    private TypeCoercions() {}
+
+    /** Store the coercion {@link Function functions} in a {@link Table table}. */
+    @GuardedBy("TypeCoercions.class")
+    private static Table<Class, Class, Function> registry = HashBasedTable.create();
+
+    /**
+     * Attempts to coerce {@code value} to {@code targetType}.
+     * <p>
+     * Maintains a registry of adapter functions for type pairs in a {@link Table} which
+     * is searched after checking various strategies, including the following:
+     * <ul>
+     * <li>{@code value.asTargetType()}
+     * <li>{@code TargetType.fromType(value)} (if {@code value instanceof Type})
+     * <li>{@code value.targetTypeValue()} (handy for primitives)
+     * <li>{@code TargetType.valueOf(value)} (for enums)
+     * </ul>
+     * <p>
+     * A default set of adapters will handle most common Java-type coercions
+     * as well as <code>String</code> coercion to:
+     * <ul>
+     * <li> {@link Set}, {@link List}, {@link Map} and similar -- parses as YAML
+     * <li> {@link Date} -- parses using {@link Time#parseDate(String)}
+     * <li> {@link Duration} -- parses using {@link Duration#parse(String)}
+     * </ul>
+     */
+    public static <T> T coerce(Object value, Class<T> targetType) {
+        return coerce(value, TypeToken.of(targetType));
+    }
+
+    /** @see #coerce(Object, Class) */
+    public static <T> Maybe<T> tryCoerce(Object value, TypeToken<T> targetTypeToken) {
+        try {
+            return Maybe.of( coerce(value, targetTypeToken) );
+        } catch (Throwable t) {
+            Exceptions.propagateIfFatal(t);
+            return Maybe.absent(t); 
+        }
+    }
+    
+    /** @see #coerce(Object, Class) */
+    @SuppressWarnings({ "unchecked" })
+    public static <T> T coerce(Object value, TypeToken<T> targetTypeToken) {
+        if (value==null) return null;
+        Class<? super T> targetType = targetTypeToken.getRawType();
+
+        //recursive coercion of parameterized collections and map entries
+        if (targetTypeToken.getType() instanceof ParameterizedType) {
+            if (value instanceof Collection && Collection.class.isAssignableFrom(targetType)) {
+                Type[] arguments = ((ParameterizedType) targetTypeToken.getType()).getActualTypeArguments();
+                if (arguments.length != 1) {
+                    throw new IllegalStateException("Unexpected number of parameters in collection type: " + arguments);
+                }
+                Collection coerced = Lists.newLinkedList();
+                TypeToken<?> listEntryType = TypeToken.of(arguments[0]);
+                for (Object entry : (Iterable<?>) value) {
+                    coerced.add(coerce(entry, listEntryType));
+                }
+                if (Set.class.isAssignableFrom(targetType)) {
+                    return (T) Sets.newLinkedHashSet(coerced);
+                } else {
+                    return (T) Lists.newArrayList(coerced);
+                }
+            } else if (value instanceof Map && Map.class.isAssignableFrom(targetType)) {
+                Type[] arguments = ((ParameterizedType) targetTypeToken.getType()).getActualTypeArguments();
+                if (arguments.length != 2) {
+                    throw new IllegalStateException("Unexpected number of parameters in map type: " + arguments);
+                }
+                Map coerced = Maps.newLinkedHashMap();
+                TypeToken<?> mapKeyType = TypeToken.of(arguments[0]);
+                TypeToken<?> mapValueType = TypeToken.of(arguments[1]);
+                for (Map.Entry entry : ((Map<?,?>) value).entrySet()) {
+                    coerced.put(coerce(entry.getKey(), mapKeyType),  coerce(entry.getValue(), mapValueType));
+                }
+                return (T) Maps.newLinkedHashMap(coerced);
+            }
+        }
+
+        if (targetType.isInstance(value)) return (T) value;
+
+        // TODO use registry first?
+
+        //deal with primitive->primitive casting
+        if (isPrimitiveOrBoxer(targetType) && isPrimitiveOrBoxer(value.getClass())) {
+            // Don't just rely on Java to do its normal casting later; if caller writes
+            // long `l = coerce(new Integer(1), Long.class)` then letting java do its casting will fail,
+            // because an Integer will not automatically be unboxed and cast to a long
+            return castPrimitive(value, (Class<T>)targetType);
+        }
+
+        //deal with string->primitive
+        if (value instanceof String && isPrimitiveOrBoxer(targetType)) {
+            return stringToPrimitive((String)value, (Class<T>)targetType);
+        }
+
+        //deal with primitive->string
+        if (isPrimitiveOrBoxer(value.getClass()) && targetType.equals(String.class)) {
+            return (T) value.toString();
+        }
+
+        //look for value.asType where Type is castable to targetType
+        String targetTypeSimpleName = getVerySimpleName(targetType);
+        if (targetTypeSimpleName!=null && targetTypeSimpleName.length()>0) {
+            for (Method m: value.getClass().getMethods()) {
+                if (m.getName().startsWith("as") && m.getParameterTypes().length==0 &&
+                        targetType.isAssignableFrom(m.getReturnType()) ) {
+                    if (m.getName().equals("as"+getVerySimpleName(m.getReturnType()))) {
+                        try {
+                            return (T) m.invoke(value);
+                        } catch (Exception e) {
+                            throw new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): "+m.getName()+" adapting failed, "+e);
+                        }
+                    }
+                }
+            }
+        }
+        
+        //now look for static TargetType.fromType(Type t) where value instanceof Type  
+        for (Method m: targetType.getMethods()) {
+            if (((m.getModifiers()&Modifier.STATIC)==Modifier.STATIC) && 
+                    m.getName().startsWith("from") && m.getParameterTypes().length==1 &&
+                    m.getParameterTypes()[0].isInstance(value)) {
+                if (m.getName().equals("from"+getVerySimpleName(m.getParameterTypes()[0]))) {
+                    try {
+                        return (T) m.invoke(null, value);
+                    } catch (Exception e) {
+                        throw new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): "+m.getName()+" adapting failed, "+e);
+                    }
+                }
+            }
+        }
+        
+       //ENHANCEMENT could look in type hierarchy of both types for a conversion method...
+        
+        //primitives get run through again boxed up
+        Class boxedT = UNBOXED_TO_BOXED_TYPES.get(targetType);
+        Class boxedVT = UNBOXED_TO_BOXED_TYPES.get(value.getClass());
+        if (boxedT!=null || boxedVT!=null) {
+            try {
+                if (boxedT==null) boxedT=targetType;
+                Object boxedV;
+                if (boxedVT==null) { boxedV = value; }
+                else { boxedV = boxedVT.getConstructor(value.getClass()).newInstance(value); }
+                return (T) coerce(boxedV, boxedT);
+            } catch (Exception e) {
+                throw new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): unboxing failed, "+e);
+            }
+        }
+
+        //for enums call valueOf with the string representation of the value
+        if (targetType.isEnum()) {
+            T result = (T) stringToEnum((Class<Enum>) targetType, null).apply(String.valueOf(value));
+            if (result != null) return result;
+        }
+
+        //now look in registry
+        synchronized (TypeCoercions.class) {
+            Map<Class, Function> adapters = registry.row(targetType);
+            for (Map.Entry<Class, Function> entry : adapters.entrySet()) {
+                if (entry.getKey().isInstance(value)) {
+                    T result = (T) entry.getValue().apply(value);
+                    
+                    // Check if need to unwrap again (e.g. if want List<Integer> and are given a String "1,2,3"
+                    // then we'll have so far converted to List.of("1", "2", "3"). Call recursively.
+                    // First check that value has changed, to avoid stack overflow!
+                    if (!Objects.equal(value, result) && targetTypeToken.getType() instanceof ParameterizedType) {
+                        // Could duplicate check for `result instanceof Collection` etc; but recursive call
+                        // will be fine as if that doesn't match we'll safely reach `targetType.isInstance(value)`
+                        // and just return the result.
+                        return coerce(result, targetTypeToken);
+                    }
+                    return result;
+                }
+            }
+        }
+
+        //not found
+        throw new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): no adapter known");
+    }
+
+    /**
+     * Returns a function that does a type coercion to the given type. For example,
+     * {@code TypeCoercions.function(Double.class)} will return a function that will
+     * coerce its input value to a {@link Double} (or throw a {@link ClassCoercionException}
+     * if that is not possible).
+     */
+    public static <T> Function<Object, T> function(final Class<T> type) {
+        return new CoerceFunction<T>(type);
+    }
+    
+    private static class CoerceFunction<T> implements Function<Object, T> {
+        private final Class<T> type;
+
+        public CoerceFunction(Class<T> type) {
+            this.type = type;
+        }
+        @Override
+        public T apply(Object input) {
+            return coerce(input, type);
+        }
+    }
+
+    /**
+     * Type coercion {@link Function function} for {@link Enum enums}.
+     * <p>
+     * Tries to convert the string to {@link CaseFormat#UPPER_UNDERSCORE} first,
+     * handling all of the different {@link CaseFormat format} possibilites. Failing 
+     * that, it tries a case-insensitive comparison with the valid enum values.
+     * <p>
+     * Returns {@code defaultValue} if the string cannot be converted.
+     *
+     * @see TypeCoercions#coerce(Object, Class)
+     * @see Enum#valueOf(Class, String)
+     */
+    public static <E extends Enum<E>> Function<String, E> stringToEnum(final Class<E> type, @Nullable final E defaultValue) {
+        return new StringToEnumFunction<E>(type, defaultValue);
+    }
+    
+    private static class StringToEnumFunction<E extends Enum<E>> implements Function<String, E> {
+        private final Class<E> type;
+        private final E defaultValue;
+        
+        public StringToEnumFunction(Class<E> type, @Nullable E defaultValue) {
+            this.type = type;
+            this.defaultValue = defaultValue;
+        }
+        @Override
+        public E apply(String input) {
+            Preconditions.checkNotNull(input, "input");
+            List<String> options = ImmutableList.of(
+                    input,
+                    CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, input),
+                    CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, input),
+                    CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, input),
+                    CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, input));
+            for (String value : options) {
+                try {
+                    return Enum.valueOf(type, value);
+                } catch (IllegalArgumentException iae) {
+                    continue;
+                }
+            }
+            Maybe<E> result = Enums.valueOfIgnoreCase(type, input);
+            return (result.isPresent()) ? result.get() : defaultValue;
+        }
+    }
+
+    /**
+     * Sometimes need to explicitly cast primitives, rather than relying on Java casting.
+     * For example, when using generics then type-erasure means it doesn't actually cast,
+     * which causes tests to fail with 0 != 0.0
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T castPrimitive(Object value, Class<T> targetType) {
+        if (value==null) return null;
+        assert isPrimitiveOrBoxer(targetType) : "targetType="+targetType;
+        assert isPrimitiveOrBoxer(value.getClass()) : "value="+targetType+"; valueType="+value.getClass();
+
+        Class<?> sourceWrapType = Primitives.wrap(value.getClass());
+        Class<?> targetWrapType = Primitives.wrap(targetType);
+        
+        // optimization, for when already correct type
+        if (sourceWrapType == targetWrapType) {
+            return (T) value;
+        }
+        
+        if (targetWrapType == Boolean.class) {
+            // only char can be mapped to boolean
+            // (we could say 0=false, nonzero=true, but there is no compelling use case so better
+            // to encourage users to write as boolean)
+            if (sourceWrapType == Character.class)
+                return (T) stringToPrimitive(value.toString(), targetType);
+            
+            throw new ClassCoercionException("Cannot cast "+sourceWrapType+" ("+value+") to "+targetType);
+        } else if (sourceWrapType == Boolean.class) {
+            // boolean can't cast to anything else
+            
+            throw new ClassCoercionException("Cannot cast "+sourceWrapType+" ("+value+") to "+targetType);
+        }
+        
+        // for whole-numbers (where casting to long won't lose anything)...
+        long v = 0;
+        boolean islong = true;
+        if (sourceWrapType == Character.class) {
+            v = (long) ((Character)value).charValue();
+        } else if (sourceWrapType == Byte.class) {
+            v = (long) ((Byte)value).byteValue();
+        } else if (sourceWrapType == Short.class) {
+            v = (long) ((Short)value).shortValue();
+        } else if (sourceWrapType == Integer.class) {
+            v = (long) ((Integer)value).intValue();
+        } else if (sourceWrapType == Long.class) {
+            v = ((Long)value).longValue();
+        } else {
+            islong = false;
+        }
+        if (islong) {
+            if (targetWrapType == Character.class) return (T) Character.valueOf((char)v); 
+            if (targetWrapType == Byte.class) return (T) Byte.valueOf((byte)v); 
+            if (targetWrapType == Short.class) return (T) Short.valueOf((short)v); 
+            if (targetWrapType == Integer.class) return (T) Integer.valueOf((int)v); 
+            if (targetWrapType == Long.class) return (T) Long.valueOf((long)v); 
+            if (targetWrapType == Float.class) return (T) Float.valueOf((float)v); 
+            if (targetWrapType == Double.class) return (T) Double.valueOf((double)v);
+            throw new IllegalStateException("Unexpected: sourceType="+sourceWrapType+"; targetType="+targetWrapType);
+        }
+        
+        // for real-numbers (cast to double)...
+        double d = 0;
+        boolean isdouble = true;
+        if (sourceWrapType == Float.class) {
+            d = (double) ((Float)value).floatValue();
+        } else if (sourceWrapType == Double.class) {
+            d = (double) ((Double)value).doubleValue();
+        } else {
+            isdouble = false;
+        }
+        if (isdouble) {
+            if (targetWrapType == Character.class) return (T) Character.valueOf((char)d); 
+            if (targetWrapType == Byte.class) return (T) Byte.valueOf((byte)d); 
+            if (targetWrapType == Short.class) return (T) Short.valueOf((short)d); 
+            if (targetWrapType == Integer.class) return (T) Integer.valueOf((int)d); 
+            if (targetWrapType == Long.class) return (T) Long.valueOf((long)d); 
+            if (targetWrapType == Float.class) return (T) Float.valueOf((float)d); 
+            if (targetWrapType == Double.class) return (T) Double.valueOf((double)d);
+            throw new IllegalStateException("Unexpected: sourceType="+sourceWrapType+"; targetType="+targetWrapType);
+        } else {
+            throw new IllegalStateException("Unexpected: sourceType="+sourceWrapType+"; targetType="+targetWrapType);
+        }
+    }
+    
+    public static boolean isPrimitiveOrBoxer(Class<?> type) {
+        return Primitives.allPrimitiveTypes().contains(type) || Primitives.allWrapperTypes().contains(type);
+    }
+    
+    @SuppressWarnings("unchecked")
+    public static <T> T stringToPrimitive(String value, Class<T> targetType) {
+        assert Primitives.allPrimitiveTypes().contains(targetType) || Primitives.allWrapperTypes().contains(targetType) : "targetType="+targetType;
+        // If char, then need to do explicit conversion
+        if (targetType == Character.class || targetType == char.class) {
+            if (value.length() == 1) {
+                return (T) (Character) value.charAt(0);
+            } else if (value.length() != 1) {
+                throw new ClassCoercionException("Cannot coerce type String to "+targetType.getCanonicalName()+" ("+value+"): adapting failed");
+            }
+        }
+        value = value.trim();
+        // For boolean we could use valueOf, but that returns false whereas we'd rather throw errors on bad values
+        if (targetType == Boolean.class || targetType == boolean.class) {
+            if ("true".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
+            if ("false".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
+            if ("yes".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
+            if ("no".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
+            if ("t".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
+            if ("f".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
+            if ("y".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
+            if ("n".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
+            
+            throw new ClassCoercionException("Cannot coerce type String to "+targetType.getCanonicalName()+" ("+value+"): adapting failed"); 
+        }
+        
+        // Otherwise can use valueOf reflectively
+        Class<?> wrappedType;
+        if (Primitives.allPrimitiveTypes().contains(targetType)) {
+            wrappedType = Primitives.wrap(targetType);
+        } else {
+            wrappedType = targetType;
+        }
+        
+        try {
+            return (T) wrappedType.getMethod("valueOf", String.class).invoke(null, value);
+        } catch (Exception e) {
+            ClassCoercionException tothrow = new ClassCoercionException("Cannot coerce "+JavaStringEscapes.wrapJavaString(value)+" to "+targetType.getCanonicalName()+" ("+value+"): adapting failed");
+            tothrow.initCause(e);
+            throw tothrow;
+        }
+    }
+    
+    /** returns the simple class name, and for any inner class the portion after the $ */
+    public static String getVerySimpleName(Class c) {
+        String s = c.getSimpleName();
+        if (s.indexOf('$')>=0)
+            s = s.substring(s.lastIndexOf('$')+1);
+        return s;
+    }
+    public static final Map<Class,Class> BOXED_TO_UNBOXED_TYPES = ImmutableMap.<Class,Class>builder().
+            put(Integer.class, Integer.TYPE).
+            put(Long.class, Long.TYPE).
+            put(Boolean.class, Boolean.TYPE).
+            put(Byte.class, Byte.TYPE).
+            put(Double.class, Double.TYPE).
+            put(Float.class, Float.TYPE).
+            put(Character.class, Character.TYPE).
+            put(Short.class, Short.TYPE).
+            build();
+    public static final Map<Class,Class> UNBOXED_TO_BOXED_TYPES = ImmutableMap.<Class,Class>builder().
+            put(Integer.TYPE, Integer.class).
+            put(Long.TYPE, Long.class).
+            put(Boolean.TYPE, Boolean.class).
+            put(Byte.TYPE, Byte.class).
+            put(Double.TYPE, Double.class).
+            put(Float.TYPE, Float.class).
+            put(Character.TYPE, Character.class).
+            put(Short.TYPE, Short.class).
+            build();
+    
+    /** for automatic conversion */
+    public static Object getMatchingConstructor(Class target, Object ...arguments) {
+        Constructor[] cc = target.getConstructors();
+        for (Constructor c: cc) {
+            if (c.getParameterTypes().length != arguments.length)
+                continue;
+            boolean matches = true;
+            Class[] tt = c.getParameterTypes();
+            for (int i=0; i<tt.length; i++) {
+                if (arguments[i]!=null && !tt[i].isInstance(arguments[i])) {
+                    matches=false;
+                    break;
+                }
+            }
+            if (matches) 
+                return c;
+        }
+        return null;
+    }
+
+    /** Registers an adapter for use with type coercion. Returns any old adapter. */
+    public synchronized static <A,B> Function registerAdapter(Class<A> sourceType, Class<B> targetType, Function<? super A,B> fn) {
+        return registry.put(targetType, sourceType, fn);
+    }
+
+    static { BrooklynInitialization.initTypeCoercionStandardAdapters(); }
+    
+    public static void initStandardAdapters() {
+        registerAdapter(CharSequence.class, String.class, new Function<CharSequence,String>() {
+            @Override
+            public String apply(CharSequence input) {
+                return input.toString();
+            }
+        });
+        registerAdapter(byte[].class, String.class, new Function<byte[],String>() {
+            @Override
+            public String apply(byte[] input) {
+                return new String(input);
+            }
+        });
+        registerAdapter(Collection.class, Set.class, new Function<Collection,Set>() {
+            @SuppressWarnings("unchecked")
+            @Override
+            public Set apply(Collection input) {
+                return Sets.newLinkedHashSet(input);
+            }
+        });
+        registerAdapter(Collection.class, List.class, new Function<Collection,List>() {
+            @SuppressWarnings("unchecked")
+            @Override
+            public List apply(Collection input) {
+                return Lists.newArrayList(input);
+            }
+        });
+        registerAdapter(String.class, InetAddress.class, new Function<String,InetAddress>() {
+            @Override
+            public InetAddress apply(String input) {
+                return Networking.getInetAddressWithFixedName(input);
+            }
+        });
+        registerAdapter(String.class, HostAndPort.class, new Function<String,HostAndPort>() {
+            @Override
+            public HostAndPort apply(String input) {
+                return HostAndPort.fromString(input);
+            }
+        });
+        registerAdapter(String.class, UserAndHostAndPort.class, new Function<String,UserAndHostAndPort>() {
+            @Override
+            public UserAndHostAndPort apply(String input) {
+                return UserAndHostAndPort.fromString(input);
+            }
+        });
+        registerAdapter(String.class, Cidr.class, new Function<String,Cidr>() {
+            @Override
+            public Cidr apply(String input) {
+                return new Cidr(input);
+            }
+        });
+        registerAdapter(String.class, URL.class, new Function<String,URL>() {
+            @Override
+            public URL apply(String input) {
+                try {
+                    return new URL(input);
+                } catch (Exception e) {
+                    throw Exceptions.propagate(e);
+                }
+            }
+        });
+        registerAdapter(String.class, URI.class, new Function<String,URI>() {
+            @Override
+            public URI apply(String input) {
+                return URI.create(input);
+            }
+        });
+        registerAdapter(Closure.class, ConfigurableEntityFactory.class, new Function<Closure,ConfigurableEntityFactory>() {
+            @SuppressWarnings("unchecked")
+            @Override
+            public ConfigurableEntityFactory apply(Closure input) {
+                return new ClosureEntityFactory(input);
+            }
+        });
+        @SuppressWarnings({"unused", "deprecation"})
+        Function<?,?> ignoredVarHereToAllowSuppressDeprecationWarning1 = registerAdapter(brooklyn.entity.basic.EntityFactory.class, ConfigurableEntityFactory.class, new Function<brooklyn.entity.basic.EntityFactory,ConfigurableEntityFactory>() {
+            @SuppressWarnings("unchecked")
+            @Override
+            public ConfigurableEntityFactory apply(brooklyn.entity.basic.EntityFactory input) {
+                if (input instanceof ConfigurableEntityFactory) return (ConfigurableEntityFactory)input;
+                return new ConfigurableEntityFactoryFromEntityFactory(input);
+            }
+        });
+        @SuppressWarnings({"unused", "deprecation"})
+        Function<?,?> ignoredVarHereToAllowSuppressDeprecationWarning2 = registerAdapter(Closure.class, brooklyn.entity.basic.EntityFactory.class, new Function<Closure,brooklyn.entity.basic.EntityFactory>() {
+            @SuppressWarnings("unchecked")
+            @Override
+            public brooklyn.entity.basic.EntityFactory apply(Closure input) {
+                return new ClosureEntityFactory(input);
+            }
+        });
+        registerAdapter(Closure.class, Predicate.class, new Function<Closure,Predicate>() {
+            @Override
+            public Predicate<?> apply(final Closure closure) {
+                return new Predicate<Object>() {
+                    @Override public boolean apply(Object input) {
+                        return (Boolean) closure.call(input);
+                    }
+                };
+            }
+        });
+        registerAdapter(Closure.class, Function.class, new Function<Closure,Function>() {
+            @Override
+            public Function apply(final Closure closure) {
+                return new Function() {
+                    @Override public Object apply(Object input) {
+                        return closure.call(input);
+                    }
+                };
+            }
+        });
+        registerAdapter(Object.class, Duration.class, new Function<Object,Duration>() {
+            @Override
+            public Duration apply(final Object input) {
+                return brooklyn.util.time.Duration.of(input);
+            }
+        });
+        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)");
+                return JavaGroovyEquivalents.toTimeDuration(input);
+            }
+        });
+        registerAdapter(TimeDuration.class, Long.class, new Function<TimeDuration,Long>() {
+            @Override
+            public Long apply(final TimeDuration input) {
+                log.warn("deprecated automatic coercion of TimeDuration to Long (set breakpoint in TypeCoercions to inspect, use Duration instead of Long!)");
+                return input.toMilliseconds();
+            }
+        });
+        registerAdapter(Integer.class, AtomicLong.class, new Function<Integer,AtomicLong>() {
+            @Override public AtomicLong apply(final Integer input) {
+                return new AtomicLong(input);
+            }
+        });
+        registerAdapter(Long.class, AtomicLong.class, new Function<Long,AtomicLong>() {
+            @Override public AtomicLong apply(final Long input) {
+                return new AtomicLong(input);
+            }
+        });
+        registerAdapter(String.class, AtomicLong.class, new Function<String,AtomicLong>() {
+            @Override public AtomicLong apply(final String input) {
+                return new AtomicLong(Long.parseLong(input.trim()));
+            }
+        });
+        registerAdapter(Integer.class, AtomicInteger.class, new Function<Integer,AtomicInteger>() {
+            @Override public AtomicInteger apply(final Integer input) {
+                return new AtomicInteger(input);
+            }
+        });
+        registerAdapter(String.class, AtomicInteger.class, new Function<String,AtomicInteger>() {
+            @Override public AtomicInteger apply(final String input) {
+                return new AtomicInteger(Integer.parseInt(input.trim()));
+            }
+        });
+        /** This always returns a {@link Double}, cast as a {@link Number}; 
+         * however primitives and boxers get exact typing due to call in #stringToPrimitive */
+        registerAdapter(String.class, Number.class, new Function<String,Number>() {
+            @Override
+            public Number apply(String input) {
+                return Double.valueOf(input);
+            }
+        });
+        registerAdapter(BigDecimal.class, Double.class, new Function<BigDecimal,Double>() {
+            @Override
+            public Double apply(BigDecimal input) {
+                return input.doubleValue();
+            }
+        });
+        registerAdapter(BigInteger.class, Long.class, new Function<BigInteger,Long>() {
+            @Override
+            public Long apply(BigInteger input) {
+                return input.longValue();
+            }
+        });
+        registerAdapter(BigInteger.class, Integer.class, new Function<BigInteger,Integer>() {
+            @Override
+            public Integer apply(BigInteger input) {
+                return input.intValue();
+            }
+        });
+        registerAdapter(String.class, BigDecimal.class, new Function<String,BigDecimal>() {
+            @Override
+            public BigDecimal apply(String input) {
+                return new BigDecimal(input);
+            }
+        });
+        registerAdapter(Double.class, BigDecimal.class, new Function<Double,BigDecimal>() {
+            @Override
+            public BigDecimal apply(Double input) {
+                return BigDecimal.valueOf(input);
+            }
+        });
+        registerAdapter(String.class, BigInteger.class, new Function<String,BigInteger>() {
+            @Override
+            public BigInteger apply(String input) {
+                return new BigInteger(input);
+            }
+        });
+        registerAdapter(Long.class, BigInteger.class, new Function<Long,BigInteger>() {
+            @Override
+            public BigInteger apply(Long input) {
+                return BigInteger.valueOf(input);
+            }
+        });
+        registerAdapter(Integer.class, BigInteger.class, new Function<Integer,BigInteger>() {
+            @Override
+            public BigInteger apply(Integer input) {
+                return BigInteger.valueOf(input);
+            }
+        });
+        registerAdapter(String.class, Date.class, new Function<String,Date>() {
+            @Override
+            public Date apply(final String input) {
+                return Time.parseDate(input);
+            }
+        });
+        registerAdapter(String.class, Class.class, new Function<String,Class>() {
+            @Override
+            public Class apply(final String input) {
+                try {
+                    return Class.forName(input);
+                } catch (ClassNotFoundException e) {
+                    throw Exceptions.propagate(e);
+                }
+            }
+        });
+        registerAdapter(String.class, AttributeSensor.class, new Function<String,AttributeSensor>() {
+            @Override
+            public AttributeSensor apply(final String input) {
+                Entity entity = BrooklynTaskTags.getContextEntity(Tasks.current());
+                if (entity!=null) {
+                    Sensor<?> result = entity.getEntityType().getSensor(input);
+                    if (result instanceof AttributeSensor) 
+                        return (AttributeSensor) result;
+                }
+                return Sensors.newSensor(Object.class, input);
+            }
+        });
+        registerAdapter(String.class, Sensor.class, new Function<String,Sensor>() {
+            @Override
+            public AttributeSensor apply(final String input) {
+                Entity entity = BrooklynTaskTags.getContextEntity(Tasks.current());
+                if (entity!=null) {
+                    Sensor<?> result = entity.getEntityType().getSensor(input);
+                    if (result != null) 
+                        return (AttributeSensor) result;
+                }
+                return Sensors.newSensor(Object.class, input);
+            }
+        });
+        registerAdapter(String.class, List.class, new Function<String,List>() {
+            @Override
+            public List<String> apply(final String input) {
+                return JavaStringEscapes.unwrapJsonishListIfPossible(input);
+            }
+        });
+        registerAdapter(String.class, Set.class, new Function<String,Set>() {
+            @Override
+            public Set<String> apply(final String input) {
+                return MutableSet.copyOf(JavaStringEscapes.unwrapJsonishListIfPossible(input)).asUnmodifiable();
+            }
+        });
+        registerAdapter(String.class, QuorumCheck.class, new Function<String,QuorumCheck>() {
+            @Override
+            public QuorumCheck apply(final String input) {
+                return QuorumChecks.of(input);
+            }
+        });
+        registerAdapter(Iterable.class, String[].class, new Function<Iterable, String[]>() {
+            @Nullable
+            @Override
+            public String[] apply(@Nullable Iterable list) {
+                if (list == null) return null;
+                String[] result = new String[Iterables.size(list)];
+                int count = 0;
+                for (Object element : list) {
+                    result[count++] = coerce(element, String.class);
+                }
+                return result;
+            }
+        });
+        registerAdapter(Iterable.class, Integer[].class, new Function<Iterable, Integer[]>() {
+            @Nullable
+            @Override
+            public Integer[] apply(@Nullable Iterable list) {
+                if (list == null) return null;
+                Integer[] result = new Integer[Iterables.size(list)];
+                int count = 0;
+                for (Object element : list) {
+                    result[count++] = coerce(element, Integer.class);
+                }
+                return result;
+            }
+        });
+        registerAdapter(Iterable.class, int[].class, new Function<Iterable, int[]>() {
+            @Nullable
+            @Override
+            public int[] apply(@Nullable Iterable list) {
+                if (list == null) return null;
+                int[] result = new int[Iterables.size(list)];
+                int count = 0;
+                for (Object element : list) {
+                    result[count++] = coerce(element, int.class);
+                }
+                return result;
+            }
+        });
+        registerAdapter(String.class, Map.class, new Function<String,Map>() {
+            @Override
+            public Map apply(final String input) {
+                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 {
+                    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;
+                    }
+                    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) );
+                }
+
+                // 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));
+            }
+        });
+    }
+}


[09/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/xstream/CompilerCompatibilityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/xstream/CompilerCompatibilityTest.java b/core/src/test/java/org/apache/brooklyn/core/util/xstream/CompilerCompatibilityTest.java
new file mode 100644
index 0000000..d18da28
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/xstream/CompilerCompatibilityTest.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+
+import org.apache.brooklyn.core.util.xstream.CompilerIndependentOuterClassFieldMapper;
+import org.apache.brooklyn.core.util.xstream.CompilerCompatibilityTest.EnclosingClass.DynamicClass;
+import org.apache.brooklyn.core.util.xstream.CompilerCompatibilityTest.EnclosingClass.DynamicExtendingClass;
+import org.apache.brooklyn.core.util.xstream.CompilerCompatibilityTest.EnclosingClass.EnclosingDynamicClass;
+import org.apache.brooklyn.core.util.xstream.CompilerCompatibilityTest.EnclosingClass.EnclosingDynamicClass.NestedDynamicClass;
+import org.eclipse.jetty.util.log.Log;
+import org.testng.annotations.Test;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.mapper.MapperWrapper;
+
+// To get the generated synthetic fields use the command:
+/*
+   find core/target/test-classes -name CompilerCompatibilityTest\$EnclosingClass\$* | \
+   sed s@core/target/test-classes/@@ | sed 's@.class$@@' | sed s@/@.@g | \
+   xargs javap -classpath core/target/test-classes/ | grep -B1 this
+*/
+@SuppressWarnings("unused")
+public class CompilerCompatibilityTest {
+    private EnclosingClass enclosingClass = new EnclosingClass();
+    private DynamicClass dynamicClass = enclosingClass.new DynamicClass();
+    private DynamicExtendingClass dynamicExtendingClass = enclosingClass.new DynamicExtendingClass();
+    private EnclosingDynamicClass enclosingDynamicClass = enclosingClass.new EnclosingDynamicClass();
+    private NestedDynamicClass nestedDynamicClass = enclosingDynamicClass.new NestedDynamicClass();
+//  NOT SUPPORTED
+//  private DynamicExtendingClassWithDifferentScope dynamicExtendingClassWithDifferentScope =
+//      enclosingClass.new DynamicExtendingClassWithDifferentScope(enclosingDynamicClass);
+
+    public static class EnclosingClass {
+        public class DynamicClass {
+            //Oracle/OpenJDK/IBM generates
+            //final EnclosingClass this$0;
+
+            //eclipse-[groovy-]compiler generates
+            //final EnclosingClass this$1;
+        }
+
+        public class DynamicExtendingClass extends DynamicClass {
+            //The field here masks the parent field
+
+            //Oracle/OpenJDK/IBM generates
+            //final EnclosingClass this$0;
+
+            //eclipse-[groovy-]compiler generates
+            //final EnclosingClass this$1;
+        }
+
+        public class EnclosingDynamicClass {
+            //Oracle/OpenJDK/IBM generates
+            //final EnclosingClass this$0;
+
+            //eclipse-[groovy-]compiler generates
+            //final EnclosingClass this$1;
+
+            public class NestedDynamicClass {
+                //Oracle/OpenJDK/IBM generates
+                //final EnclosingClass this$1;
+
+                //eclipse-[groovy-]compiler generates
+                //final EnclosingClass this$2;
+            }
+        }
+
+//        WARNING: Combination NOT SUPPORTED. Not enough information in XML to deserialize reliably,
+//        having in mind that different compilers could be used for parent/child classes.
+//        If we really need to, we can extend the heuristic to check for field types or assume that
+//        only one compiler was used for the whole class hierarchy covering some more cases.
+//
+//        The problem is that we have two fields with different names, without relation between the
+//        indexes in each one. Changing compilers (or combination of compilers) could change the 
+//        indexes independently in each field. This makes it impossible to infer which field in the xml
+//        maps to which field in the object.
+//        When having identical field names with parent classes XStream will put a defined-in attribute
+//        which makes it possible to deserialize, but it can't be forced to put it in each element.
+//
+        public class DynamicExtendingClassWithDifferentScope extends NestedDynamicClass {
+            //Oracle/OpenJDK/IBM generates
+            //final EnclosingClass this$0;
+
+            //eclipse-[groovy-]compiler generates
+            //final EnclosingClass this$1;
+
+            //constructor required to compile
+            public DynamicExtendingClassWithDifferentScope(EnclosingDynamicClass superEnclosingScope) {
+                superEnclosingScope.super();
+            }
+        }
+    }
+
+    @Test
+    public void testXStreamDeserialize() throws Exception {
+        deserialize("/brooklyn/entity/rebind/compiler_compatibility_eclipse.xml");
+        deserialize("/brooklyn/entity/rebind/compiler_compatibility_oracle.xml");
+    }
+
+    private void deserialize(String inputUrl) throws Exception {
+        XStream xstream = new XStream() {
+            @Override
+            protected MapperWrapper wrapMapper(MapperWrapper next) {
+                return new CompilerIndependentOuterClassFieldMapper(super.wrapMapper(next));
+            }
+        };
+
+        InputStream in = this.getClass().getResourceAsStream(inputUrl);
+        try {
+            Object obj = xstream.fromXML(in);
+            assertNonNullOuterFields(obj);
+        } catch (Exception e) {
+        	System.out.println(e.getMessage());
+        	throw e;
+        } finally {
+            in.close();
+        }
+    }
+
+    private void assertNonNullOuterFields(Object obj) throws Exception {
+        Field[] testInstances = obj.getClass().getDeclaredFields();
+        for (Field instanceField : testInstances) {
+            Object instance = instanceField.get(obj);
+            Class<?> type = instance.getClass();
+            do {
+                for (Field field : type.getDeclaredFields()) {
+                    if (field.getName().startsWith("this$")) {
+                        Object value = field.get(instance);
+                        assertTrue(value != null, field + " should not be null");
+                    }
+                }
+                type = type.getSuperclass();
+            } while (type != null);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/xstream/ConverterTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/xstream/ConverterTestFixture.java b/core/src/test/java/org/apache/brooklyn/core/util/xstream/ConverterTestFixture.java
new file mode 100644
index 0000000..16c7d71
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/xstream/ConverterTestFixture.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import org.testng.Assert;
+
+import com.thoughtworks.xstream.XStream;
+
+public class ConverterTestFixture {
+
+    protected Object assertX(Object obj, String fmt) {
+        XStream xstream = new XStream();
+        registerConverters(xstream);
+        String s1 = xstream.toXML(obj);
+        Assert.assertEquals(s1, fmt);
+        Object out = xstream.fromXML(s1);
+        Assert.assertEquals(out, obj);
+        return out;
+    }
+
+    protected void registerConverters(XStream xstream) {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingConverterTest.java b/core/src/test/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingConverterTest.java
new file mode 100644
index 0000000..65374e4
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingConverterTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import org.apache.brooklyn.core.util.xstream.EnumCaseForgivingConverter;
+import org.testng.annotations.Test;
+
+public class EnumCaseForgivingConverterTest {
+
+    public enum MyEnum {
+        FOO,
+        BaR;
+    }
+    
+    @Test
+    public void testFindsCaseInsensitive() throws Exception {
+        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "FOO"), MyEnum.FOO);
+        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "foo"), MyEnum.FOO);
+        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "Foo"), MyEnum.FOO);
+        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "BAR"), MyEnum.BaR);
+        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "bar"), MyEnum.BaR);
+        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "Bar"), MyEnum.BaR);
+    }
+    
+    @Test
+    public void testFailsIfNoMatch() throws Exception {
+        try {
+            assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "DoesNotExist"), MyEnum.BaR);
+            fail();
+        } catch (IllegalArgumentException e) {
+            if (!e.toString().matches(".*No enum.*MyEnum.DOESNOTEXIST")) throw e;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/xstream/ImmutableListConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/xstream/ImmutableListConverterTest.java b/core/src/test/java/org/apache/brooklyn/core/util/xstream/ImmutableListConverterTest.java
new file mode 100644
index 0000000..1a708ce
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/xstream/ImmutableListConverterTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.net.UnknownHostException;
+
+import org.apache.brooklyn.core.util.xstream.ImmutableListConverter;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.thoughtworks.xstream.XStream;
+
+@Test
+public class ImmutableListConverterTest extends ConverterTestFixture {
+
+    protected void registerConverters(XStream xstream) {
+        super.registerConverters(xstream);
+        xstream.aliasType("ImmutableList", ImmutableList.class);
+        xstream.registerConverter(new ImmutableListConverter(xstream.getMapper()));
+    }
+
+    @Test
+    public void testImmutableEmptyList() throws UnknownHostException {
+        assertX(ImmutableList.of(), "<ImmutableList/>");
+    }
+
+    @Test
+    public void testImmutableSingletonDoubleList() throws UnknownHostException {
+        assertX(ImmutableList.of(1.2d), "<ImmutableList>\n  <double>1.2</double>\n</ImmutableList>");
+    }
+
+    @Test
+    public void testImmutableTwoValStringList() throws UnknownHostException {
+        assertX(ImmutableList.of("a","b"), "<ImmutableList>\n  <string>a</string>\n  <string>b</string>\n</ImmutableList>");
+    }
+
+    @Test
+    public void testImmutableEmptyListStaysImmutable() throws UnknownHostException {
+        Object x = assertX(ImmutableList.of(), "<ImmutableList/>");
+        Assert.assertTrue(x instanceof ImmutableList);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/xstream/InetAddressConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/xstream/InetAddressConverterTest.java b/core/src/test/java/org/apache/brooklyn/core/util/xstream/InetAddressConverterTest.java
new file mode 100644
index 0000000..bec2cc4
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/xstream/InetAddressConverterTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.apache.brooklyn.core.util.xstream.Inet4AddressConverter;
+import org.testng.annotations.Test;
+
+import com.thoughtworks.xstream.XStream;
+
+@Test
+public class InetAddressConverterTest extends ConverterTestFixture {
+
+    protected void registerConverters(XStream xstream) {
+        super.registerConverters(xstream);
+        xstream.registerConverter(new Inet4AddressConverter());
+    }
+
+    public void testFoo1234() throws UnknownHostException {
+        assertX(InetAddress.getByAddress("foo", new byte[] { 1, 2, 3, 4 }), 
+                "<java.net.Inet4Address>foo/1.2.3.4</java.net.Inet4Address>");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/xstream/StringKeyMapConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/xstream/StringKeyMapConverterTest.java b/core/src/test/java/org/apache/brooklyn/core/util/xstream/StringKeyMapConverterTest.java
new file mode 100644
index 0000000..6a15ee9
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/xstream/StringKeyMapConverterTest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.net.UnknownHostException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.xstream.StringKeyMapConverter;
+import org.testng.annotations.Test;
+
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.collect.Maps;
+import com.thoughtworks.xstream.XStream;
+
+@SuppressWarnings({ "rawtypes", "unchecked" })
+@Test
+public class StringKeyMapConverterTest extends ConverterTestFixture {
+
+    protected void registerConverters(XStream xstream) {
+        super.registerConverters(xstream);
+        xstream.alias("map", Map.class, LinkedHashMap.class);
+        xstream.alias("MutableMap", MutableMap.class);
+        xstream.registerConverter(new StringKeyMapConverter(xstream.getMapper()), /* priority */ 10);
+    }
+
+    @Test
+    public void testSimple() throws UnknownHostException {
+        Map m = Maps.newLinkedHashMap();
+        m.put("a", "v");
+        assertX(m, "<map>\n  <a>v</a>\n</map>");
+    }
+    
+    @Test
+    public void testDouble() throws UnknownHostException {
+        Map m = Maps.newLinkedHashMap();
+        m.put("a", "v");
+        m.put("x", 1.0d);
+        assertX(m, "<map>\n  <a>v</a>\n  <x type=\"double\">1.0</x>\n</map>");
+    }
+    
+    @Test
+    public void testEmpty() throws UnknownHostException {
+        Map m = Maps.newLinkedHashMap();
+        assertX(m, "<map/>");
+    }
+    
+    @Test
+    public void testBigSpacedKeyInMutableMap() throws UnknownHostException {
+        Map m = MutableMap.of("a b", "x");
+        assertX(m, "<MutableMap>\n  <entry key=\"a b\">x</entry>\n</MutableMap>");
+    }
+
+    @Test
+    public void testWithNumericKey() throws UnknownHostException {
+        Map m = Maps.newLinkedHashMap();
+        m.put("123", "v");
+        m.put("a", "v2");
+        assertX(m, "<map>\n  <entry key=\"123\">v</entry>\n  <a>v2</a>\n</map>");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/xstream/XmlUtilTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/xstream/XmlUtilTest.java b/core/src/test/java/org/apache/brooklyn/core/util/xstream/XmlUtilTest.java
new file mode 100644
index 0000000..dd97d17
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/xstream/XmlUtilTest.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.core.util.xstream.XmlUtil;
+import org.testng.annotations.Test;
+
+
+public class XmlUtilTest {
+
+    @Test
+    public void testXpath() throws Exception {
+        String xml = "<a><b>myb</b></a>";
+        assertEquals(XmlUtil.xpath(xml, "/a/b[text()]"), "myb");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/location/basic/AbstractLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/AbstractLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/AbstractLocationTest.java
index 089b719..d24ac65 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/AbstractLocationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/AbstractLocationTest.java
@@ -29,6 +29,7 @@ import java.util.Map;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -37,7 +38,6 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.Entities;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/location/basic/LegacyAbstractLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LegacyAbstractLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LegacyAbstractLocationTest.java
index 10e59b3..c53762e 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/LegacyAbstractLocationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LegacyAbstractLocationTest.java
@@ -26,11 +26,11 @@ import static org.testng.Assert.assertTrue;
 import java.util.Collections;
 import java.util.Map;
 
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.collect.ImmutableList;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigTest.java
index 1f265fd..cbf1aaa 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigTest.java
@@ -22,6 +22,7 @@ import static org.testng.Assert.assertEquals;
 
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
@@ -31,7 +32,6 @@ import org.testng.annotations.Test;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigUtilsTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigUtilsTest.java
index e9197a1..1360f6b 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigUtilsTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigUtilsTest.java
@@ -23,11 +23,10 @@ import static org.testng.Assert.assertTrue;
 
 import java.io.File;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import brooklyn.util.config.ConfigBag;
-
 @Test
 public class LocationConfigUtilsTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/location/basic/PortRangesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/PortRangesTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/PortRangesTest.java
index 2b7ab69..de23b55 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/PortRangesTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/PortRangesTest.java
@@ -25,8 +25,7 @@ import java.util.Iterator;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 import org.apache.brooklyn.api.location.PortRange;
-
-import brooklyn.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationIntegrationTest.java
index 68a53c5..19b58fd 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationIntegrationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationIntegrationTest.java
@@ -23,12 +23,14 @@ import java.security.KeyPair;
 import java.util.Arrays;
 import java.util.Map;
 
-import brooklyn.util.internal.ssh.SshTool;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.internal.ssh.sshj.SshjTool;
+import org.apache.brooklyn.core.util.internal.ssh.sshj.SshjTool.SshjToolBuilder;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.Assert;
@@ -38,9 +40,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Entities;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.crypto.SecureKeys;
-import brooklyn.util.internal.ssh.sshj.SshjTool;
-import brooklyn.util.internal.ssh.sshj.SshjTool.SshjToolBuilder;
 
 import com.google.common.base.Preconditions;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationPerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationPerformanceTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationPerformanceTest.java
index 93e33a1..f7f8bf9 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationPerformanceTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationPerformanceTest.java
@@ -25,6 +25,7 @@ import java.util.Map;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
 import org.apache.brooklyn.test.PerformanceTestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,7 +34,6 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.net.Networking;
 import brooklyn.util.stream.Streams;
 import brooklyn.util.text.Identifiers;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationReuseIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationReuseIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationReuseIntegrationTest.java
index 6ba27e7..fd5884c 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationReuseIntegrationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationReuseIntegrationTest.java
@@ -35,11 +35,11 @@ import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.internal.ssh.sshj.SshjTool;
 
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.internal.ssh.sshj.SshjTool;
 import brooklyn.util.net.Networking;
 import brooklyn.util.stream.Streams;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationTest.java
index b4ee5ef..ca51182 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/SshMachineLocationTest.java
@@ -43,6 +43,13 @@ import org.apache.brooklyn.api.location.MachineDetails;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.PortRange;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.file.ArchiveUtils;
+import org.apache.brooklyn.core.util.internal.ssh.RecordingSshTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshException;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.annotations.AfterMethod;
@@ -58,18 +65,11 @@ import brooklyn.entity.effector.EffectorTaskTest;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.file.ArchiveUtils;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.internal.ssh.RecordingSshTool;
-import brooklyn.util.internal.ssh.SshException;
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.net.Networking;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.BasicExecutionContext;
-import brooklyn.util.task.BasicExecutionManager;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Charsets;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/location/cloud/CloudMachineNamerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/cloud/CloudMachineNamerTest.java b/core/src/test/java/org/apache/brooklyn/location/cloud/CloudMachineNamerTest.java
index 9d4acad..fe26a43 100644
--- a/core/src/test/java/org/apache/brooklyn/location/cloud/CloudMachineNamerTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/cloud/CloudMachineNamerTest.java
@@ -22,6 +22,7 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.cloud.names.AbstractCloudMachineNamer;
 import org.apache.brooklyn.location.cloud.names.CloudMachineNamer;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
@@ -35,8 +36,9 @@ import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
+
 import org.apache.brooklyn.location.cloud.names.BasicCloudMachineNamer;
-import brooklyn.util.config.ConfigBag;
+
 import brooklyn.util.text.Strings;
 public class CloudMachineNamerTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/location/cloud/CustomMachineNamerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/cloud/CustomMachineNamerTest.java b/core/src/test/java/org/apache/brooklyn/location/cloud/CustomMachineNamerTest.java
index f0e7d11..0afad81 100644
--- a/core/src/test/java/org/apache/brooklyn/location/cloud/CustomMachineNamerTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/cloud/CustomMachineNamerTest.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.location.cloud;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -29,8 +30,8 @@ import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
+
 import org.apache.brooklyn.location.cloud.names.CustomMachineNamer;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/test/entity/BlockingEntity.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/test/entity/BlockingEntity.java b/core/src/test/java/org/apache/brooklyn/test/entity/BlockingEntity.java
index 3586701..7df9a89 100644
--- a/core/src/test/java/org/apache/brooklyn/test/entity/BlockingEntity.java
+++ b/core/src/test/java/org/apache/brooklyn/test/entity/BlockingEntity.java
@@ -21,10 +21,10 @@ package org.apache.brooklyn.test.entity;
 import java.util.concurrent.CountDownLatch;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * Mock entity that blocks on startup via the {@link CountDownLatch} argument.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/test/entity/TestEntity.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/test/entity/TestEntity.java b/core/src/test/java/org/apache/brooklyn/test/entity/TestEntity.java
index 01b835e..8f0eff4 100644
--- a/core/src/test/java/org/apache/brooklyn/test/entity/TestEntity.java
+++ b/core/src/test/java/org/apache/brooklyn/test/entity/TestEntity.java
@@ -28,6 +28,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.testng.collections.Lists;
 import org.testng.internal.annotations.Sets;
 
@@ -49,7 +50,6 @@ import brooklyn.event.basic.MapConfigKey;
 import brooklyn.event.basic.Sensors;
 import brooklyn.event.basic.SetConfigKey;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * Mock entity for testing.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/resources/brooklyn/entity/rebind/compiler_compatibility_eclipse.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/brooklyn/entity/rebind/compiler_compatibility_eclipse.xml b/core/src/test/resources/brooklyn/entity/rebind/compiler_compatibility_eclipse.xml
index dc972ef..7dd5ac6 100644
--- a/core/src/test/resources/brooklyn/entity/rebind/compiler_compatibility_eclipse.xml
+++ b/core/src/test/resources/brooklyn/entity/rebind/compiler_compatibility_eclipse.xml
@@ -17,25 +17,25 @@
     specific language governing permissions and limitations
     under the License.
 -->
-<brooklyn.util.xstream.CompilerCompatibilityTest>
-  <enclosingClass/>
+<org.apache.brooklyn.core.util.xstream.CompilerCompatibilityTest>
+  <enclosingClass />
   <dynamicClass>
-    <this_-1 reference="../../enclosingClass"/>
+    <this_-1 reference="../../enclosingClass" />
   </dynamicClass>
   <dynamicExtendingClass>
-    <this_-1 defined-in="brooklyn.util.xstream.CompilerCompatibilityTest$EnclosingClass$DynamicClass" reference="../../enclosingClass"/>
-    <this_-1 reference="../../enclosingClass"/>
+    <this_-1 defined-in="org.apache.brooklyn.core.util.xstream.CompilerCompatibilityTest$EnclosingClass$DynamicClass" reference="../../enclosingClass" />
+    <this_-1 reference="../../enclosingClass" />
   </dynamicExtendingClass>
   <enclosingDynamicClass>
-    <this_-1 reference="../../enclosingClass"/>
+    <this_-1 reference="../../enclosingClass" />
   </enclosingDynamicClass>
   <nestedDynamicClass>
-    <this_-2 reference="../../enclosingDynamicClass"/>
+    <this_-2 reference="../../enclosingDynamicClass" />
   </nestedDynamicClass>
 <!-- NOT SUPPORTED
   <dynamicExtendingClassWithDifferentScope>
-    <this_-2 reference="../../enclosingDynamicClass"/>
-    <this_-1 reference="../../enclosingClass"/>
+    <this_-2 reference="../../enclosingDynamicClass" />
+    <this_-1 reference="../../enclosingClass" />
   </dynamicExtendingClassWithDifferentScope>
 -->
-</brooklyn.util.xstream.CompilerCompatibilityTest>
+</org.apache.brooklyn.core.util.xstream.CompilerCompatibilityTest>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/resources/brooklyn/entity/rebind/compiler_compatibility_oracle.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/brooklyn/entity/rebind/compiler_compatibility_oracle.xml b/core/src/test/resources/brooklyn/entity/rebind/compiler_compatibility_oracle.xml
index a01692e..2b49251 100644
--- a/core/src/test/resources/brooklyn/entity/rebind/compiler_compatibility_oracle.xml
+++ b/core/src/test/resources/brooklyn/entity/rebind/compiler_compatibility_oracle.xml
@@ -17,25 +17,25 @@
     specific language governing permissions and limitations
     under the License.
 -->
-<brooklyn.util.xstream.CompilerCompatibilityTest>
-  <enclosingClass/>
+<org.apache.brooklyn.core.util.xstream.CompilerCompatibilityTest>
+  <enclosingClass />
   <dynamicClass>
-    <outer-class reference="../../enclosingClass"/>
+    <outer-class reference="../../enclosingClass" />
   </dynamicClass>
   <dynamicExtendingClass>
-    <outer-class defined-in="brooklyn.util.xstream.CompilerCompatibilityTest$EnclosingClass$DynamicClass" reference="../../enclosingClass"/>
+    <outer-class defined-in="org.apache.brooklyn.core.util.xstream.CompilerCompatibilityTest$EnclosingClass$DynamicClass" reference="../../enclosingClass" />
     <outer-class reference="../../enclosingClass"/>
   </dynamicExtendingClass>
   <enclosingDynamicClass>
-    <outer-class reference="../../enclosingClass"/>
+    <outer-class reference="../../enclosingClass" />
   </enclosingDynamicClass>
   <nestedDynamicClass>
-    <this_-1 reference="../../enclosingDynamicClass"/>
+    <this_-1 reference="../../enclosingDynamicClass" />
   </nestedDynamicClass>
 <!-- NOT SUPPORTED
   <dynamicExtendingClassWithDifferentScope>
-    <this_-1 reference="../../enclosingDynamicClass"/>
-    <outer-class reference="../../enclosingClass"/>
+    <this_-1 reference="../../enclosingDynamicClass" />
+    <outer-class reference="../../enclosingClass" />
   </dynamicExtendingClassWithDifferentScope>
 -->
-</brooklyn.util.xstream.CompilerCompatibilityTest>
+</org.apache.brooklyn.core.util.xstream.CompilerCompatibilityTest>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/GlobalWebFabricExample.java
----------------------------------------------------------------------
diff --git a/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/GlobalWebFabricExample.java b/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/GlobalWebFabricExample.java
index d53cf44..6fd3462 100644
--- a/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/GlobalWebFabricExample.java
+++ b/examples/global-web-fabric/src/main/java/org/apache/brooklyn/demo/GlobalWebFabricExample.java
@@ -28,6 +28,8 @@ import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.catalog.CatalogConfig;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.util.BrooklynMavenArtifacts;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingDnsService;
 import org.apache.brooklyn.entity.proxy.AbstractController;
 import org.apache.brooklyn.entity.webapp.ElasticJavaWebAppService;
@@ -43,11 +45,9 @@ import brooklyn.entity.group.DynamicRegionsFabric;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 
 import org.apache.brooklyn.launcher.BrooklynLauncher;
-
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.BrooklynMavenArtifacts;
+
 import brooklyn.util.CommandLineUtil;
-import brooklyn.util.ResourceUtils;
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/CumulusRDFApplication.java
----------------------------------------------------------------------
diff --git a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/CumulusRDFApplication.java b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/CumulusRDFApplication.java
index 507ed37..da88bff 100644
--- a/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/CumulusRDFApplication.java
+++ b/examples/simple-nosql-cluster/src/main/java/org/apache/brooklyn/demo/CumulusRDFApplication.java
@@ -34,6 +34,10 @@ import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.apache.brooklyn.entity.nosql.cassandra.CassandraDatacenter;
 import org.apache.brooklyn.entity.nosql.cassandra.CassandraFabric;
 import org.apache.brooklyn.entity.nosql.cassandra.CassandraNode;
@@ -63,15 +67,11 @@ import brooklyn.policy.ha.ServiceFailureDetector;
 import brooklyn.policy.ha.ServiceReplacer;
 import brooklyn.policy.ha.ServiceRestarter;
 import brooklyn.util.CommandLineUtil;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.DynamicTasks;
 import brooklyn.util.text.StringEscapes.JavaStringEscapes;
 import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleApp.java
----------------------------------------------------------------------
diff --git a/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleApp.java b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleApp.java
index 5b72a58..441999f 100644
--- a/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleApp.java
+++ b/examples/simple-web-cluster/src/main/java/org/apache/brooklyn/demo/WebClusterDatabaseExampleApp.java
@@ -30,6 +30,8 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.catalog.CatalogConfig;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.BrooklynMavenArtifacts;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
 import org.apache.brooklyn.entity.webapp.DynamicWebAppCluster;
 import org.apache.brooklyn.entity.webapp.JavaWebAppService;
@@ -49,12 +51,10 @@ import brooklyn.entity.java.JavaEntityMethods;
 import brooklyn.event.basic.Sensors;
 
 import org.apache.brooklyn.launcher.BrooklynLauncher;
-
 import org.apache.brooklyn.location.basic.PortRanges;
+
 import brooklyn.policy.autoscaling.AutoScalerPolicy;
-import brooklyn.util.BrooklynMavenArtifacts;
 import brooklyn.util.CommandLineUtil;
-import brooklyn.util.ResourceUtils;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java b/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
index bc8eab1..8327cf1 100644
--- a/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
+++ b/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
@@ -26,6 +26,8 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
 import org.jclouds.compute.config.AdminAccessConfiguration;
 import org.jclouds.scriptbuilder.functions.InitAdminAccess;
 import org.jclouds.scriptbuilder.statements.login.AdminAccess;
@@ -42,8 +44,6 @@ import brooklyn.event.basic.Sensors;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.policy.basic.AbstractPolicy;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.text.Identifiers;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java
index fa71a3a..f447482 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java
@@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,7 +41,6 @@ import org.apache.brooklyn.location.jclouds.pool.MachineSet;
 import org.apache.brooklyn.location.jclouds.pool.ReusableMachineTemplate;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.task.BasicExecutionContext;
 
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java
index a835e2c..8d48cf5 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java
@@ -18,10 +18,9 @@
  */
 package org.apache.brooklyn.location.jclouds;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.jclouds.compute.ComputeService;
 
-import brooklyn.util.config.ConfigBag;
-
 public interface ComputeServiceRegistry {
     
     public ComputeService findComputeService(ConfigBag conf, boolean allowReuse);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java
index f3f39bb..670379d 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java
@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
 import org.jclouds.Constants;
 import org.jclouds.ContextBuilder;
@@ -41,7 +42,6 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.Sanitizer;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Predicates;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java
index e8f63b3..eb9ebfa 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java
@@ -31,6 +31,7 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.location.basic.BasicLocationRegistry;
@@ -41,7 +42,6 @@ import org.apache.brooklyn.location.basic.LocationInternal;
 import org.apache.brooklyn.location.basic.LocationPropertiesFromBrooklynProperties;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.text.KeyValueParser;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
index 2583234..1c1254c 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
@@ -57,6 +57,15 @@ import org.apache.brooklyn.api.location.MachineLocationCustomizer;
 import org.apache.brooklyn.api.location.MachineManagementMixins;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.management.AccessController;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
+import org.apache.brooklyn.core.util.flags.MethodCoercions;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.apache.brooklyn.location.access.PortMapping;
 import org.apache.brooklyn.location.basic.AbstractLocation;
 import org.apache.brooklyn.location.cloud.AbstractCloudMachineProvisioningLocation;
@@ -146,21 +155,13 @@ import org.apache.brooklyn.location.cloud.names.CloudMachineNamer;
 import org.apache.brooklyn.location.jclouds.JcloudsPredicates.NodeInLocation;
 import org.apache.brooklyn.location.jclouds.templates.PortableTemplateBuilder;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.crypto.SecureKeys;
 import brooklyn.util.exceptions.CompoundRuntimeException;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.ReferenceWithError;
-import brooklyn.util.flags.MethodCoercions;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.internal.ssh.ShellTool;
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.javalang.Enums;
 import brooklyn.util.javalang.Reflections;
 import brooklyn.util.net.Cidr;
@@ -177,7 +178,6 @@ import brooklyn.util.text.ByteSizeStrings;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.KeyValueParser;
 import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 import io.cloudsoft.winrm4j.pywinrm.Session;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java
index 9fce742..1922b39 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Semaphore;
 
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
 import org.apache.brooklyn.location.jclouds.networking.JcloudsPortForwarderExtension;
 import org.jclouds.Constants;
 import org.jclouds.compute.domain.Image;
@@ -33,11 +34,11 @@ import org.jclouds.domain.LoginCredentials;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicConfigKey;
+
 import org.apache.brooklyn.location.access.BrooklynAccessUtils;
 import org.apache.brooklyn.location.access.PortForwardManager;
 import org.apache.brooklyn.location.basic.LocationConfigKeys;
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
-import brooklyn.util.internal.ssh.SshTool;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java
index b6cb363..d7cf619 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java
@@ -18,6 +18,7 @@
  */
 package org.apache.brooklyn.location.jclouds;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.domain.TemplateBuilder;
@@ -25,8 +26,6 @@ import org.jclouds.compute.options.TemplateOptions;
 
 import com.google.common.annotations.Beta;
 
-import brooklyn.util.config.ConfigBag;
-
 /**
  * Customization hooks to allow apps to perform specific customisation at each stage of jclouds machine provisioning.
  * For example, an app could attach an EBS volume to an EC2 node, or configure a desired availability zone.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamer.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamer.java
index 4f4587e..e2cd2e1 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamer.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamer.java
@@ -18,8 +18,8 @@
  */
 package org.apache.brooklyn.location.jclouds;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.cloud.names.BasicCloudMachineNamer;
-import brooklyn.util.config.ConfigBag;
 
 public class JcloudsMachineNamer extends BasicCloudMachineNamer {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynProperties.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynProperties.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynProperties.java
index f307041..87e8ce4 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynProperties.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynProperties.java
@@ -20,14 +20,16 @@ package org.apache.brooklyn.location.jclouds;
 
 import java.util.Map;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.basic.DeprecatedKeysMappingBuilder;
 import org.apache.brooklyn.location.basic.LocationPropertiesFromBrooklynProperties;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigUtils;
+
 import org.apache.brooklyn.location.basic.LocationConfigKeys;
-import brooklyn.util.config.ConfigBag;
+
 import brooklyn.util.javalang.JavaClassNames;
 
 import com.google.common.base.Splitter;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsSshMachineLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsSshMachineLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsSshMachineLocation.java
index a2a5735..3ce7892 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsSshMachineLocation.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsSshMachineLocation.java
@@ -34,6 +34,7 @@ import javax.annotation.Nullable;
 import org.apache.brooklyn.api.location.HardwareDetails;
 import org.apache.brooklyn.api.location.MachineDetails;
 import org.apache.brooklyn.api.location.OsDetails;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.location.basic.BasicHardwareDetails;
 import org.apache.brooklyn.location.basic.BasicMachineDetails;
 import org.apache.brooklyn.location.basic.BasicOsDetails;
@@ -55,7 +56,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.net.Networking;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsUtil.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsUtil.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsUtil.java
index e3026df..c981f26 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsUtil.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsUtil.java
@@ -36,6 +36,7 @@ import java.util.concurrent.TimeoutException;
 
 import javax.annotation.Nullable;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.jclouds.Constants;
 import org.jclouds.ContextBuilder;
 import org.jclouds.aws.ec2.AWSEC2Api;
@@ -80,7 +81,6 @@ import com.google.inject.Module;
 
 import brooklyn.entity.basic.Sanitizer;
 import brooklyn.util.collections.MutableList;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.net.Protocol;
 import brooklyn.util.ssh.BashCommands;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsWinRmMachineLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsWinRmMachineLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsWinRmMachineLocation.java
index 753d772..1204856 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsWinRmMachineLocation.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsWinRmMachineLocation.java
@@ -25,13 +25,13 @@ import java.net.UnknownHostException;
 import java.util.Iterator;
 import java.util.Set;
 
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.location.basic.WinRmMachineLocation;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.Template;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.net.Networking;
 
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/SudoTtyFixingCustomizer.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/SudoTtyFixingCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/SudoTtyFixingCustomizer.java
index d48a67c..f7bbc2c 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/SudoTtyFixingCustomizer.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/SudoTtyFixingCustomizer.java
@@ -18,16 +18,15 @@
  */
 package org.apache.brooklyn.location.jclouds;
 
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks.OnFailingTask;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 import org.jclouds.compute.ComputeService;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Preconditions;
 
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.ssh.SshTasks;
-import brooklyn.util.task.ssh.SshTasks.OnFailingTask;
-
 /**
  * Wraps Brooklyn's sudo-tty mitigations in a {@link JcloudsLocationCustomizer} for easy(-ish) consumption
  * in YAML blueprints:

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizer.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizer.java
index 687a486..eb01bb1 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizer.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizer.java
@@ -28,6 +28,7 @@ import java.util.concurrent.ExecutionException;
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.location.geo.LocalhostExternalIpLoader;
 import org.apache.brooklyn.location.jclouds.JcloudsLocation;
 import org.apache.brooklyn.location.jclouds.JcloudsLocationCustomizer;
@@ -45,11 +46,10 @@ import org.jclouds.providers.ProviderMetadata;
 import org.jclouds.providers.Providers;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
 import org.apache.brooklyn.location.jclouds.BasicJcloudsLocationCustomizer;
+
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.net.Cidr;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BlobStoreExpiryTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BlobStoreExpiryTest.java b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BlobStoreExpiryTest.java
index 6260a8c..18211c1 100644
--- a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BlobStoreExpiryTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BlobStoreExpiryTest.java
@@ -28,6 +28,8 @@ import java.util.List;
 import java.util.Map.Entry;
 
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.http.client.HttpClient;
 import org.jclouds.blobstore.BlobStoreContext;
@@ -44,13 +46,13 @@ import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
+
 import org.apache.brooklyn.location.basic.LocationConfigKeys;
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
 import org.apache.brooklyn.location.jclouds.JcloudsLocation;
 import org.apache.brooklyn.location.jclouds.JcloudsUtil;
+
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyLiveTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyLiveTest.java b/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyLiveTest.java
index bf5f87b..75eed65 100644
--- a/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyLiveTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyLiveTest.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
@@ -40,7 +41,6 @@ import brooklyn.entity.BrooklynAppLiveTestSupport;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.text.Identifiers;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyTest.java b/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyTest.java
index af248de..ed4e2ee 100644
--- a/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyTest.java
@@ -29,6 +29,7 @@ import java.util.regex.Pattern;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
@@ -41,8 +42,6 @@ import brooklyn.entity.BrooklynAppUnitTestSupport;
 
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.util.internal.ssh.SshTool;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedLiveTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedLiveTest.java
index 9c70305..1faa83b 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedLiveTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedLiveTest.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.location.jclouds;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.RunNodesException;
 import org.jclouds.compute.domain.NodeMetadata;
@@ -30,8 +31,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.BeforeMethod;
 
-import brooklyn.util.config.ConfigBag;
-
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BailOutJcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BailOutJcloudsLocation.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BailOutJcloudsLocation.java
index c323b7f..b83bce8 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BailOutJcloudsLocation.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BailOutJcloudsLocation.java
@@ -27,6 +27,7 @@ import javax.annotation.Nullable;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.Template;
@@ -41,7 +42,6 @@ import brooklyn.config.BrooklynProperties;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.CompoundRuntimeException;
 import brooklyn.util.exceptions.Exceptions;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java
index f173646..5e535e4 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java
@@ -19,9 +19,11 @@
 package org.apache.brooklyn.location.jclouds;
 
 import brooklyn.config.ConfigKey;
-import brooklyn.util.config.ConfigBag;
+
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.ec2.domain.BlockDeviceMapping;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTest.java
index 83ffb78..de9f950 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTest.java
@@ -32,6 +32,7 @@ import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineLocationCustomizer;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.cloud.names.CustomMachineNamer;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.jclouds.scriptbuilder.domain.OsFamily;
@@ -61,7 +62,6 @@ import org.apache.brooklyn.location.jclouds.JcloudsLocation.UserCreation;
 
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamerTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamerTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamerTest.java
index 5e25a8c..90c1bbb 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamerTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamerTest.java
@@ -18,12 +18,12 @@
  */
 package org.apache.brooklyn.location.jclouds;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.text.Strings;
 
 public class JcloudsMachineNamerTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java
index 6043a09..5f717a3 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java
@@ -27,6 +27,7 @@ import java.io.File;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.OsDetails;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -35,7 +36,6 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.rebind.RebindTestUtils;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationTest.java
index cc52ef2..746ff89 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationTest.java
@@ -26,8 +26,9 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.rebind.RebindTestFixtureWithApp;
+
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.net.HostAndPort;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/enricher/DeltaEnricher.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/enricher/DeltaEnricher.java b/policy/src/main/java/brooklyn/enricher/DeltaEnricher.java
index 39b052f..9b6128c 100644
--- a/policy/src/main/java/brooklyn/enricher/DeltaEnricher.java
+++ b/policy/src/main/java/brooklyn/enricher/DeltaEnricher.java
@@ -25,9 +25,9 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import brooklyn.enricher.basic.AbstractTransformingEnricher;
-import brooklyn.util.flags.TypeCoercions;
 
 /**
  * Converts an absolute sensor into a delta sensor (i.e. the diff between the current and previous value)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/enricher/HttpLatencyDetector.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/enricher/HttpLatencyDetector.java b/policy/src/main/java/brooklyn/enricher/HttpLatencyDetector.java
index 209b45a..884d568 100644
--- a/policy/src/main/java/brooklyn/enricher/HttpLatencyDetector.java
+++ b/policy/src/main/java/brooklyn/enricher/HttpLatencyDetector.java
@@ -32,6 +32,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,7 +45,6 @@ import brooklyn.event.feed.http.HttpFeed;
 import brooklyn.event.feed.http.HttpPollConfig;
 import brooklyn.event.feed.http.HttpValueFunctions;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.javalang.AtomicReferences;
 import brooklyn.util.javalang.Boxing;
 import brooklyn.util.javalang.JavaClassNames;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/enricher/RollingMeanEnricher.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/enricher/RollingMeanEnricher.java b/policy/src/main/java/brooklyn/enricher/RollingMeanEnricher.java
index 0eca706..13648af 100644
--- a/policy/src/main/java/brooklyn/enricher/RollingMeanEnricher.java
+++ b/policy/src/main/java/brooklyn/enricher/RollingMeanEnricher.java
@@ -24,9 +24,9 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.SensorEvent;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.enricher.basic.AbstractTypeTransformingEnricher;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.javalang.JavaClassNames;
 
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/enricher/RollingTimeWindowMeanEnricher.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/enricher/RollingTimeWindowMeanEnricher.java b/policy/src/main/java/brooklyn/enricher/RollingTimeWindowMeanEnricher.java
index 9993d28..b9688d9 100644
--- a/policy/src/main/java/brooklyn/enricher/RollingTimeWindowMeanEnricher.java
+++ b/policy/src/main/java/brooklyn/enricher/RollingTimeWindowMeanEnricher.java
@@ -25,12 +25,12 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.basic.AbstractTypeTransformingEnricher;
 import brooklyn.enricher.basic.YamlRollingTimeWindowMeanEnricher;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.time.Duration;
 



[27/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/http/HttpTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/http/HttpTool.java b/core/src/main/java/org/apache/brooklyn/core/util/http/HttpTool.java
new file mode 100644
index 0000000..43b1aee
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/http/HttpTool.java
@@ -0,0 +1,387 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.http;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.net.URI;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeSocketFactory;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.LaxRedirectStrategy;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.crypto.SslTrustUtils;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.net.URLParamEncoder;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
+public class HttpTool {
+
+    private static final Logger LOG = LoggerFactory.getLogger(HttpTool.class);
+
+    /** Apache HTTP commons utility for trusting all.
+     * <p>
+     * For generic java HTTP usage, see {@link SslTrustUtils#trustAll(java.net.URLConnection)} 
+     * and static constants in the same class. */
+    public static class TrustAllStrategy implements TrustStrategy {
+        @Override
+        public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+            return true;
+        }
+    }
+
+    public static HttpClientBuilder httpClientBuilder() {
+        return new HttpClientBuilder();
+    }
+    
+    public static class HttpClientBuilder {
+        private ClientConnectionManager clientConnectionManager;
+        private HttpParams httpParams;
+        private URI uri;
+        private Integer port;
+        private Credentials credentials;
+        private boolean laxRedirect;
+        private Boolean https;
+        private SchemeSocketFactory socketFactory;
+        private ConnectionReuseStrategy reuseStrategy;
+        private boolean trustAll;
+        private boolean trustSelfSigned;
+
+        public HttpClientBuilder clientConnectionManager(ClientConnectionManager val) {
+            this.clientConnectionManager = checkNotNull(val, "clientConnectionManager");
+            return this;
+        }
+        public HttpClientBuilder httpParams(HttpParams val) {
+            checkState(httpParams == null, "Must not call httpParams multiple times, or after other methods like connectionTimeout");
+            this.httpParams = checkNotNull(val, "httpParams");
+            return this;
+        }
+        public HttpClientBuilder connectionTimeout(Duration val) {
+            if (httpParams == null) httpParams = new BasicHttpParams();
+            long millis = checkNotNull(val, "connectionTimeout").toMilliseconds();
+            if (millis > Integer.MAX_VALUE) throw new IllegalStateException("HttpClient only accepts upto max-int millis for connectionTimeout, but given "+val);
+            HttpConnectionParams.setConnectionTimeout(httpParams, (int) millis);
+            return this;
+        }
+        public HttpClientBuilder socketTimeout(Duration val) {
+            if (httpParams == null) httpParams = new BasicHttpParams();
+            long millis = checkNotNull(val, "socketTimeout").toMilliseconds();
+            if (millis > Integer.MAX_VALUE) throw new IllegalStateException("HttpClient only accepts upto max-int millis for socketTimeout, but given "+val);
+            HttpConnectionParams.setSoTimeout(httpParams, (int) millis);
+            return this;
+        }
+        public HttpClientBuilder reuseStrategy(ConnectionReuseStrategy val) {
+            this.reuseStrategy = checkNotNull(val, "reuseStrategy");
+            return this;
+        }
+        public HttpClientBuilder uri(String val) {
+            return uri(URI.create(checkNotNull(val, "uri")));
+        }
+        public HttpClientBuilder uri(URI val) {
+            this.uri = checkNotNull(val, "uri");
+            if (https == null) https = ("https".equalsIgnoreCase(uri.getScheme()));
+            return this;
+        }
+        public HttpClientBuilder port(int val) {
+            this.port = val;
+            return this;
+        }
+        public HttpClientBuilder credentials(Credentials val) {
+            this.credentials = checkNotNull(val, "credentials");
+            return this;
+        }
+        public void credential(Optional<Credentials> val) {
+            if (val.isPresent()) credentials = val.get();
+        }
+        /** similar to curl --post301 -L` */
+        public HttpClientBuilder laxRedirect(boolean val) {
+            this.laxRedirect = val;
+            return this;
+        }
+        public HttpClientBuilder https(boolean val) {
+            this.https = val;
+            return this;
+        }
+        public HttpClientBuilder socketFactory(SchemeSocketFactory val) {
+            this.socketFactory = checkNotNull(val, "socketFactory");
+            return this;
+        }
+        public HttpClientBuilder trustAll() {
+            this.trustAll = true;
+            return this;
+        }
+        public HttpClientBuilder trustSelfSigned() {
+            this.trustSelfSigned = true;
+            return this;
+        }
+        public HttpClient build() {
+            final DefaultHttpClient httpClient = new DefaultHttpClient(clientConnectionManager);
+            httpClient.setParams(httpParams);
+    
+            // support redirects for POST (similar to `curl --post301 -L`)
+            // http://stackoverflow.com/questions/3658721/httpclient-4-error-302-how-to-redirect
+            if (laxRedirect) {
+                httpClient.setRedirectStrategy(new LaxRedirectStrategy());
+            }
+            if (reuseStrategy != null) {
+                httpClient.setReuseStrategy(reuseStrategy);
+            }
+            if (https == Boolean.TRUE || (uri!=null && uri.toString().startsWith("https:"))) {
+                try {
+                    if (port == null) {
+                        port = (uri != null && uri.getPort() >= 0) ? uri.getPort() : 443;
+                    }
+                    if (socketFactory == null) {
+                        if (trustAll) {
+                            TrustStrategy trustStrategy = new TrustAllStrategy();
+                            X509HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
+                            socketFactory = new SSLSocketFactory(trustStrategy, hostnameVerifier);
+                        } else if (trustSelfSigned) {
+                            TrustStrategy trustStrategy = new TrustSelfSignedStrategy();
+                            X509HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
+                            socketFactory = new SSLSocketFactory(trustStrategy, hostnameVerifier);
+                        } else {
+                            // Using default https scheme: based on default java truststore, which is pretty strict!
+                        }
+                    }
+                    if (socketFactory != null) {
+                        Scheme sch = new Scheme("https", port, socketFactory);
+                        httpClient.getConnectionManager().getSchemeRegistry().register(sch);
+                    }
+                } catch (Exception e) {
+                    LOG.warn("Error setting trust for uri {}", uri);
+                    throw Exceptions.propagate(e);
+                }
+            }
+    
+            // Set credentials
+            if (uri != null && credentials != null) {
+                String hostname = uri.getHost();
+                int port = uri.getPort();
+                httpClient.getCredentialsProvider().setCredentials(new AuthScope(hostname, port), credentials);
+            }
+            if (uri==null && credentials!=null) {
+                LOG.warn("credentials have no effect in builder unless URI for host is specified");
+            }
+    
+            return httpClient;
+        }
+    }
+
+    protected static abstract class HttpRequestBuilder<B extends HttpRequestBuilder<B, R>, R extends HttpRequest> {
+        protected R req;
+        
+        protected HttpRequestBuilder(R req) {
+            this.req = req;
+        }
+        @SuppressWarnings("unchecked")
+        protected B self() {
+            return (B) this;
+        }
+        public B headers(Map<String,String> headers) {
+            if (headers!=null) {
+                for (Map.Entry<String,String> entry : headers.entrySet()) {
+                    req.addHeader(entry.getKey(), entry.getValue());
+                }
+            }
+            return self();
+        }
+        public B headers(Multimap<String,String> headers) {
+            if (headers!=null) {
+                for (Map.Entry<String,String> entry : headers.entries()) {
+                    req.addHeader(entry.getKey(), entry.getValue());
+                }
+            }
+            return self();
+        }
+        public R build() {
+            return req;
+        }
+    }
+    
+    protected static abstract class HttpEntityEnclosingRequestBaseBuilder<B extends HttpEntityEnclosingRequestBaseBuilder<B,R>, R extends HttpEntityEnclosingRequestBase> extends HttpRequestBuilder<B, R> {
+        protected HttpEntityEnclosingRequestBaseBuilder(R req) {
+            super(req);
+        }
+        public B body(byte[] body) {
+            if (body != null) {
+                HttpEntity httpEntity = new ByteArrayEntity(body);
+                req.setEntity(httpEntity);
+            }
+            return self();
+        }
+    }
+    
+    public static class HttpGetBuilder extends HttpRequestBuilder<HttpGetBuilder, HttpGet> {
+        public HttpGetBuilder(URI uri) {
+            super(new HttpGet(uri));
+        }
+    }
+    
+    public static class HttpHeadBuilder extends HttpRequestBuilder<HttpHeadBuilder, HttpHead> {
+        public HttpHeadBuilder(URI uri) {
+            super(new HttpHead(uri));
+        }
+    }
+    
+    public static class HttpDeleteBuilder extends HttpRequestBuilder<HttpDeleteBuilder, HttpDelete> {
+        public HttpDeleteBuilder(URI uri) {
+            super(new HttpDelete(uri));
+        }
+    }
+    
+    public static class HttpPostBuilder extends HttpEntityEnclosingRequestBaseBuilder<HttpPostBuilder, HttpPost> {
+        HttpPostBuilder(URI uri) {
+            super(new HttpPost(uri));
+        }
+    }
+
+    public static class HttpFormPostBuilder extends HttpRequestBuilder<HttpFormPostBuilder, HttpPost> {
+        HttpFormPostBuilder(URI uri) {
+            super(new HttpPost(uri));
+        }
+
+        public HttpFormPostBuilder params(Map<String, String> params) {
+            if (params != null) {
+                Collection<NameValuePair> httpParams = new ArrayList<NameValuePair>(params.size());
+                for (Entry<String, String> param : params.entrySet()) {
+                    httpParams.add(new BasicNameValuePair(param.getKey(), param.getValue()));
+                }
+                req.setEntity(new UrlEncodedFormEntity(httpParams));
+            }
+            return self();
+        }
+    }
+
+    public static class HttpPutBuilder extends HttpEntityEnclosingRequestBaseBuilder<HttpPutBuilder, HttpPut> {
+        public HttpPutBuilder(URI uri) {
+            super(new HttpPut(uri));
+        }
+    }
+    
+    public static HttpToolResponse httpGet(HttpClient httpClient, URI uri, Map<String,String> headers) {
+        HttpGet req = new HttpGetBuilder(uri).headers(headers).build();
+        return execAndConsume(httpClient, req);
+    }
+
+    public static HttpToolResponse httpPost(HttpClient httpClient, URI uri, Map<String,String> headers, byte[] body) {
+        HttpPost req = new HttpPostBuilder(uri).headers(headers).body(body).build();
+        return execAndConsume(httpClient, req);
+    }
+
+    public static HttpToolResponse httpPut(HttpClient httpClient, URI uri, Map<String, String> headers, byte[] body) {
+        HttpPut req = new HttpPutBuilder(uri).headers(headers).body(body).build();
+        return execAndConsume(httpClient, req);
+    }
+
+    public static HttpToolResponse httpPost(HttpClient httpClient, URI uri, Map<String,String> headers, Map<String, String> params) {
+        HttpPost req = new HttpFormPostBuilder(uri).headers(headers).params(params).build();
+        return execAndConsume(httpClient, req);
+    }
+
+    public static HttpToolResponse httpDelete(HttpClient httpClient, URI uri, Map<String,String> headers) {
+        HttpDelete req = new HttpDeleteBuilder(uri).headers(headers).build();
+        return execAndConsume(httpClient, req);
+    }
+    
+    public static HttpToolResponse httpHead(HttpClient httpClient, URI uri, Map<String,String> headers) {
+        HttpHead req = new HttpHeadBuilder(uri).headers(headers).build();
+        return execAndConsume(httpClient, req);
+    }
+    
+    public static HttpToolResponse execAndConsume(HttpClient httpClient, HttpUriRequest req) {
+        long startTime = System.currentTimeMillis();
+        try {
+            HttpResponse httpResponse = httpClient.execute(req);
+            
+            try {
+                return new HttpToolResponse(httpResponse, startTime);
+            } finally {
+                EntityUtils.consume(httpResponse.getEntity());
+            }
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+    public static boolean isStatusCodeHealthy(int code) { return (code>=200 && code<=299); }
+
+    public static String toBasicAuthorizationValue(UsernamePasswordCredentials credentials) {
+        return "Basic "+Base64.encodeBase64String( (credentials.getUserName()+":"+credentials.getPassword()).getBytes() );
+    }
+
+    public static String encodeUrlParams(Map<?,?> data) {
+        if (data==null) return "";
+        Iterable<String> args = Iterables.transform(data.entrySet(), 
+            new Function<Map.Entry<?,?>,String>() {
+            @Override public String apply(Map.Entry<?,?> entry) {
+                Object k = entry.getKey();
+                Object v = entry.getValue();
+                return URLParamEncoder.encode(Strings.toString(k)) + (v != null ? "=" + URLParamEncoder.encode(Strings.toString(v)) : "");
+            }
+        });
+        return Joiner.on("&").join(args);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/http/HttpToolResponse.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/http/HttpToolResponse.java b/core/src/main/java/org/apache/brooklyn/core/util/http/HttpToolResponse.java
new file mode 100644
index 0000000..97e7793
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/http/HttpToolResponse.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.http;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.event.feed.http.HttpPollValue;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.io.ByteStreams;
+
+public class HttpToolResponse implements HttpPollValue {
+
+    private static final Logger log = LoggerFactory.getLogger(HttpToolResponse.class);
+    
+    private final Object mutex = new Object();
+    private final HttpResponse response;
+    private final long startTime;
+    private final long durationMillisOfFirstResponse;
+    private final long durationMillisOfFullContent;
+    private int responseCode;
+    private String reasonPhrase;
+    private Map<String,List<String>> headerLists;
+    private byte[] content;
+
+
+    public HttpToolResponse(HttpResponse response, long startTime) {
+        this.response = response;
+        this.startTime = startTime; 
+        
+        try {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            HttpEntity entity = response.getEntity();
+            if (entity != null) {
+                entity.getContentLength();
+                durationMillisOfFirstResponse = Duration.sinceUtc(startTime).toMilliseconds();
+
+                ByteStreams.copy(entity.getContent(), out);
+                content = out.toByteArray();
+
+                entity.getContentLength();
+            } else {
+                durationMillisOfFirstResponse = Duration.sinceUtc(startTime).toMilliseconds();
+                content = new byte[0];
+            }
+            durationMillisOfFullContent = Duration.sinceUtc(startTime).toMilliseconds();
+            if (log.isTraceEnabled())
+                log.trace("HttpPollValue latency "+Time.makeTimeStringRounded(durationMillisOfFirstResponse)+" / "+Time.makeTimeStringRounded(durationMillisOfFullContent)+", content size "+content.length);
+        } catch (IOException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    public HttpToolResponse(int responseCode, Map<String,? extends List<String>> headers, byte[] content,
+            long startTime, long durationMillisOfFirstResponse, long durationMillisOfFullContent) {
+        this.response = null;
+        this.responseCode = responseCode;
+        this.headerLists = ImmutableMap.copyOf(headers);
+        this.content = content;
+        this.startTime = startTime;
+        this.durationMillisOfFirstResponse = durationMillisOfFirstResponse;
+        this.durationMillisOfFullContent = durationMillisOfFullContent;
+    }
+    
+    public int getResponseCode() {
+        synchronized (mutex) {
+            if (responseCode == 0) {
+                responseCode = response.getStatusLine().getStatusCode();
+            }
+        }
+        return responseCode;
+    }
+
+    public String getReasonPhrase() {
+        synchronized (mutex) {
+            if (reasonPhrase == null) {
+                reasonPhrase = response.getStatusLine().getReasonPhrase();
+            }
+        }
+        return reasonPhrase;
+    }
+
+    /** returns the timestamp (millis since 1970) when this request was started */ 
+    public long getStartTime() {
+        return startTime;
+    }
+    
+    /** returns latency, in milliseconds, if value was initialized with a start time */
+    public long getLatencyFullContent() {
+        return durationMillisOfFullContent;
+    }
+    
+    /** returns latency, in milliseconds, before response started coming in */
+    public long getLatencyFirstResponse() {
+        return durationMillisOfFirstResponse;
+    }
+    
+    public Map<String, List<String>> getHeaderLists() {
+        synchronized (mutex) {
+            if (headerLists == null) {
+                Map<String, List<String>> headerListsMutable = Maps.newLinkedHashMap();
+                for (Header header : response.getAllHeaders()) {
+                    List<String> vals = headerListsMutable.get(header.getName());
+                    if (vals == null) {
+                        vals = new ArrayList<String>();
+                        headerListsMutable.put(header.getName(), vals);
+                    }
+                    vals.add(header.getValue());
+                }
+                headerLists = Collections.unmodifiableMap(headerListsMutable);
+            }
+        }
+        return headerLists;
+    }
+    
+    public byte[] getContent() {
+        synchronized (mutex) {
+            if (content == null) {
+                InputStream in = null;
+                try {
+                    in = response.getEntity().getContent();
+                    ByteArrayOutputStream out = new ByteArrayOutputStream();
+                    ByteStreams.copy(in, out);
+                    content = out.toByteArray();
+                } catch (IOException e) {
+                    throw Throwables.propagate(e);
+                } finally {
+                    Streams.closeQuietly(in);
+                }
+            }
+        }
+        return content;
+    }
+
+    public String getContentAsString() {
+        return new String(getContent());
+    }
+    
+    public Maybe<HttpResponse> getResponse() {
+        return Maybe.fromNullable(response);
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(getClass())
+                .add("responseCode", responseCode)
+                .toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/ConfigKeySelfExtracting.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/ConfigKeySelfExtracting.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/ConfigKeySelfExtracting.java
new file mode 100644
index 0000000..a1d85ca
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/ConfigKeySelfExtracting.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.management.ExecutionContext;
+
+import brooklyn.config.ConfigKey;
+
+/** Interface for resolving key values; typically implemented by the config key,
+ * but discouraged for external usage.
+ */
+public interface ConfigKeySelfExtracting<T> extends ConfigKey<T> {
+
+    /**
+     * Extracts the value for this config key from the given map.
+     */
+    T extractValue(Map<?,?> configMap, ExecutionContext exec);
+
+    /**
+     * @return True if there is an entry in the configMap that could be extracted
+     */
+    boolean isSet(Map<?,?> configMap);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/Repeater.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/Repeater.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/Repeater.java
new file mode 100644
index 0000000..39e79da
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/Repeater.java
@@ -0,0 +1,370 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.JavaGroovyEquivalents;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.internal.TimeExtras;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Callables;
+
+/**
+ * Simple DSL to repeat a fragment of code periodically until a condition is satisfied.
+ *
+ * In its simplest case, it is passed two {@link groovy.lang.Closure}s / {@link Callable} - 
+ * the first is executed, then the second. If the second closure returns false, the loop
+ * is repeated; if true, it finishes. Further customization can be applied to set the period 
+ * between loops and place a maximum limit on how long the loop should run for.
+ * <p>
+ * It is configured in a <em>fluent</em> manner. For example, in Groovy:
+ * <pre>
+ * {@code
+ * Repeater.create("Wait until the Frobnitzer is ready")
+ *     .repeat {
+ *         status = frobnitzer.getStatus()
+ *     }
+ *     .until {
+ *         status == "Ready" || status == "Failed"
+ *     }
+ *     .limitIterationsTo(30)
+ *     .run()
+ * }
+ * </pre>
+ * 
+ * Or in Java:
+ * <pre>
+ * {@code
+ * Repeater.create("Wait until the Frobnitzer is ready")
+ *     .until(new Callable<Boolean>() {
+ *              public Boolean call() {
+ *                  String status = frobnitzer.getStatus()
+ *                  return "Ready".equals(status) || "Failed".equals(status);
+ *              }})
+ *     .limitIterationsTo(30)
+ *     .run()
+ * }
+ * </pre>
+ * 
+ * @deprecated since 0.7.0, use {@link brooklyn.util.repeat.Repeater} instead
+ */
+@Deprecated
+public class Repeater {
+    
+    // TODO Was converted to Java, from groovy. Needs thorough review and improvements
+    // to use idiomatic java
+    
+    private static final Logger log = LoggerFactory.getLogger(Repeater.class);
+
+    static { TimeExtras.init(); }
+
+    @SetFromFlag
+    private String description;
+    private Callable<?> body = Callables.returning(null);
+    private Callable<Boolean> exitCondition;
+    @SetFromFlag
+    private Long period = null;
+    @SetFromFlag("timeout")
+    private Long durationLimit = null;
+    private int iterationLimit = 0;
+    private boolean rethrowException = false;
+    private boolean rethrowExceptionImmediately = false;
+    private boolean warnOnUnRethrownException = true;
+
+    public Repeater() {
+        this(MutableMap.of(), null);
+    }
+
+    public Repeater(Map<?,?> flags) {
+        this(flags, null);
+    }
+
+    public Repeater(String description) {
+        this(MutableMap.of(), description);
+    }
+    
+    /**
+     * Construct a new instance of Repeater.
+     *
+     * @param flags       can include period, timeout, description
+     * @param description a description of the operation that will appear in debug logs.
+     */
+    public Repeater(Map<?,?> flags, String description) {
+        setFromFlags(flags);
+        this.description = JavaGroovyEquivalents.elvis(description, this.description, "Repeater");
+    }
+
+    public void setFromFlags(Map<?,?> flags) {
+        FlagUtils.setFieldsFromFlags(flags, this);
+    }
+    
+    public static Repeater create() {
+        return create(MutableMap.of());
+    }
+    public static Repeater create(Map<?,?> flags) {
+        return create(flags, null);
+    }
+    public static Repeater create(String description) {
+        return create(MutableMap.of(), description);
+    }
+    public static Repeater create(Map<?,?> flags, String description) {
+        return new Repeater(flags, description);
+    }
+
+    /**
+     * Sets the main body of the loop to be a no-op.
+     * 
+     * @return {@literal this} to aid coding in a fluent style.
+     */
+    public Repeater repeat() {
+        return repeat(Callables.returning(null));
+    }
+    
+    /**
+     * Sets the main body of the loop.
+     *
+     * @param body a closure or other Runnable that is executed in the main body of the loop.
+     * @return {@literal this} to aid coding in a fluent style.
+     */
+    public Repeater repeat(Runnable body) {
+        checkNotNull(body, "body must not be null");
+        this.body = (body instanceof Callable) ? (Callable<?>)body : Executors.callable(body);
+        return this;
+    }
+    
+    /**
+     * Sets the main body of the loop.
+     *
+     * @param body a closure or other Callable that is executed in the main body of the loop.
+     * @return {@literal this} to aid coding in a fluent style.
+     */
+    public Repeater repeat(Callable<?> body) {
+        checkNotNull(body, "body must not be null");
+        this.body = body;
+        return this;
+    }
+
+    /**
+     * Set how long to wait between loop iterations.
+     *
+     * @param period how long to wait between loop iterations.
+     * @param unit the unit of measurement of the period.
+     * @return {@literal this} to aid coding in a fluent style.
+     */
+    public Repeater every(long period, TimeUnit unit) {
+        Preconditions.checkArgument(period > 0, "period must be positive: %s", period);
+        checkNotNull(unit, "unit must not be null");
+        this.period = unit.toMillis(period);
+        return this;
+    }
+
+    /**
+     * @see #every(long, TimeUnit)
+     */
+    public Repeater every(Duration duration) {
+        Preconditions.checkNotNull(duration, "duration must not be null");
+        Preconditions.checkArgument(duration.toMilliseconds()>0, "period must be positive: %s", duration);
+        this.period = duration.toMilliseconds();
+        return this;
+    }
+    
+    public Repeater every(groovy.time.Duration duration) {
+        return every(Duration.of(duration));
+    }
+
+    /**
+     * @see #every(long, TimeUnit)
+     * @deprecated specify unit
+     */
+    public Repeater every(long duration) {
+        return every(duration, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Set code fragment that tests if the loop has completed.
+     *
+     * @param exitCondition a closure or other Callable that returns a boolean. If this code returns {@literal true} then the
+     * loop will stop executing.
+     * @return {@literal this} to aid coding in a fluent style.
+     */
+    public Repeater until(Callable<Boolean> exitCondition) {
+        Preconditions.checkNotNull(exitCondition, "exitCondition must not be null");
+        this.exitCondition = exitCondition;
+        return this;
+    }
+
+    /**
+     * If the exit condition check throws an exception, it will be recorded and the last exception will be thrown on failure.
+     *
+     * @return {@literal this} to aid coding in a fluent style.
+     */
+    public Repeater rethrowException() {
+        this.rethrowException = true;
+        return this;
+    }
+
+    /**
+     * If the repeated body or the exit condition check throws an exception, then propagate that exception immediately.
+     *
+     * @return {@literal this} to aid coding in a fluent style.
+     */
+    public Repeater rethrowExceptionImmediately() {
+        this.rethrowExceptionImmediately = true;
+        return this;
+    }
+
+    public Repeater suppressWarnings() {
+        this.warnOnUnRethrownException = false;
+        return this;
+    }
+
+    /**
+     * Set the maximum number of iterations.
+     *
+     * The loop will exit if the condition has not been satisfied after this number of iterations.
+     *
+     * @param iterationLimit the maximum number of iterations.
+     * @return {@literal this} to aid coding in a fluent style.
+     */
+    public Repeater limitIterationsTo(int iterationLimit) {
+        Preconditions.checkArgument(iterationLimit > 0, "iterationLimit must be positive: %s", iterationLimit);
+        this.iterationLimit = iterationLimit;
+        return this;
+    }
+
+    /**
+     * Set the amount of time to wait for the condition.
+     * The repeater will wait at least this long for the condition to be true,
+     * and will exit soon after even if the condition is false.
+     *
+     * @param deadline the time that the loop should wait.
+     * @param unit the unit of measurement of the period.
+     * @return {@literal this} to aid coding in a fluent style.
+     */
+    public Repeater limitTimeTo(long deadline, TimeUnit unit) {
+        Preconditions.checkArgument(deadline > 0, "deadline must be positive: %s", deadline);
+        Preconditions.checkNotNull(unit, "unit must not be null");
+        this.durationLimit = unit.toMillis(deadline);
+        return this;
+    }
+
+    /**
+     * @see #limitTimeTo(long, TimeUnit)
+     */
+    public Repeater limitTimeTo(Duration duration) {
+        Preconditions.checkNotNull(duration, "duration must not be null");
+        Preconditions.checkArgument(duration.toMilliseconds() > 0, "deadline must be positive: %s", duration);
+        this.durationLimit = duration.toMilliseconds();
+        return this;
+    }
+
+    /**
+     * Run the loop.
+     *
+     * @return true if the exit condition was satisfied; false if the loop terminated for any other reason.
+     */
+    public boolean run() {
+        Preconditions.checkState(body != null, "repeat() method has not been called to set the body");
+        Preconditions.checkState(exitCondition != null, "until() method has not been called to set the exit condition");
+        Preconditions.checkState(period != null, "every() method has not been called to set the loop period time units");
+
+        Throwable lastError = null;
+        int iterations = 0;
+        long endTime = -1;
+        if (durationLimit != null) {
+            endTime = System.currentTimeMillis() + durationLimit;
+        }
+
+        while (true) {
+            iterations++;
+
+            try {
+                body.call();
+            } catch (Exception e) {
+                log.warn(description, e);
+                if (rethrowExceptionImmediately) throw Exceptions.propagate(e);
+            }
+
+            boolean done = false;
+            try {
+                lastError = null;
+                done = exitCondition.call();
+            } catch (Exception e) {
+                if (log.isDebugEnabled()) log.debug(description, e);
+                lastError = e;
+                if (rethrowExceptionImmediately) throw Exceptions.propagate(e);
+            }
+            if (done) {
+                if (log.isDebugEnabled()) log.debug("{}: condition satisfied", description);
+                return true;
+            } else {
+                if (log.isDebugEnabled()) {
+                    String msg = String.format("%s: unsatisfied during iteration %s %s", description, iterations,
+                            (iterationLimit > 0 ? "(max "+iterationLimit+" attempts)" : "") + 
+                            (endTime > 0 ? "("+Time.makeTimeStringRounded(endTime - System.currentTimeMillis())+" remaining)" : ""));
+                    if (iterations == 1) {
+                        log.debug(msg);
+                    } else {
+                        log.trace(msg);
+                    }
+                }
+            }
+
+            if (iterationLimit > 0 && iterations == iterationLimit) {
+                if (log.isDebugEnabled()) log.debug("{}: condition not satisfied and exceeded iteration limit", description);
+                if (rethrowException && lastError != null) {
+                    log.warn("{}: error caught checking condition (rethrowing): {}", description, lastError.getMessage());
+                    throw Exceptions.propagate(lastError);
+                }
+                if (warnOnUnRethrownException && lastError != null)
+                    log.warn("{}: error caught checking condition: {}", description, lastError.getMessage());
+                return false;
+            }
+
+            if (endTime > 0) {
+                if (System.currentTimeMillis() > endTime) {
+                    if (log.isDebugEnabled()) log.debug("{}: condition not satisfied and deadline {} passed", 
+                            description, Time.makeTimeStringRounded(endTime - System.currentTimeMillis()));
+                    if (rethrowException && lastError != null) {
+                        log.error("{}: error caught checking condition: {}", description, lastError.getMessage());
+                        throw Exceptions.propagate(lastError);
+                    }
+                    return false;
+                }
+            }
+
+            Time.sleep(period);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/BackoffLimitedRetryHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/BackoffLimitedRetryHandler.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/BackoffLimitedRetryHandler.java
new file mode 100644
index 0000000..b8d6eac
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/BackoffLimitedRetryHandler.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.exceptions.Exceptions;
+
+/**
+ * Allow replayable request to be retried a limited number of times, and impose an exponential back-off
+ * delay before returning.
+ * <p>
+ * Copied and modified from jclouds; original author was James Murty
+ */
+public class BackoffLimitedRetryHandler {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BackoffLimitedRetryHandler.class);
+
+    private final int retryCountLimit;
+
+    private final long delayStart;
+
+    public BackoffLimitedRetryHandler() {
+        this(5, 50L);
+    }
+    
+    public BackoffLimitedRetryHandler(int retryCountLimit, long delayStart) {
+        this.retryCountLimit = retryCountLimit;
+        this.delayStart = delayStart;
+    }
+    
+    public void imposeBackoffExponentialDelay(int failureCount, String commandDescription) {
+        imposeBackoffExponentialDelay(delayStart, 2, failureCount, retryCountLimit, commandDescription);
+    }
+
+    public void imposeBackoffExponentialDelay(long period, int pow, int failureCount, int max, String commandDescription) {
+        imposeBackoffExponentialDelay(period, period * 10l, pow, failureCount, max, commandDescription);
+    }
+
+    public void imposeBackoffExponentialDelay(long period,
+            long maxPeriod,
+            int pow,
+            int failureCount,
+            int max,
+            String commandDescription) {
+        long delayMs = (long) (period * Math.pow(failureCount, pow));
+        delayMs = (delayMs > maxPeriod) ? maxPeriod : delayMs;
+        if (LOG.isDebugEnabled()) LOG.debug("Retry {}/{}: delaying for {} ms: {}", 
+                new Object[] {failureCount, max, delayMs, commandDescription});
+        try {
+            Thread.sleep(delayMs);
+        } catch (InterruptedException e) {
+            Exceptions.propagate(e);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/ShellAbstractTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/ShellAbstractTool.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/ShellAbstractTool.java
new file mode 100644
index 0000000..90dcffa
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/ShellAbstractTool.java
@@ -0,0 +1,442 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.io.ByteArrayInputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.os.Os;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.text.Identifiers;
+import brooklyn.util.text.StringEscapes.BashStringEscapes;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+
+public abstract class ShellAbstractTool implements ShellTool {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ShellAbstractTool.class);
+
+    protected final File localTempDir;
+
+    public ShellAbstractTool(String localTempDir) {
+        this(localTempDir == null ? null : new File(Os.tidyPath(localTempDir)));
+    }
+    
+    public ShellAbstractTool(File localTempDir) {
+        if (localTempDir == null) {
+            localTempDir = new File(Os.tmp(), "tmpssh-"+Os.user());
+            if (!localTempDir.exists()) localTempDir.mkdir();
+            Os.deleteOnExitEmptyParentsUpTo(localTempDir, new File(Os.tmp()));
+        }
+        this.localTempDir = localTempDir;
+    }
+    
+    public ShellAbstractTool() {
+        this((File)null);
+    }
+    
+    protected static void warnOnDeprecated(Map<String, ?> props, String deprecatedKey, String correctKey) {
+        if (props.containsKey(deprecatedKey)) {
+            if (correctKey != null && props.containsKey(correctKey)) {
+                Object dv = props.get(deprecatedKey);
+                Object cv = props.get(correctKey);
+                if (!Objects.equal(cv, dv)) {
+                    LOG.warn("SshTool detected deprecated key '"+deprecatedKey+"' with different value ("+dv+") "+
+                            "than new key '"+correctKey+"' ("+cv+"); ambiguous which will be used");
+                } else {
+                    // ignore, the deprecated key populated for legacy reasons
+                }
+            } else {
+                Object dv = props.get(deprecatedKey);
+                LOG.warn("SshTool detected deprecated key '"+deprecatedKey+"' used, with value ("+dv+")");     
+            }
+        }
+    }
+
+    protected static Boolean hasVal(Map<String,?> map, ConfigKey<?> keyC) {
+        String key = keyC.getName();
+        return map.containsKey(key);
+    }
+    
+    protected static <T> T getMandatoryVal(Map<String,?> map, ConfigKey<T> keyC) {
+        String key = keyC.getName();
+        checkArgument(map.containsKey(key), "must contain key '"+keyC+"'");
+        return TypeCoercions.coerce(map.get(key), keyC.getTypeToken());
+    }
+    
+    public static <T> T getOptionalVal(Map<String,?> map, ConfigKey<T> keyC) {
+        if (keyC==null) return null;
+        String key = keyC.getName();
+        if (map!=null && map.containsKey(key) && map.get(key) != null) {
+            return TypeCoercions.coerce(map.get(key), keyC.getTypeToken());
+        } else {
+            return keyC.getDefaultValue();
+        }
+    }
+
+    /** returns the value of the key if specified, otherwise defaultValue */
+    protected static <T> T getOptionalVal(Map<String,?> map, ConfigKey<T> keyC, T defaultValue) {
+        String key = keyC.getName();
+        if (map!=null && map.containsKey(key) && map.get(key) != null) {
+            return TypeCoercions.coerce(map.get(key), keyC.getTypeToken());
+        } else {
+            return defaultValue;
+        }
+    }
+
+    protected void closeWhispering(Closeable closeable, Object context) {
+        closeWhispering(closeable, this, context);
+    }
+    
+    /**
+     * Similar to Guava's Closeables.closeQuitely, except logs exception at debug with context in message.
+     */
+    protected static void closeWhispering(Closeable closeable, Object context1, Object context2) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (IOException e) {
+                if (LOG.isDebugEnabled()) {
+                    String msg = String.format("<< exception during close, for %s -> %s (%s); continuing.", 
+                            context1, context2, closeable);
+                    if (LOG.isTraceEnabled())
+                        LOG.debug(msg + ": " + e);
+                    else
+                        LOG.trace(msg, e);
+                }
+            }
+        }
+    }
+
+    protected File writeTempFile(InputStream contents) {
+        File tempFile = Os.writeToTempFile(contents, localTempDir, "sshcopy", "data");
+        tempFile.setReadable(false, false);
+        tempFile.setReadable(true, true);
+        tempFile.setWritable(false);
+        tempFile.setExecutable(false);
+        return tempFile;
+    }
+
+    protected File writeTempFile(String contents) {
+        return writeTempFile(contents.getBytes());
+    }
+
+    protected File writeTempFile(byte[] contents) {
+        return writeTempFile(new ByteArrayInputStream(contents));
+    }
+
+    protected String toScript(Map<String,?> props, List<String> commands, Map<String,?> env) {
+        List<String> allcmds = toCommandSequence(commands, env);
+        StringBuilder result = new StringBuilder();
+        result.append(getOptionalVal(props, PROP_SCRIPT_HEADER)).append('\n');
+        
+        for (String cmd : allcmds) {
+            result.append(cmd).append('\n');
+        }
+        
+        return result.toString();
+    }
+
+    /**
+     * Merges the commands and env, into a single set of commands. Also escapes the commands as required.
+     * 
+     * Not all ssh servers handle "env", so instead convert env into exported variables
+     */
+    protected List<String> toCommandSequence(List<String> commands, Map<String,?> env) {
+        List<String> result = new ArrayList<String>((env!=null ? env.size() : 0) + commands.size());
+        
+        if (env!=null) {
+            for (Entry<String,?> entry : env.entrySet()) {
+                if (entry.getKey() == null || entry.getValue() == null) {
+                    LOG.warn("env key-values must not be null; ignoring: key="+entry.getKey()+"; value="+entry.getValue());
+                    continue;
+                }
+                String escapedVal = BashStringEscapes.escapeLiteralForDoubleQuotedBash(entry.getValue().toString());
+                result.add("export "+entry.getKey()+"=\""+escapedVal+"\"");
+            }
+        }
+        for (CharSequence cmd : commands) { // objects in commands can be groovy GString so can't treat as String here
+            result.add(cmd.toString());
+        }
+
+        return result;
+    }
+
+    @Override
+    public int execScript(Map<String,?> props, List<String> commands) {
+        return execScript(props, commands, Collections.<String,Object>emptyMap());
+    }
+
+    @Override
+    public int execCommands(Map<String,?> props, List<String> commands) {
+        return execCommands(props, commands, Collections.<String,Object>emptyMap());
+    }
+
+    protected static int asInt(Integer input, int valueIfInputNull) {
+        return input != null ? input : valueIfInputNull;
+    }
+
+    protected abstract class ToolAbstractExecScript {
+        protected final Map<String, ?> props;
+        protected final String separator;
+        protected final OutputStream out;
+        protected final OutputStream err;
+        protected final String scriptDir;
+        protected final Boolean runAsRoot;
+        protected final Boolean noExtraOutput;
+        protected final Boolean noDeleteAfterExec;
+        protected final String scriptNameWithoutExtension;
+        protected final String scriptPath;
+        protected final Duration execTimeout;
+
+        public ToolAbstractExecScript(Map<String,?> props) {
+            this.props = props;
+            this.separator = getOptionalVal(props, PROP_SEPARATOR);
+            this.out = getOptionalVal(props, PROP_OUT_STREAM);
+            this.err = getOptionalVal(props, PROP_ERR_STREAM);
+            
+            this.scriptDir = getOptionalVal(props, PROP_SCRIPT_DIR);
+            this.runAsRoot = getOptionalVal(props, PROP_RUN_AS_ROOT);
+            this.noExtraOutput = getOptionalVal(props, PROP_NO_EXTRA_OUTPUT);
+            this.noDeleteAfterExec = getOptionalVal(props, PROP_NO_DELETE_SCRIPT);
+            this.execTimeout = getOptionalVal(props, PROP_EXEC_TIMEOUT);
+            
+            String summary = getOptionalVal(props, PROP_SUMMARY);
+            if (summary!=null) {
+                summary = Strings.makeValidFilename(summary);
+                if (summary.length()>30) 
+                    summary = summary.substring(0,30);
+            }
+            this.scriptNameWithoutExtension = "brooklyn-"+
+                    Time.makeDateStampString()+"-"+Identifiers.makeRandomId(4)+
+                    (Strings.isBlank(summary) ? "" : "-"+summary);
+            this.scriptPath = Os.mergePathsUnix(scriptDir, scriptNameWithoutExtension+".sh");
+        }
+
+        /** builds the command to run the given script;
+         * note that some modes require \$RESULT passed in order to access a variable, whereas most just need $ */
+        protected List<String> buildRunScriptCommand() {
+            MutableList.Builder<String> cmds = MutableList.<String>builder()
+                    .add((runAsRoot ? BashCommands.sudo(scriptPath) : scriptPath) + " < /dev/null")
+                    .add("RESULT=$?");
+            if (noExtraOutput==null || !noExtraOutput)
+                cmds.add("echo Executed "+scriptPath+", result $RESULT"); 
+            if (noDeleteAfterExec!=Boolean.TRUE) {
+                // use "-f" because some systems have "rm" aliased to "rm -i"
+                // use "< /dev/null" to guarantee doesn't hang
+                cmds.add("rm -f "+scriptPath+" < /dev/null");
+            }
+            cmds.add("exit $RESULT");
+            return cmds.build();
+        }
+
+        protected String getSummary() {
+            String summary = getOptionalVal(props, PROP_SUMMARY);
+            return (summary != null) ? summary : scriptPath; 
+        }
+
+        public abstract int run();
+    }
+    
+    protected abstract class ToolAbstractAsyncExecScript extends ToolAbstractExecScript {
+        protected final String stdoutPath;
+        protected final String stderrPath;
+        protected final String exitStatusPath;
+        protected final String pidPath;
+
+        public ToolAbstractAsyncExecScript(Map<String,?> props) {
+            super(props);
+
+            stdoutPath = Os.mergePathsUnix(scriptDir, scriptNameWithoutExtension + ".stdout");
+            stderrPath = Os.mergePathsUnix(scriptDir, scriptNameWithoutExtension + ".stderr");
+            exitStatusPath = Os.mergePathsUnix(scriptDir, scriptNameWithoutExtension + ".exitstatus");
+            pidPath = Os.mergePathsUnix(scriptDir, scriptNameWithoutExtension + ".pid");
+        }
+
+        /**
+         * Builds the command to run the given script, asynchronously.
+         * The executed command will return immediately, but the output from the script
+         * will continue to be written 
+         * note that some modes require \$RESULT passed in order to access a variable, whereas most just need $ */
+        @Override
+        protected List<String> buildRunScriptCommand() {
+            String touchCmd = String.format("touch %s %s %s %s", stdoutPath, stderrPath, exitStatusPath, pidPath);
+            String cmd = String.format("nohup sh -c \"( %s > %s 2> %s < /dev/null ) ; echo \\$? > %s \" > /dev/null 2>&1 < /dev/null &", scriptPath, stdoutPath, stderrPath, exitStatusPath);
+            MutableList.Builder<String> cmds = MutableList.<String>builder()
+                    .add(runAsRoot ? BashCommands.sudo(touchCmd) : touchCmd)
+                    .add(runAsRoot ? BashCommands.sudo(cmd) : cmd)
+                    .add("echo $! > "+pidPath)
+                    .add("RESULT=$?");
+            if (noExtraOutput==null || !noExtraOutput) {
+                cmds.add("echo Executing async "+scriptPath);
+            }
+            cmds.add("exit $RESULT");
+            return cmds.build();
+        }
+
+        /**
+         * Builds the command to retrieve the exit status of the command, written to stdout.
+         */
+        protected List<String> buildRetrieveStatusCommand() {
+            // Retrieve exit status from file (writtent to stdout), if populated;
+            // if not found and pid still running, then return empty string; else exit code 1.
+            List<String> cmdParts = ImmutableList.of(
+                    "# Retrieve status", // comment is to aid testing - see SshjToolAsyncStubIntegrationTest
+                    "if test -s "+exitStatusPath+"; then",
+                    "    cat "+exitStatusPath,
+                    "elif test -s "+pidPath+"; then",
+                    "    pid=`cat "+pidPath+"`",
+                    "    if ! ps -p $pid > /dev/null < /dev/null; then",
+                    "        # no exit status, and not executing; give a few seconds grace in case just about to write exit status",
+                    "        sleep 3",
+                    "        if test -s "+exitStatusPath+"; then",
+                    "            cat "+exitStatusPath+"",
+                    "        else",
+                    "            echo \"No exit status in "+exitStatusPath+", and pid in "+pidPath+" ($pid) not executing\"",
+                    "            exit 1",
+                    "        fi",
+                    "    fi",
+                    "else",
+                    "    echo \"No exit status in "+exitStatusPath+", and "+pidPath+" is empty\"",
+                    "    exit 1",
+                    "fi"+"\n");
+            String cmd = Joiner.on("\n").join(cmdParts);
+
+            MutableList.Builder<String> cmds = MutableList.<String>builder()
+                    .add((runAsRoot ? BashCommands.sudo(cmd) : cmd))
+                    .add("RESULT=$?");
+            cmds.add("exit $RESULT");
+            return cmds.build();
+        }
+
+        /**
+         * Builds the command to retrieve the stdout and stderr of the async command.
+         * An offset can be given, to only retrieve data starting at a particular character (indexed from 0).
+         */
+        protected List<String> buildRetrieveStdoutAndStderrCommand(int stdoutPosition, int stderrPosition) {
+            // Note that `tail -c +1` means start at the *first* character (i.e. start counting from 1, not 0)
+            String catStdoutCmd = "tail -c +"+(stdoutPosition+1)+" "+stdoutPath+" 2> /dev/null";
+            String catStderrCmd = "tail -c +"+(stderrPosition+1)+" "+stderrPath+" 2>&1 > /dev/null";
+            MutableList.Builder<String> cmds = MutableList.<String>builder()
+                    .add((runAsRoot ? BashCommands.sudo(catStdoutCmd) : catStdoutCmd))
+                    .add((runAsRoot ? BashCommands.sudo(catStderrCmd) : catStderrCmd))
+                    .add("RESULT=$?");
+            cmds.add("exit $RESULT");
+            return cmds.build();
+        }
+
+        /**
+         * Builds the command to retrieve the stdout and stderr of the async command.
+         * An offset can be given, to only retrieve data starting at a particular character (indexed from 0).
+         */
+        protected List<String> buildLongPollCommand(int stdoutPosition, int stderrPosition, Duration timeout) {
+            long maxTime = Math.max(1, timeout.toSeconds());
+            
+            // Note that `tail -c +1` means start at the *first* character (i.e. start counting from 1, not 0)
+            List<String> waitForExitStatusParts = ImmutableList.of(
+                    //Should be careful here because any output will be part of the stdout/stderr streams
+                    "# Long poll", // comment is to aid testing - see SshjToolAsyncStubIntegrationTest
+                    // disown to avoid Terminated message after killing the process
+                    // redirect error output to avoid "file truncated" messages
+                    "tail -c +"+(stdoutPosition+1)+" -f "+stdoutPath+" 2> /dev/null & export TAIL_STDOUT_PID=$!; disown",
+                    "tail -c +"+(stderrPosition+1)+" -f "+stderrPath+" 1>&2 2> /dev/null & export TAIL_STDERR_PID=$!; disown",
+                    "EXIT_STATUS_PATH="+exitStatusPath,
+                    "PID_PATH="+pidPath,
+                    "MAX_TIME="+maxTime,
+                    "COUNTER=0",
+                    "while [ \"$COUNTER\" -lt $MAX_TIME ]; do",
+                    "    if test -s $EXIT_STATUS_PATH; then",
+                    "        EXIT_STATUS=`cat $EXIT_STATUS_PATH`",
+                    "        kill ${TAIL_STDERR_PID} ${TAIL_STDOUT_PID} 2> /dev/null",
+                    "        exit $EXIT_STATUS",
+                    "    elif test -s $PID_PATH; then",
+                    "        PID=`cat $PID_PATH`",
+                    "        if ! ps -p $PID > /dev/null 2>&1 < /dev/null; then",
+                    "            # no exit status, and not executing; give a few seconds grace in case just about to write exit status",
+                    "            sleep 3",
+                    "            if test -s $EXIT_STATUS_PATH; then",
+                    "                EXIT_STATUS=`cat $EXIT_STATUS_PATH`",
+                    "                kill ${TAIL_STDERR_PID} ${TAIL_STDOUT_PID} 2> /dev/null",
+                    "                exit $EXIT_STATUS",
+                    "            else",
+                    "                echo \"No exit status in $EXIT_STATUS_PATH, and pid in $PID_PATH ($PID) not executing\"",
+                    "                kill ${TAIL_STDERR_PID} ${TAIL_STDOUT_PID} 2> /dev/null",
+                    "                exit 126",
+                    "            fi",
+                    "        fi",
+                    "    fi",
+                    "    # No exit status in $EXIT_STATUS_PATH; keep waiting",
+                    "    sleep 1",
+                    "    COUNTER+=1",
+                    "done",
+                    "kill ${TAIL_STDERR_PID} ${TAIL_STDOUT_PID} 2> /dev/null",
+                    "exit 125"+"\n");
+            String waitForExitStatus = Joiner.on("\n").join(waitForExitStatusParts);
+
+            return ImmutableList.of(runAsRoot ? BashCommands.sudo(waitForExitStatus) : waitForExitStatus);
+        }
+
+        protected List<String> deleteTemporaryFilesCommand() {
+            ImmutableList.Builder<String> cmdParts = ImmutableList.builder();
+            
+            if (!Boolean.TRUE.equals(noDeleteAfterExec)) {
+                // use "-f" because some systems have "rm" aliased to "rm -i"
+                // use "< /dev/null" to guarantee doesn't hang
+                cmdParts.add(
+                        "rm -f "+scriptPath+" "+stdoutPath+" "+stderrPath+" "+exitStatusPath+" "+pidPath+" < /dev/null");
+            }
+            
+            // If the buildLongPollCommand didn't complete properly then it might have left tail command running;
+            // ensure they are killed.
+            cmdParts.add(
+                    //ignore error output for the case where there are no running processes and kill is called without arguments
+                    "ps aux | grep \"tail -c\" | grep \""+stdoutPath+"\" | grep -v grep | awk '{ printf $2 }' | xargs kill 2> /dev/null",
+                    "ps aux | grep \"tail -c\" | grep \""+stderrPath+"\" | grep -v grep | awk '{ printf $2 }' | xargs kill 2> /dev/null");
+
+            String cmd = Joiner.on("\n").join(cmdParts.build());
+            
+            return ImmutableList.of(runAsRoot ? BashCommands.sudo(cmd) : cmd);
+        }
+
+        @Override
+        public abstract int run();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/ShellTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/ShellTool.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/ShellTool.java
new file mode 100644
index 0000000..13bfb62
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/ShellTool.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh;
+
+import static brooklyn.entity.basic.ConfigKeys.newConfigKey;
+import static brooklyn.entity.basic.ConfigKeys.newStringConfigKey;
+
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.util.os.Os;
+import brooklyn.util.time.Duration;
+
+/** Methods for executing things in an environment (localhost process, or ssh) */
+public interface ShellTool {
+
+    // config which applies to sessions
+    
+    public static final ConfigKey<String> PROP_LOCAL_TEMP_DIR = newStringConfigKey(
+            "localTempDir", 
+            "The directory on the local machine (i.e. running brooklyn) for writing temp files", 
+            Os.mergePaths(Os.tmp(), "brooklyn-"+Os.user()+"-ssh-tmp"));
+    
+    // config which applies to calls:
+    
+    public static final ConfigKey<Boolean> PROP_RUN_AS_ROOT = newConfigKey("runAsRoot", "When running a script, whether to run as root", Boolean.FALSE);
+    
+    public static final ConfigKey<OutputStream> PROP_OUT_STREAM = newConfigKey(OutputStream.class, "out", "Stream to which to capture stdout");
+    public static final ConfigKey<OutputStream> PROP_ERR_STREAM = newConfigKey(OutputStream.class, "err", "Stream to which to capture stderr");
+    
+    public static final ConfigKey<Boolean> PROP_NO_EXTRA_OUTPUT = newConfigKey("noExtraOutput", "Suppresses any decorative output such as result code which some tool commands insert", false);
+    
+    public static final ConfigKey<String> PROP_SEPARATOR = newConfigKey("separator", "string to insert between caller-supplied commands being executed as commands", " ; ");
+    
+    public static final ConfigKey<String> PROP_SCRIPT_DIR = newConfigKey("scriptDir", "directory where scripts should be copied", "/tmp");
+    public static final ConfigKey<String> PROP_SCRIPT_HEADER = newConfigKey("scriptHeader", "lines to insert at the start of scripts generated for caller-supplied commands for script execution", "#!/bin/bash -e\n");
+    public static final ConfigKey<String> PROP_DIRECT_HEADER = newConfigKey("directHeader", "commands to run at the target before any caller-supplied commands for direct execution", "exec bash -e");
+
+    ConfigKey<Boolean> PROP_NO_DELETE_SCRIPT = newConfigKey("noDeleteAfterExec", "Retains the generated script file after executing the commands instead of deleting it", false);
+
+    ConfigKey<String> PROP_SUMMARY = ConfigKeys.newStringConfigKey("summary", "Provides a human-readable summary, used in file generation etc");
+    
+    ConfigKey<Duration> PROP_EXEC_TIMEOUT = newConfigKey("execTimeout", "Timeout when executing a script", Duration.PRACTICALLY_FOREVER);
+
+    ConfigKey<Boolean> PROP_EXEC_ASYNC = newConfigKey("execAsync", "Executes the script asynchronously, and then polls for the result (and for stdout/stderr)", false);
+
+    ConfigKey<Duration> PROP_EXEC_ASYNC_POLLING_TIMEOUT = newConfigKey("execAsyncPollTimeout", "Timeout per poll when executing a script asynchronously", Duration.ONE_MINUTE);
+
+    /**
+     * Executes the set of commands in a shell script. Blocks until completion.
+     * <p>
+     * 
+     * Optional properties are the same common ones as for {@link #execCommands(Map, List, Map)} with the addition of:
+     * <ul>
+     * <li>{@link #PROP_RUN_AS_ROOT}
+     * <li>{@link #PROP_SCRIPT_DIR}
+     * </ul>
+     * 
+     * @return exit status of script
+     */
+    public int execScript(Map<String,?> props, List<String> commands, Map<String,?> env);
+
+    /**
+     * @see #execScript(Map, List, Map)
+     */
+    public int execScript(Map<String,?> props, List<String> commands);
+
+    /**
+     * Executes the set of commands using ssh exec.
+     * 
+     * This is generally more efficient than ssh shell mode (cf {@link #execScript(Map, List, Map)}), 
+     * but is not suitable if you need env values which are only set on a fully-fledged shell,
+     * or if you want the entire block executed with root permission.
+     *
+     * Common optional properties (which also apply to {@link #execScript(Map, List, Map)}) are:
+     * <ul>
+     * <li>{@link #PROP_OUT_STREAM}
+     * <li>{@link #PROP_ERR_STREAM}
+     * <li>{@link #PROP_SEPARATOR} (for some modes)
+     * <li>{@link #PROP_NO_EXTRA_OUTPUT} (often there is no extra output here)
+     * </ul>
+     * 
+     * Note that {@link #PROP_RUN_AS_ROOT} is <i>not</i> typically supported here. Prefer {@link #execScript(Map, List, Map)}).
+     * 
+     * @return exit status of commands
+     */
+    public int execCommands(Map<String,?> properties, List<String> commands, Map<String,?> env);
+
+    /**
+     * @see #execCommands(Map, List, Map)
+     */
+    public int execCommands(Map<String,?> properties, List<String> commands);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshAbstractTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshAbstractTool.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshAbstractTool.java
new file mode 100644
index 0000000..ea7a71e
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshAbstractTool.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh;
+
+import static brooklyn.util.net.Networking.checkPortValid;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Set;
+
+import brooklyn.util.os.Os;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+public abstract class SshAbstractTool extends ShellAbstractTool implements SshTool {
+
+    protected final String toString;
+
+    protected final String host;
+    protected final String user;
+    protected final String password;
+    protected final int port;
+    protected String privateKeyPassphrase;
+    protected String privateKeyData;
+    protected File privateKeyFile;
+    protected boolean strictHostKeyChecking;
+    protected boolean allocatePTY;
+
+    public static interface SshAction<T> {
+        void clear() throws Exception;
+        T create() throws Exception;
+    }
+
+    public static abstract class AbstractSshToolBuilder<T extends SshTool, B extends AbstractSshToolBuilder<T,B>> {
+        protected String host;
+        protected int port = 22;
+        protected String user = System.getProperty("user.name");
+        protected String password;
+        protected String privateKeyData;
+        protected String privateKeyPassphrase;
+        protected Set<String> privateKeyFiles = Sets.newLinkedHashSet();
+        protected boolean strictHostKeyChecking = false;
+        protected boolean allocatePTY = false;
+        protected File localTempDir = null;
+
+        @SuppressWarnings("unchecked")
+        protected B self() {
+           return (B) this;
+        }
+
+        public B from(Map<String,?> props) {
+            host = getMandatoryVal(props, PROP_HOST);
+            port = getOptionalVal(props, PROP_PORT);
+            user = getOptionalVal(props, PROP_USER);
+            
+            password = getOptionalVal(props, PROP_PASSWORD);
+            
+            warnOnDeprecated(props, "privateKey", "privateKeyData");
+            privateKeyData = getOptionalVal(props, PROP_PRIVATE_KEY_DATA);
+            privateKeyPassphrase = getOptionalVal(props, PROP_PRIVATE_KEY_PASSPHRASE);
+            
+            // for backwards compatibility accept keyFiles and privateKey
+            // but sshj accepts only a single privateKeyFile; leave blank to use defaults (i.e. ~/.ssh/id_rsa and id_dsa)
+            warnOnDeprecated(props, "keyFiles", null);
+            String privateKeyFile = getOptionalVal(props, PROP_PRIVATE_KEY_FILE);
+            if (privateKeyFile != null) privateKeyFiles.add(privateKeyFile);
+            
+            strictHostKeyChecking = getOptionalVal(props, PROP_STRICT_HOST_KEY_CHECKING);
+            allocatePTY = getOptionalVal(props, PROP_ALLOCATE_PTY);
+            
+            String localTempDirPath = getOptionalVal(props, PROP_LOCAL_TEMP_DIR);
+            localTempDir = (localTempDirPath == null) ? null : new File(Os.tidyPath(localTempDirPath));
+            
+            return self();
+        }
+        public B host(String val) {
+            this.host = val; return self();
+        }
+        public B user(String val) {
+            this.user = val; return self();
+        }
+        public B password(String val) {
+            this.password = val; return self();
+        }
+        public B port(int val) {
+            this.port = val; return self();
+        }
+        public B privateKeyPassphrase(String val) {
+            this.privateKeyPassphrase = val; return self();
+        }
+        /** @deprecated 1.4.0, use privateKeyData */
+        public B privateKey(String val) {
+            this.privateKeyData = val; return self();
+        }
+        public B privateKeyData(String val) {
+            this.privateKeyData = val; return self();
+        }
+        public B privateKeyFile(String val) {
+            this.privateKeyFiles.add(val); return self();
+        }
+        public B localTempDir(File val) {
+            this.localTempDir = val; return self();
+        }
+        public abstract T build();
+    }
+
+    protected SshAbstractTool(AbstractSshToolBuilder<?,?> builder) {
+        super(builder.localTempDir);
+        
+        host = checkNotNull(builder.host, "host");
+        port = builder.port;
+        user = builder.user;
+        password = builder.password;
+        strictHostKeyChecking = builder.strictHostKeyChecking;
+        allocatePTY = builder.allocatePTY;
+        privateKeyPassphrase = builder.privateKeyPassphrase;
+        privateKeyData = builder.privateKeyData;
+        
+        if (builder.privateKeyFiles.size() > 1) {
+            throw new IllegalArgumentException("sshj supports only a single private key-file; " +
+                    "for defaults of ~/.ssh/id_rsa and ~/.ssh/id_dsa leave blank");
+        } else if (builder.privateKeyFiles.size() == 1) {
+            String privateKeyFileStr = Iterables.get(builder.privateKeyFiles, 0);
+            String amendedKeyFile = privateKeyFileStr.startsWith("~") ? (System.getProperty("user.home")+privateKeyFileStr.substring(1)) : privateKeyFileStr;
+            privateKeyFile = new File(amendedKeyFile);
+        } else {
+            privateKeyFile = null;
+        }
+        
+        checkArgument(host.length() > 0, "host value must not be an empty string");
+        checkPortValid(port, "ssh port");
+        
+        toString = String.format("%s@%s:%d", user, host, port);
+    }
+
+    @Override
+    public String toString() {
+        return toString;
+    }
+
+    public String getHostAddress() {
+        return this.host;
+    }
+
+    public String getUsername() {
+        return this.user;
+    }
+
+    protected SshException propagate(Exception e, String message) throws SshException {
+        throw new SshException("(" + toString() + ") " + message + ": " + e.getMessage(), e);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshException.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshException.java
new file mode 100644
index 0000000..c13aa42
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshException.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh;
+
+public class SshException extends RuntimeException {
+
+    private static final long serialVersionUID = -5690230838066860965L;
+
+    public SshException(String msg) {
+        super(msg);
+    }
+    
+    public SshException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+}


[05/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/JavaWebAppSshDriver.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/JavaWebAppSshDriver.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/JavaWebAppSshDriver.java
index f7c3e04..b7cae85 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/JavaWebAppSshDriver.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/JavaWebAppSshDriver.java
@@ -26,10 +26,12 @@ import java.util.Set;
 
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.java.JavaSoftwareProcessSshDriver;
+
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ssh.SshTasks;
+
 import brooklyn.util.text.Strings;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/WebAppServiceConstants.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/WebAppServiceConstants.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/WebAppServiceConstants.java
index 4320c9b..693b4c4 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/WebAppServiceConstants.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/WebAppServiceConstants.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.entity.webapp;
 import java.util.Set;
 
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import com.google.common.collect.ImmutableSet;
 
@@ -29,7 +30,6 @@ import brooklyn.entity.basic.Attributes;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 public interface WebAppServiceConstants extends WebAppServiceMetrics {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Server.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Server.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Server.java
index 6a558e7..0935690 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Server.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Server.java
@@ -20,6 +20,7 @@ package org.apache.brooklyn.entity.webapp.jboss;
 
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.webapp.JavaWebAppSoftwareProcess;
 
 import brooklyn.config.ConfigKey;
@@ -27,7 +28,6 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.entity.java.UsesJmx;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 @Catalog(name="JBoss Application Server 6", description="AS6: an open source Java application server from JBoss", iconUrl="classpath:///jboss-logo.png")
 @ImplementedBy(JBoss6ServerImpl.class)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Server.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Server.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Server.java
index 4872b68..dd5f2bd 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Server.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Server.java
@@ -22,6 +22,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.entity.trait.HasShortName;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.webapp.JavaWebAppSoftwareProcess;
 
 import brooklyn.config.ConfigKey;
@@ -31,7 +32,6 @@ import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.StringAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.javalang.JavaClassNames;
 
 @Catalog(name="JBoss Application Server 7", description="AS7: an open source Java application server from JBoss", iconUrl="classpath:///jboss-logo.png")

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jetty/Jetty6Server.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jetty/Jetty6Server.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jetty/Jetty6Server.java
index bd5cfce..70baeee 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jetty/Jetty6Server.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jetty/Jetty6Server.java
@@ -22,6 +22,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.entity.trait.HasShortName;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.webapp.JavaWebAppSoftwareProcess;
 
 import brooklyn.config.ConfigKey;
@@ -30,7 +31,6 @@ import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.entity.java.UsesJmx;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.time.Duration;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/nodejs/NodeJsWebAppService.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/nodejs/NodeJsWebAppService.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/nodejs/NodeJsWebAppService.java
index 166d2ad..9e34c57 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/nodejs/NodeJsWebAppService.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/nodejs/NodeJsWebAppService.java
@@ -23,6 +23,7 @@ import java.util.List;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.location.PortRange;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.webapp.WebAppService;
 
 import brooklyn.config.ConfigKey;
@@ -32,8 +33,6 @@ import brooklyn.entity.basic.SoftwareProcess;
 
 import org.apache.brooklyn.location.basic.PortRanges;
 
-import brooklyn.util.flags.SetFromFlag;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.reflect.TypeToken;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/nodejs/NodeJsWebAppSshDriver.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/nodejs/NodeJsWebAppSshDriver.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/nodejs/NodeJsWebAppSshDriver.java
index e336a93..1735cb8 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/nodejs/NodeJsWebAppSshDriver.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/nodejs/NodeJsWebAppSshDriver.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.brooklyn.core.util.file.ArchiveUtils;
 import org.apache.brooklyn.entity.webapp.WebAppService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -30,10 +31,11 @@ import org.slf4j.LoggerFactory;
 import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver;
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.SoftwareProcess;
+
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.file.ArchiveUtils;
 import brooklyn.util.net.Networking;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/tomcat/Tomcat8Server.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/tomcat/Tomcat8Server.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/tomcat/Tomcat8Server.java
index 766c7a0..aef6ffd 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/tomcat/Tomcat8Server.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/tomcat/Tomcat8Server.java
@@ -20,12 +20,12 @@ package org.apache.brooklyn.entity.webapp.tomcat;
 
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.javalang.JavaClassNames;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/tomcat/TomcatServer.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/tomcat/TomcatServer.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/tomcat/TomcatServer.java
index d9327cd..95a8c31 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/tomcat/TomcatServer.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/tomcat/TomcatServer.java
@@ -22,6 +22,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.entity.trait.HasShortName;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.webapp.JavaWebAppSoftwareProcess;
 
 import brooklyn.config.ConfigKey;
@@ -31,8 +32,9 @@ import brooklyn.entity.java.UsesJmx;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
+
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.flags.SetFromFlag;
+
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGeneratorTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGeneratorTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGeneratorTest.java
index f5c3f41..dc8dd41 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGeneratorTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGeneratorTest.java
@@ -26,9 +26,8 @@ import java.util.LinkedHashSet;
 import java.util.Set;
 
 import org.testng.annotations.Test;
-
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.location.geo.HostGeoInfo;
-import brooklyn.util.ResourceUtils;
 
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java
index 4602840..d068b48 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java
@@ -26,6 +26,7 @@ import static org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.PROV
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 
+import org.apache.brooklyn.core.util.http.HttpTool;
 import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient;
 import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.Domain;
 import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.SmartSubdomain;
@@ -34,7 +35,6 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import brooklyn.util.http.HttpTool;
 import brooklyn.util.text.Strings;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractControllerTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractControllerTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractControllerTest.java
index facde8d..f3af752 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractControllerTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractControllerTest.java
@@ -38,6 +38,7 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityImpl;
 import org.slf4j.Logger;
@@ -60,7 +61,6 @@ import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/ProxySslConfigTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/ProxySslConfigTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/ProxySslConfigTest.java
index 218debf..37a8061 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/ProxySslConfigTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/ProxySslConfigTest.java
@@ -18,12 +18,12 @@
  */
 package org.apache.brooklyn.entity.proxy;
 
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.apache.brooklyn.entity.proxy.ProxySslConfig;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.TypeCoercions;
 
 @Test
 public class ProxySslConfigTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/nginx/NginxRebindWithHaIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/nginx/NginxRebindWithHaIntegrationTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/nginx/NginxRebindWithHaIntegrationTest.java
index 57ea76d..9318fc9 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/nginx/NginxRebindWithHaIntegrationTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/nginx/NginxRebindWithHaIntegrationTest.java
@@ -32,6 +32,8 @@ import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
 import org.apache.brooklyn.entity.proxy.nginx.NginxController;
 import org.apache.brooklyn.entity.webapp.tomcat.TomcatServer;
 import org.apache.brooklyn.test.EntityTestUtils;
@@ -56,10 +58,8 @@ import brooklyn.entity.rebind.RebindTestUtils;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocationReuseIntegrationTest.RecordingSshjTool;
 
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.net.Networking;
 import brooklyn.util.repeat.Repeater;
-import brooklyn.util.task.BasicExecutionManager;
 import brooklyn.util.time.Duration;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/AbstractWebAppFixtureIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/AbstractWebAppFixtureIntegrationTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/AbstractWebAppFixtureIntegrationTest.java
index cf42c5e..b21cd38 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/AbstractWebAppFixtureIntegrationTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/AbstractWebAppFixtureIntegrationTest.java
@@ -68,6 +68,8 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.SubscriptionContext;
 import org.apache.brooklyn.api.management.SubscriptionHandle;
+import org.apache.brooklyn.core.util.crypto.FluentKeySigner;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
 import org.apache.brooklyn.entity.webapp.JavaWebAppService;
 import org.apache.brooklyn.entity.webapp.JavaWebAppSoftwareProcess;
 import org.apache.brooklyn.entity.webapp.WebAppService;
@@ -79,8 +81,6 @@ import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.crypto.FluentKeySigner;
-import brooklyn.util.crypto.SecureKeys;
 import brooklyn.util.net.Urls;
 import brooklyn.util.stream.Streams;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/HttpsSslConfigTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/HttpsSslConfigTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/HttpsSslConfigTest.java
index d21c0d9..830775c 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/HttpsSslConfigTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/HttpsSslConfigTest.java
@@ -18,12 +18,12 @@
  */
 package org.apache.brooklyn.entity.webapp;
 
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.apache.brooklyn.entity.webapp.HttpsSslConfig;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.TypeCoercions;
 
 public class HttpsSslConfigTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/WebAppConcurrentDeployTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/WebAppConcurrentDeployTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/WebAppConcurrentDeployTest.java
index 9d0e1eb..28f331d 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/WebAppConcurrentDeployTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/WebAppConcurrentDeployTest.java
@@ -40,6 +40,8 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.entity.webapp.JavaWebAppSoftwareProcess;
 import org.apache.brooklyn.entity.webapp.jboss.JBoss7Server;
 import org.apache.brooklyn.entity.webapp.tomcat.TomcatServer;
@@ -48,8 +50,6 @@ import org.apache.brooklyn.test.TestResourceUnavailableException;
 
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/test/java/org/apache/brooklyn/test/entity/TestJavaWebAppEntity.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/test/entity/TestJavaWebAppEntity.java b/software/webapp/src/test/java/org/apache/brooklyn/test/entity/TestJavaWebAppEntity.java
index c5967d0..b9d57ca 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/test/entity/TestJavaWebAppEntity.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/test/entity/TestJavaWebAppEntity.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.test.entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.entity.webapp.WebAppService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,7 +34,6 @@ import brooklyn.entity.basic.ServiceStateLogic;
 import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.entity.basic.SoftwareProcessDriverLifecycleEffectorTasks;
 import brooklyn.entity.java.VanillaJavaApp;
-import brooklyn.util.config.ConfigBag;
 
 /**
  * Mock web application server entity for testing.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/test/java/org/apache/brooklyn/test/entity/TestJavaWebAppEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/test/entity/TestJavaWebAppEntityImpl.java b/software/webapp/src/test/java/org/apache/brooklyn/test/entity/TestJavaWebAppEntityImpl.java
index 7e909ab..04b8bd0 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/test/entity/TestJavaWebAppEntityImpl.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/test/entity/TestJavaWebAppEntityImpl.java
@@ -21,10 +21,10 @@ package org.apache.brooklyn.test.entity;
 import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.webapp.WebAppServiceConstants;
 
 import brooklyn.entity.java.VanillaJavaAppImpl;
-import brooklyn.util.flags.SetFromFlag;
 
 public class TestJavaWebAppEntityImpl extends VanillaJavaAppImpl implements TestJavaWebAppEntity {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/archetypes/quickstart/src/brooklyn-sample/src/test/java/com/acme/sample/brooklyn/sample/app/SampleLocalhostIntegrationTest.java
----------------------------------------------------------------------
diff --git a/usage/archetypes/quickstart/src/brooklyn-sample/src/test/java/com/acme/sample/brooklyn/sample/app/SampleLocalhostIntegrationTest.java b/usage/archetypes/quickstart/src/brooklyn-sample/src/test/java/com/acme/sample/brooklyn/sample/app/SampleLocalhostIntegrationTest.java
index 471d5e3..f679425 100644
--- a/usage/archetypes/quickstart/src/brooklyn-sample/src/test/java/com/acme/sample/brooklyn/sample/app/SampleLocalhostIntegrationTest.java
+++ b/usage/archetypes/quickstart/src/brooklyn-sample/src/test/java/com/acme/sample/brooklyn/sample/app/SampleLocalhostIntegrationTest.java
@@ -17,7 +17,7 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.entity.webapp.JavaWebAppService;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
-import brooklyn.util.ResourceUtils;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import brooklyn.util.text.Strings;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/YamlLauncherAbstract.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/YamlLauncherAbstract.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/YamlLauncherAbstract.java
index 1ad7868..6296339 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/YamlLauncherAbstract.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/YamlLauncherAbstract.java
@@ -28,13 +28,13 @@ import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.BrooklynShutdownHooks;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.stream.Streams;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
index 9241a73..3931a82 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
@@ -51,12 +51,12 @@ import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
 import org.apache.brooklyn.core.management.internal.EntityManagementUtils;
 import org.apache.brooklyn.core.management.internal.EntityManagementUtils.CreationResult;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.basic.BasicApplicationImpl;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.net.Urls;
 
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index 33bb6a1..03e3920 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -50,6 +50,11 @@ import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.ManagementContextInjectable;
 import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.FlagUtils.FlagConfigKeyAndValueRecord;
+import org.apache.brooklyn.core.util.task.Tasks;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.AbstractEntity;
@@ -57,15 +62,10 @@ import brooklyn.entity.basic.BrooklynTags;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.proxying.InternalEntityFactory;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.FlagUtils.FlagConfigKeyAndValueRecord;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.Reflections;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
index 527a44c..35c8b4c 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
@@ -33,9 +33,9 @@ import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator.InstantiatorFromKey;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.util.collections.MutableList;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.annotations.Beta;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java
index 2954ee7..87b1f01 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java
@@ -25,11 +25,11 @@ import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.Reflections;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
index 3babd31..f8d03c9 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
@@ -27,6 +27,7 @@ import io.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.core.util.task.DeferredSupplier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,7 +36,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.effector.EffectorTasks;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.DeferredSupplier;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslUtils.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslUtils.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslUtils.java
index e5194bf..4eaf37d 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslUtils.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslUtils.java
@@ -18,7 +18,7 @@
  */
 package org.apache.brooklyn.camp.brooklyn.spi.dsl;
 
-import brooklyn.util.task.DeferredSupplier;
+import org.apache.brooklyn.core.util.task.DeferredSupplier;
 
 import com.google.common.collect.Iterables;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
index 71a72a5..5b1b1e8 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
@@ -34,18 +34,18 @@ import org.apache.brooklyn.camp.brooklyn.spi.creation.EntitySpecConfiguration;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslUtils;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.ClassCoercionException;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.task.DeferredSupplier;
 import org.apache.commons.beanutils.BeanUtils;
 
 import brooklyn.entity.basic.EntityDynamicType;
 import brooklyn.event.basic.DependentConfiguration;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.ClassCoercionException;
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.javalang.Reflections;
-import brooklyn.util.task.DeferredSupplier;
 import brooklyn.util.text.StringEscapes.JavaStringEscapes;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
index 601a46a..791c79f 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
@@ -29,6 +29,8 @@ import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier;
 import org.apache.brooklyn.core.management.internal.EntityManagerInternal;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.Tasks;
 
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.ConfigKeys;
@@ -38,8 +40,6 @@ import brooklyn.entity.basic.EntityPredicates;
 import brooklyn.event.basic.DependentConfiguration;
 import brooklyn.event.basic.Sensors;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.StringEscapes.JavaStringEscapes;
 
 import com.google.common.base.Optional;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
index c0b77f4..8922059 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
@@ -32,6 +32,8 @@ import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatform;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
@@ -42,8 +44,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.StartableApplication;
 import brooklyn.entity.rebind.RebindOptions;
 import brooklyn.entity.rebind.RebindTestFixture;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
index d96b132..0396009 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
@@ -32,6 +32,8 @@ import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatform;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,8 +42,6 @@ import org.testng.annotations.BeforeMethod;
 
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/DslAndRebindYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/DslAndRebindYamlTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/DslAndRebindYamlTest.java
index 87d014b..110851a 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/DslAndRebindYamlTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/DslAndRebindYamlTest.java
@@ -27,6 +27,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -42,7 +43,6 @@ import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.rebind.RebindTestUtils;
 import brooklyn.event.basic.Sensors;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.collect.Iterables;
 import com.google.common.io.Files;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
index 06f596a..a0278b2 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
@@ -40,6 +40,7 @@ import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
 import org.apache.brooklyn.core.management.internal.EntityManagerInternal;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityImpl;
 import org.slf4j.Logger;
@@ -65,7 +66,6 @@ import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Functionals;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Suppliers;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppWithDslYamlRebindIntegrationTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppWithDslYamlRebindIntegrationTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppWithDslYamlRebindIntegrationTest.java
index e558288..72b06e3 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppWithDslYamlRebindIntegrationTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppWithDslYamlRebindIntegrationTest.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -38,7 +39,6 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.rebind.RebindTestUtils;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.stream.Streams;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppsIntegrationTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppsIntegrationTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppsIntegrationTest.java
index 94b3f66..7314e66 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppsIntegrationTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppsIntegrationTest.java
@@ -38,6 +38,7 @@ import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatform;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.entity.webapp.DynamicWebAppCluster;
 import org.apache.brooklyn.entity.webapp.JavaWebAppService;
 import org.apache.brooklyn.entity.webapp.WebAppService;
@@ -55,7 +56,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.Lifecycle;
 import brooklyn.policy.autoscaling.AutoScalerPolicy;
 import brooklyn.test.Asserts;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.net.Urls;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppsMatchingTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppsMatchingTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppsMatchingTest.java
index b4a647e..7e45019 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppsMatchingTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/JavaWebAppsMatchingTest.java
@@ -32,6 +32,8 @@ import java.util.Map;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatform;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.task.DeferredSupplier;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -41,10 +43,8 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Entities;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.DeferredSupplier;
 
 @Test
 public class JavaWebAppsMatchingTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/MapReferenceYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/MapReferenceYamlTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/MapReferenceYamlTest.java
index f20af51..7a8ca3c 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/MapReferenceYamlTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/MapReferenceYamlTest.java
@@ -22,6 +22,7 @@ import java.util.Map;
 import java.util.concurrent.Callable;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.entity.proxy.ProxySslConfig;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
@@ -31,7 +32,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.BasicEntity;
 import brooklyn.entity.basic.Entities;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.collect.Iterables;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java
index ba29051..6b19d3b 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java
@@ -26,6 +26,9 @@ import org.apache.brooklyn.api.entity.trait.Configurable;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.core.management.ManagementContextInjectable;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.apache.brooklyn.entity.proxy.ProxySslConfig;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
@@ -37,9 +40,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.flags.TypeCoercions;
 
 import com.google.common.collect.Lists;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ReloadBrooklynPropertiesTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ReloadBrooklynPropertiesTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ReloadBrooklynPropertiesTest.java
index 3774e7e..961e2fd 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ReloadBrooklynPropertiesTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ReloadBrooklynPropertiesTest.java
@@ -28,6 +28,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,7 +39,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.trait.Startable;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.stream.Streams;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/TestSensorAndEffectorInitializer.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/TestSensorAndEffectorInitializer.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/TestSensorAndEffectorInitializer.java
index 55b9c27..c03a282 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/TestSensorAndEffectorInitializer.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/TestSensorAndEffectorInitializer.java
@@ -24,6 +24,7 @@ import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntityInitializer;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.testng.Assert;
 
 import com.google.common.base.Preconditions;
@@ -32,7 +33,6 @@ import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.effector.EffectorBody;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.config.ConfigBag;
 
 public class TestSensorAndEffectorInitializer implements EntityInitializer {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/AbstractCatalogXmlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/AbstractCatalogXmlTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/AbstractCatalogXmlTest.java
index 65bb7e0..5eab943 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/AbstractCatalogXmlTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/AbstractCatalogXmlTest.java
@@ -22,6 +22,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.core.management.osgi.OsgiTestResources;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 
@@ -33,7 +34,6 @@ import java.io.StringReader;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.os.Os;
 import brooklyn.util.stream.ReaderInputStream;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
index 66fee85..d08cfed 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
@@ -39,9 +39,9 @@ import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityMatcher;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.osgi.OsgiVersionMoreEntityTest;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.text.Strings;
 
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index 3aa9b74..4cece0a 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -35,6 +35,7 @@ import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
 import org.apache.brooklyn.core.management.osgi.OsgiTestResources;
+import org.apache.brooklyn.core.util.ResourceUtils;
 
 import brooklyn.entity.basic.BasicEntity;
 
@@ -42,7 +43,6 @@ import org.apache.brooklyn.test.TestResourceUnavailableException;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityImpl;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.exceptions.Exceptions;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/cli/src/main/java/org/apache/brooklyn/cli/ItemLister.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/org/apache/brooklyn/cli/ItemLister.java b/usage/cli/src/main/java/org/apache/brooklyn/cli/ItemLister.java
index 88780aa..56f691c 100644
--- a/usage/cli/src/main/java/org/apache/brooklyn/cli/ItemLister.java
+++ b/usage/cli/src/main/java/org/apache/brooklyn/cli/ItemLister.java
@@ -42,13 +42,13 @@ import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.cli.lister.ClassFinder;
 import org.apache.brooklyn.cli.lister.ItemDescriptors;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonInclude;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/cli/src/main/java/org/apache/brooklyn/cli/Main.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/org/apache/brooklyn/cli/Main.java b/usage/cli/src/main/java/org/apache/brooklyn/cli/Main.java
index 8d3ed07..95c10a1 100644
--- a/usage/cli/src/main/java/org/apache/brooklyn/cli/Main.java
+++ b/usage/cli/src/main/java/org/apache/brooklyn/cli/Main.java
@@ -78,6 +78,7 @@ import org.apache.brooklyn.cli.CloudExplorer.ComputeTerminateInstancesCommand;
 import org.apache.brooklyn.cli.ItemLister.ListAllCommand;
 import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
 import org.apache.brooklyn.core.management.ha.OsgiManager;
+import org.apache.brooklyn.core.util.ResourceUtils;
 
 import brooklyn.entity.basic.AbstractApplication;
 import brooklyn.entity.basic.AbstractEntity;
@@ -95,7 +96,6 @@ import org.apache.brooklyn.launcher.config.StopWhichAppsOnShutdown;
 import org.apache.brooklyn.rest.security.PasswordHasher;
 import org.apache.brooklyn.rest.util.ShutdownHandler;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.FatalConfigurationRuntimeException;
 import brooklyn.util.exceptions.FatalRuntimeException;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java
index 63ad940..4d22b3f 100644
--- a/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java
+++ b/usage/cli/src/main/java/org/apache/brooklyn/cli/lister/ClassFinder.java
@@ -31,6 +31,8 @@ import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.javalang.UrlClassLoader;
 import org.reflections.Reflections;
 import org.reflections.scanners.FieldAnnotationsScanner;
 import org.reflections.scanners.SubTypesScanner;
@@ -44,8 +46,6 @@ import brooklyn.entity.basic.AbstractApplication;
 import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.basic.SoftwareProcessImpl;
 import brooklyn.policy.basic.AbstractPolicy;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.javalang.UrlClassLoader;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java b/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java
index ff6a5d3..44207c1 100644
--- a/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java
+++ b/usage/cli/src/test/java/org/apache/brooklyn/cli/CliTest.java
@@ -51,6 +51,7 @@ import org.apache.brooklyn.cli.AbstractMain.HelpCommand;
 import org.apache.brooklyn.cli.Main.AppShutdownHandler;
 import org.apache.brooklyn.cli.Main.GeneratePasswordCommand;
 import org.apache.brooklyn.cli.Main.LaunchCommand;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -70,7 +71,6 @@ import brooklyn.entity.trait.Startable;
 import org.apache.brooklyn.location.basic.SimulatedLocation;
 
 import brooklyn.test.Asserts;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.FatalConfigurationRuntimeException;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java
index 13ee15a..82c881f 100644
--- a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java
+++ b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynWebServer.java
@@ -68,6 +68,13 @@ import org.apache.brooklyn.api.location.PortRange;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.internal.BrooklynInitialization;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.BrooklynNetworkUtils;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.crypto.FluentKeySigner;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.apache.brooklyn.launcher.config.CustomResourceLocator;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.PortRanges;
@@ -83,15 +90,8 @@ import org.apache.brooklyn.rest.util.ManagementContextProvider;
 import org.apache.brooklyn.rest.util.ShutdownHandler;
 import org.apache.brooklyn.rest.util.ShutdownHandlerProvider;
 
-import brooklyn.util.BrooklynNetworkUtils;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.crypto.FluentKeySigner;
-import brooklyn.util.crypto.SecureKeys;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.io.FileUtil;
 import brooklyn.util.javalang.Threads;
 import brooklyn.util.logging.LoggingSetup;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/launcher/src/main/java/org/apache/brooklyn/launcher/config/CustomResourceLocator.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/config/CustomResourceLocator.java b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/config/CustomResourceLocator.java
index 78ad7c6..eb1052d 100644
--- a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/config/CustomResourceLocator.java
+++ b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/config/CustomResourceLocator.java
@@ -23,12 +23,12 @@ import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.BrooklynVersion;
 import brooklyn.config.ConfigMap;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.os.Os;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/launcher/src/test/java/org/apache/brooklyn/entity/basic/VanillaSoftwareYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/entity/basic/VanillaSoftwareYamlTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/entity/basic/VanillaSoftwareYamlTest.java
index a8c9f98..1d14d72 100644
--- a/usage/launcher/src/test/java/org/apache/brooklyn/entity/basic/VanillaSoftwareYamlTest.java
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/entity/basic/VanillaSoftwareYamlTest.java
@@ -28,11 +28,11 @@ import brooklyn.entity.trait.Startable;
 
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.launcher.SimpleYamlLauncherForTests;
 import org.apache.brooklyn.launcher.camp.SimpleYamlLauncher;
 
 import brooklyn.test.Asserts;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/launcher/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java
index a8d301e..933071c 100644
--- a/usage/launcher/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/entity/brooklynnode/BrooklynNodeRestTest.java
@@ -44,15 +44,15 @@ import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.launcher.SimpleYamlLauncherForTests;
 import org.apache.brooklyn.launcher.camp.SimpleYamlLauncher;
 
 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;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/launcher/src/test/java/org/apache/brooklyn/entity/database/mssql/MssqlBlueprintLiveTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/entity/database/mssql/MssqlBlueprintLiveTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/entity/database/mssql/MssqlBlueprintLiveTest.java
index 9103792..6badab5 100644
--- a/usage/launcher/src/test/java/org/apache/brooklyn/entity/database/mssql/MssqlBlueprintLiveTest.java
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/entity/database/mssql/MssqlBlueprintLiveTest.java
@@ -22,10 +22,9 @@ import java.io.StringReader;
 import java.util.Map;
 
 import org.testng.annotations.Test;
-
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.apache.brooklyn.launcher.blueprints.AbstractBlueprintTest;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.text.TemplateProcessor;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogTest.java
index 49596d6..060aa8f 100644
--- a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogTest.java
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherRebindCatalogTest.java
@@ -37,10 +37,10 @@ import com.google.common.io.Files;
 import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 
 import brooklyn.entity.rebind.persister.PersistMode;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.os.Os;
 
 public class BrooklynLauncherRebindCatalogTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynWebServerTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynWebServerTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynWebServerTest.java
index 8ee48f4..f743aea 100644
--- a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynWebServerTest.java
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynWebServerTest.java
@@ -19,6 +19,8 @@
 package org.apache.brooklyn.launcher;
 
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.launcher.BrooklynWebServer;
 
 import static org.testng.Assert.assertEquals;
@@ -55,8 +57,6 @@ import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/AbstractBlueprintTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/AbstractBlueprintTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/AbstractBlueprintTest.java
index 4fcc63d..707d9a7 100644
--- a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/AbstractBlueprintTest.java
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/AbstractBlueprintTest.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherAbstract;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,7 +50,6 @@ import org.apache.brooklyn.launcher.SimpleYamlLauncherForTests;
 import org.apache.brooklyn.launcher.camp.BrooklynCampPlatformLauncher;
 
 import brooklyn.test.Asserts;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.os.Os;
 
 public abstract class AbstractBlueprintTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedMySqlNodeImpl.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedMySqlNodeImpl.java b/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedMySqlNodeImpl.java
index 7570cc5..b5f0cf4 100644
--- a/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedMySqlNodeImpl.java
+++ b/usage/qa/src/main/java/org/apache/brooklyn/qa/load/SimulatedMySqlNodeImpl.java
@@ -30,10 +30,12 @@ import brooklyn.entity.database.mysql.MySqlSshDriver;
 import brooklyn.entity.software.SshEffectorTasks;
 import brooklyn.event.feed.function.FunctionFeed;
 import brooklyn.event.feed.function.FunctionPollConfig;
+
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.time.CountdownTimer;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorUtils.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorUtils.java b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorUtils.java
index 54acb3b..9dbfe0a 100644
--- a/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorUtils.java
+++ b/usage/qa/src/main/java/org/apache/brooklyn/qa/longevity/MonitorUtils.java
@@ -35,11 +35,11 @@ import java.util.Map;
 import java.util.Set;
 import java.util.regex.Pattern;
 
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.stream.StreamGobbler;
 
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/qa/src/test/java/org/apache/brooklyn/qa/brooklynnode/SoftlayerObtainPrivateLiveTest.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/test/java/org/apache/brooklyn/qa/brooklynnode/SoftlayerObtainPrivateLiveTest.java b/usage/qa/src/test/java/org/apache/brooklyn/qa/brooklynnode/SoftlayerObtainPrivateLiveTest.java
index 59c5b0b..8a02698 100644
--- a/usage/qa/src/test/java/org/apache/brooklyn/qa/brooklynnode/SoftlayerObtainPrivateLiveTest.java
+++ b/usage/qa/src/test/java/org/apache/brooklyn/qa/brooklynnode/SoftlayerObtainPrivateLiveTest.java
@@ -44,10 +44,10 @@ import brooklyn.entity.brooklynnode.BrooklynNode.DeployBlueprintEffector;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.BrooklynMavenArtifacts;
 import org.apache.brooklyn.launcher.BrooklynLauncher;
 import org.apache.brooklyn.location.jclouds.JcloudsLocationConfig;
 
-import brooklyn.util.BrooklynMavenArtifacts;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.maven.MavenRetriever;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
index 9313987..830214e 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AbstractBrooklynRestResource.java
@@ -34,12 +34,12 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.management.ManagementContextInjectable;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider;
 
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 
 public abstract class AbstractBrooklynRestResource implements ManagementContextInjectable {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
index 98d5648..d195af6 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
@@ -66,6 +66,7 @@ import org.apache.brooklyn.core.management.entitlement.Entitlements.EntityAndIte
 import org.apache.brooklyn.core.management.entitlement.Entitlements.StringAndArgument;
 import org.apache.brooklyn.core.management.internal.EntityManagementUtils;
 import org.apache.brooklyn.core.management.internal.EntityManagementUtils.CreationResult;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.rest.api.ApplicationApi;
 import org.apache.brooklyn.rest.domain.ApplicationSpec;
 import org.apache.brooklyn.rest.domain.ApplicationSummary;
@@ -79,7 +80,6 @@ import org.apache.brooklyn.rest.transform.TaskTransformer;
 import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
index 80dc2a3..08ed505 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
@@ -51,6 +51,7 @@ import org.apache.brooklyn.core.catalog.internal.CatalogItemComparator;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
 import org.apache.brooklyn.core.management.entitlement.Entitlements.StringAndArgument;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.rest.api.CatalogApi;
 import org.apache.brooklyn.rest.domain.ApiError;
 import org.apache.brooklyn.rest.domain.CatalogEntitySummary;
@@ -61,7 +62,6 @@ import org.apache.brooklyn.rest.filter.HaHotStateRequired;
 import org.apache.brooklyn.rest.transform.CatalogTransformer;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
index 5353f50..f5f43ea 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
@@ -34,14 +34,14 @@ import brooklyn.event.basic.BasicConfigKey;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.task.ValueResolver;
 import org.apache.brooklyn.rest.api.EntityConfigApi;
 import org.apache.brooklyn.rest.domain.EntityConfigSummary;
 import org.apache.brooklyn.rest.filter.HaHotStateRequired;
 import org.apache.brooklyn.rest.transform.EntityTransformer;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.task.ValueResolver;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
index a50a86e..eef52e9 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
@@ -50,6 +50,7 @@ import org.apache.brooklyn.core.management.entitlement.EntitlementPredicates;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
 import org.apache.brooklyn.core.management.internal.EntityManagementUtils;
 import org.apache.brooklyn.core.management.internal.EntityManagementUtils.CreationResult;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.rest.api.EntityApi;
 import org.apache.brooklyn.rest.domain.EntitySummary;
 import org.apache.brooklyn.rest.domain.LocationSummary;
@@ -61,7 +62,6 @@ import org.apache.brooklyn.rest.transform.LocationTransformer.LocationDetailLeve
 import org.apache.brooklyn.rest.transform.TaskTransformer;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java
index 655c20c..a6ca58b 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.apache.brooklyn.rest.api.PolicyConfigApi;
 import org.apache.brooklyn.rest.domain.PolicyConfigSummary;
 import org.apache.brooklyn.rest.filter.HaHotStateRequired;
@@ -37,8 +38,6 @@ import org.apache.brooklyn.rest.transform.PolicyTransformer;
 import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 
-import brooklyn.util.flags.TypeCoercions;
-
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/SensorResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/SensorResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/SensorResource.java
index e8b85ef..d8be210 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/SensorResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/SensorResource.java
@@ -34,13 +34,13 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.util.task.ValueResolver;
 import org.apache.brooklyn.rest.api.SensorApi;
 import org.apache.brooklyn.rest.domain.SensorSummary;
 import org.apache.brooklyn.rest.filter.HaHotStateRequired;
 import org.apache.brooklyn.rest.transform.SensorTransformer;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 
-import brooklyn.util.task.ValueResolver;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 



[04/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
index 9c48ddf..1fed7c6 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
@@ -63,6 +63,9 @@ import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
 import org.apache.brooklyn.api.management.ha.MementoCopyMode;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.file.ArchiveBuilder;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.apache.brooklyn.rest.api.ServerApi;
 import org.apache.brooklyn.rest.domain.BrooklynFeatureSummary;
 import org.apache.brooklyn.rest.domain.HighAvailabilitySummary;
@@ -72,11 +75,8 @@ import org.apache.brooklyn.rest.transform.HighAvailabilityTransformer;
 import org.apache.brooklyn.rest.util.ShutdownHandler;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.file.ArchiveBuilder;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.Identifiers;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EffectorTransformer.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EffectorTransformer.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EffectorTransformer.java
index f78df8b..32a8887 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EffectorTransformer.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EffectorTransformer.java
@@ -27,14 +27,14 @@ import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.ParameterType;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ValueResolver;
 import org.apache.brooklyn.rest.domain.EffectorSummary;
 import org.apache.brooklyn.rest.domain.EffectorSummary.ParameterSummary;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ValueResolver;
 
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/LocationTransformer.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/LocationTransformer.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/LocationTransformer.java
index 72d17db..f1b6959 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/LocationTransformer.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/LocationTransformer.java
@@ -32,11 +32,11 @@ import org.apache.brooklyn.location.basic.LocationInternal;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationDefinition;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.rest.domain.LocationSummary;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/TaskTransformer.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/TaskTransformer.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/TaskTransformer.java
index 6b337a0..ead9ba5 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/TaskTransformer.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/TaskTransformer.java
@@ -36,13 +36,13 @@ import brooklyn.entity.basic.BrooklynTaskTags.WrappedStream;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.HasTaskChildren;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.TaskInternal;
 import org.apache.brooklyn.rest.domain.LinkWithMetadata;
 import org.apache.brooklyn.rest.domain.TaskSummary;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.TaskInternal;
 import brooklyn.util.text.Strings;
 
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
index d09d05b..d29b6e8 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
@@ -53,6 +53,7 @@ import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
 import org.apache.brooklyn.core.management.entitlement.Entitlements.StringAndArgument;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.Enrichers;
@@ -70,7 +71,6 @@ import org.apache.brooklyn.rest.domain.EntitySpec;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.javalang.Reflections;
 import brooklyn.util.net.Urls;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
index 491c1a7..8d78462 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
@@ -31,13 +31,13 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.yaml.snakeyaml.error.YAMLException;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.util.flags.ClassCoercionException;
 import org.apache.brooklyn.rest.domain.ApiError;
 import org.apache.brooklyn.rest.domain.ApiError.Builder;
 
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.UserFacingException;
-import brooklyn.util.flags.ClassCoercionException;
 import brooklyn.util.text.Strings;
 
 @Provider

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynPropertiesSecurityFilterTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynPropertiesSecurityFilterTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynPropertiesSecurityFilterTest.java
index 6d8e15e..4320321 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynPropertiesSecurityFilterTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynPropertiesSecurityFilterTest.java
@@ -35,11 +35,11 @@ import org.eclipse.jetty.server.Server;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.Test;
-
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.rest.security.provider.AnyoneSecurityProvider;
+
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.time.Time;
 
 import com.fasterxml.jackson.databind.ObjectMapper;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/test/java/org/apache/brooklyn/rest/HaMasterCheckFilterTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/HaMasterCheckFilterTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/HaMasterCheckFilterTest.java
index f5ac071..3f529ca 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/HaMasterCheckFilterTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/HaMasterCheckFilterTest.java
@@ -32,6 +32,8 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.http.HttpStatus;
 import org.apache.http.client.HttpClient;
 import org.eclipse.jetty.server.Server;
@@ -45,8 +47,6 @@ import brooklyn.entity.rebind.RebindTestUtils;
 import org.apache.brooklyn.rest.security.provider.AnyoneSecurityProvider;
 
 import brooklyn.test.Asserts;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.os.Os;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResetTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResetTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResetTest.java
index b073c2c..e9440e8 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResetTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResetTest.java
@@ -27,11 +27,11 @@ import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import org.apache.brooklyn.api.catalog.BrooklynCatalog;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
 
 import brooklyn.test.TestHttpRequestHandler;
 import brooklyn.test.TestHttpServer;
-import brooklyn.util.ResourceUtils;
 
 import com.sun.jersey.api.client.UniformInterfaceException;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java
index 6af06cb..354f4ed 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java
@@ -35,14 +35,14 @@ import brooklyn.entity.basic.EntityPredicates;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.rest.BrooklynRestApiLauncher;
 import org.apache.brooklyn.rest.BrooklynRestApiLauncherTestFixture;
 import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
 import org.apache.brooklyn.test.HttpTestUtils;
 
 import brooklyn.util.collections.MutableList;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.net.Urls;
 
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceIntegrationTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceIntegrationTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceIntegrationTest.java
index c228b95..0dcfd44 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceIntegrationTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceIntegrationTest.java
@@ -18,7 +18,7 @@
  */
 package org.apache.brooklyn.rest.resources;
 
-import static brooklyn.util.http.HttpTool.httpClientBuilder;
+import static org.apache.brooklyn.core.util.http.HttpTool.httpClientBuilder;
 import static org.testng.Assert.assertEquals;
 
 import java.net.URI;
@@ -37,14 +37,13 @@ import brooklyn.config.BrooklynProperties;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.rest.BrooklynRestApiLauncher;
 import org.apache.brooklyn.rest.BrooklynRestApiLauncherTestFixture;
 import org.apache.brooklyn.rest.security.provider.TestSecurityProvider;
 import org.apache.brooklyn.test.HttpTestUtils;
 
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
-
 import com.google.common.collect.ImmutableMap;
 
 public class ServerResourceIntegrationTest extends BrooklynRestApiLauncherTestFixture {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java
index 04baadf..1bb5677 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,8 +35,8 @@ import brooklyn.entity.basic.MethodEffector;
 import brooklyn.entity.basic.SoftwareProcessImpl;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicConfigKey;
+
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.flags.SetFromFlag;
 
 public class RestMockSimpleEntity extends SoftwareProcessImpl {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java
index 92d488f..d6e2b77 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java
@@ -20,13 +20,13 @@ package org.apache.brooklyn.rest.testing.mocks;
 
 import java.util.Map;
 
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.policy.basic.AbstractPolicy;
-import brooklyn.util.flags.SetFromFlag;
 
 public class RestMockSimplePolicy extends AbstractPolicy {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java
index 642fc10..7968089 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java
@@ -43,6 +43,7 @@ import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.http.HttpTool;
 import org.apache.brooklyn.rest.BrooklynRestApiLauncher;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
@@ -51,7 +52,6 @@ import org.apache.brooklyn.test.entity.TestEntity;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.http.HttpTool;
 import brooklyn.util.stream.Streams;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/utils/jmx/jmxmp-ssl-agent/src/test/java/brooklyn/util/jmx/jmxmp/JmxmpAgentSslTest.java
----------------------------------------------------------------------
diff --git a/utils/jmx/jmxmp-ssl-agent/src/test/java/brooklyn/util/jmx/jmxmp/JmxmpAgentSslTest.java b/utils/jmx/jmxmp-ssl-agent/src/test/java/brooklyn/util/jmx/jmxmp/JmxmpAgentSslTest.java
index c0f78ac..771ba6d 100644
--- a/utils/jmx/jmxmp-ssl-agent/src/test/java/brooklyn/util/jmx/jmxmp/JmxmpAgentSslTest.java
+++ b/utils/jmx/jmxmp-ssl-agent/src/test/java/brooklyn/util/jmx/jmxmp/JmxmpAgentSslTest.java
@@ -34,15 +34,14 @@ import java.util.Properties;
 
 import javax.management.remote.JMXConnectorServer;
 
+import org.apache.brooklyn.core.util.crypto.FluentKeySigner;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import brooklyn.util.crypto.FluentKeySigner;
-import brooklyn.util.crypto.SecureKeys;
-
 public class JmxmpAgentSslTest {
 
     KeyPair caRootKey;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/utils/jmx/jmxmp-ssl-agent/src/test/java/brooklyn/util/jmx/jmxmp/JmxmpClient.java
----------------------------------------------------------------------
diff --git a/utils/jmx/jmxmp-ssl-agent/src/test/java/brooklyn/util/jmx/jmxmp/JmxmpClient.java b/utils/jmx/jmxmp-ssl-agent/src/test/java/brooklyn/util/jmx/jmxmp/JmxmpClient.java
index d8fc178..3896631 100644
--- a/utils/jmx/jmxmp-ssl-agent/src/test/java/brooklyn/util/jmx/jmxmp/JmxmpClient.java
+++ b/utils/jmx/jmxmp-ssl-agent/src/test/java/brooklyn/util/jmx/jmxmp/JmxmpClient.java
@@ -40,7 +40,8 @@ import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 
-import brooklyn.util.crypto.SecureKeys;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
+
 import brooklyn.util.crypto.SslTrustUtils;
 
 @SuppressWarnings({"rawtypes","unchecked"})

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimpleLocation.java
----------------------------------------------------------------------
diff --git a/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimpleLocation.java b/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimpleLocation.java
index bf64795..9817017 100644
--- a/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimpleLocation.java
+++ b/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimpleLocation.java
@@ -20,8 +20,9 @@ package brooklyn.osgi.tests;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
+
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.location.basic.AbstractLocation;
-import brooklyn.util.flags.SetFromFlag;
 
 public class SimpleLocation extends AbstractLocation {
     @SetFromFlag("config1")

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java
----------------------------------------------------------------------
diff --git a/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java b/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java
index 10a0094..f70b60b 100644
--- a/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java
+++ b/utils/test-bundles/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java
@@ -19,10 +19,11 @@
 package brooklyn.osgi.tests;
 
 
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.config.ConfigKey;
 import brooklyn.policy.basic.AbstractPolicy;
-import brooklyn.util.flags.SetFromFlag;
 
 public class SimplePolicy extends AbstractPolicy {
     @SetFromFlag("config1")

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/utils/test-bundles/more-entities-v1/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
----------------------------------------------------------------------
diff --git a/utils/test-bundles/more-entities-v1/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java b/utils/test-bundles/more-entities-v1/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
index 80d6b97..ece1b1e 100644
--- a/utils/test-bundles/more-entities-v1/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
+++ b/utils/test-bundles/more-entities-v1/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
@@ -18,9 +18,10 @@
  */
 package brooklyn.osgi.tests.more;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
+
 import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.effector.EffectorBody;
-import brooklyn.util.config.ConfigBag;
 
 
 public class MoreEntityImpl extends AbstractEntity implements MoreEntity {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/utils/test-bundles/more-entities-v2-evil-twin/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
----------------------------------------------------------------------
diff --git a/utils/test-bundles/more-entities-v2-evil-twin/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java b/utils/test-bundles/more-entities-v2-evil-twin/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
index 238d743..3dd4059 100644
--- a/utils/test-bundles/more-entities-v2-evil-twin/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
+++ b/utils/test-bundles/more-entities-v2-evil-twin/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
@@ -21,7 +21,7 @@ package brooklyn.osgi.tests.more;
 import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.effector.EffectorBody;
 import org.apache.brooklyn.api.policy.PolicySpec;
-import brooklyn.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 
 public class MoreEntityImpl extends AbstractEntity implements MoreEntity {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/utils/test-bundles/more-entities-v2/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
----------------------------------------------------------------------
diff --git a/utils/test-bundles/more-entities-v2/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java b/utils/test-bundles/more-entities-v2/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
index 327fd23..e560b54 100644
--- a/utils/test-bundles/more-entities-v2/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
+++ b/utils/test-bundles/more-entities-v2/src/main/java/brooklyn/osgi/tests/more/MoreEntityImpl.java
@@ -21,7 +21,7 @@ package brooklyn.osgi.tests.more;
 import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.effector.EffectorBody;
 import org.apache.brooklyn.api.policy.PolicySpec;
-import brooklyn.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 
 public class MoreEntityImpl extends AbstractEntity implements MoreEntity {


[11/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/osgi/OsgisTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/osgi/OsgisTest.java b/core/src/test/java/org/apache/brooklyn/core/util/osgi/OsgisTest.java
new file mode 100644
index 0000000..cdaf433
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/osgi/OsgisTest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.osgi;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+import org.apache.brooklyn.core.util.osgi.Osgis;
+import org.apache.brooklyn.core.util.osgi.Osgis.VersionedName;
+import org.osgi.framework.Version;
+import org.testng.annotations.Test;
+
+public class OsgisTest {
+
+    @Test
+    public void testParseOsgiIdentifier() throws Exception {
+        assertEquals(Osgis.parseOsgiIdentifier("a.b").get(), new VersionedName("a.b", null));
+        assertEquals(Osgis.parseOsgiIdentifier("a.b:0.1.2").get(), new VersionedName("a.b", Version.parseVersion("0.1.2")));
+        assertEquals(Osgis.parseOsgiIdentifier("a.b:0.0.0.SNAPSHOT").get(), new VersionedName("a.b", Version.parseVersion("0.0.0.SNAPSHOT")));
+        assertFalse(Osgis.parseOsgiIdentifier("a.b:0.notanumber.2").isPresent()); // invalid version
+        assertFalse(Osgis.parseOsgiIdentifier("a.b:0.1.2:3.4.5").isPresent());    // too many colons
+        assertFalse(Osgis.parseOsgiIdentifier("a.b:0.0.0_SNAPSHOT").isPresent()); // invalid version
+        assertFalse(Osgis.parseOsgiIdentifier("").isPresent());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/ssh/BashCommandsIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/ssh/BashCommandsIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/util/ssh/BashCommandsIntegrationTest.java
new file mode 100644
index 0000000..77f91f6
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/ssh/BashCommandsIntegrationTest.java
@@ -0,0 +1,504 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.ssh;
+
+import static brooklyn.util.ssh.BashCommands.sudo;
+import static java.lang.String.format;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+import org.apache.commons.io.FileUtils;
+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;
+
+import brooklyn.entity.basic.Entities;
+
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.javalang.JavaClassNames;
+import brooklyn.util.net.Networking;
+import brooklyn.util.os.Os;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.text.Identifiers;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+
+public class BashCommandsIntegrationTest {
+
+    private static final Logger log = LoggerFactory.getLogger(BashCommandsIntegrationTest.class);
+    
+    private ManagementContext mgmt;
+    private BasicExecutionContext exec;
+    
+    private File destFile;
+    private File sourceNonExistantFile;
+    private File sourceFile1;
+    private File sourceFile2;
+    private String sourceNonExistantFileUrl;
+    private String sourceFileUrl1;
+    private String sourceFileUrl2;
+    private SshMachineLocation loc;
+
+    private String localRepoFilename = "localrepofile.txt";
+    private File localRepoBasePath;
+    private File localRepoEntityBasePath;
+    private String localRepoEntityVersionPath;
+    private File localRepoEntityFile;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        mgmt = new LocalManagementContextForTests();
+        exec = new BasicExecutionContext(mgmt.getExecutionManager());
+        
+        destFile = Os.newTempFile(getClass(), "commoncommands-test-dest.txt");
+        
+        sourceNonExistantFile = new File("/this/does/not/exist/ERQBETJJIG1234");
+        sourceNonExistantFileUrl = sourceNonExistantFile.toURI().toString();
+        
+        sourceFile1 = Os.newTempFile(getClass(), "commoncommands-test.txt");
+        sourceFileUrl1 = sourceFile1.toURI().toString();
+        Files.write("mysource1".getBytes(), sourceFile1);
+        
+        sourceFile2 = Os.newTempFile(getClass(), "commoncommands-test2.txt");
+        sourceFileUrl2 = sourceFile2.toURI().toString();
+        Files.write("mysource2".getBytes(), sourceFile2);
+
+        localRepoEntityVersionPath = JavaClassNames.simpleClassName(this)+"-test-dest-"+Identifiers.makeRandomId(8);
+        localRepoBasePath = new File(format("%s/.brooklyn/repository", System.getProperty("user.home")));
+        localRepoEntityBasePath = new File(localRepoBasePath, localRepoEntityVersionPath);
+        localRepoEntityFile = new File(localRepoEntityBasePath, localRepoFilename);
+        localRepoEntityBasePath.mkdirs();
+        Files.write("mylocal1".getBytes(), localRepoEntityFile);
+
+        loc = mgmt.getLocationManager().createLocation(LocalhostMachineProvisioningLocation.spec()).obtain();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (sourceFile1 != null) sourceFile1.delete();
+        if (sourceFile2 != null) sourceFile2.delete();
+        if (destFile != null) destFile.delete();
+        if (localRepoEntityFile != null) localRepoEntityFile.delete();
+        if (localRepoEntityBasePath != null) FileUtils.deleteDirectory(localRepoEntityBasePath);
+        if (loc != null) loc.close();
+        if (mgmt != null) Entities.destroyAll(mgmt);
+    }
+    
+    @Test(groups="Integration")
+    public void testSudo() throws Exception {
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream errStream = new ByteArrayOutputStream();
+        String cmd = sudo("whoami");
+        int exitcode = loc.execCommands(ImmutableMap.of("out", outStream, "err", errStream), "test", ImmutableList.of(cmd));
+        String outstr = new String(outStream.toByteArray());
+        String errstr = new String(errStream.toByteArray());
+        
+        assertEquals(exitcode, 0, "out="+outstr+"; err="+errstr);
+        assertTrue(outstr.contains("root"), "out="+outstr+"; err="+errstr);
+    }
+    
+    public void testDownloadUrl() throws Exception {
+        List<String> cmds = BashCommands.commandsToDownloadUrlsAs(
+                ImmutableList.of(sourceFileUrl1), 
+                destFile.getAbsolutePath());
+        int exitcode = loc.execCommands("test", cmds);
+        
+        assertEquals(0, exitcode);
+        assertEquals(Files.readLines(destFile, Charsets.UTF_8), ImmutableList.of("mysource1"));
+    }
+    
+    @Test(groups="Integration")
+    public void testDownloadFirstSuccessfulFile() throws Exception {
+        List<String> cmds = BashCommands.commandsToDownloadUrlsAs(
+                ImmutableList.of(sourceNonExistantFileUrl, sourceFileUrl1, sourceFileUrl2), 
+                destFile.getAbsolutePath());
+        int exitcode = loc.execCommands("test", cmds);
+        
+        assertEquals(0, exitcode);
+        assertEquals(Files.readLines(destFile, Charsets.UTF_8), ImmutableList.of("mysource1"));
+    }
+    
+    @Test(groups="Integration")
+    public void testDownloadToStdout() throws Exception {
+        ProcessTaskWrapper<String> t = SshTasks.newSshExecTaskFactory(loc, 
+                "cd "+destFile.getParentFile().getAbsolutePath(),
+                BashCommands.downloadToStdout(Arrays.asList(sourceFileUrl1))+" | sed s/my/your/")
+            .requiringZeroAndReturningStdout().newTask();
+
+        String result = exec.submit(t).get();
+        assertTrue(result.trim().equals("yoursource1"), "Wrong contents of stdout download: "+result);
+    }
+
+    @Test(groups="Integration")
+    public void testAlternativesWhereFirstSucceeds() throws Exception {
+        ProcessTaskWrapper<Integer> t = SshTasks.newSshExecTaskFactory(loc)
+                .add(BashCommands.alternatives(Arrays.asList("echo first", "exit 88")))
+                .newTask();
+
+        Integer returnCode = exec.submit(t).get();
+        String stdout = t.getStdout();
+        String stderr = t.getStderr();
+        log.info("alternatives for good first command gave: "+returnCode+"; err="+stderr+"; out="+stdout);
+        assertTrue(stdout.contains("first"), "errcode="+returnCode+"; stdout="+stdout+"; stderr="+stderr);
+        assertEquals(returnCode, (Integer)0);
+    }
+
+    @Test(groups="Integration")
+    public void testAlternatives() throws Exception {
+        ProcessTaskWrapper<Integer> t = SshTasks.newSshExecTaskFactory(loc)
+                .add(BashCommands.alternatives(Arrays.asList("asdfj_no_such_command_1", "exit 88")))
+                .newTask();
+
+        Integer returnCode = exec.submit(t).get();
+        log.info("alternatives for bad commands gave: "+returnCode+"; err="+new String(t.getStderr())+"; out="+new String(t.getStdout()));
+        assertEquals(returnCode, (Integer)88);
+    }
+
+    @Test(groups="Integration")
+    public void testRequireTestHandlesFailure() throws Exception {
+        ProcessTaskWrapper<?> t = SshTasks.newSshExecTaskFactory(loc)
+            .add(BashCommands.requireTest("-f "+sourceNonExistantFile.getPath(),
+                    "The requested file does not exist")).newTask();
+
+        exec.submit(t).get();
+        assertNotEquals(t.getExitCode(), (Integer)0);
+        assertTrue(t.getStderr().contains("The requested file"), "Expected message in: "+t.getStderr());
+        assertTrue(t.getStdout().contains("The requested file"), "Expected message in: "+t.getStdout());
+    }
+
+    @Test(groups="Integration")
+    public void testRequireTestHandlesSuccess() throws Exception {
+        ProcessTaskWrapper<?> t = SshTasks.newSshExecTaskFactory(loc)
+            .add(BashCommands.requireTest("-f "+sourceFile1.getPath(),
+                    "The requested file does not exist")).newTask();
+
+        exec.submit(t).get();
+        assertEquals(t.getExitCode(), (Integer)0);
+        assertTrue(t.getStderr().equals(""), "Expected no stderr messages, but got: "+t.getStderr());
+    }
+
+    @Test(groups="Integration")
+    public void testRequireFileHandlesFailure() throws Exception {
+        ProcessTaskWrapper<?> t = SshTasks.newSshExecTaskFactory(loc)
+            .add(BashCommands.requireFile(sourceNonExistantFile.getPath())).newTask();
+
+        exec.submit(t).get();
+        assertNotEquals(t.getExitCode(), (Integer)0);
+        assertTrue(t.getStderr().contains("required file"), "Expected message in: "+t.getStderr());
+        assertTrue(t.getStderr().contains(sourceNonExistantFile.getPath()), "Expected message in: "+t.getStderr());
+        assertTrue(t.getStdout().contains("required file"), "Expected message in: "+t.getStdout());
+        assertTrue(t.getStdout().contains(sourceNonExistantFile.getPath()), "Expected message in: "+t.getStdout());
+    }
+
+    @Test(groups="Integration")
+    public void testRequireFileHandlesSuccess() throws Exception {
+        ProcessTaskWrapper<?> t = SshTasks.newSshExecTaskFactory(loc)
+            .add(BashCommands.requireFile(sourceFile1.getPath())).newTask();
+
+        exec.submit(t).get();
+        assertEquals(t.getExitCode(), (Integer)0);
+        assertTrue(t.getStderr().equals(""), "Expected no stderr messages, but got: "+t.getStderr());
+    }
+
+    @Test(groups="Integration")
+    public void testRequireFailureExitsImmediately() throws Exception {
+        ProcessTaskWrapper<?> t = SshTasks.newSshExecTaskFactory(loc)
+            .add(BashCommands.requireTest("-f "+sourceNonExistantFile.getPath(),
+                    "The requested file does not exist"))
+            .add("echo shouldnae come here").newTask();
+
+        exec.submit(t).get();
+        assertNotEquals(t.getExitCode(), (Integer)0);
+        assertTrue(t.getStderr().contains("The requested file"), "Expected message in: "+t.getStderr());
+        assertTrue(t.getStdout().contains("The requested file"), "Expected message in: "+t.getStdout());
+        Assert.assertFalse(t.getStdout().contains("shouldnae"), "Expected message in: "+t.getStdout());
+    }
+
+    @Test(groups="Integration")
+    public void testPipeMultiline() throws Exception {
+        String output = execRequiringZeroAndReturningStdout(loc,
+                BashCommands.pipeTextTo("hello world\n"+"and goodbye\n", "wc")).get();
+
+        assertEquals(Strings.replaceAllRegex(output, "\\s+", " ").trim(), "3 4 25");
+    }
+
+    @Test(groups="Integration")
+    public void testWaitForFileContentsWhenAbortingOnFail() throws Exception {
+        String fileContent = "mycontents";
+        String cmd = BashCommands.waitForFileContents(destFile.getAbsolutePath(), fileContent, Duration.ONE_SECOND, true);
+
+        int exitcode = loc.execCommands("test", ImmutableList.of(cmd));
+        assertEquals(exitcode, 1);
+        
+        Files.write(fileContent, destFile, Charsets.UTF_8);
+        int exitcode2 = loc.execCommands("test", ImmutableList.of(cmd));
+        assertEquals(exitcode2, 0);
+    }
+
+    @Test(groups="Integration")
+    public void testWaitForFileContentsWhenNotAbortingOnFail() throws Exception {
+        String fileContent = "mycontents";
+        String cmd = BashCommands.waitForFileContents(destFile.getAbsolutePath(), fileContent, Duration.ONE_SECOND, false);
+
+        String output = execRequiringZeroAndReturningStdout(loc, cmd).get();
+        assertTrue(output.contains("Couldn't find"), "output="+output);
+
+        Files.write(fileContent, destFile, Charsets.UTF_8);
+        String output2 = execRequiringZeroAndReturningStdout(loc, cmd).get();
+        assertFalse(output2.contains("Couldn't find"), "output="+output2);
+    }
+    
+    @Test(groups="Integration")
+    public void testWaitForFileContentsWhenContentsAppearAfterStart() throws Exception {
+        String fileContent = "mycontents";
+
+        String cmd = BashCommands.waitForFileContents(destFile.getAbsolutePath(), fileContent, Duration.THIRTY_SECONDS, false);
+        ProcessTaskWrapper<String> t = execRequiringZeroAndReturningStdout(loc, cmd);
+        exec.submit(t);
+        
+        // sleep for long enough to ensure the ssh command is definitely executing
+        Thread.sleep(5*1000);
+        assertFalse(t.isDone());
+        
+        Files.write(fileContent, destFile, Charsets.UTF_8);
+        String output = t.get();
+        assertFalse(output.contains("Couldn't find"), "output="+output);
+    }
+    
+    @Test(groups="Integration", dependsOnMethods="testSudo")
+    public void testWaitForPortFreeWhenAbortingOnTimeout() throws Exception {
+        ServerSocket serverSocket = openServerSocket();
+        try {
+            int port = serverSocket.getLocalPort();
+            String cmd = BashCommands.waitForPortFree(port, Duration.ONE_SECOND, true);
+    
+            int exitcode = loc.execCommands("test", ImmutableList.of(cmd));
+            assertEquals(exitcode, 1);
+            
+            serverSocket.close();
+            assertTrue(Networking.isPortAvailable(port));
+            int exitcode2 = loc.execCommands("test", ImmutableList.of(cmd));
+            assertEquals(exitcode2, 0);
+        } finally {
+            serverSocket.close();
+        }
+    }
+
+    @Test(groups="Integration", dependsOnMethods="testSudo")
+    public void testWaitForPortFreeWhenNotAbortingOnTimeout() throws Exception {
+        ServerSocket serverSocket = openServerSocket();
+        try {
+            int port = serverSocket.getLocalPort();
+            String cmd = BashCommands.waitForPortFree(port, Duration.ONE_SECOND, false);
+    
+            String output = execRequiringZeroAndReturningStdout(loc, cmd).get();
+            assertTrue(output.contains(port+" still in use"), "output="+output);
+    
+            serverSocket.close();
+            assertTrue(Networking.isPortAvailable(port));
+            String output2 = execRequiringZeroAndReturningStdout(loc, cmd).get();
+            assertFalse(output2.contains("still in use"), "output="+output2);
+        } finally {
+            serverSocket.close();
+        }
+    }
+    
+    @Test(groups="Integration", dependsOnMethods="testSudo")
+    public void testWaitForPortFreeWhenFreedAfterStart() throws Exception {
+        ServerSocket serverSocket = openServerSocket();
+        try {
+            int port = serverSocket.getLocalPort();
+    
+            String cmd = BashCommands.waitForPortFree(port, Duration.THIRTY_SECONDS, false);
+            ProcessTaskWrapper<String> t = execRequiringZeroAndReturningStdout(loc, cmd);
+            exec.submit(t);
+            
+            // sleep for long enough to ensure the ssh command is definitely executing
+            Thread.sleep(5*1000);
+            assertFalse(t.isDone());
+            
+            serverSocket.close();
+            assertTrue(Networking.isPortAvailable(port));
+            String output = t.get();
+            assertFalse(output.contains("still in use"), "output="+output);
+        } finally {
+            serverSocket.close();
+        }
+    }
+
+    
+    // Disabled by default because of risk of overriding /etc/hosts in really bad way if doesn't work properly!
+    // As a manual visual inspection test, consider first manually creating /etc/hostname and /etc/sysconfig/network
+    // so that it looks like debian+ubuntu / CentOS/RHEL.
+    @Test(groups={"Integration"}, enabled=false)
+    public void testSetHostnameUnqualified() throws Exception {
+        runSetHostname("br-"+Identifiers.makeRandomId(8).toLowerCase(), null, false);
+    }
+
+    @Test(groups={"Integration"}, enabled=false)
+    public void testSetHostnameQualified() throws Exception {
+        runSetHostname("br-"+Identifiers.makeRandomId(8).toLowerCase()+".brooklyn.incubator.apache.org", null, false);
+    }
+
+    @Test(groups={"Integration"}, enabled=false)
+    public void testSetHostnameNullDomain() throws Exception {
+        runSetHostname("br-"+Identifiers.makeRandomId(8).toLowerCase(), null, true);
+    }
+
+    @Test(groups={"Integration"}, enabled=false)
+    public void testSetHostnameNonNullDomain() throws Exception {
+        runSetHostname("br-"+Identifiers.makeRandomId(8).toLowerCase(), "brooklyn.incubator.apache.org", true);
+    }
+
+    protected void runSetHostname(String newHostname, String newDomain, boolean includeDomain) throws Exception {
+        String fqdn = (includeDomain && Strings.isNonBlank(newDomain)) ? newHostname + "." + newDomain : newHostname;
+        
+        LocalManagementContextForTests mgmt = new LocalManagementContextForTests();
+        SshMachineLocation loc = mgmt.getLocationManager().createLocation(LocalhostMachineProvisioningLocation.spec()).obtain();
+
+        execRequiringZeroAndReturningStdout(loc, sudo("cp /etc/hosts /etc/hosts-orig-testSetHostname")).get();
+        execRequiringZeroAndReturningStdout(loc, BashCommands.ifFileExistsElse0("/etc/hostname", sudo("cp /etc/hostname /etc/hostname-orig-testSetHostname"))).get();
+        execRequiringZeroAndReturningStdout(loc, BashCommands.ifFileExistsElse0("/etc/sysconfig/network", sudo("cp /etc/sysconfig/network /etc/sysconfig/network-orig-testSetHostname"))).get();
+        
+        String origHostname = getHostnameNoArgs(loc);
+        assertTrue(Strings.isNonBlank(origHostname));
+        
+        try {
+            List<String> cmd = (includeDomain) ? BashCommands.setHostname(newHostname, newDomain) : BashCommands.setHostname(newHostname);
+            execRequiringZeroAndReturningStdout(loc, cmd).get();
+
+            String actualHostnameUnqualified = getHostnameUnqualified(loc);
+            String actualHostnameFullyQualified = getHostnameFullyQualified(loc);
+
+            // TODO On OS X at least, we aren't actually setting the domain name; we're just letting 
+            //      the user pass in what the domain name is. We do add this properly to /etc/hosts
+            //      (e.g. first line is "127.0.0.1 br-g4x5wgx8.brooklyn.incubator.apache.org br-g4x5wgx8 localhost")
+            //      but subsequent calls to `hostname -f` returns the unqualified. Similarly, `domainname` 
+            //      returns blank. Therefore we can't assert that it equals our expected val (because we just made  
+            //      it up - "brooklyn.incubator.apache.org").
+            //      assertEquals(actualHostnameFullyQualified, fqdn);
+            assertEquals(actualHostnameUnqualified, Strings.getFragmentBetween(newHostname, null, "."));
+            execRequiringZeroAndReturningStdout(loc, "ping -c1 -n -q "+actualHostnameUnqualified).get();
+            execRequiringZeroAndReturningStdout(loc, "ping -c1 -n -q "+actualHostnameFullyQualified).get();
+            
+            String result = execRequiringZeroAndReturningStdout(loc, "grep -n "+fqdn+" /etc/hosts").get();
+            assertTrue(result.contains("localhost"), "line="+result);
+            log.info("result="+result);
+            
+        } finally {
+            execRequiringZeroAndReturningStdout(loc, sudo("cp /etc/hosts-orig-testSetHostname /etc/hosts")).get();
+            execRequiringZeroAndReturningStdout(loc, BashCommands.ifFileExistsElse0("/etc/hostname-orig-testSetHostname", sudo("cp /etc/hostname-orig-testSetHostname /etc/hostname"))).get();
+            execRequiringZeroAndReturningStdout(loc, BashCommands.ifFileExistsElse0("/etc/sysconfig/network-orig-testSetHostname", sudo("cp /etc/sysconfig/network-orig-testSetHostname /etc/sysconfig/network"))).get();
+            execRequiringZeroAndReturningStdout(loc, sudo("hostname "+origHostname)).get();
+        }
+    }
+
+    // Marked disabled because not safe to run on your normal machine! It modifies /etc/hosts, which is dangerous if things go wrong!
+    @Test(groups={"Integration"}, enabled=false)
+    public void testModifyEtcHosts() throws Exception {
+        LocalManagementContextForTests mgmt = new LocalManagementContextForTests();
+        SshMachineLocation loc = mgmt.getLocationManager().createLocation(LocalhostMachineProvisioningLocation.spec()).obtain();
+
+        execRequiringZeroAndReturningStdout(loc, sudo("cp /etc/hosts /etc/hosts-orig-testModifyEtcHosts")).get();
+        int numLinesOrig = Integer.parseInt(execRequiringZeroAndReturningStdout(loc, "wc -l /etc/hosts").get().trim().split("\\s")[0]);
+        
+        try {
+            String cmd = BashCommands.prependToEtcHosts("1.2.3.4", "myhostnamefor1234.at.start", "myhostnamefor1234b");
+            execRequiringZeroAndReturningStdout(loc, cmd).get();
+            
+            String cmd2 = BashCommands.appendToEtcHosts("5.6.7.8", "myhostnamefor5678.at.end", "myhostnamefor5678");
+            execRequiringZeroAndReturningStdout(loc, cmd2).get();
+            
+            String grepFirst = execRequiringZeroAndReturningStdout(loc, "grep -n myhostnamefor1234 /etc/hosts").get();
+            String grepLast = execRequiringZeroAndReturningStdout(loc, "grep -n myhostnamefor5678 /etc/hosts").get();
+            int numLinesAfter = Integer.parseInt(execRequiringZeroAndReturningStdout(loc, "wc -l /etc/hosts").get().trim().split("\\s")[0]);
+            log.info("result: numLinesBefore="+numLinesOrig+"; numLinesAfter="+numLinesAfter+"; first="+grepFirst+"; last="+grepLast);
+            
+            assertTrue(grepFirst.startsWith("1:") && grepFirst.contains("1.2.3.4 myhostnamefor1234.at.start myhostnamefor1234"), "first="+grepFirst);
+            assertTrue(grepLast.startsWith((numLinesOrig+2)+":") && grepLast.contains("5.6.7.8 myhostnamefor5678.at.end myhostnamefor5678"), "last="+grepLast);
+            assertEquals(numLinesOrig + 2, numLinesAfter, "lines orig="+numLinesOrig+", after="+numLinesAfter);
+        } finally {
+            execRequiringZeroAndReturningStdout(loc, sudo("cp /etc/hosts-orig-testModifyEtcHosts /etc/hosts")).get();
+        }
+    }
+    
+    private String getHostnameNoArgs(SshMachineLocation machine) {
+        String hostnameStdout = execRequiringZeroAndReturningStdout(machine, "echo FOREMARKER; hostname; echo AFTMARKER").get();
+        return Strings.getFragmentBetween(hostnameStdout, "FOREMARKER", "AFTMARKER").trim();
+    }
+
+    private String getHostnameUnqualified(SshMachineLocation machine) {
+        String hostnameStdout = execRequiringZeroAndReturningStdout(machine, "echo FOREMARKER; hostname -s 2> /dev/null || hostname; echo AFTMARKER").get();
+        return Strings.getFragmentBetween(hostnameStdout, "FOREMARKER", "AFTMARKER").trim();
+    }
+
+    private String getHostnameFullyQualified(SshMachineLocation machine) {
+        String hostnameStdout = execRequiringZeroAndReturningStdout(machine, "echo FOREMARKER; hostname --fqdn 2> /dev/null || hostname -f; echo AFTMARKER").get();
+        return Strings.getFragmentBetween(hostnameStdout, "FOREMARKER", "AFTMARKER").trim();
+    }
+
+    private ProcessTaskWrapper<String> execRequiringZeroAndReturningStdout(SshMachineLocation loc, Collection<String> cmds) {
+        return execRequiringZeroAndReturningStdout(loc, cmds.toArray(new String[cmds.size()]));
+    }
+    
+    private ProcessTaskWrapper<String> execRequiringZeroAndReturningStdout(SshMachineLocation loc, String... cmds) {
+        ProcessTaskWrapper<String> t = SshTasks.newSshExecTaskFactory(loc, cmds)
+                .requiringZeroAndReturningStdout().newTask();
+        exec.submit(t);
+        return t;
+    }
+
+    private ServerSocket openServerSocket() {
+        int lowerBound = 40000;
+        int upperBound = 40100;
+        for (int i = lowerBound; i < upperBound; i++) {
+            try {
+                return new ServerSocket(i);
+            } catch (IOException e) {
+                // try next number
+            }
+        }
+        throw new IllegalStateException("No ports available in range "+lowerBound+" to "+upperBound);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTaskExecutionPerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTaskExecutionPerformanceTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTaskExecutionPerformanceTest.java
new file mode 100644
index 0000000..71d2586
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTaskExecutionPerformanceTest.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.BasicTask;
+import org.apache.brooklyn.core.util.task.ScheduledTask;
+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.util.collections.MutableMap;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+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 BasicTaskExecutionPerformanceTest {
+    private static final Logger log = LoggerFactory.getLogger(BasicTaskExecutionPerformanceTest.class);
+ 
+    private static final int TIMEOUT_MS = 10*1000;
+    
+    private BasicExecutionManager em;
+
+    public static final int MAX_OVERHEAD_MS = 1500; // was 750ms but saw 1.3s on buildhive
+    public static final int EARLY_RETURN_GRACE = 25; // saw 13ms early return on jenkins!
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        em = new BasicExecutionManager("mycontext");
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (em != null) em.shutdownNow();
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testScheduledTaskExecutedAfterDelay() throws Exception {
+        int delay = 100;
+        final CountDownLatch latch = new CountDownLatch(1);
+        
+        Callable<Task<?>> taskFactory = new Callable<Task<?>>() {
+            @Override public Task<?> call() {
+                return new BasicTask<Void>(new Runnable() {
+                    @Override public void run() {
+                        latch.countDown();
+                    }});
+            }};
+        ScheduledTask t = new ScheduledTask(taskFactory).delay(delay);
+
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        em.submit(t);
+        
+        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        long actualDelay = stopwatch.elapsed(TimeUnit.MILLISECONDS);
+        
+        assertTrue(actualDelay > (delay-EARLY_RETURN_GRACE), "actualDelay="+actualDelay+"; delay="+delay);
+        assertTrue(actualDelay < (delay+MAX_OVERHEAD_MS), "actualDelay="+actualDelay+"; delay="+delay);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testScheduledTaskExecutedAtRegularPeriod() throws Exception {
+        final int period = 100;
+        final int numTimestamps = 4;
+        final CountDownLatch latch = new CountDownLatch(1);
+        final List<Long> timestamps = Collections.synchronizedList(Lists.<Long>newArrayList());
+        final Stopwatch stopwatch = Stopwatch.createStarted();
+        
+        Callable<Task<?>> taskFactory = new Callable<Task<?>>() {
+            @Override public Task<?> call() {
+                return new BasicTask<Void>(new Runnable() {
+                    @Override public void run() {
+                        timestamps.add(stopwatch.elapsed(TimeUnit.MILLISECONDS));
+                        if (timestamps.size() >= numTimestamps) latch.countDown();
+                    }});
+            }};
+        ScheduledTask t = new ScheduledTask(taskFactory).delay(1).period(period);
+        em.submit(t);
+        
+        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        
+        synchronized (timestamps) {
+            long prev = timestamps.get(0);
+            for (long timestamp : timestamps.subList(1, timestamps.size())) {
+                assertTrue(timestamp > prev+period-EARLY_RETURN_GRACE, "timestamps="+timestamps);
+                assertTrue(timestamp < prev+period+MAX_OVERHEAD_MS, "timestamps="+timestamps);
+                prev = timestamp;
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testCanCancelScheduledTask() throws Exception {
+        final int period = 1;
+        final long checkPeriod = 250;
+        final List<Long> timestamps = Collections.synchronizedList(Lists.<Long>newArrayList());
+        
+        Callable<Task<?>> taskFactory = new Callable<Task<?>>() {
+            @Override public Task<?> call() {
+                return new BasicTask<Void>(new Runnable() {
+                    @Override public void run() {
+                        timestamps.add(System.currentTimeMillis());
+                    }});
+            }};
+        ScheduledTask t = new ScheduledTask(taskFactory).period(period);
+        em.submit(t);
+
+        t.cancel();
+        long cancelTime = System.currentTimeMillis();
+        int countImmediatelyAfterCancel = timestamps.size();
+        Thread.sleep(checkPeriod);
+        int countWellAfterCancel = timestamps.size();
+
+        // should have at most 1 more execution after cancel
+        log.info("testCanCancelScheduledTask saw "+countImmediatelyAfterCancel+" then cancel then "+countWellAfterCancel+" total");                
+        assertTrue(countWellAfterCancel - countImmediatelyAfterCancel <= 2, "timestamps="+timestamps+"; cancelTime="+cancelTime);
+    }
+
+    // Previously, when we used a CopyOnWriteArraySet, performance for submitting new tasks was
+    // terrible, and it degraded significantly as the number of previously executed tasks increased
+    // (e.g. 9s for first 1000; 26s for next 1000; 42s for next 1000).
+    @Test
+    public void testExecutionManagerPerformance() throws Exception {
+        // Was fixed at 1000 tasks, but was running out of virtual memory due to excessive thread creation
+        // on machines which were not able to execute the threads quickly.
+        final int NUM_TASKS = Math.min(500 * Runtime.getRuntime().availableProcessors(), 1000);
+        final int NUM_TIMES = 10;
+        final int MAX_ACCEPTABLE_TIME = 7500; // saw 5601ms on buildhive
+        
+        long tWarmup = execTasksAndWaitForDone(NUM_TASKS, ImmutableList.of("A"));
+        
+        List<Long> times = Lists.newArrayList();
+        for (int i = 1; i <= NUM_TIMES; i++) {
+            times.add(execTasksAndWaitForDone(NUM_TASKS, ImmutableList.of("A")));
+        }
+        
+        Long toobig = Iterables.find(
+                times, 
+                new Predicate<Long>() {
+                    public boolean apply(Long input) {
+                        return input > MAX_ACCEPTABLE_TIME;
+                    }},
+                null);
+        assertNull(toobig, "warmup="+tWarmup+"; times="+times);
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private long execTasksAndWaitForDone(int numTasks, List<?> tags) throws Exception {
+        List<Task<?>> tasks = Lists.newArrayList();
+        long startTimestamp = System.currentTimeMillis();
+        for (int i = 1; i < numTasks; i++) {
+            Task<?> t = new BasicTask(Callables.returning(null)); // no-op
+            em.submit(MutableMap.of("tags", tags), t);
+            tasks.add(t);
+        }
+        long submittedTimestamp = System.currentTimeMillis();
+
+        for (Task t : tasks) {
+            t.get();
+        }
+        long endTimestamp = System.currentTimeMillis();
+        long submitTime = submittedTimestamp - startTimestamp;
+        long totalTime = endTimestamp - startTimestamp;
+        
+        log.info("Executed {} tasks; {}ms total; {}ms to submit", new Object[] {numTasks, totalTime, submitTime});
+
+        return totalTime;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTaskExecutionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTaskExecutionTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTaskExecutionTest.java
new file mode 100644
index 0000000..c730738
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTaskExecutionTest.java
@@ -0,0 +1,462 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.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.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.BasicTask;
+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.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.getSubmittedByTask());
+        assertEquals(-1, t.submitTimeUtc);
+        assertNull(t.getInternalFuture());
+
+        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.getInternalFuture());
+        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.getSubmittedByTask() );
+        
+        BasicTask<?> submitter = (BasicTask<?>) tb.getSubmittedByTask();
+        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/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTasksFutureTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTasksFutureTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTasksFutureTest.java
new file mode 100644
index 0000000..020a98c
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/BasicTasksFutureTest.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.Tasks;
+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;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Stopwatch;
+
+public class BasicTasksFutureTest {
+
+    private static final Logger log = LoggerFactory.getLogger(BasicTasksFutureTest.class);
+    
+    private BasicExecutionManager em;
+    private BasicExecutionContext ec;
+    private Map<Object,Object> data;
+    private ExecutorService ex;
+    private Semaphore started;
+    private Semaphore waitInTask;
+    private Semaphore cancelledWhileSleeping;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() {
+        em = new BasicExecutionManager("mycontext");
+        ec = new BasicExecutionContext(em);
+        ex = Executors.newCachedThreadPool();
+        data = Collections.synchronizedMap(new LinkedHashMap<Object,Object>());
+        started = new Semaphore(0);
+        waitInTask = new Semaphore(0);
+        cancelledWhileSleeping = new Semaphore(0);
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (em != null) em.shutdownNow();
+        if (ex != null) ex.shutdownNow();
+    }
+
+    @Test
+    public void testBlockAndGetWithTimeoutsAndListenableFuture() throws InterruptedException {
+        Task<String> t = waitForSemaphore(Duration.FIVE_SECONDS, true, "x");
+        
+        Assert.assertFalse(t.blockUntilEnded(Duration.millis(1)));
+        Assert.assertFalse(t.blockUntilEnded(Duration.ZERO));
+        boolean didNotThrow = false;
+        
+        try { t.getUnchecked(Duration.millis(1)); didNotThrow = true; }
+        catch (Exception e) { /* expected */ }
+        Assert.assertFalse(didNotThrow);
+        
+        try { t.getUnchecked(Duration.ZERO); didNotThrow = true; }
+        catch (Exception e) { /* expected */ }
+        Assert.assertFalse(didNotThrow);
+
+        addFutureListener(t, "before");
+        ec.submit(t);
+        
+        Assert.assertFalse(t.blockUntilEnded(Duration.millis(1)));
+        Assert.assertFalse(t.blockUntilEnded(Duration.ZERO));
+        
+        try { t.getUnchecked(Duration.millis(1)); didNotThrow = true; }
+        catch (Exception e) { /* expected */ }
+        Assert.assertFalse(didNotThrow);
+        
+        try { t.getUnchecked(Duration.ZERO); didNotThrow = true; }
+        catch (Exception e) { /* expected */ }
+        Assert.assertFalse(didNotThrow);
+
+        addFutureListener(t, "during");
+            
+        synchronized (data) {
+            // now let it finish
+            waitInTask.release();
+            Assert.assertTrue(t.blockUntilEnded(Duration.TEN_SECONDS));
+
+            Assert.assertEquals(t.getUnchecked(Duration.millis(1)), "x");
+            Assert.assertEquals(t.getUnchecked(Duration.ZERO), "x");
+            
+            Assert.assertNull(data.get("before"));
+            Assert.assertNull(data.get("during"));
+            // can't set the data(above) until we release the lock (in assert call below)
+            assertSoonGetsData("before");
+            assertSoonGetsData("during");
+        }
+
+        // and see that a listener added late also runs
+        synchronized (data) {
+            addFutureListener(t, "after");
+            Assert.assertNull(data.get("after"));
+            assertSoonGetsData("after");
+        }
+    }
+
+    private void addFutureListener(Task<String> t, final String key) {
+        t.addListener(new Runnable() { public void run() {
+            synchronized (data) {
+                log.info("notifying for "+key);
+                data.notifyAll();
+                data.put(key, true);
+            }
+        }}, ex);
+    }
+
+    private void assertSoonGetsData(String key) throws InterruptedException {
+        for (int i=0; i<10; i++) {
+            if (Boolean.TRUE.equals(data.get(key))) {
+                log.info("got data for "+key);
+                return;
+            }
+            data.wait(Duration.ONE_SECOND.toMilliseconds());
+        }
+        Assert.fail("did not get data for '"+key+"' in time");
+    }
+
+    private <T> Task<T> waitForSemaphore(final Duration time, final boolean requireSemaphore, final T result) {
+        return Tasks.<T>builder().body(new Callable<T>() {
+            public T call() { 
+                try {
+                    started.release();
+                    log.info("waiting up to "+time+" to acquire before returning "+result);
+                    if (!waitInTask.tryAcquire(time.toMilliseconds(), TimeUnit.MILLISECONDS)) {
+                        log.info("did not get semaphore");
+                        if (requireSemaphore) Assert.fail("task did not get semaphore");
+                    } else {
+                        log.info("got semaphore");
+                    }
+                } catch (Exception e) {
+                    log.info("cancelled before returning "+result);
+                    cancelledWhileSleeping.release();
+                    throw Exceptions.propagate(e);
+                }
+                log.info("task returning "+result);
+                return result; 
+            }
+        }).build();
+    }
+
+    @Test
+    public void testCancelAfterStartTriggersListenableFuture() throws Exception {
+        doTestCancelTriggersListenableFuture(Duration.millis(50));
+    }
+    @Test
+    public void testCancelImmediateTriggersListenableFuture() throws Exception {
+        // if cancel fires after submit but before it passes to the executor,
+        // that needs handling separately; this doesn't guarantee this code path,
+        // but it happens sometimes (and it should be handled)
+        doTestCancelTriggersListenableFuture(Duration.ZERO);
+    }
+    public void doTestCancelTriggersListenableFuture(Duration delay) throws Exception {
+        Task<String> t = waitForSemaphore(Duration.TEN_SECONDS, true, "x");
+        addFutureListener(t, "before");
+
+        Stopwatch watch = Stopwatch.createStarted();
+        ec.submit(t);
+        
+        addFutureListener(t, "during");
+
+        log.info("test cancelling "+t+" ("+t.getClass()+") after "+delay);
+        // NB: two different code paths (callers to this method) for notifying futures 
+        // depending whether task is started 
+        Time.sleep(delay);
+
+        synchronized (data) {
+            t.cancel(true);
+            
+            assertSoonGetsData("before");
+            assertSoonGetsData("during");
+
+            addFutureListener(t, "after");
+            Assert.assertNull(data.get("after"));
+            assertSoonGetsData("after");
+        }
+        
+        Assert.assertTrue(t.isDone());
+        Assert.assertTrue(t.isCancelled());
+        try {
+            t.get();
+            Assert.fail("should have thrown CancellationException");
+        } catch (CancellationException e) { /* expected */ }
+        
+        Assert.assertTrue(watch.elapsed(TimeUnit.MILLISECONDS) < Duration.FIVE_SECONDS.toMilliseconds(), 
+            Time.makeTimeStringRounded(watch.elapsed(TimeUnit.MILLISECONDS))+" is too long; should have cancelled very quickly");
+
+        if (started.tryAcquire())
+            // if the task is begun, this should get released
+            Assert.assertTrue(cancelledWhileSleeping.tryAcquire(5, TimeUnit.SECONDS));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/CompoundTaskExecutionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/CompoundTaskExecutionTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/CompoundTaskExecutionTest.java
new file mode 100644
index 0000000..89bde95
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/CompoundTaskExecutionTest.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Semaphore;
+
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.BasicTask;
+import org.apache.brooklyn.core.util.task.CompoundTask;
+import org.apache.brooklyn.core.util.task.ParallelTask;
+import org.apache.brooklyn.core.util.task.SequentialTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.testng.collections.Lists;
+
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Test the operation of the {@link CompoundTask} class.
+ */
+public class CompoundTaskExecutionTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CompoundTaskExecutionTest.class);
+
+    BasicExecutionManager em;
+    BasicExecutionContext ec;
+
+    @BeforeClass
+    public void setup() {
+        em = new BasicExecutionManager("mycontext");
+        ec = new BasicExecutionContext(em);
+    }
+
+    @AfterClass
+    public void teardown() {
+        if (em != null) em.shutdownNow();
+        em = null;
+    }
+
+    private BasicTask<String> taskReturning(final String val) {
+        return new BasicTask<String>(new Callable<String>() {
+                @Override public String call() {
+                    return val;
+                }
+            });
+    }
+
+    private BasicTask<String> slowTaskReturning(final String val, final Duration pauseTime) {
+        return new BasicTask<String>(new Callable<String>() {
+                @Override public String call() {
+                    Time.sleep(pauseTime);
+                    return val;
+                }
+            });
+    }
+
+
+    @Test
+    public void runSequenceTask() throws Exception {
+        BasicTask<String> t1 = taskReturning("a");
+        BasicTask<String> t2 = taskReturning("b");
+        BasicTask<String> t3 = taskReturning("c");
+        BasicTask<String> t4 = taskReturning("d");
+        Task<List<String>> tSequence = ec.submit(new SequentialTask<String>(t1, t2, t3, t4));
+        assertEquals(tSequence.get(), ImmutableList.of("a", "b", "c", "d"));
+    }
+
+    @Test
+    public void testSequentialTaskFailsWhenIntermediateTaskThrowsException() throws Exception {
+        BasicTask<String> t1 = taskReturning("a");
+        BasicTask<String> t2 = new BasicTask<String>(new Callable<String>() {
+                @Override public String call() throws Exception {
+                    throw new IllegalArgumentException("forced exception");
+                }
+            });
+        BasicTask<String> t3 = taskReturning("c");
+        SequentialTask<String> task = new SequentialTask<String>(t1, t2, t3);
+        Task<List<String>> tSequence = ec.submit(task);
+
+        try {
+            tSequence.get();
+            fail("t2 should have thrown an exception");
+        } catch (Exception e) {}
+
+        assertTrue(task.isDone());
+        assertTrue(task.isError());
+        assertTrue(t1.isDone());
+        assertFalse(t1.isError());
+        assertTrue(t2.isDone());
+        assertTrue(t2.isError());
+        // t3 not run because of t2 exception
+        assertFalse(t3.isDone());
+        assertFalse(t3.isBegun());
+    }
+
+    @Test
+    public void testParallelTaskFailsWhenIntermediateTaskThrowsException() throws Exception {
+        // differs from test above of SequentialTask in that expect t3 to be executed,
+        // despite t2 failing.
+        // TODO Do we expect tSequence.get() to block for everything to either fail or complete,
+        // and then to throw exception? Currently it does *not* do that so test was previously failing.
+
+        BasicTask<String> t1 = taskReturning("a");
+        BasicTask<String> t2 = new BasicTask<String>(new Callable<String>() {
+                @Override public String call() throws Exception {
+                    throw new IllegalArgumentException("forced exception");
+                }
+            });
+        BasicTask<String> t3 = slowTaskReturning("c", Duration.millis(100));
+        ParallelTask<String> task = new ParallelTask<String>(t1, t2, t3);
+        Task<List<String>> tSequence = ec.submit(task);
+
+        try {
+            tSequence.get();
+            fail("t2 should have thrown an exception");
+        } catch (Exception e) {}
+
+        assertTrue(task.isDone());
+        assertTrue(task.isError());
+        assertTrue(t1.isDone());
+        assertFalse(t1.isError());
+        assertTrue(t2.isDone());
+        assertTrue(t2.isError());
+        assertTrue(t3.isBegun());
+        assertTrue(t3.isDone());
+        assertFalse(t3.isError());
+    }
+
+    @Test
+    public void runParallelTask() throws Exception {
+        BasicTask<String> t1 = taskReturning("a");
+        BasicTask<String> t2 = taskReturning("b");
+        BasicTask<String> t3 = taskReturning("c");
+        BasicTask<String> t4 = taskReturning("d");
+        Task<List<String>> tSequence = ec.submit(new ParallelTask<String>(t4, t2, t1, t3));
+        assertEquals(new HashSet<String>(tSequence.get()), ImmutableSet.of("a", "b", "c", "d"));
+    }
+
+    @Test
+    public void runParallelTaskWithDelay() throws Exception {
+        final Semaphore locker = new Semaphore(0);
+        BasicTask<String> t1 = new BasicTask<String>(new Callable<String>() {
+                @Override public String call() {
+                    try {
+                        locker.acquire();
+                    } catch (InterruptedException e) {
+                        throw Throwables.propagate(e);
+                    }
+                    return "a";
+                }
+            });
+        BasicTask<String> t2 = taskReturning("b");
+        BasicTask<String> t3 = taskReturning("c");
+        BasicTask<String> t4 = taskReturning("d");
+        final Task<List<String>> tSequence = ec.submit(new ParallelTask<String>(t4, t2, t1, t3));
+
+        assertEquals(ImmutableSet.of(t2.get(), t3.get(), t4.get()), ImmutableSet.of("b", "c", "d"));
+        assertFalse(t1.isDone());
+        assertFalse(tSequence.isDone());
+
+        // get blocks until tasks have completed
+        Thread t = new Thread() {
+            @Override public void run() {
+                try {
+                    tSequence.get();
+                } catch (Exception e) {
+                    throw Throwables.propagate(e);
+                }
+                locker.release();
+            }
+        };
+        t.start();
+        Thread.sleep(30);
+        assertTrue(t.isAlive());
+
+        locker.release();
+
+        assertEquals(new HashSet<String>(tSequence.get()), ImmutableSet.of("a", "b", "c", "d"));
+        assertTrue(t1.isDone());
+        assertTrue(tSequence.isDone());
+
+        locker.acquire();
+    }
+
+    @Test
+    public void testComplexOrdering() throws Exception {
+        List<String> data = new CopyOnWriteArrayList<String>();
+        SequentialTask<String> taskA = new SequentialTask<String>(
+                appendAfterDelay(data, "a1"), appendAfterDelay(data, "a2"), appendAfterDelay(data, "a3"), appendAfterDelay(data, "a4"));
+        SequentialTask<String> taskB = new SequentialTask<String>(
+                appendAfterDelay(data, "b1"), appendAfterDelay(data, "b2"), appendAfterDelay(data, "b3"), appendAfterDelay(data, "b4"));
+        Task<List<String>> t = ec.submit(new ParallelTask<String>(taskA, taskB));
+        t.get();
+
+        LOG.debug("Tasks happened in order: {}", data);
+        assertEquals(data.size(), 8);
+        assertEquals(new HashSet<String>(data), ImmutableSet.of("a1", "a2", "a3", "a4", "b1", "b2", "b3", "b4"));
+
+        // a1, ..., a4 should be in order
+        List<String> as = Lists.newArrayList(), bs = Lists.newArrayList();
+        for (String value : data) {
+            ((value.charAt(0) == 'a') ? as : bs).add(value);
+        }
+        assertEquals(as, ImmutableList.of("a1", "a2", "a3", "a4"));
+        assertEquals(bs, ImmutableList.of("b1", "b2", "b3", "b4"));
+    }
+
+    private BasicTask<String> appendAfterDelay(final List<String> list, final String value) {
+        return new BasicTask<String>(new Callable<String>() {
+                @Override public String call() {
+                    try {
+                        Thread.sleep((int) (100 * Math.random()));
+                    } catch (InterruptedException e) {
+                        throw Throwables.propagate(e);
+                    }
+                    LOG.debug("running {}", value);
+                    list.add(value);
+                    return value;
+                }
+            });
+    }
+
+}


[30/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/XmlUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/XmlUtil.java b/core/src/main/java/brooklyn/util/xstream/XmlUtil.java
deleted file mode 100644
index 1ab1293..0000000
--- a/core/src/main/java/brooklyn/util/xstream/XmlUtil.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.xstream;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.xpath.XPathExpression;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
-
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-import brooklyn.util.exceptions.Exceptions;
-
-public class XmlUtil {
-
-    public static Object xpath(String xml, String xpath) {
-        // TODO Could share factory/doc in thread-local storage; see http://stackoverflow.com/questions/9828254/is-documentbuilderfactory-thread-safe-in-java-5
-        try {
-            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-            DocumentBuilder builder = factory.newDocumentBuilder();
-            Document doc = builder.parse(new ByteArrayInputStream(xml.getBytes()));
-            XPathFactory xPathfactory = XPathFactory.newInstance();
-            XPathExpression expr = xPathfactory.newXPath().compile(xpath);
-            
-            return expr.evaluate(doc);
-            
-        } catch (ParserConfigurationException e) {
-            throw Exceptions.propagate(e);
-        } catch (SAXException e) {
-            throw Exceptions.propagate(e);
-        } catch (IOException e) {
-            throw Exceptions.propagate(e);
-        } catch (XPathExpressionException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
index 0e8ec80..ae3f39f 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -55,6 +55,7 @@ import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.core.catalog.CatalogPredicates;
 import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import brooklyn.config.BrooklynServerConfig;
 
@@ -64,7 +65,6 @@ import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.AggregateClassLoader;
 import brooklyn.util.javalang.LoadedClassLoader;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java
index 2bf9cca..822a8d2 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java
@@ -39,13 +39,13 @@ import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.javalang.ReflectionScanner;
+import org.apache.brooklyn.core.util.javalang.UrlClassLoader;
 
 import brooklyn.entity.basic.ApplicationBuilder;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.javalang.AggregateClassLoader;
-import brooklyn.util.javalang.ReflectionScanner;
-import brooklyn.util.javalang.UrlClassLoader;
 import brooklyn.util.os.Os;
 import brooklyn.util.stream.Streams;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDto.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDto.java
index 847f114..aefb635 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDto.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDto.java
@@ -27,8 +27,8 @@ import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.core.util.ResourceUtils;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoUtils.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoUtils.java
index e2df123..6a27393 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoUtils.java
@@ -22,10 +22,10 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 
 import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 
 public class CatalogDtoUtils {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInitialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInitialization.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInitialization.java
index 047a168..e00211c 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInitialization.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInitialization.java
@@ -29,14 +29,14 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
 import org.apache.brooklyn.core.management.ManagementContextInjectable;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import brooklyn.config.BrooklynServerConfig;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.FatalRuntimeException;
 import brooklyn.util.exceptions.RuntimeInterruptedException;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.os.Os;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
index 5aa073b..a7e52ee 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
@@ -34,12 +34,12 @@ import brooklyn.basic.AbstractBrooklynObject;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.rebind.RebindSupport;
 import org.apache.brooklyn.api.mementos.CatalogItemMemento;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.rebind.BasicCatalogItemRebindSupport;
 import brooklyn.util.collections.MutableList;
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogXmlSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogXmlSerializer.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogXmlSerializer.java
index 462e00e..836cac3 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogXmlSerializer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogXmlSerializer.java
@@ -24,10 +24,10 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
+import org.apache.brooklyn.core.util.xstream.EnumCaseForgivingSingleValueConverter;
+import org.apache.brooklyn.core.util.xstream.XmlSerializer;
 
 import brooklyn.basic.AbstractBrooklynObject;
-import brooklyn.util.xstream.EnumCaseForgivingSingleValueConverter;
-import brooklyn.util.xstream.XmlSerializer;
 
 public class CatalogXmlSerializer extends XmlSerializer<Object> {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynFeatureEnablement.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynFeatureEnablement.java b/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynFeatureEnablement.java
index 24d138b..686fd35 100644
--- a/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynFeatureEnablement.java
+++ b/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynFeatureEnablement.java
@@ -21,12 +21,12 @@ package org.apache.brooklyn.core.internal;
 import java.util.Map;
 
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.internal.storage.BrooklynStorage;
-import brooklyn.util.internal.ssh.ShellTool;
 
 import com.google.common.annotations.Beta;
 import com.google.common.collect.Maps;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynInitialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynInitialization.java b/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynInitialization.java
index 1858306..d1104af 100644
--- a/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynInitialization.java
+++ b/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynInitialization.java
@@ -20,9 +20,10 @@ package org.apache.brooklyn.core.internal;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.crypto.SecureKeys;
-import brooklyn.util.flags.TypeCoercions;
+
 import brooklyn.util.net.Networking;
 
 import com.google.common.annotations.Beta;
@@ -52,7 +53,7 @@ public class BrooklynInitialization {
 
     @SuppressWarnings("deprecation")
     public static void initLegacyLanguageExtensions() {
-        brooklyn.util.BrooklynLanguageExtensions.init();
+        org.apache.brooklyn.core.util.BrooklynLanguageExtensions.init();
     }
 
     /* other things:
@@ -74,7 +75,7 @@ public class BrooklynInitialization {
     @SuppressWarnings("deprecation")
     public synchronized static void reinitAll() {
         done.set(false);
-        brooklyn.util.BrooklynLanguageExtensions.reinit();
+        org.apache.brooklyn.core.util.BrooklynLanguageExtensions.reinit();
         initAll();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/entitlement/Entitlements.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/entitlement/Entitlements.java b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/Entitlements.java
index 6bf9329..dc49053 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/entitlement/Entitlements.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/Entitlements.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.management.entitlement.EntitlementClass;
 import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
 import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,7 +50,6 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.javalang.Reflections;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 
 /** @since 0.7.0 */

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerImpl.java b/core/src/main/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerImpl.java
index bda7f6f..f369124 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerImpl.java
@@ -51,6 +51,8 @@ import org.apache.brooklyn.core.management.internal.LocalEntityManager;
 import org.apache.brooklyn.core.management.internal.LocationManagerInternal;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.management.internal.ManagementTransitionMode;
+import org.apache.brooklyn.core.util.task.ScheduledTask;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -71,8 +73,6 @@ import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.ReferenceWithError;
-import brooklyn.util.task.ScheduledTask;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/ha/OsgiManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/ha/OsgiManager.java b/core/src/main/java/org/apache/brooklyn/core/management/ha/OsgiManager.java
index 6a133fc..8241479 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/ha/OsgiManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/ha/OsgiManager.java
@@ -39,6 +39,8 @@ import brooklyn.BrooklynVersion;
 
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.osgi.Osgis;
+import org.apache.brooklyn.core.util.osgi.Osgis.BundleFinder;
 
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.BrooklynServerPaths;
@@ -49,8 +51,6 @@ import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.os.Os;
 import brooklyn.util.os.Os.DeletionResult;
-import brooklyn.util.osgi.Osgis;
-import brooklyn.util.osgi.Osgis.BundleFinder;
 import brooklyn.util.repeat.Repeater;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/internal/AbstractManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/AbstractManagementContext.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/AbstractManagementContext.java
index 58e2f60..7c1e473 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/AbstractManagementContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/AbstractManagementContext.java
@@ -56,6 +56,10 @@ import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
 import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
+import org.apache.brooklyn.core.util.task.Tasks;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.StringConfigMap;
@@ -74,13 +78,9 @@ import brooklyn.internal.storage.impl.inmemory.InMemoryDataGridFactory;
 import org.apache.brooklyn.location.basic.BasicLocationRegistry;
 
 import brooklyn.util.GroovyJavaMethods;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.BasicExecutionContext;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.base.Function;
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/internal/AsyncCollectionChangeAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/AsyncCollectionChangeAdapter.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/AsyncCollectionChangeAdapter.java
index 038ec90..1fc1060 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/AsyncCollectionChangeAdapter.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/AsyncCollectionChangeAdapter.java
@@ -21,13 +21,13 @@ package org.apache.brooklyn.core.management.internal;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import org.apache.brooklyn.api.management.ExecutionManager;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.SingleThreadedScheduler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.BasicExecutionManager;
-import brooklyn.util.task.SingleThreadedScheduler;
 
 public class AsyncCollectionChangeAdapter<Item> implements CollectionChangeListener<Item> {
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynGarbageCollector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynGarbageCollector.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynGarbageCollector.java
index c02ff81..45876c5 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynGarbageCollector.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynGarbageCollector.java
@@ -39,6 +39,9 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.HasTaskChildren;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.ExecutionListener;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -55,9 +58,6 @@ import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.javalang.MemoryUsageTracker;
-import brooklyn.util.task.BasicExecutionManager;
-import brooklyn.util.task.ExecutionListener;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/internal/EffectorUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/EffectorUtils.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/EffectorUtils.java
index 028f2d2..0f3ab99 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/EffectorUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/EffectorUtils.java
@@ -33,6 +33,8 @@ import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.ParameterType;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -41,10 +43,8 @@ import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.PropagatedRuntimeException;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 
 import com.google.common.collect.Lists;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagementUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagementUtils.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagementUtils.java
index 3f59774..b243cda 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagementUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagementUtils.java
@@ -38,6 +38,8 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
 import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -54,8 +56,6 @@ import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalEntityManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalEntityManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalEntityManager.java
index 53d31eb..23039ba 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalEntityManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalEntityManager.java
@@ -43,6 +43,7 @@ import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -62,7 +63,6 @@ import brooklyn.internal.storage.BrooklynStorage;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.collections.SetFromLiveMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.CountdownTimer;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalLocationManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalLocationManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalLocationManager.java
index 54708f5..f3e6aef 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalLocationManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalLocationManager.java
@@ -30,6 +30,8 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.ProvisioningLocation;
 import org.apache.brooklyn.api.management.AccessController;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,11 +46,9 @@ import brooklyn.internal.storage.BrooklynStorage;
 import org.apache.brooklyn.location.basic.AbstractLocation;
 import org.apache.brooklyn.location.basic.LocationInternal;
 
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.RuntimeInterruptedException;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalManagementContext.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalManagementContext.java
index 3c353e2..b29c178 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalManagementContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalManagementContext.java
@@ -45,6 +45,11 @@ import org.apache.brooklyn.api.management.TaskAdaptable;
 import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
 import org.apache.brooklyn.core.management.ha.OsgiManager;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskTags;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -58,11 +63,6 @@ import brooklyn.entity.proxying.InternalPolicyFactory;
 import brooklyn.internal.storage.DataGridFactory;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.BasicExecutionContext;
-import brooklyn.util.task.BasicExecutionManager;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskTags;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManager.java
index 99b79ec..9a75621 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManager.java
@@ -40,12 +40,12 @@ import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.management.ExecutionManager;
 import org.apache.brooklyn.api.management.SubscriptionHandle;
 import org.apache.brooklyn.api.management.SubscriptionManager;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.SingleThreadedScheduler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.Entities;
-import brooklyn.util.task.BasicExecutionManager;
-import brooklyn.util.task.SingleThreadedScheduler;
 import brooklyn.util.text.Identifiers;
 
 import com.google.common.base.Predicate;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalUsageManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalUsageManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalUsageManager.java
index dc95d11..991f930 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalUsageManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalUsageManager.java
@@ -39,6 +39,7 @@ import org.apache.brooklyn.core.management.ManagementContextInjectable;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
 import org.apache.brooklyn.core.management.usage.ApplicationUsage;
 import org.apache.brooklyn.core.management.usage.LocationUsage;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -52,7 +53,6 @@ import org.apache.brooklyn.location.basic.LocationConfigKeys;
 import org.apache.brooklyn.location.basic.LocationInternal;
 
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.javalang.Reflections;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementContextInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementContextInternal.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementContextInternal.java
index 6c993d8..3fc0677 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementContextInternal.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementContextInternal.java
@@ -31,6 +31,7 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
 import org.apache.brooklyn.core.management.ha.OsgiManager;
+import org.apache.brooklyn.core.util.task.TaskTags;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.BrooklynTaskTags;
@@ -39,7 +40,6 @@ import brooklyn.entity.proxying.InternalLocationFactory;
 import brooklyn.entity.proxying.InternalPolicyFactory;
 import brooklyn.internal.storage.BrooklynStorage;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.TaskTags;
 
 import com.google.common.annotations.Beta;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/BrooklynLanguageExtensions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/BrooklynLanguageExtensions.java b/core/src/main/java/org/apache/brooklyn/core/util/BrooklynLanguageExtensions.java
new file mode 100644
index 0000000..9e1b80e
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/BrooklynLanguageExtensions.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.brooklyn.core.internal.BrooklynInitialization;
+
+import brooklyn.util.internal.TimeExtras;
+
+/** @deprecated since 0.7.0 use {@link BrooklynInitialization} */
+public class BrooklynLanguageExtensions {
+
+    private BrooklynLanguageExtensions() {}
+    
+    private static AtomicBoolean done = new AtomicBoolean(false);
+    
+    public synchronized static void reinit() {
+        done.set(false);
+        init();
+    }
+    
+    /** performs the language extensions required for this project */
+    public synchronized static void init() {
+        if (done.getAndSet(true)) return;
+        TimeExtras.init();
+        BrooklynInitialization.initPortRanges();
+    }
+    
+    static { BrooklynInitialization.initLegacyLanguageExtensions(); }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/BrooklynMavenArtifacts.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/BrooklynMavenArtifacts.java b/core/src/main/java/org/apache/brooklyn/core/util/BrooklynMavenArtifacts.java
new file mode 100644
index 0000000..c00fec0
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/BrooklynMavenArtifacts.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util;
+
+import brooklyn.BrooklynVersion;
+import brooklyn.util.maven.MavenArtifact;
+import brooklyn.util.maven.MavenRetriever;
+import brooklyn.util.text.Strings;
+
+public class BrooklynMavenArtifacts {
+
+    public static MavenArtifact jar(String artifactId) {
+        return artifact(null, artifactId, "jar");
+    }
+    
+    public static MavenArtifact artifact(String subgroupUnderIoBrooklyn, String artifactId, String packaging) {
+        return artifact(subgroupUnderIoBrooklyn, artifactId, packaging, null);
+    }
+
+    public static MavenArtifact artifact(String subgroupUnderIoBrooklyn, String artifactId, String packaging, String classifier) {
+        return new MavenArtifact(
+                Strings.isEmpty(subgroupUnderIoBrooklyn) ? "org.apache.brooklyn" : "org.apache.brooklyn."+subgroupUnderIoBrooklyn,
+                artifactId, packaging, classifier, BrooklynVersion.get());
+    }
+
+    public static String localUrlForJar(String artifactId) {
+        return MavenRetriever.localUrl(jar(artifactId));
+    }
+    
+    public static String localUrl(String subgroupUnderIoBrooklyn, String artifactId, String packaging) {
+        return MavenRetriever.localUrl(artifact(subgroupUnderIoBrooklyn, artifactId, packaging));
+    }
+
+    public static String hostedUrlForJar(String artifactId) {
+        return MavenRetriever.hostedUrl(jar(artifactId));
+    }
+    
+    public static String hostedUrl(String subgroupUnderIoBrooklyn, String artifactId, String packaging) {
+        return MavenRetriever.hostedUrl(artifact(subgroupUnderIoBrooklyn, artifactId, packaging));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/BrooklynNetworkUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/BrooklynNetworkUtils.java b/core/src/main/java/org/apache/brooklyn/core/util/BrooklynNetworkUtils.java
new file mode 100644
index 0000000..7240425
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/BrooklynNetworkUtils.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util;
+
+import java.net.InetAddress;
+
+import brooklyn.config.BrooklynServiceAttributes;
+
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.location.geo.LocalhostExternalIpLoader;
+
+import brooklyn.util.JavaGroovyEquivalents;
+import brooklyn.util.net.Networking;
+
+public class BrooklynNetworkUtils {
+
+    /** returns the externally-facing IP address from which this host comes, or 127.0.0.1 if not resolvable */
+    public static String getLocalhostExternalIp() {
+        return LocalhostExternalIpLoader.getLocalhostIpQuicklyOrDefault();
+    }
+
+    /** returns a IP address for localhost paying attention to a system property to prevent lookup in some cases */ 
+    public static InetAddress getLocalhostInetAddress() {
+        return TypeCoercions.coerce(JavaGroovyEquivalents.elvis(BrooklynServiceAttributes.LOCALHOST_IP_ADDRESS.getValue(), 
+                Networking.getLocalHost()), InetAddress.class);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/ResourceUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/ResourceUtils.java b/core/src/main/java/org/apache/brooklyn/core/util/ResourceUtils.java
new file mode 100644
index 0000000..bbd88d5
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/ResourceUtils.java
@@ -0,0 +1,639 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
+import org.apache.brooklyn.core.internal.BrooklynInitialization;
+import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpTool.HttpClientBuilder;
+import org.apache.brooklyn.core.util.text.DataUriSchemeParser;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.javalang.Threads;
+import brooklyn.util.net.Urls;
+import brooklyn.util.os.Os;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+
+public class ResourceUtils {
+    
+    private static final Logger log = LoggerFactory.getLogger(ResourceUtils.class);
+    private static final List<Function<Object,BrooklynClassLoadingContext>> classLoaderProviders = Lists.newCopyOnWriteArrayList();
+
+    private BrooklynClassLoadingContext loader = null;
+    private String context = null;
+    private Object contextObject = null;
+    
+    static { BrooklynInitialization.initNetworking(); }
+    
+    /**
+     * Creates a {@link ResourceUtils} object with a specific class loader and context.
+     * <p>
+     * Use the provided {@link ClassLoader} object for class loading with the
+     * {@code contextObject} for context and the {@code contextMessage} string for
+     * error messages.
+     *
+     * @see ResourceUtils#create(Object, String)
+     * @see ResourceUtils#create(Object)
+     */
+    public static final ResourceUtils create(ClassLoader loader, Object contextObject, String contextMessage) {
+        return new ResourceUtils(loader, contextObject, contextMessage);
+    }
+
+    /**
+     * Creates a {@link ResourceUtils} object with a specific class loader and context.
+     * <p>
+     * Use the provided {@link BrooklynClassLoadingContext} object for class loading with the
+     * {@code contextObject} for context and the {@code contextMessage} string for
+     * error messages.
+     *
+     * @see ResourceUtils#create(Object, String)
+     * @see ResourceUtils#create(Object)
+     */
+    public static final ResourceUtils create(BrooklynClassLoadingContext loader, Object contextObject, String contextMessage) {
+        return new ResourceUtils(loader, contextObject, contextMessage);
+    }
+
+    /**
+     * Creates a {@link ResourceUtils} object with the given context.
+     * <p>
+     * Uses the {@link ClassLoader} of the given {@code contextObject} for class
+     * loading and the {@code contextMessage} string for error messages.
+     *
+     * @see ResourceUtils#create(ClassLoader, Object, String)
+     * @see ResourceUtils#create(Object)
+     */
+    public static final ResourceUtils create(Object contextObject, String contextMessage) {
+        return new ResourceUtils(contextObject, contextMessage);
+    }
+
+    /**
+     * Creates a {@link ResourceUtils} object with the given context.
+     * <p>
+     * Uses the {@link ClassLoader} of the given {@code contextObject} for class
+     * loading and its {@link Object#toString()} (preceded by the word 'for') as
+     * the string used in error messages.
+     *
+     * @see ResourceUtils#create(ClassLoader, Object, String)
+     * @see ResourceUtils#create(Object)
+     */
+    public static final ResourceUtils create(Object contextObject) {
+        return new ResourceUtils(contextObject);
+    }
+
+    /**
+     * Creates a {@link ResourceUtils} object with itself as the context.
+     *
+     * @see ResourceUtils#create(Object)
+     */
+    public static final ResourceUtils create() {
+        return new ResourceUtils(null);
+    }
+
+    public ResourceUtils(ClassLoader loader, Object contextObject, String contextMessage) {
+        this(getClassLoadingContextInternal(loader, contextObject), contextObject, contextMessage);
+    }
+    
+    public ResourceUtils(BrooklynClassLoadingContext loader, Object contextObject, String contextMessage) {
+        this.loader = loader;
+        this.contextObject = contextObject;
+        this.context = contextMessage;
+    }
+
+    public ResourceUtils(Object contextObject, String contextMessage) {
+        this(contextObject==null ? null : getClassLoadingContextInternal(null, contextObject), contextObject, contextMessage);
+    }
+
+    public ResourceUtils(Object contextObject) {
+        this(contextObject, Strings.toString(contextObject));
+    }
+    
+    /** used to register custom mechanisms for getting classloaders given an object */
+    public static void addClassLoaderProvider(Function<Object,BrooklynClassLoadingContext> provider) {
+        classLoaderProviders.add(provider);
+    }
+    
+    // TODO rework this class so it accepts but does not require a BCLC ?
+    @SuppressWarnings("deprecation")
+    protected static BrooklynClassLoadingContext getClassLoadingContextInternal(ClassLoader loader, Object contextObject) {
+        if (contextObject instanceof BrooklynClassLoadingContext)
+            return (BrooklynClassLoadingContext) contextObject;
+        
+        for (Function<Object,BrooklynClassLoadingContext> provider: classLoaderProviders) {
+            BrooklynClassLoadingContext result = provider.apply(contextObject);
+            if (result!=null) return result;
+        }
+
+        BrooklynClassLoadingContext bl = BrooklynLoaderTracker.getLoader();
+        ManagementContext mgmt = (bl!=null ? bl.getManagementContext() : null);
+
+        ClassLoader cl = loader;
+        if (cl==null) cl = contextObject instanceof Class ? ((Class<?>)contextObject).getClassLoader() : 
+            contextObject instanceof ClassLoader ? ((ClassLoader)contextObject) : 
+                contextObject.getClass().getClassLoader();
+            
+        return JavaBrooklynClassLoadingContext.create(mgmt, cl);
+    }
+    
+    /** This should not be exposed as it risks it leaking into places where it would be serialized.
+     * Better for callers use {@link CatalogUtils#getClassLoadingContext(org.apache.brooklyn.api.entity.Entity)} or similar. }.
+     */
+    private BrooklynClassLoadingContext getLoader() {
+        return (loader!=null ? loader : getClassLoadingContextInternal(null, contextObject!=null ? contextObject : this));
+    }
+
+    /**
+     * @return all resources in Brooklyn's {@link BrooklynClassLoadingContext} with the given name.
+     */
+    public Iterable<URL> getResources(String name) {
+        return getLoader().getResources(name);
+    }
+    
+    /**
+     * Takes a string which is treated as a URL (with some extended "schemes" also expected),
+     * or as a path to something either on the classpath (absolute only) or the local filesystem (relative or absolute, depending on leading slash)
+     * <p>
+     * URLs can be of the form <b>classpath://com/acme/Foo.properties</b>
+     * as well as <b>file:///home/...</b> and <b>http://acme.com/...</b>.
+     * <p>
+     * Throws exception if not found, using the context parameter passed into the constructor.
+     * <p>
+     * TODO may want OSGi, or typed object; should consider pax url
+     * 
+     * @return a stream, or throws exception (never returns null)
+     */
+    public InputStream getResourceFromUrl(String url) {
+        try {
+            if (url==null) throw new NullPointerException("Cannot read from null");
+            if (url=="") throw new NullPointerException("Cannot read from empty string");
+            String orig = url;
+            String protocol = Urls.getProtocol(url);
+            if (protocol!=null) {
+                if ("classpath".equals(protocol)) {
+                    try {
+                        return getResourceViaClasspath(url);
+                    } catch (IOException e) {
+                        //catch the above because both orig and modified url may be interesting
+                        throw new IOException("Error accessing "+orig+": "+e, e);
+                    }
+                }
+                if ("sftp".equals(protocol)) {
+                    try {
+                        return getResourceViaSftp(url);
+                    } catch (IOException e) {
+                        throw new IOException("Error accessing "+orig+": "+e, e);
+                    }
+                }
+
+                if ("file".equals(protocol))
+                    url = tidyFileUrl(url);
+                
+                if ("data".equals(protocol)) {
+                    return new DataUriSchemeParser(url).lax().parse().getDataAsInputStream();
+                }
+
+                if ("http".equals(protocol) || "https".equals(protocol)) {
+                    return getResourceViaHttp(url);
+                }
+
+                return new URL(url).openStream();
+            }
+
+            try {
+                //try as classpath reference, then as file
+                try {
+                    URL u = getLoader().getResource(url);
+                    if (u!=null) return u.openStream();
+                } catch (IllegalArgumentException e) {
+                    //Felix installs an additional URL to the system classloader
+                    //which throws an IllegalArgumentException when passed a
+                    //windows path. See ExtensionManager.java static initializer.
+
+                    //ignore, not a classpath resource
+                }
+                if (url.startsWith("/")) {
+                    //some getResource calls fail if argument starts with /
+                    String urlNoSlash = url;
+                    while (urlNoSlash.startsWith("/")) urlNoSlash = urlNoSlash.substring(1);
+                    URL u = getLoader().getResource(urlNoSlash);
+                    if (u!=null) return u.openStream();
+//                    //Class.getResource can require a /  (else it attempts to be relative) but Class.getClassLoader doesn't
+//                    u = getLoader().getResource("/"+urlNoSlash);
+//                    if (u!=null) return u.openStream();
+                }
+                File f;
+                // but first, if it starts with tilde, treat specially
+                if (url.startsWith("~/")) {
+                    f = new File(Os.home(), url.substring(2));
+                } else if (url.startsWith("~\\")) {
+                    f = new File(Os.home(), url.substring(2));
+                } else {
+                    f = new File(url);
+                }
+                if (f.exists()) return new FileInputStream(f);
+            } catch (IOException e) {
+                //catch the above because both u and modified url will be interesting
+                throw new IOException("Error accessing "+orig+": "+e, e);
+            }
+            throw new IOException("'"+orig+"' not found on classpath or filesystem");
+        } catch (Exception e) {
+            if (context!=null) {
+                throw new RuntimeException("Error getting resource '"+url+"' for "+context+": "+e, e);
+            } else {
+                throw Exceptions.propagate(e);
+            }
+        }
+    }
+    
+    private final static Pattern pattern = Pattern.compile("^file:/*~/+(.*)$");
+
+    public static URL tidy(URL url) {
+        // File class has helpful methods for URIs but not URLs. So we convert.
+        URI in;
+        try {
+            in = url.toURI();
+        } catch (URISyntaxException e) {
+            throw Exceptions.propagate(e);
+        }
+        URI out;
+
+        Matcher matcher = pattern.matcher(in.toString());
+        if (matcher.matches()) {
+            // home-relative
+            File home = new File(Os.home());
+            File file = new File(home, matcher.group(1));
+            out = file.toURI();
+        } else if (in.getScheme().equals("file:")) {
+            // some other file, so canonicalize
+            File file = new File(in);
+            out = file.toURI();
+        } else {
+            // some other scheme, so no-op
+            out = in;
+        }
+
+        URL urlOut;
+        try {
+            urlOut = out.toURL();
+        } catch (MalformedURLException e) {
+            throw Exceptions.propagate(e);
+        }
+        if (!urlOut.equals(url) && log.isDebugEnabled()) {
+            log.debug("quietly changing " + url + " to " + urlOut);
+        }
+        return urlOut;
+    }
+    
+    public static String tidyFileUrl(String url) {
+        try {
+            return tidy(new URL(url)).toString();
+        } catch (MalformedURLException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    /** @deprecated since 0.7.0; use method {@link Os#mergePaths(String...)} */ @Deprecated
+    public static String mergeFilePaths(String... items) {
+        return Os.mergePaths(items);
+    }
+    
+    /** @deprecated since 0.7.0; use method {@link Os#tidyPath(String)} */ @Deprecated
+    public static String tidyFilePath(String path) {
+        return Os.tidyPath(path);
+    }
+    
+    /** @deprecated since 0.7.0; use method {@link Urls#getProtocol(String)} */ @Deprecated
+    public static String getProtocol(String url) {
+        return Urls.getProtocol(url);
+    }
+    
+    private InputStream getResourceViaClasspath(String url) throws IOException {
+        assert url.startsWith("classpath:");
+        String subUrl = url.substring("classpath:".length());
+        while (subUrl.startsWith("/")) subUrl = subUrl.substring(1);
+        URL u = getLoader().getResource(subUrl);
+        if (u!=null) return u.openStream();
+        else throw new IOException(subUrl+" not found on classpath");
+    }
+    
+    private InputStream getResourceViaSftp(String url) throws IOException {
+        assert url.startsWith("sftp://");
+        String subUrl = url.substring("sftp://".length());
+        String user;
+        String address;
+        String path;
+        int atIndex = subUrl.indexOf("@");
+        int colonIndex = subUrl.indexOf(":", (atIndex > 0 ? atIndex : 0));
+        if (colonIndex <= 0 || colonIndex <= atIndex) {
+            throw new IllegalArgumentException("Invalid sftp url ("+url+"); IP or hostname must be specified, such as sftp://localhost:/path/to/file");
+        }
+        if (subUrl.length() <= (colonIndex+1)) {
+            throw new IllegalArgumentException("Invalid sftp url ("+url+"); must specify path of remote file, such as sftp://localhost:/path/to/file");
+        }
+        if (atIndex >= 0) {
+            user = subUrl.substring(0, atIndex);
+        } else {
+            user = null;
+        }
+        address = subUrl.substring(atIndex + 1, colonIndex);
+        path = subUrl.substring(colonIndex+1);
+        
+        // TODO messy way to get an SCP session 
+        SshMachineLocation machine = new SshMachineLocation(MutableMap.builder()
+                .putIfNotNull("user", user)
+                .put("address", InetAddress.getByName(address))
+                .build());
+        try {
+            final File tempFile = Os.newTempFile("brooklyn-sftp", "tmp");
+            tempFile.setReadable(true, true);
+            machine.copyFrom(path, tempFile.getAbsolutePath());
+            return new FileInputStream(tempFile) {
+                @Override
+                public void close() throws IOException {
+                    super.close();
+                    tempFile.delete();
+                }
+            };
+        } finally {
+            Streams.closeQuietly(machine);
+        }
+    }
+    
+    //For HTTP(S) targets use HttpClient so
+    //we can do authentication
+    private InputStream getResourceViaHttp(String resource) throws IOException {
+        URI uri = URI.create(resource);
+        HttpClientBuilder builder = HttpTool.httpClientBuilder()
+                .laxRedirect(true)
+                .uri(uri);
+        Credentials credentials = getUrlCredentials(uri.getRawUserInfo());
+        if (credentials != null) {
+            builder.credentials(credentials);
+        }
+        HttpClient client = builder.build();
+        HttpResponse result = client.execute(new HttpGet(resource));
+        int statusCode = result.getStatusLine().getStatusCode();
+        if (HttpTool.isStatusCodeHealthy(statusCode)) {
+            HttpEntity entity = result.getEntity();
+            if (entity != null) {
+                return entity.getContent();
+            } else {
+                return new ByteArrayInputStream(new byte[0]);
+            }
+        } else {
+            EntityUtils.consume(result.getEntity());
+            throw new IllegalStateException("Invalid response invoking " + resource + ": response code " + statusCode);
+        }
+    }
+
+    private Credentials getUrlCredentials(String userInfo) {
+        if (userInfo != null) {
+            String[] arr = userInfo.split(":");
+            String username;
+            String password = null;
+            if (arr.length == 1) {
+                username = urlDecode(arr[0]);
+            } else if (arr.length == 2) {
+                username = urlDecode(arr[0]);
+                password = urlDecode(arr[1]);
+            } else {
+                return null;
+            }
+            return new UsernamePasswordCredentials(username, password);
+        } else {
+            return null;
+        }
+    }
+
+    private String urlDecode(String str) {
+        try {
+            return URLDecoder.decode(str, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    /** takes {@link #getResourceFromUrl(String)} and reads fully, into a string */
+    public String getResourceAsString(String url) {
+        try {
+            return readFullyString(getResourceFromUrl(url));
+        } catch (Exception e) {
+            log.debug("ResourceUtils got error reading "+url+(context==null?"":" "+context)+" (rethrowing): "+e);
+            throw Throwables.propagate(e);
+        }
+    }
+
+    /** allows failing-fast if URL cannot be read */
+    public String checkUrlExists(String url) {
+        return checkUrlExists(url, null);
+    }
+    
+    public String checkUrlExists(String url, String message) {
+        if (url==null) throw new NullPointerException("URL "+(message!=null ? message+" " : "")+"must not be null");
+        InputStream s;
+        try {
+            s = getResourceFromUrl(url);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            throw new IllegalArgumentException("Unable to access URL "+(message!=null ? message : "")+": "+url, e);
+        }
+        Streams.closeQuietly(s); 
+        return url;
+    }
+
+    /** tests whether the url exists, returning true or false */
+    public boolean doesUrlExist(String url) {
+        InputStream s = null;
+        try {
+            s = getResourceFromUrl(url);
+            return true;
+        } catch (Exception e) {
+            return false;
+        } finally {
+            Streams.closeQuietly(s);
+        }
+    }
+    
+    /** returns the first available URL */
+    public Optional<String> firstAvailableUrl(String ...urls) {
+        for (String url: urls) {
+            if (doesUrlExist(url)) return Optional.of(url);
+        }
+        return Optional.absent();
+    }
+    
+    /** returns the base directory or JAR from which the context is class-loaded, if possible;
+     * throws exception if not found */
+    public String getClassLoaderDir() {
+        if (contextObject==null) throw new IllegalArgumentException("No suitable context ("+context+") to auto-detect classloader dir");
+        Class<?> cc = contextObject instanceof Class ? (Class<?>)contextObject : contextObject.getClass();
+        return getClassLoaderDir(cc.getCanonicalName().replace('.', '/')+".class");
+    }
+    
+    public String getClassLoaderDir(String resourceInThatDir) {
+        resourceInThatDir = Strings.removeFromStart(resourceInThatDir, "/");
+        URL resourceUrl = getLoader().getResource(resourceInThatDir);
+        if (resourceUrl==null) throw new NoSuchElementException("Resource ("+resourceInThatDir+") not found");
+
+        URL containerUrl = getContainerUrl(resourceUrl, resourceInThatDir);
+
+        if (!"file".equals(containerUrl.getProtocol())) throw new IllegalStateException("Resource ("+resourceInThatDir+") not on file system (at "+containerUrl+")");
+
+        //convert from file: URL to File
+        File file;
+        try {
+            file = new File(containerUrl.toURI());
+        } catch (URISyntaxException e) {
+            throw new IllegalStateException("Resource ("+resourceInThatDir+") found at invalid URI (" + containerUrl + ")", e);
+        }
+        
+        if (!file.exists()) throw new IllegalStateException("Context class url substring ("+containerUrl+") not found on filesystem");
+        return file.getPath();
+        
+    }
+
+    public static URL getContainerUrl(URL url, String resourceInThatDir) {
+        //Switching from manual parsing of jar: and file: URLs to java provided functionality.
+        //The old code was breaking on any Windows path and instead of fixing it, using
+        //the provided Java APIs seemed like the better option since they are already tested
+        //on multiple platforms.
+        boolean isJar = "jar".equals(url.getProtocol());
+        if(isJar) {
+            try {
+                //let java handle the parsing of jar URL, no network connection is established.
+                //Strips the jar protocol:
+                //  jar:file:/<path to jar>!<resourceInThatDir>
+                //  becomes
+                //  file:/<path to jar>
+                JarURLConnection connection = (JarURLConnection) url.openConnection();
+                url = connection.getJarFileURL();
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        } else {
+            //Remove the trailing resouceInThatDir path from the URL, thus getting the parent folder.
+            String path = url.toString();
+            int i = path.indexOf(resourceInThatDir);
+            if (i==-1) throw new IllegalStateException("Resource path ("+resourceInThatDir+") not in url substring ("+url+")");
+            String parent = path.substring(0, i);
+            try {
+                url = new URL(parent);
+            } catch (MalformedURLException e) {
+                throw new IllegalStateException("Resource ("+resourceInThatDir+") found at invalid URL parent (" + parent + ")", e);
+            }
+        }
+        return url;
+    }
+    
+    /** @deprecated since 0.7.0 use {@link Streams#readFullyString(InputStream) */ @Deprecated
+    public static String readFullyString(InputStream is) throws IOException {
+        return Streams.readFullyString(is);
+    }
+
+    /** @deprecated since 0.7.0 use {@link Streams#readFully(InputStream) */ @Deprecated
+    public static byte[] readFullyBytes(InputStream is) throws IOException {
+        return Streams.readFully(is);
+    }
+    
+    /** @deprecated since 0.7.0 use {@link Streams#copy(InputStream, OutputStream)} */ @Deprecated
+    public static void copy(InputStream input, OutputStream output) throws IOException {
+        Streams.copy(input, output);
+    }
+
+    /** @deprecated since 0.7.0; use same method in {@link Os} */ @Deprecated
+    public static File mkdirs(File dir) {
+        return Os.mkdirs(dir);
+    }
+
+    /** @deprecated since 0.7.0; use same method in {@link Os} */ @Deprecated
+    public static File writeToTempFile(InputStream is, String prefix, String suffix) {
+        return Os.writeToTempFile(is, prefix, suffix);
+    }
+    
+    /** @deprecated since 0.7.0; use same method in {@link Os} */ @Deprecated
+    public static File writeToTempFile(InputStream is, File tempDir, String prefix, String suffix) {
+        return Os.writeToTempFile(is, tempDir, prefix, suffix);
+    }
+
+    /** @deprecated since 0.7.0; use method {@link Os#writePropertiesToTempFile(Properties, String, String)} */ @Deprecated
+    public static File writeToTempFile(Properties props, String prefix, String suffix) {
+        return Os.writePropertiesToTempFile(props, prefix, suffix);
+    }
+    
+    /** @deprecated since 0.7.0; use method {@link Os#writePropertiesToTempFile(Properties, File, String, String)} */ @Deprecated
+    public static File writeToTempFile(Properties props, File tempDir, String prefix, String suffix) {
+        return Os.writePropertiesToTempFile(props, tempDir, prefix, suffix);
+    }
+
+    /** @deprecated since 0.7.0; use method {@link Threads#addShutdownHook(Runnable)} */ @Deprecated
+    public static Thread addShutdownHook(final Runnable task) {
+        return Threads.addShutdownHook(task);
+    }
+    /** @deprecated since 0.7.0; use method {@link Threads#removeShutdownHook(Thread)} */ @Deprecated
+    public static boolean removeShutdownHook(Thread hook) {
+        return Threads.removeShutdownHook(hook);
+    }
+
+    /** returns the items with exactly one "/" between items (whether or not the individual items start or end with /),
+     * except where character before the / is a : (url syntax) in which case it will permit multiple (will not remove any) 
+     * @deprecated since 0.7.0 use either {@link Os#mergePathsUnix(String...)} {@link Urls#mergePaths(String...) */ @Deprecated
+    public static String mergePaths(String ...items) {
+        return Urls.mergePaths(items);
+    }
+}


[24/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/osgi/Osgis.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/osgi/Osgis.java b/core/src/main/java/org/apache/brooklyn/core/util/osgi/Osgis.java
new file mode 100644
index 0000000..849a33b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/osgi/Osgis.java
@@ -0,0 +1,720 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.osgi;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import javax.annotation.Nullable;
+
+import org.apache.felix.framework.FrameworkFactory;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.util.manifestparser.ManifestParser;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.osgi.Osgis;
+
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.collections.MutableSet;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.ReferenceWithError;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.net.Urls;
+import brooklyn.util.os.Os;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.base.Stopwatch;
+
+/** 
+ * utilities for working with osgi.
+ * osgi support is in early days (June 2014) so this class is beta, subject to change,
+ * particularly in how framework is started and bundles installed.
+ * 
+ * @since 0.7.0  */
+@Beta
+public class Osgis {
+    private static final Logger LOG = LoggerFactory.getLogger(Osgis.class);
+
+    private static final String EXTENSION_PROTOCOL = "system";
+    private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
+    private static final Set<String> SYSTEM_BUNDLES = MutableSet.of();
+
+    public static class VersionedName {
+        private final String symbolicName;
+        private final Version version;
+        public VersionedName(Bundle b) {
+            this.symbolicName = b.getSymbolicName();
+            this.version = b.getVersion();
+        }
+        public VersionedName(String symbolicName, Version version) {
+            this.symbolicName = symbolicName;
+            this.version = version;
+        }
+        @Override public String toString() {
+            return symbolicName + ":" + Strings.toString(version);
+        }
+        public boolean equals(String sn, String v) {
+            return symbolicName.equals(sn) && (version == null && v == null || version != null && version.toString().equals(v));
+        }
+        public boolean equals(String sn, Version v) {
+            return symbolicName.equals(sn) && (version == null && v == null || version != null && version.equals(v));
+        }
+        public String getSymbolicName() {
+            return symbolicName;
+        }
+        public Version getVersion() {
+            return version;
+        }
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(symbolicName, version);
+        }
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof VersionedName)) return false;
+            VersionedName o = (VersionedName) other;
+            return Objects.equal(symbolicName, o.symbolicName) && Objects.equal(version, o.version);
+        }
+    }
+    
+    public static class BundleFinder {
+        protected final Framework framework;
+        protected String symbolicName;
+        protected String version;
+        protected String url;
+        protected boolean urlMandatory = false;
+        protected final List<Predicate<? super Bundle>> predicates = MutableList.of();
+        
+        protected BundleFinder(Framework framework) {
+            this.framework = framework;
+        }
+
+        public BundleFinder symbolicName(String symbolicName) {
+            this.symbolicName = symbolicName;
+            return this;
+        }
+
+        public BundleFinder version(String version) {
+            this.version = version;
+            return this;
+        }
+        
+        public BundleFinder id(String symbolicNameOptionallyWithVersion) {
+            if (Strings.isBlank(symbolicNameOptionallyWithVersion))
+                return this;
+            
+            Maybe<VersionedName> nv = parseOsgiIdentifier(symbolicNameOptionallyWithVersion);
+            if (nv.isAbsent())
+                throw new IllegalArgumentException("Cannot parse symbolic-name:version string '"+symbolicNameOptionallyWithVersion+"'");
+
+            return id(nv.get());
+        }
+
+        private BundleFinder id(VersionedName nv) {
+            symbolicName(nv.getSymbolicName());
+            if (nv.getVersion() != null) {
+                version(nv.getVersion().toString());
+            }
+            return this;
+        }
+
+        public BundleFinder bundle(CatalogBundle bundle) {
+            if (bundle.isNamed()) {
+                symbolicName(bundle.getSymbolicName());
+                version(bundle.getVersion());
+            }
+            if (bundle.getUrl() != null) {
+                requiringFromUrl(bundle.getUrl());
+            }
+            return this;
+        }
+
+        /** Looks for a bundle matching the given URL;
+         * unlike {@link #requiringFromUrl(String)} however, if the URL does not match any bundles
+         * it will return other matching bundles <i>if</if> a {@link #symbolicName(String)} is specified.
+         */
+        public BundleFinder preferringFromUrl(String url) {
+            this.url = url;
+            urlMandatory = false;
+            return this;
+        }
+
+        /** Requires the bundle to have the given URL set as its location. */
+        public BundleFinder requiringFromUrl(String url) {
+            this.url = url;
+            urlMandatory = true;
+            return this;
+        }
+
+        /** Finds the best matching bundle. */
+        public Maybe<Bundle> find() {
+            return findOne(false);
+        }
+        
+        /** Finds the matching bundle, requiring it to be unique. */
+        public Maybe<Bundle> findUnique() {
+            return findOne(true);
+        }
+
+        protected Maybe<Bundle> findOne(boolean requireExactlyOne) {
+            if (symbolicName==null && url==null)
+                throw new IllegalStateException(this+" must be given either a symbolic name or a URL");
+            
+            List<Bundle> result = findAll();
+            if (result.isEmpty())
+                return Maybe.absent("No bundle matching "+getConstraintsDescription());
+            if (requireExactlyOne && result.size()>1)
+                return Maybe.absent("Multiple bundles ("+result.size()+") matching "+getConstraintsDescription());
+            
+            return Maybe.of(result.get(0));
+        }
+        
+        /** Finds all matching bundles, in decreasing version order. */
+        public List<Bundle> findAll() {
+            boolean urlMatched = false;
+            List<Bundle> result = MutableList.of();
+            for (Bundle b: framework.getBundleContext().getBundles()) {
+                if (symbolicName!=null && !symbolicName.equals(b.getSymbolicName())) continue;
+                if (version!=null && !Version.parseVersion(version).equals(b.getVersion())) continue;
+                for (Predicate<? super Bundle> predicate: predicates) {
+                    if (!predicate.apply(b)) continue;
+                }
+
+                // check url last, because if it isn't mandatory we should only clear if we find a url
+                // for which the other items also match
+                if (url!=null) {
+                    boolean matches = url.equals(b.getLocation());
+                    if (urlMandatory) {
+                        if (!matches) continue;
+                        else urlMatched = true;
+                    } else {
+                        if (matches) {
+                            if (!urlMatched) {
+                                result.clear();
+                                urlMatched = true;
+                            }
+                        } else {
+                            if (urlMatched) {
+                                // can't use this bundle as we have previously found a preferred bundle, with a matching url
+                                continue;
+                            }
+                        }
+                    }
+                }
+                                
+                result.add(b);
+            }
+            
+            if (symbolicName==null && url!=null && !urlMatched) {
+                // if we only "preferred" the url, and we did not match it, and we did not have a symbolic name,
+                // then clear the results list!
+                result.clear();
+            }
+
+            Collections.sort(result, new Comparator<Bundle>() {
+                @Override
+                public int compare(Bundle o1, Bundle o2) {
+                    return o2.getVersion().compareTo(o1.getVersion());
+                }
+            });
+            
+            return result;
+        }
+        
+        public String getConstraintsDescription() {
+            List<String> parts = MutableList.of();
+            if (symbolicName!=null) parts.add("symbolicName="+symbolicName);
+            if (version!=null) parts.add("version="+version);
+            if (url!=null)
+                parts.add("url["+(urlMandatory ? "required" : "preferred")+"]="+url);
+            if (!predicates.isEmpty())
+                parts.add("predicates="+predicates);
+            return Joiner.on(";").join(parts);
+        }
+        
+        public String toString() {
+            return getClass().getCanonicalName()+"["+getConstraintsDescription()+"]";
+        }
+
+        public BundleFinder version(final Predicate<Version> versionPredicate) {
+            return satisfying(new Predicate<Bundle>() {
+                @Override
+                public boolean apply(Bundle input) {
+                    return versionPredicate.apply(input.getVersion());
+                }
+            });
+        }
+        
+        public BundleFinder satisfying(Predicate<? super Bundle> predicate) {
+            predicates.add(predicate);
+            return this;
+        }
+    }
+    
+    public static BundleFinder bundleFinder(Framework framework) {
+        return new BundleFinder(framework);
+    }
+
+    /** @deprecated since 0.7.0 use {@link #bundleFinder(Framework)} */ @Deprecated
+    public static List<Bundle> getBundlesByName(Framework framework, String symbolicName, Predicate<Version> versionMatcher) {
+        return bundleFinder(framework).symbolicName(symbolicName).version(versionMatcher).findAll();
+    }
+
+    /** @deprecated since 0.7.0 use {@link #bundleFinder(Framework)} */ @Deprecated
+    public static List<Bundle> getBundlesByName(Framework framework, String symbolicName) {
+        return bundleFinder(framework).symbolicName(symbolicName).findAll();
+    }
+
+    /**
+     * Tries to find a bundle in the given framework with name matching either `name' or `name:version'.
+     * @deprecated since 0.7.0 use {@link #bundleFinder(Framework)} */ @Deprecated
+    public static Maybe<Bundle> getBundle(Framework framework, String symbolicNameOptionallyWithVersion) {
+        return bundleFinder(framework).id(symbolicNameOptionallyWithVersion).find();
+    }
+    
+    /** @deprecated since 0.7.0 use {@link #bundleFinder(Framework)} */ @Deprecated
+    public static Maybe<Bundle> getBundle(Framework framework, String symbolicName, String version) {
+        return bundleFinder(framework).symbolicName(symbolicName).version(version).find();
+    }
+
+    /** @deprecated since 0.7.0 use {@link #bundleFinder(Framework)} */ @Deprecated
+    public static Maybe<Bundle> getBundle(Framework framework, String symbolicName, Version version) {
+        return bundleFinder(framework).symbolicName(symbolicName).version(Predicates.equalTo(version)).findUnique();
+    }
+
+    // -------- creating
+    
+    /*
+     * loading framework factory and starting framework based on:
+     * http://felix.apache.org/documentation/subprojects/apache-felix-framework/apache-felix-framework-launching-and-embedding.html
+     */
+    
+    public static FrameworkFactory newFrameworkFactory() {
+        URL url = Osgis.class.getClassLoader().getResource(
+                "META-INF/services/org.osgi.framework.launch.FrameworkFactory");
+        if (url != null) {
+            try {
+                BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
+                try {
+                    for (String s = br.readLine(); s != null; s = br.readLine()) {
+                        s = s.trim();
+                        // load the first non-empty, non-commented line
+                        if ((s.length() > 0) && (s.charAt(0) != '#')) {
+                            return (FrameworkFactory) Class.forName(s).newInstance();
+                        }
+                    }
+                } finally {
+                    if (br != null) br.close();
+                }
+            } catch (Exception e) {
+                // class creation exceptions are not interesting to caller...
+                throw Exceptions.propagate(e);
+            }
+        }
+        throw new IllegalStateException("Could not find framework factory.");
+    }
+    
+    public static Framework newFrameworkStarted(String felixCacheDir, boolean clean, Map<?,?> extraStartupConfig) {
+        Map<Object,Object> cfg = MutableMap.copyOf(extraStartupConfig);
+        if (clean) cfg.put(Constants.FRAMEWORK_STORAGE_CLEAN, "onFirstInit");
+        if (felixCacheDir!=null) cfg.put(Constants.FRAMEWORK_STORAGE, felixCacheDir);
+        cfg.put(Constants.FRAMEWORK_BSNVERSION, Constants.FRAMEWORK_BSNVERSION_MULTIPLE);
+        FrameworkFactory factory = newFrameworkFactory();
+
+        Stopwatch timer = Stopwatch.createStarted();
+        Framework framework = factory.newFramework(cfg);
+        try {
+            framework.init();
+            installBootBundles(framework);
+            framework.start();
+        } catch (Exception e) {
+            // framework bundle start exceptions are not interesting to caller...
+            throw Exceptions.propagate(e);
+        }
+        LOG.debug("System bundles are: "+SYSTEM_BUNDLES);
+        LOG.debug("OSGi framework started in " + Duration.of(timer));
+        return framework;
+    }
+
+    private static void installBootBundles(Framework framework) {
+        Stopwatch timer = Stopwatch.createStarted();
+        LOG.debug("Installing OSGi boot bundles from "+Osgis.class.getClassLoader()+"...");
+        Enumeration<URL> resources;
+        try {
+            resources = Osgis.class.getClassLoader().getResources(MANIFEST_PATH);
+        } catch (IOException e) {
+            throw Exceptions.propagate(e);
+        }
+        BundleContext bundleContext = framework.getBundleContext();
+        Map<String, Bundle> installedBundles = getInstalledBundlesById(bundleContext);
+        while(resources.hasMoreElements()) {
+            URL url = resources.nextElement();
+            ReferenceWithError<?> installResult = installExtensionBundle(bundleContext, url, installedBundles, getVersionedId(framework));
+            if (installResult.hasError() && !installResult.masksErrorIfPresent()) {
+                // it's reported as a critical error, so warn here
+                LOG.warn("Unable to install manifest from "+url+": "+installResult.getError(), installResult.getError());
+            } else {
+                Object result = installResult.getWithoutError();
+                if (result instanceof Bundle) {
+                    String v = getVersionedId( (Bundle)result );
+                    SYSTEM_BUNDLES.add(v);
+                    if (installResult.hasError()) {
+                        LOG.debug(installResult.getError().getMessage()+(result!=null ? " ("+result+"/"+v+")" : ""));
+                    } else {
+                        LOG.debug("Installed "+v+" from "+url);
+                    }
+                } else if (installResult.hasError()) {
+                    LOG.debug(installResult.getError().getMessage());
+                }
+            }
+        }
+        LOG.debug("Installed OSGi boot bundles in "+Time.makeTimeStringRounded(timer)+": "+Arrays.asList(framework.getBundleContext().getBundles()));
+    }
+
+    private static Map<String, Bundle> getInstalledBundlesById(BundleContext bundleContext) {
+        Map<String, Bundle> installedBundles = new HashMap<String, Bundle>();
+        Bundle[] bundles = bundleContext.getBundles();
+        for (Bundle b : bundles) {
+            installedBundles.put(getVersionedId(b), b);
+        }
+        return installedBundles;
+    }
+
+    /** Wraps the bundle if successful or already installed, wraps TRUE if it's the system entry,
+     * wraps null if the bundle is already installed from somewhere else;
+     * in all these cases <i>masking</i> an explanatory error if already installed or it's the system entry.
+     * <p>
+     * Returns an instance wrapping null and <i>throwing</i> an error if the bundle could not be installed.
+     */
+    private static ReferenceWithError<?> installExtensionBundle(BundleContext bundleContext, URL manifestUrl, Map<String, Bundle> installedBundles, String frameworkVersionedId) {
+        //ignore http://felix.extensions:9/ system entry
+        if("felix.extensions".equals(manifestUrl.getHost())) 
+            return ReferenceWithError.newInstanceMaskingError(null, new IllegalArgumentException("Skipping install of internal extension bundle from "+manifestUrl));
+
+        try {
+            Manifest manifest = readManifest(manifestUrl);
+            if (!isValidBundle(manifest)) 
+                return ReferenceWithError.newInstanceMaskingError(null, new IllegalArgumentException("Resource at "+manifestUrl+" is not an OSGi bundle: no valid manifest"));
+            
+            String versionedId = getVersionedId(manifest);
+            URL bundleUrl = ResourceUtils.getContainerUrl(manifestUrl, MANIFEST_PATH);
+
+            Bundle existingBundle = installedBundles.get(versionedId);
+            if (existingBundle != null) {
+                if (!bundleUrl.equals(existingBundle.getLocation()) &&
+                        //the framework bundle is always pre-installed, don't display duplicate info
+                        !versionedId.equals(frameworkVersionedId)) {
+                    return ReferenceWithError.newInstanceMaskingError(null, new IllegalArgumentException("Bundle "+versionedId+" (from manifest " + manifestUrl + ") is already installed, from " + existingBundle.getLocation()));
+                }
+                return ReferenceWithError.newInstanceMaskingError(existingBundle, new IllegalArgumentException("Bundle "+versionedId+" from manifest " + manifestUrl + " is already installed"));
+            }
+            
+            byte[] jar = buildExtensionBundle(manifest);
+            LOG.debug("Installing boot bundle " + bundleUrl);
+            //mark the bundle as extension so we can detect it later using the "system:" protocol
+            //(since we cannot access BundleImpl.isExtension)
+            Bundle newBundle = bundleContext.installBundle(EXTENSION_PROTOCOL + ":" + bundleUrl.toString(), new ByteArrayInputStream(jar));
+            installedBundles.put(versionedId, newBundle);
+            return ReferenceWithError.newInstanceWithoutError(newBundle);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            return ReferenceWithError.newInstanceThrowingError(null, 
+                new IllegalStateException("Problem installing extension bundle " + manifestUrl + ": "+e, e));
+        }
+    }
+
+    private static Manifest readManifest(URL manifestUrl) throws IOException {
+        Manifest manifest;
+        InputStream in = null;
+        try {
+            in = manifestUrl.openStream();
+            manifest = new Manifest(in);
+        } finally {
+            if (in != null) {
+                try {in.close();} 
+                catch (Exception e) {};
+            }
+        }
+        return manifest;
+    }
+
+    private static byte[] buildExtensionBundle(Manifest manifest) throws IOException {
+        Attributes atts = manifest.getMainAttributes();
+
+        //the following properties are invalid in extension bundles
+        atts.remove(new Attributes.Name(Constants.IMPORT_PACKAGE));
+        atts.remove(new Attributes.Name(Constants.REQUIRE_BUNDLE));
+        atts.remove(new Attributes.Name(Constants.BUNDLE_NATIVECODE));
+        atts.remove(new Attributes.Name(Constants.DYNAMICIMPORT_PACKAGE));
+        atts.remove(new Attributes.Name(Constants.BUNDLE_ACTIVATOR));
+        
+        //mark as extension bundle
+        atts.putValue(Constants.FRAGMENT_HOST, "system.bundle; extension:=framework");
+
+        //create the jar containing the manifest
+        ByteArrayOutputStream jar = new ByteArrayOutputStream();
+        JarOutputStream out = new JarOutputStream(jar, manifest);
+        out.close();
+        return jar.toByteArray();
+    }
+
+    private static boolean isValidBundle(Manifest manifest) {
+        Attributes atts = manifest.getMainAttributes();
+        return atts.containsKey(new Attributes.Name(Constants.BUNDLE_MANIFESTVERSION));
+    }
+
+    private static String getVersionedId(Bundle b) {
+        return b.getSymbolicName() + ":" + b.getVersion();
+    }
+
+    private static String getVersionedId(Manifest manifest) {
+        Attributes atts = manifest.getMainAttributes();
+        return atts.getValue(Constants.BUNDLE_SYMBOLICNAME) + ":" +
+            atts.getValue(Constants.BUNDLE_VERSION);
+    }
+
+    /**
+     * Installs a bundle from the given URL, doing a check if already installed, and
+     * using the {@link ResourceUtils} loader for this project (brooklyn core)
+     */
+    public static Bundle install(Framework framework, String url) throws BundleException {
+        boolean isLocal = isLocalUrl(url);
+        String localUrl = url;
+        if (!isLocal) {
+            localUrl = cacheFile(url);
+        }
+
+        try {
+            Bundle bundle = getInstalledBundle(framework, localUrl);
+            if (bundle != null) {
+                return bundle;
+            }
+    
+            // use our URL resolution so we get classpath items
+            LOG.debug("Installing bundle into {} from url: {}", framework, url);
+            InputStream stream = getUrlStream(localUrl);
+            Bundle installedBundle = framework.getBundleContext().installBundle(url, stream);
+            
+            return installedBundle;
+        } finally {
+            if (!isLocal) {
+                try {
+                    new File(new URI(localUrl)).delete();
+                } catch (URISyntaxException e) {
+                    throw Exceptions.propagate(e);
+                }
+            }
+        }
+    }
+
+    private static String cacheFile(String url) {
+        InputStream in = getUrlStream(url);
+        File cache = Os.writeToTempFile(in, "bundle-cache", "jar");
+        return cache.toURI().toString();
+    }
+
+    private static boolean isLocalUrl(String url) {
+        String protocol = Urls.getProtocol(url);
+        return "file".equals(protocol) ||
+                "classpath".equals(protocol) ||
+                "jar".equals(protocol);
+    }
+
+    private static Bundle getInstalledBundle(Framework framework, String url) {
+        Bundle bundle = framework.getBundleContext().getBundle(url);
+        if (bundle != null) {
+            return bundle;
+        }
+
+        // We now support same version installed multiple times (avail since OSGi 4.3+).
+        // However we do not support overriding *system* bundles, ie anything already on the classpath.
+        // If we wanted to disable multiple versions, see comments below, and reference to FRAMEWORK_BSNVERSION_MULTIPLE above.
+        
+        // Felix already assumes the stream is pointing to a JAR
+        JarInputStream stream;
+        try {
+            stream = new JarInputStream(getUrlStream(url));
+        } catch (IOException e) {
+            throw Exceptions.propagate(e);
+        }
+        Manifest manifest = stream.getManifest();
+        Streams.closeQuietly(stream);
+        if (manifest == null) {
+            throw new IllegalStateException("Missing manifest file in bundle or not a jar file.");
+        }
+        String versionedId = getVersionedId(manifest);
+        for (Bundle installedBundle : framework.getBundleContext().getBundles()) {
+            if (versionedId.equals(getVersionedId(installedBundle))) {
+                if (SYSTEM_BUNDLES.contains(versionedId)) {
+                    LOG.debug("Already have system bundle "+versionedId+" from "+installedBundle+"/"+installedBundle.getLocation()+" when requested "+url+"; not installing");
+                    // "System bundles" (ie things on the classpath) cannot be overridden
+                    return installedBundle;
+                } else {
+                    LOG.debug("Already have bundle "+versionedId+" from "+installedBundle+"/"+installedBundle.getLocation()+" when requested "+url+"; but it is not a system bundle so proceeding");
+                    // Other bundles can be installed multiple times. To ignore multiples and continue to use the old one, 
+                    // just return the installedBundle as done just above for system bundles.
+                }
+            }
+        }
+        return null;
+    }
+
+    private static InputStream getUrlStream(String url) {
+        return ResourceUtils.create(Osgis.class).getResourceFromUrl(url);
+    }
+    
+    public static boolean isExtensionBundle(Bundle bundle) {
+        String location = bundle.getLocation();
+        return location != null && 
+                EXTENSION_PROTOCOL.equals(Urls.getProtocol(location));
+    }
+
+    /** Takes a string which might be of the form "symbolic-name" or "symbolic-name:version" (or something else entirely)
+     * and returns a VersionedName. The versionedName.getVersion() will be null if if there was no version in the input
+     * (or returning {@link Maybe#absent()} if not valid, with a suitable error message). */
+    public static Maybe<VersionedName> parseOsgiIdentifier(String symbolicNameOptionalWithVersion) {
+        if (Strings.isBlank(symbolicNameOptionalWithVersion))
+            return Maybe.absent("OSGi identifier is blank");
+        
+        String[] parts = symbolicNameOptionalWithVersion.split(":");
+        if (parts.length>2)
+            return Maybe.absent("OSGi identifier has too many parts; max one ':' symbol");
+        
+        Version v = null;
+        if (parts.length == 2) {
+            try {
+                v = Version.parseVersion(parts[1]);
+            } catch (IllegalArgumentException e) {
+                return Maybe.absent("OSGi identifier has invalid version string ("+e.getMessage()+")");
+            }
+        }
+        
+        return Maybe.of(new VersionedName(parts[0], v));
+    }
+
+    /**
+     * The class is not used, staying for future reference.
+     * Remove after OSGi transition is completed.
+     */
+    public static class ManifestHelper {
+        
+        private static ManifestParser parse;
+        private Manifest manifest;
+        private String source;
+
+        private static final String WIRING_PACKAGE = PackageNamespace.PACKAGE_NAMESPACE;
+        
+        public static ManifestHelper forManifestContents(String contents) throws IOException, BundleException {
+            ManifestHelper result = forManifest(Streams.newInputStreamWithContents(contents));
+            result.source = contents;
+            return result;
+        }
+        
+        public static ManifestHelper forManifest(URL url) throws IOException, BundleException {
+            InputStream in = null;
+            try {
+                in = url.openStream();
+                return forManifest(in);
+            } finally {
+                if (in != null) in.close();
+            }
+        }
+        
+        public static ManifestHelper forManifest(InputStream in) throws IOException, BundleException {
+            return forManifest(new Manifest(in));
+        }
+
+        public static ManifestHelper forManifest(Manifest manifest) throws BundleException {
+            ManifestHelper result = new ManifestHelper();
+            result.manifest = manifest;
+            parse = new ManifestParser(null, null, null, new StringMap(manifest.getMainAttributes()));
+            return result;
+        }
+        
+        public String getSymbolicName() {
+            return parse.getSymbolicName();
+        }
+
+        public Version getVersion() {
+            return parse.getBundleVersion();
+        }
+
+        public String getSymbolicNameVersion() {
+            return getSymbolicName()+":"+getVersion();
+        }
+
+        public List<String> getExportedPackages() {
+            MutableList<String> result = MutableList.of();
+            for (BundleCapability c: parse.getCapabilities()) {
+                if (WIRING_PACKAGE.equals(c.getNamespace())) {
+                    result.add((String)c.getAttributes().get(WIRING_PACKAGE));
+                }
+            }
+            return result;
+        }
+        
+        @Nullable public String getSource() {
+            return source;
+        }
+        
+        public Manifest getManifest() {
+            return manifest;
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/AbstractExecutionContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/AbstractExecutionContext.java b/core/src/main/java/org/apache/brooklyn/core/util/task/AbstractExecutionContext.java
new file mode 100644
index 0000000..3eea5d9
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/AbstractExecutionContext.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.ExecutionManager;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskAdaptable;
+
+import com.google.common.collect.Maps;
+
+public abstract class AbstractExecutionContext implements ExecutionContext {
+
+    /**
+     * Submits the given runnable/callable/task for execution (in a separate thread);
+     * supported keys in the map include: tags (add'l tags to put on the resulting task), 
+     * description (string), and others as described in the reference below
+     *   
+     * @see ExecutionManager#submit(Map, Task) 
+     */
+    @Override
+    public Task<?> submit(Map<?,?> properties, Runnable runnable) { return submitInternal(properties, runnable); }
+    
+    /** @see #submit(Map, Runnable) */
+    @Override
+    public Task<?> submit(Runnable runnable) { return submitInternal(Maps.newLinkedHashMap(), runnable); }
+ 
+    /** @see #submit(Map, Runnable) */
+    @Override
+    public <T> Task<T> submit(Callable<T> callable) { return submitInternal(Maps.newLinkedHashMap(), callable); }
+    
+    /** @see #submit(Map, Runnable) */
+    @Override
+    public <T> Task<T> submit(Map<?,?> properties, Callable<T> callable) { return submitInternal(properties, callable); }
+ 
+    /** @see #submit(Map, Runnable) */
+    @Override
+    public <T> Task<T> submit(TaskAdaptable<T> task) { return submitInternal(Maps.newLinkedHashMap(), task.asTask()); }
+
+    /** @see #submit(Map, Runnable) */
+    @Override
+    public <T> Task<T> submit(Map<?,?> properties, TaskAdaptable<T> task) { return submitInternal(properties, task.asTask()); }
+
+    /**
+     * Provided for compatibility
+     * 
+     * Submit is preferred if a handle on the resulting Task is desired (although a task can be passed in so this is not always necessary) 
+     *
+     * @see #submit(Map, Runnable) 
+     */
+    public void execute(Runnable r) { submit(r); }
+
+    /** does the work internally of submitting the task; note that the return value may be a wrapper task even if a task is passed in,
+     * if the execution context where the target should run is different (e.g. submitting an effector task cross-context) */
+    protected abstract <T> Task<T> submitInternal(Map<?,?> properties, Object task);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/BasicExecutionContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/BasicExecutionContext.java b/core/src/main/java/org/apache/brooklyn/core/util/task/BasicExecutionContext.java
new file mode 100644
index 0000000..1d28b4e
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/BasicExecutionContext.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.ExecutionManager;
+import org.apache.brooklyn.api.management.HasTaskChildren;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.BrooklynTaskTags.WrappedEntity;
+import brooklyn.entity.basic.EntityInternal;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+
+/**
+ * A means of executing tasks against an ExecutionManager with a given bucket/set of tags pre-defined
+ * (so that it can look like an {@link Executor} and also supply {@link ExecutorService#submit(Callable)}
+ */
+public class BasicExecutionContext extends AbstractExecutionContext {
+    
+    private static final Logger log = LoggerFactory.getLogger(BasicExecutionContext.class);
+    
+    static final ThreadLocal<BasicExecutionContext> perThreadExecutionContext = new ThreadLocal<BasicExecutionContext>();
+    
+    public static BasicExecutionContext getCurrentExecutionContext() { return perThreadExecutionContext.get(); }
+
+    final ExecutionManager executionManager;
+    final Set<Object> tags = new LinkedHashSet<Object>();
+
+    public BasicExecutionContext(ExecutionManager executionManager) {
+        this(Collections.emptyMap(), executionManager);
+    }
+    
+    /**
+     * Supported flags are {@code tag} and {@code tags}
+     * 
+     * @see ExecutionManager#submit(Map, Task)
+     */
+    public BasicExecutionContext(Map<?, ?> flags, ExecutionManager executionManager) {
+        this.executionManager = executionManager;
+
+        if (flags.get("tag") != null) tags.add(flags.remove("tag"));
+        if (flags.containsKey("tags")) tags.addAll((Collection<?>)flags.remove("tags"));
+
+        // FIXME brooklyn-specific check, just for sanity
+        // the context tag should always be a non-proxy entity, because that is what is passed to effector tasks
+        // which may require access to internal methods
+        for (Object tag: tags) {
+            if (tag instanceof BrooklynTaskTags.WrappedEntity) {
+                if (Proxy.isProxyClass(((WrappedEntity)tag).entity.getClass())) {
+                    log.warn(""+this+" has entity proxy in "+tag);
+                }
+            }
+        }
+    }
+
+    public ExecutionManager getExecutionManager() {
+        return executionManager;
+    }
+    
+    /** returns tasks started by this context (or tasks which have all the tags on this object) */
+    public Set<Task<?>> getTasks() { return executionManager.getTasksWithAllTags((Set<?>)tags); }
+     
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    protected <T> Task<T> submitInternal(Map<?,?> propertiesQ, final Object task) {
+        if (task instanceof TaskAdaptable<?> && !(task instanceof Task<?>)) 
+            return submitInternal(propertiesQ, ((TaskAdaptable<?>)task).asTask());
+        
+        Map properties = propertiesQ;
+        if (properties.get("tags")==null) properties.put("tags", new ArrayList()); 
+        Collection taskTags = (Collection)properties.get("tags");
+        
+        // FIXME some of this is brooklyn-specific logic, should be moved to a BrooklynExecContext subclass;
+        // the issue is that we want to ensure that cross-entity calls switch execution contexts;
+        // previously it was all very messy how that was handled (and it didn't really handle it in many cases)
+        if (task instanceof Task<?>) taskTags.addAll( ((Task<?>)task).getTags() ); 
+        Entity target = BrooklynTaskTags.getWrappedEntityOfType(taskTags, BrooklynTaskTags.TARGET_ENTITY);
+        
+        if (target!=null && !tags.contains(BrooklynTaskTags.tagForContextEntity(target))) {
+            // task is switching execution context boundaries
+            /* 
+             * longer notes:
+             * you fall in to this block if the caller requests a target entity different to the current context 
+             * (e.g. where entity X is invoking an effector on Y, it will start in X's context, 
+             * but the effector should run in Y's context).
+             * 
+             * if X is invoking an effector on himself in his own context, or a sensor or other task, it will not come in to this block.
+             */
+            final ExecutionContext tc = ((EntityInternal)target).getExecutionContext();
+            if (log.isDebugEnabled())
+                log.debug("Switching task context on execution of "+task+": from "+this+" to "+target+" (in "+Tasks.current()+")");
+            
+            if (task instanceof Task<?>) {
+                final Task<T> t = (Task<T>)task;
+                if (!Tasks.isQueuedOrSubmitted(t) && (!(Tasks.current() instanceof HasTaskChildren) || 
+                        !Iterables.contains( ((HasTaskChildren)Tasks.current()).getChildren(), t ))) {
+                    // this task is switching execution context boundaries _and_ it is not a child and not yet queued,
+                    // so wrap it in a task running in this context to keep a reference to the child
+                    // (this matters when we are navigating in the GUI; without it we lose the reference to the child 
+                    // when browsing in the context of the parent)
+                    return submit(Tasks.<T>builder().name("Cross-context execution: "+t.getDescription()).dynamic(true).body(new Callable<T>() {
+                        public T call() { 
+                            return DynamicTasks.get(t); 
+                        }
+                    }).build());
+                } else {
+                    // if we are already tracked by parent, just submit it 
+                    return tc.submit(t);
+                }
+            } else {
+                // as above, but here we are definitely not a child (what we are submitting isn't even a task)
+                // (will only come here if properties defines tags including a target entity, which probably never happens) 
+                submit(Tasks.<T>builder().name("Cross-context execution").dynamic(true).body(new Callable<T>() {
+                    public T call() {
+                        if (task instanceof Callable) {
+                            return DynamicTasks.queue( Tasks.<T>builder().dynamic(false).body((Callable<T>)task).build() ).getUnchecked();
+                        } else if (task instanceof Runnable) {
+                            return DynamicTasks.queue( Tasks.<T>builder().dynamic(false).body((Runnable)task).build() ).getUnchecked();
+                        } else {
+                            throw new IllegalArgumentException("Unhandled task type: "+task+"; type="+(task!=null ? task.getClass() : "null"));
+                        }
+                    }
+                }).build());
+            }
+        }
+        
+        EntitlementContext entitlementContext = BrooklynTaskTags.getEntitlement(taskTags);
+        if (entitlementContext==null)
+        entitlementContext = Entitlements.getEntitlementContext();
+        if (entitlementContext!=null) {
+            taskTags.add(BrooklynTaskTags.tagForEntitlement(entitlementContext));
+        }
+
+        taskTags.addAll(tags);
+        
+        if (Tasks.current()!=null && BrooklynTaskTags.isTransient(Tasks.current()) 
+                && !taskTags.contains(BrooklynTaskTags.NON_TRANSIENT_TASK_TAG) && !taskTags.contains(BrooklynTaskTags.TRANSIENT_TASK_TAG)) {
+            // tag as transient if submitter is transient, unless explicitly tagged as non-transient
+            taskTags.add(BrooklynTaskTags.TRANSIENT_TASK_TAG);
+        }
+        
+        final Object startCallback = properties.get("newTaskStartCallback");
+        properties.put("newTaskStartCallback", new Function<Object,Void>() {
+            public Void apply(Object it) {
+                registerPerThreadExecutionContext();
+                if (startCallback!=null) ExecutionUtils.invoke(startCallback, it);
+                return null;
+            }});
+        
+        final Object endCallback = properties.get("newTaskEndCallback");
+        properties.put("newTaskEndCallback", new Function<Object,Void>() {
+            public Void apply(Object it) {
+                try {
+                    if (endCallback!=null) ExecutionUtils.invoke(endCallback, it);
+                } finally {
+                    clearPerThreadExecutionContext();
+                }
+                return null;
+            }});
+        
+        if (task instanceof Task) {
+            return executionManager.submit(properties, (Task)task);
+        } else if (task instanceof Callable) {
+            return executionManager.submit(properties, (Callable)task);
+        } else if (task instanceof Runnable) {
+            return (Task<T>) executionManager.submit(properties, (Runnable)task);
+        } else {
+            throw new IllegalArgumentException("Unhandled task type: task="+task+"; type="+(task!=null ? task.getClass() : "null"));
+        }
+    }
+    
+    private void registerPerThreadExecutionContext() { perThreadExecutionContext.set(this); }
+
+    private void clearPerThreadExecutionContext() { perThreadExecutionContext.remove(); }
+
+    @Override
+    public boolean isShutdown() {
+        return getExecutionManager().isShutdown();
+    }
+
+    @Override
+    public String toString() {
+        return super.toString()+"("+tags+")";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/BasicExecutionManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/BasicExecutionManager.java b/core/src/main/java/org/apache/brooklyn/core/util/task/BasicExecutionManager.java
new file mode 100644
index 0000000..f93abe1
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/BasicExecutionManager.java
@@ -0,0 +1,755 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.brooklyn.api.management.ExecutionManager;
+import org.apache.brooklyn.api.management.HasTaskChildren;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.text.Identifiers;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ExecutionList;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+/**
+ * Manages the execution of atomic tasks and scheduled (recurring) tasks,
+ * including setting tags and invoking callbacks.
+ */
+public class BasicExecutionManager implements ExecutionManager {
+    private static final Logger log = LoggerFactory.getLogger(BasicExecutionManager.class);
+
+    private static final boolean RENAME_THREADS = BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_RENAME_THREADS);
+    
+    private static class PerThreadCurrentTaskHolder {
+        public static final ThreadLocal<Task<?>> perThreadCurrentTask = new ThreadLocal<Task<?>>();
+    }
+
+    public static ThreadLocal<Task<?>> getPerThreadCurrentTask() {
+        return PerThreadCurrentTaskHolder.perThreadCurrentTask;
+    }
+
+    private final ThreadFactory threadFactory;
+    
+    private final ThreadFactory daemonThreadFactory;
+    
+    private final ExecutorService runner;
+        
+    private final ScheduledExecutorService delayedRunner;
+    
+    // TODO Could have a set of all knownTasks; but instead we're having a separate set per tag,
+    // so the same task could be listed multiple times if it has multiple tags...
+
+    //access to this field AND to members in this field is synchronized, 
+    //to allow us to preserve order while guaranteeing thread-safe
+    //(but more testing is needed before we are completely sure it is thread-safe!)
+    //synch blocks are as finely grained as possible for efficiency;
+    //NB CopyOnWriteArraySet is a perf bottleneck, and the simple map makes it easier to remove when a tag is empty
+    private Map<Object,Set<Task<?>>> tasksByTag = new HashMap<Object,Set<Task<?>>>();
+    
+    private ConcurrentMap<String,Task<?>> tasksById = new ConcurrentHashMap<String,Task<?>>();
+
+    private ConcurrentMap<Object, TaskScheduler> schedulerByTag = new ConcurrentHashMap<Object, TaskScheduler>();
+
+    /** count of all tasks submitted, including finished */
+    private final AtomicLong totalTaskCount = new AtomicLong();
+    
+    /** tasks submitted but not yet done (or in cases of interruption/cancelled not yet GC'd) */
+    private Map<String,String> incompleteTaskIds = new ConcurrentHashMap<String,String>();
+    
+    /** tasks started but not yet finished */
+    private final AtomicInteger activeTaskCount = new AtomicInteger();
+    
+    private final List<ExecutionListener> listeners = new CopyOnWriteArrayList<ExecutionListener>();
+    
+    private final static ThreadLocal<String> threadOriginalName = new ThreadLocal<String>() {
+        protected String initialValue() {
+            // should not happen, as only access is in _afterEnd with a check that _beforeStart was invoked 
+            log.warn("No original name recorded for thread "+Thread.currentThread().getName()+"; task "+Tasks.current());
+            return "brooklyn-thread-pool-"+Identifiers.makeRandomId(8);
+        }
+    };
+    
+    public BasicExecutionManager(String contextid) {
+        threadFactory = newThreadFactory(contextid);
+        daemonThreadFactory = new ThreadFactoryBuilder()
+                .setThreadFactory(threadFactory)
+                .setDaemon(true)
+                .build();
+                
+        // use Executors.newCachedThreadPool(daemonThreadFactory), but timeout of 1s rather than 60s for better shutdown!
+        runner = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 10L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), 
+                daemonThreadFactory);
+            
+        delayedRunner = new ScheduledThreadPoolExecutor(1, daemonThreadFactory);
+    }
+    
+    private final static class UncaughtExceptionHandlerImplementation implements Thread.UncaughtExceptionHandler {
+        @Override
+        public void uncaughtException(Thread t, Throwable e) {
+            log.error("Uncaught exception in thread "+t.getName(), e);
+        }
+    }
+    
+    /** 
+     * For use by overriders to use custom thread factory.
+     * But be extremely careful: called by constructor, so before sub-class' constructor will
+     * have been invoked!
+     */
+    protected ThreadFactory newThreadFactory(String contextid) {
+        return new ThreadFactoryBuilder()
+                .setNameFormat("brooklyn-execmanager-"+contextid+"-%d")
+                .setUncaughtExceptionHandler(new UncaughtExceptionHandlerImplementation())
+                .build();
+    }
+    
+    public void shutdownNow() {
+        runner.shutdownNow();
+        delayedRunner.shutdownNow();
+    }
+    
+    public void addListener(ExecutionListener listener) {
+        listeners.add(listener);
+    }
+    
+    public void removeListener(ExecutionListener listener) {
+        listeners.remove(listener);
+    }
+    
+    /**
+     * Deletes the given tag, including all tasks using this tag.
+     * 
+     * Useful, for example, if an entity is being expunged so that we don't keep holding
+     * a reference to it as a tag.
+     */
+    public void deleteTag(Object tag) {
+        Set<Task<?>> tasks;
+        synchronized (tasksByTag) {
+            tasks = tasksByTag.remove(tag);
+        }
+        if (tasks != null) {
+            for (Task<?> task : tasks) {
+                deleteTask(task);
+            }
+        }
+    }
+
+    public void deleteTask(Task<?> task) {
+        boolean removed = deleteTaskNonRecursive(task);
+        if (!removed) return;
+        
+        if (task instanceof HasTaskChildren) {
+            List<Task<?>> children = ImmutableList.copyOf(((HasTaskChildren)task).getChildren());
+            for (Task<?> child : children) {
+                deleteTask(child);
+            }
+        }
+    }
+
+    protected boolean deleteTaskNonRecursive(Task<?> task) {
+        Set<?> tags = checkNotNull(task, "task").getTags();
+        for (Object tag : tags) {
+            synchronized (tasksByTag) {
+                Set<Task<?>> tasks = tasksWithTagLiveOrNull(tag);
+                if (tasks != null) {
+                    tasks.remove(task);
+                    if (tasks.isEmpty()) {
+                        tasksByTag.remove(tag);
+                    }
+                }
+            }
+        }
+        Task<?> removed = tasksById.remove(task.getId());
+        incompleteTaskIds.remove(task.getId());
+        if (removed!=null && removed.isSubmitted() && !removed.isDone()) {
+            log.warn("Deleting submitted task before completion: "+removed+"; this task will continue to run in the background outwith "+this+", but perhaps it should have been cancelled?");
+        }
+        return removed != null;
+    }
+
+    public boolean isShutdown() {
+        return runner.isShutdown();
+    }
+    
+    /** count of all tasks submitted */
+    public long getTotalTasksSubmitted() {
+        return totalTaskCount.get();
+    }
+    
+    /** count of tasks submitted but not ended */
+    public long getNumIncompleteTasks() {
+        return incompleteTaskIds.size();
+    }
+    
+    /** count of tasks started but not ended */
+    public long getNumActiveTasks() {
+        return activeTaskCount.get();
+    }
+
+    /** count of tasks kept in memory, often including ended tasks */
+    public long getNumInMemoryTasks() {
+        return tasksById.size();
+    }
+
+    private Set<Task<?>> tasksWithTagCreating(Object tag) {
+        Preconditions.checkNotNull(tag);
+        synchronized (tasksByTag) {
+            Set<Task<?>> result = tasksWithTagLiveOrNull(tag);
+            if (result==null) {
+                result = Collections.synchronizedSet(new LinkedHashSet<Task<?>>());
+                tasksByTag.put(tag, result);
+            }
+            return result;
+        }
+    }
+
+    /** exposes live view, for internal use only */
+    @Beta
+    public Set<Task<?>> tasksWithTagLiveOrNull(Object tag) {
+        synchronized (tasksByTag) {
+            return tasksByTag.get(tag);
+        }
+    }
+
+    @Override
+    public Task<?> getTask(String id) {
+        return tasksById.get(id);
+    }
+    
+    /** not on interface because potentially expensive */
+    public List<Task<?>> getAllTasks() {
+        // not sure if synching makes any difference; have not observed CME's yet
+        // (and so far this is only called when a CME was caught on a previous operation)
+        synchronized (tasksById) {
+            return MutableList.copyOf(tasksById.values());
+        }
+    }
+    
+    @Override
+    public Set<Task<?>> getTasksWithTag(Object tag) {
+        Set<Task<?>> result = tasksWithTagLiveOrNull(tag);
+        if (result==null) return Collections.emptySet();
+        synchronized (result) {
+            return (Set<Task<?>>)Collections.unmodifiableSet(new LinkedHashSet<Task<?>>(result));
+        }
+    }
+    
+    @Override
+    public Set<Task<?>> getTasksWithAnyTag(Iterable<?> tags) {
+        Set<Task<?>> result = new LinkedHashSet<Task<?>>();
+        Iterator<?> ti = tags.iterator();
+        while (ti.hasNext()) {
+            Set<Task<?>> tasksForTag = tasksWithTagLiveOrNull(ti.next());
+            if (tasksForTag!=null) {
+                synchronized (tasksForTag) {
+                    result.addAll(tasksForTag);
+                }
+            }
+        }
+        return Collections.unmodifiableSet(result);
+    }
+
+    /** only works with at least one tag; returns empty if no tags */
+    @Override
+    public Set<Task<?>> getTasksWithAllTags(Iterable<?> tags) {
+        //NB: for this method retrieval for multiple tags could be made (much) more efficient (if/when it is used with multiple tags!)
+        //by first looking for the least-used tag, getting those tasks, and then for each of those tasks
+        //checking whether it contains the other tags (looking for second-least used, then third-least used, etc)
+        Set<Task<?>> result = new LinkedHashSet<Task<?>>();
+        boolean first = true;
+        Iterator<?> ti = tags.iterator();
+        while (ti.hasNext()) {
+            Object tag = ti.next();
+            if (first) { 
+                first = false;
+                result.addAll(getTasksWithTag(tag));
+            } else {
+                result.retainAll(getTasksWithTag(tag));
+            }
+        }
+        return Collections.unmodifiableSet(result);
+    }
+
+    /** live view of all tasks, for internal use only */
+    @Beta
+    public Collection<Task<?>> allTasksLive() { return tasksById.values(); }
+    
+    public Set<Object> getTaskTags() { 
+        synchronized (tasksByTag) {
+            return Collections.unmodifiableSet(Sets.newLinkedHashSet(tasksByTag.keySet())); 
+        }
+    }
+
+    public Task<?> submit(Runnable r) { return submit(new LinkedHashMap<Object,Object>(1), r); }
+    public Task<?> submit(Map<?,?> flags, Runnable r) { return submit(flags, new BasicTask<Void>(flags, r)); }
+
+    public <T> Task<T> submit(Callable<T> c) { return submit(new LinkedHashMap<Object,Object>(1), c); }
+    public <T> Task<T> submit(Map<?,?> flags, Callable<T> c) { return submit(flags, new BasicTask<T>(flags, c)); }
+
+    public <T> Task<T> submit(TaskAdaptable<T> t) { return submit(new LinkedHashMap<Object,Object>(1), t); }
+    public <T> Task<T> submit(Map<?,?> flags, TaskAdaptable<T> task) {
+        if (!(task instanceof Task))
+            task = task.asTask();
+        synchronized (task) {
+            if (((TaskInternal<?>)task).getInternalFuture()!=null) return (Task<T>)task;
+            return submitNewTask(flags, (Task<T>) task);
+        }
+    }
+
+    public <T> Task<T> scheduleWith(Task<T> task) { return scheduleWith(Collections.emptyMap(), task); }
+    public <T> Task<T> scheduleWith(Map<?,?> flags, Task<T> task) {
+        synchronized (task) {
+            if (((TaskInternal<?>)task).getInternalFuture()!=null) return task;
+            return submitNewTask(flags, task);
+        }
+    }
+
+    protected Task<?> submitNewScheduledTask(final Map<?,?> flags, final ScheduledTask task) {
+        tasksById.put(task.getId(), task);
+        totalTaskCount.incrementAndGet();
+        
+        beforeSubmitScheduledTaskAllIterations(flags, task);
+        
+        return submitSubsequentScheduledTask(flags, task);
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected Task<?> submitSubsequentScheduledTask(final Map<?,?> flags, final ScheduledTask task) {
+        if (!task.isDone()) {
+            task.internalFuture = delayedRunner.schedule(new ScheduledTaskCallable(task, flags),
+                task.delay.toNanoseconds(), TimeUnit.NANOSECONDS);
+        } else {
+            afterEndScheduledTaskAllIterations(flags, task);
+        }
+        return task;
+    }
+
+    protected class ScheduledTaskCallable implements Callable<Object> {
+        public ScheduledTask task;
+        public Map<?,?> flags;
+
+        public ScheduledTaskCallable(ScheduledTask task, Map<?, ?> flags) {
+            this.task = task;
+            this.flags = flags;
+        }
+
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        public Object call() {
+            if (task.startTimeUtc==-1) task.startTimeUtc = System.currentTimeMillis();
+            TaskInternal<?> taskScheduled = null;
+            try {
+                beforeStartScheduledTaskSubmissionIteration(flags, task);
+                taskScheduled = (TaskInternal<?>) task.newTask();
+                taskScheduled.setSubmittedByTask(task);
+                final Callable<?> oldJob = taskScheduled.getJob();
+                final TaskInternal<?> taskScheduledF = taskScheduled;
+                taskScheduled.setJob(new Callable() { public Object call() {
+                    boolean resubmitted = false;
+                    task.recentRun = taskScheduledF;
+                    try {
+                        synchronized (task) {
+                            task.notifyAll();
+                        }
+                        Object result;
+                        try {
+                            result = oldJob.call();
+                        } catch (Exception e) {
+                            if (!Tasks.isInterrupted()) {
+                                log.warn("Error executing "+oldJob+" (scheduled job of "+task+" - "+task.getDescription()+"); cancelling scheduled execution", e);
+                            } else {
+                                log.debug("Interrupted executing "+oldJob+" (scheduled job of "+task+" - "+task.getDescription()+"); cancelling scheduled execution: "+e);
+                            }
+                            throw Exceptions.propagate(e);
+                        }
+                        task.runCount++;
+                        if (task.period!=null && !task.isCancelled()) {
+                            task.delay = task.period;
+                            submitSubsequentScheduledTask(flags, task);
+                            resubmitted = true;
+                        }
+                        return result;
+                    } finally {
+                        // do in finally block in case we were interrupted
+                        if (!resubmitted)
+                            afterEndScheduledTaskAllIterations(flags, task);
+                    }
+                }});
+                task.nextRun = taskScheduled;
+                BasicExecutionContext ec = BasicExecutionContext.getCurrentExecutionContext();
+                if (ec!=null) return ec.submit(taskScheduled);
+                else return submit(taskScheduled);
+            } finally {
+                afterEndScheduledTaskSubmissionIteration(flags, task, taskScheduled);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "ScheduledTaskCallable["+task+","+flags+"]";
+        }
+    }
+
+    private final class SubmissionCallable<T> implements Callable<T> {
+        private final Map<?, ?> flags;
+        private final Task<T> task;
+
+        private SubmissionCallable(Map<?, ?> flags, Task<T> task) {
+            this.flags = flags;
+            this.task = task;
+        }
+
+        public T call() {
+            try {
+                T result = null;
+                Throwable error = null;
+                String oldThreadName = Thread.currentThread().getName();
+                try {
+                    if (RENAME_THREADS) {
+                        String newThreadName = oldThreadName+"-"+task.getDisplayName()+
+                            "["+task.getId().substring(0, 8)+"]";
+                        Thread.currentThread().setName(newThreadName);
+                    }
+                    beforeStartAtomicTask(flags, task);
+                    if (!task.isCancelled()) {
+                        result = ((TaskInternal<T>)task).getJob().call();
+                    } else throw new CancellationException();
+                } catch(Throwable e) {
+                    error = e;
+                } finally {
+                    if (RENAME_THREADS) {
+                        Thread.currentThread().setName(oldThreadName);
+                    }
+                    afterEndAtomicTask(flags, task);
+                }
+                if (error!=null) {
+                    /* we throw, after logging debug.
+                     * the throw means the error is available for task submitters to monitor.
+                     * however it is possible no one is monitoring it, in which case we will have debug logging only for errors.
+                     * (the alternative, of warn-level logging in lots of places where we don't want it, seems worse!) 
+                     */
+                    if (log.isDebugEnabled()) {
+                        // debug only here, because most submitters will handle failures
+                        log.debug("Exception running task "+task+" (rethrowing): "+error.getMessage(), error);
+                        if (log.isTraceEnabled())
+                            log.trace("Trace for exception running task "+task+" (rethrowing): "+error.getMessage(), error);
+                    }
+                    throw Exceptions.propagate(error);
+                }
+                return result;
+            } finally {
+                ((TaskInternal<?>)task).runListeners();
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "BEM.call("+task+","+flags+")";
+        }
+    }
+
+    private final static class ListenableForwardingFutureForTask<T> extends ListenableForwardingFuture<T> {
+        private final Task<T> task;
+
+        private ListenableForwardingFutureForTask(Future<T> delegate, ExecutionList list, Task<T> task) {
+            super(delegate, list);
+            this.task = task;
+        }
+
+        @Override
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            boolean result = false;
+            if (!task.isCancelled()) result |= task.cancel(mayInterruptIfRunning);
+            result |= super.cancel(mayInterruptIfRunning);
+            ((TaskInternal<?>)task).runListeners();
+            return result;
+        }
+    }
+
+    private final class SubmissionListenerToCallOtherListeners<T> implements Runnable {
+        private final Task<T> task;
+
+        private SubmissionListenerToCallOtherListeners(Task<T> task) {
+            this.task = task;
+        }
+
+        @Override
+        public void run() {
+            try {
+                ((TaskInternal<?>)task).runListeners();
+            } catch (Exception e) {
+                log.warn("Error running task listeners for task "+task+" done", e);
+            }
+            
+            for (ExecutionListener listener : listeners) {
+                try {
+                    listener.onTaskDone(task);
+                } catch (Exception e) {
+                    log.warn("Error running execution listener "+listener+" of task "+task+" done", e);
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T> Task<T> submitNewTask(final Map<?,?> flags, final Task<T> task) {
+        if (task instanceof ScheduledTask)
+            return (Task<T>) submitNewScheduledTask(flags, (ScheduledTask)task);
+        
+        tasksById.put(task.getId(), task);
+        totalTaskCount.incrementAndGet();
+        
+        beforeSubmitAtomicTask(flags, task);
+        
+        if (((TaskInternal<T>)task).getJob() == null) 
+            throw new NullPointerException("Task "+task+" submitted with with null job: job must be supplied.");
+        
+        Callable<T> job = new SubmissionCallable<T>(flags, task);
+        
+        // If there's a scheduler then use that; otherwise execute it directly
+        Set<TaskScheduler> schedulers = null;
+        for (Object tago: task.getTags()) {
+            TaskScheduler scheduler = getTaskSchedulerForTag(tago);
+            if (scheduler!=null) {
+                if (schedulers==null) schedulers = new LinkedHashSet<TaskScheduler>(2);
+                schedulers.add(scheduler);
+            }
+        }
+        Future<T> future;
+        if (schedulers!=null && !schedulers.isEmpty()) {
+            if (schedulers.size()>1) log.warn("multiple schedulers detected, using only the first, for "+task+": "+schedulers);
+            future = schedulers.iterator().next().submit(job);
+        } else {
+            future = runner.submit(job);
+        }
+        // on completion, listeners get triggered above; here, below we ensure they get triggered on cancel
+        // (and we make sure the same ExecutionList is used in the future as in the task)
+        ListenableFuture<T> listenableFuture = new ListenableForwardingFutureForTask<T>(future, ((TaskInternal<T>)task).getListeners(), task);
+        // doesn't matter whether the listener is added to the listenableFuture or the task,
+        // except that for the task we can more easily wrap it so that it only logs debug if the executor is shutdown
+        // (avoid a bunch of ugly warnings in tests which start and stop things a lot!)
+        // [probably even nicer to run this in the same thread, it doesn't do much; but that is messier to implement]
+        ((TaskInternal<T>)task).addListener(new SubmissionListenerToCallOtherListeners<T>(task), runner);
+        
+        ((TaskInternal<T>)task).initInternalFuture(listenableFuture);
+        
+        return task;
+    }
+    
+    protected void beforeSubmitScheduledTaskAllIterations(Map<?,?> flags, Task<?> task) {
+        internalBeforeSubmit(flags, task);
+    }
+    protected void beforeSubmitAtomicTask(Map<?,?> flags, Task<?> task) {
+        internalBeforeSubmit(flags, task);
+    }
+    /** invoked when a task is submitted */
+    protected void internalBeforeSubmit(Map<?,?> flags, Task<?> task) {
+        incompleteTaskIds.put(task.getId(), task.getId());
+        
+        Task<?> currentTask = Tasks.current();
+        if (currentTask!=null) ((TaskInternal<?>)task).setSubmittedByTask(currentTask);
+        ((TaskInternal<?>)task).setSubmitTimeUtc(System.currentTimeMillis());
+        
+        if (flags.get("tag")!=null) ((TaskInternal<?>)task).getMutableTags().add(flags.remove("tag"));
+        if (flags.get("tags")!=null) ((TaskInternal<?>)task).getMutableTags().addAll((Collection<?>)flags.remove("tags"));
+
+        for (Object tag: ((TaskInternal<?>)task).getTags()) {
+            tasksWithTagCreating(tag).add(task);
+        }
+    }
+
+    protected void beforeStartScheduledTaskSubmissionIteration(Map<?,?> flags, Task<?> task) {
+        internalBeforeStart(flags, task);
+    }
+    protected void beforeStartAtomicTask(Map<?,?> flags, Task<?> task) {
+        internalBeforeStart(flags, task);
+    }
+    
+    /** invoked in a task's thread when a task is starting to run (may be some time after submitted), 
+     * but before doing any of the task's work, so that we can update bookkeeping and notify callbacks */
+    protected void internalBeforeStart(Map<?,?> flags, Task<?> task) {
+        activeTaskCount.incrementAndGet();
+        
+        //set thread _before_ start time, so we won't get a null thread when there is a start-time
+        if (log.isTraceEnabled()) log.trace(""+this+" beforeStart, task: "+task);
+        if (!task.isCancelled()) {
+            Thread thread = Thread.currentThread();
+            ((TaskInternal<?>)task).setThread(thread);
+            if (RENAME_THREADS) {
+                threadOriginalName.set(thread.getName());
+                String newThreadName = "brooklyn-" + CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, task.getDisplayName().replace(" ", "")) + "-" + task.getId().substring(0, 8);
+                thread.setName(newThreadName);
+            }
+            PerThreadCurrentTaskHolder.perThreadCurrentTask.set(task);
+            ((TaskInternal<?>)task).setStartTimeUtc(System.currentTimeMillis());
+        }
+        ExecutionUtils.invoke(flags.get("newTaskStartCallback"), task);
+    }
+
+    /** normally (if not interrupted) called once for each call to {@link #beforeSubmitScheduledTaskAllIterations(Map, Task)} */
+    protected void afterEndScheduledTaskAllIterations(Map<?,?> flags, Task<?> task) {
+        internalAfterEnd(flags, task, false, true);
+    }
+    /** called once for each call to {@link #beforeStartScheduledTaskSubmissionIteration(Map, Task)},
+     * with a per-iteration task generated by the surrounding scheduled task */
+    protected void afterEndScheduledTaskSubmissionIteration(Map<?,?> flags, Task<?> scheduledTask, Task<?> taskIteration) {
+        internalAfterEnd(flags, scheduledTask, true, false);
+    }
+    /** called once for each task on which {@link #beforeStartAtomicTask(Map, Task)} is invoked,
+     * and normally (if not interrupted prior to start) 
+     * called once for each task on which {@link #beforeSubmitAtomicTask(Map, Task)} */
+    protected void afterEndAtomicTask(Map<?,?> flags, Task<?> task) {
+        internalAfterEnd(flags, task, true, true);
+    }
+    /** normally (if not interrupted) called once for each call to {@link #internalBeforeSubmit(Map, Task)},
+     * and, for atomic tasks and scheduled-task submission iterations where 
+     * always called once if {@link #internalBeforeStart(Map, Task)} is invoked and in the same thread as that method */
+    protected void internalAfterEnd(Map<?,?> flags, Task<?> task, boolean startedInThisThread, boolean isEndingAllIterations) {
+        if (log.isTraceEnabled()) log.trace(this+" afterEnd, task: "+task);
+        if (startedInThisThread) {
+            activeTaskCount.decrementAndGet();
+        }
+        if (isEndingAllIterations) {
+            incompleteTaskIds.remove(task.getId());
+            ExecutionUtils.invoke(flags.get("newTaskEndCallback"), task);
+            ((TaskInternal<?>)task).setEndTimeUtc(System.currentTimeMillis());
+        }
+
+        if (startedInThisThread) {
+            PerThreadCurrentTaskHolder.perThreadCurrentTask.remove();
+            //clear thread _after_ endTime set, so we won't get a null thread when there is no end-time
+            if (RENAME_THREADS && startedInThisThread) {
+                Thread thread = task.getThread();
+                if (thread==null) {
+                    log.warn("BasicTask.afterEnd invoked without corresponding beforeStart");
+                } else {
+                    thread.setName(threadOriginalName.get());
+                    threadOriginalName.remove();
+                }
+            }
+            ((TaskInternal<?>)task).setThread(null);
+        }
+        synchronized (task) { task.notifyAll(); }
+    }
+
+    public TaskScheduler getTaskSchedulerForTag(Object tag) {
+        return schedulerByTag.get(tag);
+    }
+    
+    public void setTaskSchedulerForTag(Object tag, Class<? extends TaskScheduler> scheduler) {
+        synchronized (schedulerByTag) {
+            TaskScheduler old = getTaskSchedulerForTag(tag);
+            if (old!=null) {
+                if (scheduler.isAssignableFrom(old.getClass())) {
+                    /* already have such an instance */
+                    return;
+                }
+                //might support multiple in future...
+                throw new IllegalStateException("Not allowed to set multiple TaskSchedulers on ExecutionManager tag (tag "+tag+", has "+old+", setting new "+scheduler+")");
+            }
+            try {
+                TaskScheduler schedulerI = scheduler.newInstance();
+                // allow scheduler to have a nice name, for logging etc
+                if (schedulerI instanceof CanSetName) ((CanSetName)schedulerI).setName(""+tag);
+                setTaskSchedulerForTag(tag, schedulerI);
+            } catch (InstantiationException e) {
+                throw Exceptions.propagate(e);
+            } catch (IllegalAccessException e) {
+                throw Exceptions.propagate(e);
+            }
+        }
+    }
+    
+    /**
+     * Defines a {@link TaskScheduler} to run on all subsequently submitted jobs with the given tag.
+     *
+     * Maximum of one allowed currently. Resubmissions of the same scheduler (or scheduler class)
+     * allowed. If changing, you must call {@link #clearTaskSchedulerForTag(Object)} between the two.
+     *
+     * @see #setTaskSchedulerForTag(Object, Class)
+     */
+    public void setTaskSchedulerForTag(Object tag, TaskScheduler scheduler) {
+        synchronized (schedulerByTag) {
+            scheduler.injectExecutor(runner);
+
+            Object old = schedulerByTag.put(tag, scheduler);
+            if (old!=null && old!=scheduler) {
+                //might support multiple in future...
+                throw new IllegalStateException("Not allowed to set multiple TaskSchedulers on ExecutionManager tag (tag "+tag+")");
+            }
+        }
+    }
+
+    /**
+     * Forgets that any scheduler was associated with a tag.
+     *
+     * @see #setTaskSchedulerForTag(Object, TaskScheduler)
+     * @see #setTaskSchedulerForTag(Object, Class)
+     */
+    public boolean clearTaskSchedulerForTag(Object tag) {
+        synchronized (schedulerByTag) {
+            Object old = schedulerByTag.remove(tag);
+            return (old!=null);
+        }
+    }
+    
+    @VisibleForTesting
+    public ConcurrentMap<Object, TaskScheduler> getSchedulerByTag() {
+        return schedulerByTag;
+    }
+
+}


[12/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/ShellToolAbstractTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/ShellToolAbstractTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/ShellToolAbstractTest.java
new file mode 100644
index 0000000..794a512
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/ShellToolAbstractTest.java
@@ -0,0 +1,441 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.text.Identifiers;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public abstract class ShellToolAbstractTest {
+
+    protected List<ShellTool> tools = Lists.newArrayList();
+    protected List<String> filesCreated;
+    protected String localFilePath;
+    
+    protected ShellTool tool;
+    
+    protected ShellTool newTool() {
+        return newTool(MutableMap.<String,Object>of());
+    }
+    
+    protected ShellTool newTool(Map<String,?> flags) {
+        ShellTool t = newUnregisteredTool(flags);
+        tools.add(t);
+        return t;
+    }
+
+    protected abstract ShellTool newUnregisteredTool(Map<String,?> flags);
+    
+    protected ShellTool tool() { return tool; }
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        localFilePath = "/tmp/ssh-test-local-"+Identifiers.makeRandomId(8);
+        filesCreated = new ArrayList<String>();
+        filesCreated.add(localFilePath);
+
+        tool = newTool();
+        connect(tool);
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void afterMethod() throws Exception {
+        for (ShellTool t : tools) {
+            if (t instanceof SshTool) ((SshTool)t).disconnect();
+        }
+        for (String fileCreated : filesCreated) {
+            new File(fileCreated).delete();
+        }
+    }
+
+    protected static void connect(ShellTool tool) {
+        if (tool instanceof SshTool)
+            ((SshTool)tool).connect();
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecConsecutiveCommands() throws Exception {
+        String out = execScript("echo run1");
+        String out2 = execScript("echo run2");
+        
+        assertTrue(out.contains("run1"), "out="+out);
+        assertTrue(out2.contains("run2"), "out="+out);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecScriptChainOfCommands() throws Exception {
+        String out = execScript("export MYPROP=abc", "echo val is $MYPROP");
+
+        assertTrue(out.contains("val is abc"), "out="+out);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecScriptReturningNonZeroExitCode() throws Exception {
+        int exitcode = tool.execScript(MutableMap.<String,Object>of(), ImmutableList.of("exit 123"));
+        assertEquals(exitcode, 123);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecScriptReturningZeroExitCode() throws Exception {
+        int exitcode = tool.execScript(MutableMap.<String,Object>of(), ImmutableList.of("date"));
+        assertEquals(exitcode, 0);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecScriptCommandWithEnvVariables() throws Exception {
+        String out = execScript(ImmutableList.of("echo val is $MYPROP2"), ImmutableMap.of("MYPROP2", "myval"));
+
+        assertTrue(out.contains("val is myval"), "out="+out);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testScriptDataNotLost() throws Exception {
+        String out = execScript("echo `echo foo``echo bar`");
+
+        assertTrue(out.contains("foobar"), "out="+out);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecScriptWithSleepThenExit() throws Exception {
+        Stopwatch watch = Stopwatch.createStarted();
+        execScript("sleep 1", "exit 0");
+        assertTrue(watch.elapsed(TimeUnit.MILLISECONDS) > 900, "only slept "+Time.makeTimeStringRounded(watch));
+    }
+
+    // Really just tests that it returns; the command will be echo'ed automatically so this doesn't assert the command will have been executed
+    @Test(groups = {"Integration"})
+    public void testExecScriptBigCommand() throws Exception {
+        String bigstring = Strings.repeat("a", 10000);
+        String out = execScript("echo "+bigstring);
+        
+        assertTrue(out.contains(bigstring), "out="+out);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecScriptBigChainOfCommand() throws Exception {
+        String bigstring = Strings.repeat("abcdefghij", 100); // 1KB
+        List<String> cmds = Lists.newArrayList();
+        for (int i = 0; i < 10; i++) {
+            cmds.add("export MYPROP"+i+"="+bigstring);
+            cmds.add("echo val"+i+" is $MYPROP"+i);
+        }
+        String out = execScript(cmds);
+        
+        for (int i = 0; i < 10; i++) {
+            assertTrue(out.contains("val"+i+" is "+bigstring), "out="+out);
+        }
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecScriptAbortsOnCommandFailure() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int exitcode = tool.execScript(ImmutableMap.of("out", out), ImmutableList.of("export MYPROP=myval", "acmdthatdoesnotexist", "echo val is $MYPROP"));
+        String outstr = new String(out.toByteArray());
+
+        assertFalse(outstr.contains("val is myval"), "out="+out);
+        assertNotEquals(exitcode,  0);
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testExecScriptWithSleepThenBigCommand() throws Exception {
+        String bigstring = Strings.repeat("abcdefghij", 1000); // 10KB
+        String out = execScript("sleep 2", "export MYPROP="+bigstring, "echo val is $MYPROP");
+        assertTrue(out.contains("val is "+bigstring), "out="+out);
+    }
+    
+    @Test(groups = {"WIP", "Integration"})
+    public void testExecScriptBigConcurrentCommand() throws Exception {
+        ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
+        List<ListenableFuture<?>> futures = new ArrayList<ListenableFuture<?>>();
+        try {
+            for (int i = 0; i < 10; i++) {
+                final ShellTool localtool = newTool();
+                connect(localtool);
+                
+                futures.add(executor.submit(new Runnable() {
+                        public void run() {
+                            String bigstring = Strings.repeat("abcdefghij", 1000); // 10KB
+                            String out = execScript(localtool, ImmutableList.of("export MYPROP="+bigstring, "echo val is $MYPROP"));
+                            assertTrue(out.contains("val is "+bigstring), "outSize="+out.length()+"; out="+out);
+                        }}));
+            }
+            Futures.allAsList(futures).get();
+        } finally {
+            executor.shutdownNow();
+        }
+    }
+
+    @Test(groups = {"WIP", "Integration"})
+    public void testExecScriptBigConcurrentSleepyCommand() throws Exception {
+        ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
+        List<ListenableFuture<?>> futures = new ArrayList<ListenableFuture<?>>();
+        try {
+            long starttime = System.currentTimeMillis();
+            for (int i = 0; i < 10; i++) {
+                final ShellTool localtool = newTool();
+                connect(localtool);
+                
+                futures.add(executor.submit(new Runnable() {
+                        public void run() {
+                            String bigstring = Strings.repeat("abcdefghij", 1000); // 10KB
+                            String out = execScript(localtool, ImmutableList.of("sleep 2", "export MYPROP="+bigstring, "echo val is $MYPROP"));
+                            assertTrue(out.contains("val is "+bigstring), "out="+out);
+                        }}));
+            }
+            Futures.allAsList(futures).get();
+            long runtime = System.currentTimeMillis() - starttime;
+            
+            long OVERHEAD = 20*1000;
+            assertTrue(runtime < 2000+OVERHEAD, "runtime="+runtime);
+            
+        } finally {
+            executor.shutdownNow();
+        }
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecChainOfCommands() throws Exception {
+        String out = execCommands("MYPROP=abc", "echo val is $MYPROP");
+
+        assertEquals(out, "val is abc\n");
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecReturningNonZeroExitCode() throws Exception {
+        int exitcode = tool.execCommands(MutableMap.<String,Object>of(), ImmutableList.of("exit 123"));
+        assertEquals(exitcode, 123);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecReturningZeroExitCode() throws Exception {
+        int exitcode = tool.execCommands(MutableMap.<String,Object>of(), ImmutableList.of("date"));
+        assertEquals(exitcode, 0);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecCommandWithEnvVariables() throws Exception {
+        String out = execCommands(ImmutableList.of("echo val is $MYPROP2"), ImmutableMap.of("MYPROP2", "myval"));
+
+        assertEquals(out, "val is myval\n");
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecBigCommand() throws Exception {
+        String bigstring = Strings.repeat("abcdefghij", 1000); // 10KB
+        String out = execCommands("echo "+bigstring);
+
+        assertEquals(out, bigstring+"\n", "actualSize="+out.length()+"; expectedSize="+bigstring.length());
+    }
+
+    @Test(groups = {"Integration"})
+    public void testExecBigConcurrentCommand() throws Exception {
+        runExecBigConcurrentCommand(10, 0L);
+    }
+    
+    // TODO Fails I believe due to synchronization model in SshjTool of calling connect/disconnect.
+    // Even with a retry-count of 4, it still fails because some commands are calling disconnect
+    // while another concurrently executing command expects to be still connected.
+    @Test(groups = {"Integration", "WIP"})
+    public void testExecBigConcurrentCommandWithStaggeredStart() throws Exception {
+        // This test is to vary the concurrency of concurrent actions
+        runExecBigConcurrentCommand(50, 100L);
+    }
+    
+    protected void runExecBigConcurrentCommand(int numCommands, long staggeredDelayBeforeStart) throws Exception {
+        ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
+        List<ListenableFuture<?>> futures = new ArrayList<ListenableFuture<?>>();
+        try {
+            for (int i = 0; i < numCommands; i++) {
+                long delay = (long) (Math.random() * staggeredDelayBeforeStart);
+                if (i > 0) Time.sleep(delay);
+                
+                futures.add(executor.submit(new Runnable() {
+                        public void run() {
+                            String bigstring = Strings.repeat("abcdefghij", 1000); // 10KB
+                            String out = execCommands("echo "+bigstring);
+                            assertEquals(out, bigstring+"\n", "actualSize="+out.length()+"; expectedSize="+bigstring.length());
+                        }}));
+            }
+            Futures.allAsList(futures).get();
+        } finally {
+            executor.shutdownNow();
+        }
+    }
+
+    // fails if terminal enabled
+    @Test(groups = {"Integration"})
+    @Deprecated // tests deprecated code
+    public void testExecScriptCapturesStderr() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ByteArrayOutputStream err = new ByteArrayOutputStream();
+        String nonExistantCmd = "acmdthatdoesnotexist";
+        tool.execScript(ImmutableMap.of("out", out, "err", err), ImmutableList.of(nonExistantCmd));
+        assertTrue(new String(err.toByteArray()).contains(nonExistantCmd+": command not found"), "out="+out+"; err="+err);
+    }
+
+    // fails if terminal enabled
+    @Test(groups = {"Integration"})
+    @Deprecated // tests deprecated code
+    public void testExecCapturesStderr() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ByteArrayOutputStream err = new ByteArrayOutputStream();
+        String nonExistantCmd = "acmdthatdoesnotexist";
+        tool.execCommands(ImmutableMap.of("out", out, "err", err), ImmutableList.of(nonExistantCmd));
+        String errMsg = new String(err.toByteArray());
+        assertTrue(errMsg.contains(nonExistantCmd+": command not found\n"), "errMsg="+errMsg+"; out="+out+"; err="+err);
+        
+    }
+
+    @Test(groups = {"Integration"})
+    public void testScriptHeader() {
+        final ShellTool localtool = newTool();
+        String out = execScript(MutableMap.of("scriptHeader", "#!/bin/bash -e\necho hello world\n"), 
+                localtool, Arrays.asList("echo goodbye world"), null);
+        assertTrue(out.contains("goodbye world"), "no goodbye in output: "+out);
+        assertTrue(out.contains("hello world"), "no hello in output: "+out);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testStdErr() {
+        final ShellTool localtool = newTool();
+        Map<String,Object> props = new LinkedHashMap<String, Object>();
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ByteArrayOutputStream err = new ByteArrayOutputStream();
+        props.put("out", out);
+        props.put("err", err);
+        int exitcode = localtool.execScript(props, Arrays.asList("echo hello err > /dev/stderr"), null);
+        assertFalse(out.toString().contains("hello err"), "hello found where it shouldn't have been, in stdout: "+out);
+        assertTrue(err.toString().contains("hello err"), "no hello in stderr: "+err);
+        assertEquals(0, exitcode);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testRunAsRoot() {
+        final ShellTool localtool = newTool();
+        Map<String,Object> props = new LinkedHashMap<String, Object>();
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ByteArrayOutputStream err = new ByteArrayOutputStream();
+        props.put("out", out);
+        props.put("err", err);
+        props.put(SshTool.PROP_RUN_AS_ROOT.getName(), true);
+        int exitcode = localtool.execScript(props, Arrays.asList("whoami"), null);
+        assertTrue(out.toString().contains("root"), "not running as root; whoami is: "+out+" (err is '"+err+"')");
+        assertEquals(0, exitcode);
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testExecScriptEchosExecute() throws Exception {
+        String out = execScript("date");
+        assertTrue(out.toString().contains("Executed"), "Executed did not display: "+out);
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testExecScriptEchosDontExecuteWhenToldNoExtraOutput() throws Exception {
+        final ShellTool localtool = newTool();
+        Map<String,Object> props = new LinkedHashMap<String, Object>();
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ByteArrayOutputStream err = new ByteArrayOutputStream();
+        props.put("out", out);
+        props.put("err", err);
+        props.put(SshTool.PROP_NO_EXTRA_OUTPUT.getName(), true);
+        int exitcode = localtool.execScript(props, Arrays.asList("echo hello world"), null);
+        assertFalse(out.toString().contains("Executed"), "Executed should not have displayed: "+out);
+        assertEquals(out.toString().trim(), "hello world");
+        assertEquals(0, exitcode);
+    }
+    
+    protected String execCommands(String... cmds) {
+        return execCommands(Arrays.asList(cmds));
+    }
+    
+    protected String execCommands(List<String> cmds) {
+        return execCommands(cmds, ImmutableMap.<String,Object>of());
+    }
+
+    protected String execCommands(List<String> cmds, Map<String,?> env) {
+        return execCommands(null, cmds, env);
+    }
+
+    protected String execCommands(ConfigBag config, List<String> cmds, Map<String,?> env) {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        MutableMap<String,Object> flags = MutableMap.<String,Object>of("out", out);
+        if (config!=null) flags.add(config.getAllConfig());
+        tool.execCommands(flags, cmds, env);
+        return new String(out.toByteArray());
+    }
+
+    protected String execScript(String... cmds) {
+        return execScript(tool, Arrays.asList(cmds));
+    }
+
+    protected String execScript(ShellTool t, List<String> cmds) {
+        return execScript(ImmutableMap.<String,Object>of(), t, cmds, ImmutableMap.<String,Object>of());
+    }
+
+    protected String execScript(List<String> cmds) {
+        return execScript(cmds, ImmutableMap.<String,Object>of());
+    }
+    
+    protected String execScript(List<String> cmds, Map<String,?> env) {
+        return execScript(MutableMap.<String,Object>of(), tool, cmds, env);
+    }
+    
+    protected String execScript(Map<String, ?> props, ShellTool tool, List<String> cmds, Map<String,?> env) {
+        Map<String, Object> props2 = new LinkedHashMap<String, Object>(props);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        props2.put("out", out);
+        int exitcode = tool.execScript(props2, cmds, env);
+        String outstr = new String(out.toByteArray());
+        assertEquals(exitcode, 0, outstr);
+        return outstr;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/SshToolAbstractIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/SshToolAbstractIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/SshToolAbstractIntegrationTest.java
new file mode 100644
index 0000000..309d4fb
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/SshToolAbstractIntegrationTest.java
@@ -0,0 +1,304 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshException;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.os.Os;
+import brooklyn.util.text.Identifiers;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+
+/**
+ * Test the operation of the {@link SshTool} utility class; to be extended to test concrete implementations.
+ * 
+ * Requires keys set up, e.g. running:
+ * 
+ * <pre>
+ * cd ~/.ssh
+ * ssh-keygen
+ * id_rsa_with_passphrase
+ * mypassphrase
+ * mypassphrase
+ * </pre>
+ * 
+ */
+public abstract class SshToolAbstractIntegrationTest extends ShellToolAbstractTest {
+
+    private static final Logger log = LoggerFactory.getLogger(SshToolAbstractIntegrationTest.class);
+    
+    // FIXME need tests which take properties set in entities and brooklyn.properties;
+    // but not in this class because it is lower level than entities, Aled would argue.
+
+    // TODO No tests for retry logic and exception handing yet
+
+    public static final String SSH_KEY_WITH_PASSPHRASE = System.getProperty("sshPrivateKeyWithPassphrase", "~/.ssh/id_rsa_with_passphrase");
+    public static final String SSH_PASSPHRASE = System.getProperty("sshPrivateKeyPassphrase", "mypassphrase");
+
+    protected String remoteFilePath;
+
+    protected SshTool tool() { return (SshTool)tool; }
+    
+    protected abstract SshTool newUnregisteredTool(Map<String,?> flags);
+
+    @Override
+    protected SshTool newTool() {
+        return newTool(ImmutableMap.of("host", "localhost", "privateKeyFile", "~/.ssh/id_rsa"));
+    }
+    
+    @Override
+    protected SshTool newTool(Map<String,?> flags) {
+        return (SshTool) super.newTool(flags);
+    }
+    
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        super.setUp();
+        remoteFilePath = "/tmp/ssh-test-remote-"+Identifiers.makeRandomId(8);
+        filesCreated.add(remoteFilePath);
+    }
+    
+    protected void assertRemoteFileContents(String remotePath, String expectedContents) {
+        String catout = execCommands("cat "+remotePath);
+        assertEquals(catout, expectedContents);
+    }
+    
+    /**
+     * @param remotePath
+     * @param expectedPermissions Of the form, for example, "-rw-r--r--"
+     */
+    protected void assertRemoteFilePermissions(String remotePath, String expectedPermissions) {
+        String lsout = execCommands("ls -l "+remotePath);
+        assertTrue(lsout.contains(expectedPermissions), lsout);
+    }
+    
+    protected void assertRemoteFileLastModifiedIsNow(String remotePath) {
+        // Check default last-modified time is `now`.
+        // Be lenient in assertion, in case unlucky that clock ticked over to next hour/minute as test was running.
+        // TODO Code could be greatly improved, but low priority!
+        // Output format:
+        //   -rw-r--r--  1   aled  wheel  18  Apr 24  15:03 /tmp/ssh-test-remote-CvFN9zQA
+        //   [0]         [1] [2]   [3]    [4] [5] [6] [7]   [8]
+        
+        String lsout = execCommands("ls -l "+remotePath);
+        
+        String[] lsparts = lsout.split("\\s+");
+        int day = Integer.parseInt(lsparts[6]);
+        int hour = Integer.parseInt(lsparts[7].split(":")[0]);
+        int minute = Integer.parseInt(lsparts[7].split(":")[1]);
+        
+        Calendar expected = Calendar.getInstance();
+        int expectedDay = expected.get(Calendar.DAY_OF_MONTH);
+        int expectedHour = expected.get(Calendar.HOUR_OF_DAY);
+        int expectedMinute = expected.get(Calendar.MINUTE);
+        
+        assertEquals(day, expectedDay, "ls="+lsout+"; lsparts="+Arrays.toString(lsparts)+"; expected="+expected+"; expectedDay="+expectedDay+"; day="+day+"; zone="+expected.getTimeZone());
+        assertTrue(Math.abs(hour - expectedHour) <= 1, "ls="+lsout+"; lsparts="+Arrays.toString(lsparts)+"; expected="+expected+"; expectedHour="+expectedHour+"; hour="+hour+"; zone="+expected.getTimeZone());
+        assertTrue(Math.abs(minute - expectedMinute) <= 1, "ls="+lsout+"; lsparts="+Arrays.toString(lsparts)+"; expected="+expected+"; expectedMinute="+expectedMinute+"; minute="+minute+"; zone="+expected.getTimeZone());
+    }
+
+    @Test(groups = {"Integration"})
+    public void testCopyToServerFromBytes() throws Exception {
+        String contents = "echo hello world!\n";
+        byte[] contentBytes = contents.getBytes();
+        tool().copyToServer(MutableMap.<String,Object>of(), contentBytes, remoteFilePath);
+
+        assertRemoteFileContents(remoteFilePath, contents);
+        assertRemoteFilePermissions(remoteFilePath, "-rw-r--r--");
+        
+        // TODO would like to also assert lastModified time, but on jenkins the jvm locale
+        // and the OS locale are different (i.e. different timezones) so the file time-stamp 
+        // is several hours out.
+        //assertRemoteFileLastModifiedIsNow(remoteFilePath);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testCopyToServerFromInputStream() throws Exception {
+        String contents = "echo hello world!\n";
+        ByteArrayInputStream contentsStream = new ByteArrayInputStream(contents.getBytes());
+        tool().copyToServer(MutableMap.<String,Object>of(), contentsStream, remoteFilePath);
+
+        assertRemoteFileContents(remoteFilePath, contents);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testCopyToServerWithPermissions() throws Exception {
+        tool().copyToServer(ImmutableMap.of("permissions","0754"), "echo hello world!\n".getBytes(), remoteFilePath);
+
+        assertRemoteFilePermissions(remoteFilePath, "-rwxr-xr--");
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testCopyToServerWithLastModifiedDate() throws Exception {
+        long lastModificationTime = 1234567;
+        tool().copyToServer(ImmutableMap.of("lastModificationDate", lastModificationTime), "echo hello world!\n".getBytes(), remoteFilePath);
+
+        String lsout = execCommands("ls -l "+remoteFilePath);//+" | awk '{print \$6 \" \" \$7 \" \" \$8}'"])
+        //execCommands([ "ls -l "+remoteFilePath+" | awk '{print \$6 \" \" \$7 \" \" \$8}'"])
+        //varies depending on timezone
+        assertTrue(lsout.contains("Jan 15  1970") || lsout.contains("Jan 14  1970") || lsout.contains("Jan 16  1970"), lsout);
+        //assertLastModified(lsout, lastModifiedDate)
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testCopyFileToServerWithPermissions() throws Exception {
+        String contents = "echo hello world!\n";
+        Files.write(contents, new File(localFilePath), Charsets.UTF_8);
+        tool().copyToServer(ImmutableMap.of("permissions", "0754"), new File(localFilePath), remoteFilePath);
+
+        assertRemoteFileContents(remoteFilePath, contents);
+
+        String lsout = execCommands("ls -l "+remoteFilePath);
+        assertTrue(lsout.contains("-rwxr-xr--"), lsout);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testCopyFromServer() throws Exception {
+        String contentsWithoutLineBreak = "echo hello world!";
+        String contents = contentsWithoutLineBreak+"\n";
+        tool().copyToServer(MutableMap.<String,Object>of(), contents.getBytes(), remoteFilePath);
+        
+        tool().copyFromServer(MutableMap.<String,Object>of(), remoteFilePath, new File(localFilePath));
+
+        List<String> actual = Files.readLines(new File(localFilePath), Charsets.UTF_8);
+        assertEquals(actual, ImmutableList.of(contentsWithoutLineBreak));
+    }
+    
+    // TODO No config options in sshj or scp for auto-creating the parent directories
+    @Test(enabled=false, groups = {"Integration"})
+    public void testCopyFileToNonExistantDir() throws Exception {
+        String contents = "echo hello world!\n";
+        String remoteFileDirPath = "/tmp/ssh-test-remote-dir-"+Identifiers.makeRandomId(8);
+        String remoteFileInDirPath = remoteFileDirPath + File.separator + "ssh-test-remote-"+Identifiers.makeRandomId(8);
+        filesCreated.add(remoteFileInDirPath);
+        filesCreated.add(remoteFileDirPath);
+        
+        tool().copyToServer(MutableMap.<String,Object>of(), contents.getBytes(), remoteFileInDirPath);
+
+        assertRemoteFileContents(remoteFileInDirPath, contents);
+    }
+    
+
+    @Test(groups = {"Integration"})
+    public void testAllocatePty() {
+        final ShellTool localtool = newTool(MutableMap.of("host", "localhost", SshTool.PROP_ALLOCATE_PTY.getName(), true));
+        Map<String,Object> props = new LinkedHashMap<String, Object>();
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ByteArrayOutputStream err = new ByteArrayOutputStream();
+        props.put("out", out);
+        props.put("err", err);
+        int exitcode = localtool.execScript(props, Arrays.asList("echo hello err > /dev/stderr"), null);
+        assertTrue(out.toString().contains("hello err"), "no hello in output: "+out+" (err is '"+err+"')");
+        assertFalse(err.toString().contains("hello err"), "hello found in stderr: "+err);
+        assertEquals(0, exitcode);
+    }
+
+    // Requires setting up an extra ssh key, with a passphrase, and adding it to ~/.ssh/authorized_keys
+    @Test(groups = {"Integration"})
+    public void testSshKeyWithPassphrase() throws Exception {
+        final SshTool localtool = newTool(ImmutableMap.<String,Object>builder()
+                .put(SshTool.PROP_HOST.getName(), "localhost")
+                .put(SshTool.PROP_PRIVATE_KEY_FILE.getName(), SSH_KEY_WITH_PASSPHRASE)
+                .put(SshTool.PROP_PRIVATE_KEY_PASSPHRASE.getName(), SSH_PASSPHRASE)
+                .build());
+        localtool.connect();
+        
+        assertEquals(tool.execScript(MutableMap.<String,Object>of(), ImmutableList.of("date")), 0);
+
+        // Also needs the negative test to prove that we're really using an ssh-key with a passphrase
+        try {
+            final SshTool localtool2 = newTool(ImmutableMap.<String,Object>builder()
+                    .put(SshTool.PROP_HOST.getName(), "localhost")
+                    .put(SshTool.PROP_PRIVATE_KEY_FILE.getName(), SSH_KEY_WITH_PASSPHRASE)
+                    .build());
+            localtool2.connect();
+            fail();
+        } catch (Exception e) {
+            SshException se = Exceptions.getFirstThrowableOfType(e, SshException.class);
+            if (se == null) throw e;
+        }
+    }
+
+    @Test(groups = {"Integration"})
+    public void testConnectWithInvalidUserThrowsException() throws Exception {
+        final ShellTool localtool = newTool(ImmutableMap.of("user", "wronguser", "host", "localhost", "privateKeyFile", "~/.ssh/id_rsa"));
+        tools.add(localtool);
+        try {
+            connect(localtool);
+            fail();
+        } catch (SshException e) {
+            if (!e.toString().contains("failed to connect")) throw e;
+        }
+    }
+
+    @Test(groups = {"Integration"})
+    public void testOutputAsExpected() throws Exception {
+        final String CONTENTS = "hello world\n"
+            + "bye bye\n";
+        execCommands("cat > "+Os.mergePaths(Os.tmp(), "test1")+" << X\n"
+            + CONTENTS
+            + "X\n");
+        String read = execCommands("echo START_FOO", "cat "+Os.mergePaths(Os.tmp(), "test1"), "echo END_FOO");
+        log.debug("read back data written, as:\n"+read);
+        String contents = Strings.getFragmentBetween(read, "START_FOO", "END_FOO");
+        Assert.assertEquals(CONTENTS.trim(), contents.trim());
+    }
+
+    @Test(groups = {"Integration"})
+    public void testScriptDirPropertiesIsRespected() {
+        // For explanation of (some of) the magic behind this command, see http://stackoverflow.com/a/229606/68898
+        final String command = "if [[ \"$0\" == \"/var/tmp/\"* ]]; then true; else false; fi";
+
+        SshTool sshTool = newTool(ImmutableMap.<String, Object>builder()
+                .put(SshTool.PROP_HOST.getName(), "localhost")
+                .build());
+        int rc = sshTool.execScript(ImmutableMap.<String, Object>builder()
+                .put(SshTool.PROP_SCRIPT_DIR.getName(), "/var/tmp")
+                .build(), ImmutableList.of(command));
+        assertEquals(rc, 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/SshToolAbstractPerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/SshToolAbstractPerformanceTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/SshToolAbstractPerformanceTest.java
new file mode 100644
index 0000000..1730816
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/SshToolAbstractPerformanceTest.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh;
+
+import java.io.ByteArrayOutputStream;
+import java.lang.management.ManagementFactory;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+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.util.collections.MutableMap;
+import brooklyn.util.text.Identifiers;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Test the performance of different variants of invoking the sshj tool.
+ * 
+ * Intended for human-invocation and inspection, to see which parts are most expensive.
+ */
+public abstract class SshToolAbstractPerformanceTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SshToolAbstractPerformanceTest.class);
+    
+    private SshTool tool;
+    
+    protected abstract SshTool newSshTool(Map<String,?> flags);
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (tool != null) tool.disconnect();
+    }
+
+    @Test(groups = {"Integration"})
+    public void testConsecutiveConnectAndDisconnect() throws Exception {
+        Runnable task = new Runnable() {
+            public void run() {
+                tool = newSshTool(MutableMap.of("host", "localhost"));
+                tool.connect();
+                tool.disconnect();
+            }
+        };
+        runMany(task, "connect-disconnect", 10);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testConsecutiveSmallCommands() throws Exception {
+        runExecManyCommands(ImmutableList.of("true"), false, "small-cmd", 10);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testConsecutiveSmallCommandsWithStdouterr() throws Exception {
+        runExecManyCommands(ImmutableList.of("true"), true, "small-cmd-with-stdout", 10);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testConsecutiveBigStdoutCommands() throws Exception {
+        runExecManyCommands(ImmutableList.of("head -c 100000 /dev/urandom"), true, "big-stdout", 10);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testConsecutiveBigStdinCommands() throws Exception {
+        String bigstr = Identifiers.makeRandomId(100000);
+        runExecManyCommands(ImmutableList.of("echo "+bigstr+" | wc -c"), true, "big-stdin", 10);
+    }
+
+    private void runExecManyCommands(final List<String> cmds, final boolean captureOutAndErr, String context, int iterations) throws Exception {
+        Runnable task = new Runnable() {
+                @Override public void run() {
+                    execScript(cmds, captureOutAndErr);
+                }};
+        runMany(task, context, iterations);
+    }
+
+    private void runMany(Runnable task, String context, int iterations) throws Exception {
+        MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
+        ObjectName osMBeanName = ObjectName.getInstance(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME);
+        long preCpuTime = (Long) mbeanServer.getAttribute(osMBeanName, "ProcessCpuTime");
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        
+        for (int i = 0; i < iterations; i++) {
+            task.run();
+            
+            long postCpuTime = (Long) mbeanServer.getAttribute(osMBeanName, "ProcessCpuTime");
+            long elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS);
+            double fractionCpu = (elapsedTime > 0) ? ((double)postCpuTime-preCpuTime) / TimeUnit.MILLISECONDS.toNanos(elapsedTime) : -1;
+            LOG.info("Executing {}; completed {}; took {}; fraction cpu {}", new Object[] {context, (i+1), Time.makeTimeStringRounded(elapsedTime), fractionCpu});
+        }
+    }
+
+    private int execScript(List<String> cmds, boolean captureOutandErr) {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ByteArrayOutputStream err = new ByteArrayOutputStream();
+        MutableMap<String,?> flags = (captureOutandErr) ? MutableMap.of("out", out, "err", err) : MutableMap.<String,Object>of();
+        
+        tool = newSshTool(MutableMap.of("host", "localhost"));
+        tool.connect();
+        int result = tool.execScript(flags, cmds);
+        tool.disconnect();
+        
+        int outlen = out.toByteArray().length;
+        int errlen = out.toByteArray().length;
+        if (LOG.isTraceEnabled()) LOG.trace("Executed: result={}; stdout={}; stderr={}", new Object[] {result, outlen, errlen});
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliToolIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliToolIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliToolIntegrationTest.java
new file mode 100644
index 0000000..f8efa87
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliToolIntegrationTest.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh.cli;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.internal.ssh.SshException;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshToolAbstractIntegrationTest;
+import org.apache.brooklyn.core.util.internal.ssh.cli.SshCliTool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Test the operation of the {@link SshJschTool} utility class.
+ */
+public class SshCliToolIntegrationTest extends SshToolAbstractIntegrationTest {
+
+    private static final Logger log = LoggerFactory.getLogger(SshCliToolIntegrationTest.class);
+    
+    protected SshTool newUnregisteredTool(Map<String,?> flags) {
+        return new SshCliTool(flags);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testFlags() throws Exception {
+        final SshTool localtool = newTool(ImmutableMap.of("sshFlags", "-vvv -tt", "host", "localhost"));
+        tools.add(localtool);
+        try {
+            localtool.connect();
+            Map<String,Object> props = new LinkedHashMap<String, Object>();
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            ByteArrayOutputStream err = new ByteArrayOutputStream();
+            props.put("out", out);
+            props.put("err", err);
+            int exitcode = localtool.execScript(props, Arrays.asList("echo hello err > /dev/stderr"), null);
+            Assert.assertEquals(0, exitcode, "exitCode="+exitcode+", but expected 0");
+            log.debug("OUT from ssh -vvv command is: "+out);
+            log.debug("ERR from ssh -vvv command is: "+err);
+            assertFalse(err.toString().contains("hello err"), "hello found where it shouldn't have been, in stderr (should have been tty merged to stdout): "+err);
+            assertTrue(out.toString().contains("hello err"), "no hello in stdout: "+err);
+            // look for word 'ssh' to confirm we got verbose output
+            assertTrue(err.toString().toLowerCase().contains("ssh"), "no mention of ssh in stderr: "+err);
+        } catch (SshException e) {
+            if (!e.toString().contains("failed to connect")) throw e;
+        }
+    }
+
+    // Need to have at least one test method here (rather than just inherited) for eclipse to recognize it
+    @Test(enabled = false)
+    public void testDummy() throws Exception {
+    }
+    
+    // TODO When running mvn on the command line (for Aled), this test hangs when prompting for a password (but works in the IDE!)
+    // Doing .connect() isn't enough; need to cause ssh or scp to be invoked
+    @Test(enabled=false, groups = {"Integration"})
+    public void testConnectWithInvalidUserThrowsException() throws Exception {
+        final SshTool localtool = newTool(ImmutableMap.of("user", "wronguser", "host", "localhost", "privateKeyFile", "~/.ssh/id_rsa"));
+        tools.add(localtool);
+        try {
+            localtool.connect();
+            int result = localtool.execScript(ImmutableMap.<String,Object>of(), ImmutableList.of("date"));
+            fail("exitCode="+result+", but expected exception");
+        } catch (SshException e) {
+            if (!e.toString().contains("failed to connect")) throw e;
+        }
+    }
+    
+    // TODO ssh-cli doesn't support pass-phrases yet
+    @Test(enabled=false, groups = {"Integration"})
+    public void testSshKeyWithPassphrase() throws Exception {
+        super.testSshKeyWithPassphrase();
+    }
+
+    // Setting last modified date not yet supported for cli-based ssh
+    @Override
+    @Test(enabled=false, groups = {"Integration"})
+    public void testCopyToServerWithLastModifiedDate() throws Exception {
+        super.testCopyToServerWithLastModifiedDate();
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testExecReturningNonZeroExitCode() throws Exception {
+        int exitcode = tool.execCommands(MutableMap.<String,Object>of(), ImmutableList.of("exit 123"));
+        assertEquals(exitcode, 123);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliToolPerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliToolPerformanceTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliToolPerformanceTest.java
new file mode 100644
index 0000000..fd01180
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliToolPerformanceTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh.cli;
+
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshToolAbstractPerformanceTest;
+import org.apache.brooklyn.core.util.internal.ssh.cli.SshCliTool;
+import org.testng.annotations.Test;
+
+/**
+ * Test the performance of different variants of invoking the sshj tool.
+ * 
+ * Intended for human-invocation and inspection, to see which parts are most expensive.
+ */
+public class SshCliToolPerformanceTest extends SshToolAbstractPerformanceTest {
+
+    @Override
+    protected SshTool newSshTool(Map<String,?> flags) {
+        return new SshCliTool(flags);
+    }
+    
+    // Need to have at least one test method here (rather than just inherited) for eclipse to recognize it
+    @Test(enabled = false)
+    public void testDummy() throws Exception {
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessToolIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessToolIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessToolIntegrationTest.java
new file mode 100644
index 0000000..3dd558d
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessToolIntegrationTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh.process;
+
+import static org.testng.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.internal.ssh.ShellToolAbstractTest;
+import org.apache.brooklyn.core.util.internal.ssh.process.ProcessTool;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * Test the operation of the {@link ProcessTool} utility class.
+ */
+public class ProcessToolIntegrationTest extends ShellToolAbstractTest {
+
+    @Override
+    protected ProcessTool newUnregisteredTool(Map<String,?> flags) {
+        return new ProcessTool(flags);
+    }
+
+    // ones here included as *non*-integration tests. must run on windows and linux.
+    // (also includes integration tests from parent)
+
+    @Test(groups="UNIX")
+    public void testPortableCommand() throws Exception {
+        String out = execScript("echo hello world");
+        assertTrue(out.contains("hello world"), "out="+out);
+    }
+
+    @Test(groups="Integration")
+    public void testLoginShell() {
+        // this detection scheme only works for commands; can't test whether it works for scripts without 
+        // requiring stuff in bash_profile / profile / etc, which gets hard to make portable;
+        // it is nearly the same code path on the impl so this is probably enough 
+        
+        final String LOGIN_SHELL_CHECK = "shopt -q login_shell && echo 'yes, login shell' || echo 'no, not login shell'";
+        ConfigBag config = ConfigBag.newInstance().configure(ProcessTool.PROP_NO_EXTRA_OUTPUT, true);
+        String out;
+        
+        out = execCommands(config, Arrays.asList(LOGIN_SHELL_CHECK), null);
+        Assert.assertEquals(out.trim(), "no, not login shell", "out = "+out);
+        
+        config.configure(ProcessTool.PROP_LOGIN_SHELL, true);
+        out = execCommands(config, Arrays.asList(LOGIN_SHELL_CHECK), null);
+        Assert.assertEquals(out.trim(), "yes, login shell", "out = "+out);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessToolStaticsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessToolStaticsTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessToolStaticsTest.java
new file mode 100644
index 0000000..eacd761
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessToolStaticsTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh.process;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.brooklyn.core.util.internal.ssh.process.ProcessTool;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.os.Os;
+
+public class ProcessToolStaticsTest {
+
+    ByteArrayOutputStream out;
+    ByteArrayOutputStream err;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void clear() {
+        out = new ByteArrayOutputStream();
+        err = new ByteArrayOutputStream();
+    }
+    
+    private List<String> getTestCommand() {
+        if(Os.isMicrosoftWindows()) {
+            return Arrays.asList("cmd", "/c", "echo", "hello", "world");
+        } else {
+            return Arrays.asList("echo", "hello", "world");
+        }
+    }
+
+    @Test
+    public void testRunsWithStdout() throws Exception {
+        int code = ProcessTool.execSingleProcess(getTestCommand(), null, (File)null, out, err, this);
+        Assert.assertEquals(err.toString().trim(), "");
+        Assert.assertEquals(out.toString().trim(), "hello world");
+        Assert.assertEquals(code, 0);
+    }
+
+    @Test(groups="Integration") // *nix only
+    public void testRunsWithBashEnvVarAndStderr() throws Exception {
+        int code = ProcessTool.execSingleProcess(Arrays.asList("/bin/bash", "-c", "echo hello $NAME | tee /dev/stderr"), 
+                MutableMap.of("NAME", "BOB"), (File)null, out, err, this);
+        Assert.assertEquals(err.toString().trim(), "hello BOB", "err is: "+err);
+        Assert.assertEquals(out.toString().trim(), "hello BOB", "out is: "+out);
+        Assert.assertEquals(code, 0);
+    }
+
+    @Test(groups="Integration") // *nix only
+    public void testRunsManyCommandsWithBashEnvVarAndStderr() throws Exception {
+        int code = ProcessTool.execProcesses(Arrays.asList("echo hello $NAME", "export NAME=JOHN", "echo goodbye $NAME | tee /dev/stderr"), 
+                MutableMap.of("NAME", "BOB"), (File)null, out, err, " ; ", false, this);
+        Assert.assertEquals(err.toString().trim(), "goodbye JOHN", "err is: "+err);
+        Assert.assertEquals(out.toString().trim(), "hello BOB\ngoodbye JOHN", "out is: "+out);
+        Assert.assertEquals(code, 0);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
new file mode 100644
index 0000000..e3b4b7d
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh.sshj;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
+import org.apache.brooklyn.core.util.internal.ssh.SshAbstractTool.SshAction;
+import org.apache.brooklyn.core.util.internal.ssh.sshj.SshjTool;
+import org.apache.brooklyn.core.util.internal.ssh.sshj.SshjTool.ShellAction;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+
+/**
+ * Tests for async-exec with {@link SshjTool}, where it stubs out the actual ssh commands
+ * to return a controlled sequence of responses.
+ */
+public class SshjToolAsyncStubIntegrationTest {
+
+    static class InjectedResult {
+        Predicate<SshjTool.ShellAction> expected;
+        Function<SshjTool.ShellAction, Integer> result;
+        
+        InjectedResult(Predicate<SshjTool.ShellAction> expected, Function<SshjTool.ShellAction, Integer> result) {
+            this.expected = expected;
+            this.result = result;
+        }
+    }
+    
+    private SshjTool tool;
+    private List<InjectedResult> sequence;
+    int counter = 0;
+    private boolean origFeatureEnablement;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
+        sequence = Lists.newArrayList();
+        counter = 0;
+        
+        tool = new SshjTool(ImmutableMap.<String,Object>of("host", "localhost")) {
+            @SuppressWarnings("unchecked")
+            protected <T, C extends SshAction<T>> T acquire(C action, int sshTries, Duration sshTriesTimeout) {
+                if (action instanceof SshjTool.ShellAction) {
+                    SshjTool.ShellAction shellAction = (SshjTool.ShellAction) action;
+                    InjectedResult injectedResult = sequence.get(counter);
+                    assertTrue(injectedResult.expected.apply(shellAction), "counter="+counter+"; cmds="+shellAction.commands);
+                    counter++;
+                    return (T) injectedResult.result.apply(shellAction);
+                }
+                return super.acquire(action, sshTries, sshTriesTimeout);
+            }
+        };
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        try {
+            if (tool != null) tool.disconnect();
+        } finally {
+            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
+        }
+    }
+    
+    private Predicate<SshjTool.ShellAction> containsCmd(final String cmd) {
+        return new Predicate<SshjTool.ShellAction>() {
+            @Override public boolean apply(ShellAction input) {
+                return input != null && input.commands.toString().contains(cmd);
+            }
+        };
+    }
+    
+    private Function<SshjTool.ShellAction, Integer> returning(final int result, final String stdout, final String stderr) {
+        return new Function<SshjTool.ShellAction, Integer>() {
+            @Override public Integer apply(ShellAction input) {
+                try {
+                    if (stdout != null && input.out != null) input.out.write(stdout.getBytes());
+                    if (stderr != null && input.err != null) input.err.write(stderr.getBytes());
+                } catch (IOException e) {
+                    throw Exceptions.propagate(e);
+                }
+                return result;
+            }
+        };
+    }
+    
+    @Test(groups="Integration")
+    public void testPolls() throws Exception {
+        sequence = ImmutableList.of(
+                new InjectedResult(containsCmd("nohup"), returning(0, "", "")),
+                new InjectedResult(containsCmd("# Long poll"), returning(0, "mystringToStdout", "mystringToStderr")));
+
+        runTest(0, "mystringToStdout", "mystringToStderr");
+        assertEquals(counter, sequence.size());
+    }
+    
+    @Test(groups="Integration")
+    public void testPollsAndReturnsNonZeroExitCode() throws Exception {
+        sequence = ImmutableList.of(
+                new InjectedResult(containsCmd("nohup"), returning(0, "", "")),
+                new InjectedResult(containsCmd("# Long poll"), returning(123, "mystringToStdout", "mystringToStderr")),
+                new InjectedResult(containsCmd("# Retrieve status"), returning(0, "123", "")));
+
+        runTest(123, "mystringToStdout", "mystringToStderr");
+        assertEquals(counter, sequence.size());
+    }
+    
+    @Test(groups="Integration")
+    public void testPollsRepeatedly() throws Exception {
+        sequence = ImmutableList.of(
+                new InjectedResult(containsCmd("nohup"), returning(0, "", "")),
+                new InjectedResult(containsCmd("# Long poll"), returning(125, "mystringToStdout", "mystringToStderr")),
+                new InjectedResult(containsCmd("# Retrieve status"), returning(0, "", "")),
+                new InjectedResult(containsCmd("# Long poll"), returning(125, "mystringToStdout2", "mystringToStderr2")),
+                new InjectedResult(containsCmd("# Retrieve status"), returning(0, "", "")),
+                new InjectedResult(containsCmd("# Long poll"), returning(-1, "mystringToStdout3", "mystringToStderr3")),
+                new InjectedResult(containsCmd("# Long poll"), returning(125, "mystringToStdout4", "mystringToStderr4")),
+                new InjectedResult(containsCmd("# Retrieve status"), returning(0, "", "")),
+                new InjectedResult(containsCmd("# Long poll"), returning(0, "mystringToStdout5", "mystringToStderr5")));
+
+        runTest(0,
+                "mystringToStdout"+"mystringToStdout2"+"mystringToStdout3"+"mystringToStdout4"+"mystringToStdout5",
+                "mystringToStderr"+"mystringToStderr2"+"mystringToStderr3"+"mystringToStderr4"+"mystringToStderr5");
+        assertEquals(counter, sequence.size());
+    }
+    
+    protected void runTest(int expectedExit, String expectedStdout, String expectedStderr) throws Exception {
+        List<String> cmds = ImmutableList.of("abc");
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ByteArrayOutputStream err = new ByteArrayOutputStream();
+        int exitCode = tool.execScript(
+                ImmutableMap.of(
+                        "out", out, 
+                        "err", err, 
+                        SshjTool.PROP_EXEC_ASYNC.getName(), true, 
+                        SshjTool.PROP_NO_EXTRA_OUTPUT.getName(), true,
+                        SshjTool.PROP_EXEC_ASYNC_POLLING_TIMEOUT.getName(), Duration.ONE_MILLISECOND), 
+                cmds, 
+                ImmutableMap.<String,String>of());
+        String outStr = new String(out.toByteArray());
+        String errStr = new String(err.toByteArray());
+
+        assertEquals(exitCode, expectedExit);
+        assertEquals(outStr.trim(), expectedStdout);
+        assertEquals(errStr.trim(), expectedStderr);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolIntegrationTest.java
new file mode 100644
index 0000000..bf1aafb
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolIntegrationTest.java
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh.sshj;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import net.schmizz.sshj.connection.channel.direct.Session;
+
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
+import org.apache.brooklyn.core.util.internal.ssh.SshException;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshToolAbstractIntegrationTest;
+import org.apache.brooklyn.core.util.internal.ssh.sshj.SshjTool;
+import org.testng.annotations.Test;
+
+import brooklyn.test.Asserts;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.RuntimeTimeoutException;
+import brooklyn.util.os.Os;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Test the operation of the {@link SshJschTool} utility class.
+ */
+public class SshjToolIntegrationTest extends SshToolAbstractIntegrationTest {
+
+    @Override
+    protected SshTool newUnregisteredTool(Map<String,?> flags) {
+        return new SshjTool(flags);
+    }
+
+    // TODO requires vt100 terminal emulation to work?
+    @Test(enabled = false, groups = {"Integration"})
+    public void testExecShellWithCommandTakingStdin() throws Exception {
+        // Uses `tee` to redirect stdin to the given file; cntr-d (i.e. char 4) stops tee with exit code 0
+        String content = "blah blah";
+        String out = execShellDirectWithTerminalEmulation("tee "+remoteFilePath, content, ""+(char)4, "echo file contents: `cat "+remoteFilePath+"`");
+
+        assertTrue(out.contains("file contents: blah blah"), "out="+out);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testGivesUpAfterMaxRetries() throws Exception {
+        final AtomicInteger callCount = new AtomicInteger();
+        
+        final SshTool localtool = new SshjTool(ImmutableMap.of("sshTries", 3, "host", "localhost", "privateKeyFile", "~/.ssh/id_rsa")) {
+            protected SshAction<Session> newSessionAction() {
+                callCount.incrementAndGet();
+                throw new RuntimeException("Simulating ssh execution failure");
+            }
+        };
+        
+        tools.add(localtool);
+        try {
+            localtool.execScript(ImmutableMap.<String,Object>of(), ImmutableList.of("true"));
+            fail();
+        } catch (SshException e) {
+            if (!e.toString().contains("out of retries")) throw e;
+            assertEquals(callCount.get(), 3);
+        }
+    }
+
+    @Test(groups = {"Integration"})
+    public void testReturnsOnSuccessWhenRetrying() throws Exception {
+        final AtomicInteger callCount = new AtomicInteger();
+        final int successOnAttempt = 2;
+        final SshTool localtool = new SshjTool(ImmutableMap.of("sshTries", 3, "host", "localhost", "privateKeyFile", "~/.ssh/id_rsa")) {
+            protected SshAction<Session> newSessionAction() {
+                callCount.incrementAndGet();
+                if (callCount.incrementAndGet() >= successOnAttempt) {
+                    return super.newSessionAction();
+                } else {
+                    throw new RuntimeException("Simulating ssh execution failure");
+                }
+            }
+        };
+        
+        tools.add(localtool);
+        localtool.execScript(ImmutableMap.<String,Object>of(), ImmutableList.of("true"));
+        assertEquals(callCount.get(), successOnAttempt);
+    }
+
+    @Test(groups = {"Integration"})
+    public void testGivesUpAfterMaxTime() throws Exception {
+        final AtomicInteger callCount = new AtomicInteger();
+        final SshTool localtool = new SshjTool(ImmutableMap.of("sshTriesTimeout", 1000, "host", "localhost", "privateKeyFile", "~/.ssh/id_rsa")) {
+            protected SshAction<Session> newSessionAction() {
+                callCount.incrementAndGet();
+                try {
+                    Thread.sleep(600);
+                } catch (InterruptedException e) {
+                    throw Exceptions.propagate(e);
+                }
+                throw new RuntimeException("Simulating ssh execution failure");
+            }
+        };
+        
+        tools.add(localtool);
+        try {
+            localtool.execScript(ImmutableMap.<String,Object>of(), ImmutableList.of("true"));
+            fail();
+        } catch (RuntimeTimeoutException e) {
+            if (!e.toString().contains("out of time")) throw e;
+            assertEquals(callCount.get(), 2);
+        }
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testUsesCustomLocalTempDir() throws Exception {
+        class SshjToolForTest extends SshjTool {
+            public SshjToolForTest(Map<String, ?> map) {
+                super(map);
+            }
+            public File getLocalTempDir() {
+                return localTempDir;
+            }
+        };
+        
+        final SshjToolForTest localtool = new SshjToolForTest(ImmutableMap.<String, Object>of("host", "localhost"));
+        assertNotNull(localtool.getLocalTempDir());
+        assertEquals(localtool.getLocalTempDir(), new File(Os.tidyPath(SshjTool.PROP_LOCAL_TEMP_DIR.getDefaultValue())));
+        
+        String customTempDir = Os.tmp();
+        final SshjToolForTest localtool2 = new SshjToolForTest(ImmutableMap.of(
+                "host", "localhost", 
+                SshjTool.PROP_LOCAL_TEMP_DIR.getName(), customTempDir));
+        assertEquals(localtool2.getLocalTempDir(), new File(customTempDir));
+        
+        String customRelativeTempDir = "~/tmp";
+        final SshjToolForTest localtool3 = new SshjToolForTest(ImmutableMap.of(
+                "host", "localhost", 
+                SshjTool.PROP_LOCAL_TEMP_DIR.getName(), customRelativeTempDir));
+        assertEquals(localtool3.getLocalTempDir(), new File(Os.tidyPath(customRelativeTempDir)));
+    }
+
+    @Test(groups = {"Integration"})
+    public void testAsyncExecStdoutAndStderr() throws Exception {
+        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
+        try {
+            // Include a sleep, to ensure that the contents retrieved in first poll and subsequent polls are appended
+            List<String> cmds = ImmutableList.of(
+                    "echo mystringToStdout",
+                    "echo mystringToStderr 1>&2",
+                    "sleep 5",
+                    "echo mystringPostSleepToStdout",
+                    "echo mystringPostSleepToStderr 1>&2");
+            
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            ByteArrayOutputStream err = new ByteArrayOutputStream();
+            int exitCode = tool.execScript(
+                    ImmutableMap.of(
+                            "out", out, 
+                            "err", err, 
+                            SshjTool.PROP_EXEC_ASYNC.getName(), true, 
+                            SshjTool.PROP_NO_EXTRA_OUTPUT.getName(), true,
+                            SshjTool.PROP_EXEC_ASYNC_POLLING_TIMEOUT.getName(), Duration.ONE_SECOND), 
+                    cmds, 
+                    ImmutableMap.<String,String>of());
+            String outStr = new String(out.toByteArray());
+            String errStr = new String(err.toByteArray());
+    
+            assertEquals(exitCode, 0);
+            assertEquals(outStr.trim(), "mystringToStdout\nmystringPostSleepToStdout");
+            assertEquals(errStr.trim(), "mystringToStderr\nmystringPostSleepToStderr");
+        } finally {
+            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
+        }
+    }
+
+    @Test(groups = {"Integration"})
+    public void testAsyncExecReturnsExitCode() throws Exception {
+        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
+        try {
+            int exitCode = tool.execScript(
+                    ImmutableMap.of(SshjTool.PROP_EXEC_ASYNC.getName(), true), 
+                    ImmutableList.of("exit 123"), 
+                    ImmutableMap.<String,String>of());
+            assertEquals(exitCode, 123);
+        } finally {
+            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
+        }
+    }
+
+    @Test(groups = {"Integration"})
+    public void testAsyncExecTimesOut() throws Exception {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
+        try {
+            tool.execScript(
+                ImmutableMap.of(SshjTool.PROP_EXEC_ASYNC.getName(), true, SshjTool.PROP_EXEC_TIMEOUT.getName(), Duration.millis(1)), 
+                ImmutableList.of("sleep 60"), 
+                ImmutableMap.<String,String>of());
+            fail();
+        } catch (Exception e) {
+            TimeoutException te = Exceptions.getFirstThrowableOfType(e, TimeoutException.class);
+            if (te == null) throw e;
+        } finally {
+            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
+        }
+        
+        long seconds = stopwatch.elapsed(TimeUnit.SECONDS);
+        assertTrue(seconds < 30, "exec took "+seconds+" seconds");
+    }
+
+    @Test(groups = {"Integration"})
+    public void testAsyncExecAbortsIfProcessFails() throws Exception {
+        final AtomicReference<Throwable> error = new AtomicReference<Throwable>();
+        Thread thread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Stopwatch stopwatch = Stopwatch.createStarted();
+                    int exitStatus = tool.execScript(
+                        ImmutableMap.of(SshjTool.PROP_EXEC_ASYNC.getName(), true, SshjTool.PROP_EXEC_TIMEOUT.getName(), Duration.millis(1)), 
+                        ImmutableList.of("sleep 63"), 
+                        ImmutableMap.<String,String>of());
+                    
+                    assertEquals(exitStatus, 143 /* 128 + Signal number (SIGTERM) */);
+                    
+                    long seconds = stopwatch.elapsed(TimeUnit.SECONDS);
+                    assertTrue(seconds < 30, "exec took "+seconds+" seconds");
+                } catch (Throwable t) {
+                    error.set(t);
+                }
+            }});
+        
+        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
+        try {
+            thread.start();
+            
+            Asserts.succeedsEventually(new Runnable() {
+                @Override
+                public void run() {
+                    int exitStatus = tool.execCommands(ImmutableMap.<String,Object>of(), ImmutableList.of("ps aux| grep \"sleep 63\" | grep -v grep"));
+                    assertEquals(exitStatus, 0);
+                }});
+            
+            tool.execCommands(ImmutableMap.<String,Object>of(), ImmutableList.of("ps aux| grep \"sleep 63\" | grep -v grep | awk '{print($2)}' | xargs kill"));
+            
+            thread.join(30*1000);
+            assertFalse(thread.isAlive());
+            if (error.get() != null) {
+                throw Exceptions.propagate(error.get());
+            }
+        } finally {
+            thread.interrupt();
+            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
+        }
+    }
+
+    
+    protected String execShellDirect(List<String> cmds) {
+        return execShellDirect(cmds, ImmutableMap.<String,Object>of());
+    }
+    
+    protected String execShellDirect(List<String> cmds, Map<String,?> env) {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int exitcode = ((SshjTool)tool).execShellDirect(ImmutableMap.of("out", out), cmds, env);
+        String outstr = new String(out.toByteArray());
+        assertEquals(exitcode, 0, outstr);
+        return outstr;
+    }
+
+    private String execShellDirectWithTerminalEmulation(String... cmds) {
+        return execShellDirectWithTerminalEmulation(Arrays.asList(cmds));
+    }
+    
+    private String execShellDirectWithTerminalEmulation(List<String> cmds) {
+        return execShellDirectWithTerminalEmulation(cmds, ImmutableMap.<String,Object>of());
+    }
+    
+    private String execShellDirectWithTerminalEmulation(List<String> cmds, Map<String,?> env) {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int exitcode = ((SshjTool)tool).execShellDirect(ImmutableMap.of("allocatePTY", true, "out", out), cmds, env);
+        String outstr = new String(out.toByteArray());
+        assertEquals(exitcode, 0, outstr);
+        return outstr;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolPerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolPerformanceTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolPerformanceTest.java
new file mode 100644
index 0000000..71ea4e6
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjToolPerformanceTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh.sshj;
+
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshToolAbstractPerformanceTest;
+import org.apache.brooklyn.core.util.internal.ssh.sshj.SshjTool;
+import org.testng.annotations.Test;
+
+/**
+ * Test the performance of different variants of invoking the sshj tool.
+ * 
+ * Intended for human-invocation and inspection, to see which parts are most expensive.
+ */
+public class SshjToolPerformanceTest extends SshToolAbstractPerformanceTest {
+
+    @Override
+    protected SshTool newSshTool(Map<String,?> flags) {
+        return new SshjTool(flags);
+    }
+    
+    // Need to have at least one test method here (rather than just inherited) for eclipse to recognize it
+    @Test(enabled = false)
+    public void testDummy() throws Exception {
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/mutex/WithMutexesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/mutex/WithMutexesTest.java b/core/src/test/java/org/apache/brooklyn/core/util/mutex/WithMutexesTest.java
new file mode 100644
index 0000000..c37ab30
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/mutex/WithMutexesTest.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.mutex;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.mutex.MutexSupport;
+import org.apache.brooklyn.core.util.mutex.SemaphoreWithOwners;
+import org.apache.brooklyn.core.util.mutex.WithMutexes;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class WithMutexesTest {
+
+    @Test
+    public void testOneAcquisitionAndRelease() throws InterruptedException {
+        MutexSupport m = new MutexSupport();
+        Map<String, SemaphoreWithOwners> sems;
+        SemaphoreWithOwners s;
+        try {
+            m.acquireMutex("foo", "something foo");
+            sems = m.getAllSemaphores();
+            Assert.assertEquals(sems.size(), 1);
+            s = sems.get("foo");
+            Assert.assertEquals(s.getDescription(), "something foo");
+            Assert.assertEquals(s.getOwningThreads(), Arrays.asList(Thread.currentThread()));
+            Assert.assertEquals(s.getRequestingThreads(), Collections.emptyList());
+            Assert.assertTrue(s.isInUse());
+            Assert.assertTrue(s.isCallingThreadAnOwner());
+        } finally {
+            m.releaseMutex("foo");
+        }
+        Assert.assertFalse(s.isInUse());
+        Assert.assertFalse(s.isCallingThreadAnOwner());
+        Assert.assertEquals(s.getDescription(), "something foo");
+        Assert.assertEquals(s.getOwningThreads(), Collections.emptyList());
+        Assert.assertEquals(s.getRequestingThreads(), Collections.emptyList());
+        
+        sems = m.getAllSemaphores();
+        Assert.assertEquals(sems, Collections.emptyMap());
+    }
+
+    @Test(groups = "Integration")  //just because it takes a wee while
+    public void testBlockingAcquisition() throws InterruptedException {
+        final MutexSupport m = new MutexSupport();
+        m.acquireMutex("foo", "something foo");
+        
+        Assert.assertFalse(m.tryAcquireMutex("foo", "something else"));
+
+        Thread t = new Thread() {
+            public void run() {
+                try {
+                    m.acquireMutex("foo", "thread 2 foo");
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                m.releaseMutex("foo");
+            }
+        };
+        t.start();
+        
+        t.join(500);
+        Assert.assertTrue(t.isAlive());
+        Assert.assertEquals(m.getSemaphore("foo").getRequestingThreads(), Arrays.asList(t));
+
+        m.releaseMutex("foo");
+        
+        t.join(1000);
+        Assert.assertFalse(t.isAlive());
+
+        Assert.assertEquals(m.getAllSemaphores(), Collections.emptyMap());
+    }
+
+    
+    public static class SampleWithMutexesDelegatingMixin implements WithMutexes {
+        
+        /* other behaviour would typically go here... */
+        
+        WithMutexes mutexSupport = new MutexSupport();
+        
+        @Override
+        public void acquireMutex(String mutexId, String description) throws InterruptedException {
+            mutexSupport.acquireMutex(mutexId, description);
+        }
+
+        @Override
+        public boolean tryAcquireMutex(String mutexId, String description) {
+            return mutexSupport.tryAcquireMutex(mutexId, description);
+        }
+
+        @Override
+        public void releaseMutex(String mutexId) {
+            mutexSupport.releaseMutex(mutexId);
+        }
+
+        @Override
+        public boolean hasMutex(String mutexId) {
+            return mutexSupport.hasMutex(mutexId);
+        }
+    }
+    
+    @Test
+    public void testDelegatingMixinPattern() throws InterruptedException {
+        WithMutexes m = new SampleWithMutexesDelegatingMixin();
+        m.acquireMutex("foo", "sample");
+        Assert.assertTrue(m.hasMutex("foo"));
+        Assert.assertFalse(m.hasMutex("bar"));
+        m.releaseMutex("foo");
+        Assert.assertFalse(m.hasMutex("foo"));
+    }
+}



[02/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/policy

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/org/apache/brooklyn/core/policy/basic/AbstractPolicy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/policy/basic/AbstractPolicy.java b/core/src/main/java/org/apache/brooklyn/core/policy/basic/AbstractPolicy.java
new file mode 100644
index 0000000..9d809e3
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/policy/basic/AbstractPolicy.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.brooklyn.api.entity.rebind.RebindSupport;
+import org.apache.brooklyn.api.entity.trait.Configurable;
+import org.apache.brooklyn.api.mementos.PolicyMemento;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.policy.PolicyType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.rebind.BasicPolicyRebindSupport;
+
+import com.google.common.base.Objects;
+
+/**
+ * Base {@link Policy} implementation; all policies should extend this or its children
+ */
+public abstract class AbstractPolicy extends AbstractEntityAdjunct implements Policy, Configurable {
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(AbstractPolicy.class);
+
+    protected String policyStatus;
+    protected AtomicBoolean suspended = new AtomicBoolean(false);
+
+    private final PolicyDynamicType policyType;
+    
+    public AbstractPolicy() {
+        this(Collections.emptyMap());
+    }
+    
+    public AbstractPolicy(Map<?,?> flags) {
+        super(flags);
+        
+        // TODO Don't let `this` reference escape during construction
+        policyType = new PolicyDynamicType(this);
+        
+        if (isLegacyConstruction() && !isLegacyNoConstructionInit()) {
+            init();
+        }
+    }
+
+    @Override
+    public PolicyType getPolicyType() {
+        return policyType.getSnapshot();
+    }
+
+    @Override
+    public void suspend() {
+        suspended.set(true);
+    }
+
+    @Override
+    public void resume() {
+        suspended.set(false);
+    }
+
+    @Override
+    public boolean isSuspended() {
+        if (suspended==null) {
+            // only if accessed during construction in super, e.g. by a call to toString in configure
+            return true;
+        }
+        return suspended.get();
+    }
+
+    @Override
+    public void destroy(){
+        suspend();
+        super.destroy();
+    }
+
+    @Override
+    public boolean isRunning() {
+        return !isSuspended() && !isDestroyed();
+    }
+
+    @Override
+    protected void onChanged() {
+        // currently changes simply trigger re-persistence; there is no intermediate listener as we do for EntityChangeListener
+        if (getManagementContext() != null) {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(this);
+        }
+    }
+    
+    @Override
+    public RebindSupport<PolicyMemento> getRebindSupport() {
+        return new BasicPolicyRebindSupport(this);
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(getClass())
+                .add("name", name)
+                .add("running", isRunning())
+                .toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/org/apache/brooklyn/core/policy/basic/AdjunctType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/policy/basic/AdjunctType.java b/core/src/main/java/org/apache/brooklyn/core/policy/basic/AdjunctType.java
new file mode 100644
index 0000000..f3d6a6c
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/policy/basic/AdjunctType.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.policy.EntityAdjunct;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.config.ConfigKey.HasConfigKey;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+/**
+ * This is the actual type of a policy instance at runtime.
+ */
+public class AdjunctType implements Serializable {
+    private static final long serialVersionUID = -662979234559595903L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(AdjunctType.class);
+
+    private final String name;
+    private final Map<String, ConfigKey<?>> configKeys;
+    private final Set<ConfigKey<?>> configKeysSet;
+
+    public AdjunctType(AbstractEntityAdjunct adjunct) {
+        this(adjunct.getClass(), adjunct);
+    }
+    
+    protected AdjunctType(Class<? extends EntityAdjunct> clazz) {
+        this(clazz, null);
+    }
+    
+    private AdjunctType(Class<? extends EntityAdjunct> clazz, AbstractEntityAdjunct adjunct) {
+        name = clazz.getCanonicalName();
+        configKeys = Collections.unmodifiableMap(findConfigKeys(clazz, null));
+        configKeysSet = ImmutableSet.copyOf(this.configKeys.values());
+        if (LOG.isTraceEnabled())
+            LOG.trace("Policy {} config keys: {}", name, Joiner.on(", ").join(configKeys.keySet()));
+    }
+    
+    AdjunctType(String name, Map<String, ConfigKey<?>> configKeys) {
+        this.name = name;
+        this.configKeys = ImmutableMap.copyOf(configKeys);
+        this.configKeysSet = ImmutableSet.copyOf(this.configKeys.values());
+    }
+
+    public String getName() {
+        return name;
+    }
+    
+    public Set<ConfigKey<?>> getConfigKeys() {
+        return configKeysSet;
+    }
+    
+    public ConfigKey<?> getConfigKey(String name) {
+        return configKeys.get(name);
+    }
+    
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(name, configKeys);
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (getClass() != obj.getClass()) return false;
+        AdjunctType o = (AdjunctType) obj;
+        if (!Objects.equal(name, o.getName())) return false;
+        if (!Objects.equal(getConfigKeys(), o.getConfigKeys())) return false;
+        return true;
+    }
+    
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(name)
+                .add("configKeys", configKeys)
+                .toString();
+    }
+    
+    /**
+     * Finds the config keys defined on the entity's class, statics and optionally any non-static (discouraged).
+     */
+    // TODO Remove duplication from EntityDynamicType
+    protected static Map<String,ConfigKey<?>> findConfigKeys(Class<? extends EntityAdjunct> clazz, EntityAdjunct optionalInstance) {
+        try {
+            Map<String,ConfigKey<?>> result = Maps.newLinkedHashMap();
+            Map<String,Field> configFields = Maps.newLinkedHashMap();
+            for (Field f : clazz.getFields()) {
+                boolean isConfigKey = ConfigKey.class.isAssignableFrom(f.getType());
+                if (!isConfigKey) {
+                    if (!HasConfigKey.class.isAssignableFrom(f.getType())) {
+                        // neither ConfigKey nor HasConfigKey
+                        continue;
+                    }
+                }
+                if (!Modifier.isStatic(f.getModifiers())) {
+                    // require it to be static or we have an instance
+                    LOG.warn("Discouraged use of non-static config key "+f+" defined in " + (optionalInstance!=null ? optionalInstance : clazz));
+                    if (optionalInstance==null) continue;
+                }
+                ConfigKey<?> k = isConfigKey ? (ConfigKey<?>) f.get(optionalInstance) : 
+                    ((HasConfigKey<?>)f.get(optionalInstance)).getConfigKey();
+
+                Field alternativeField = configFields.get(k.getName());
+                // Allow overriding config keys (e.g. to set default values) when there is an assignable-from relationship between classes
+                Field definitiveField = alternativeField != null ? inferSubbestField(alternativeField, f) : f;
+                boolean skip = false;
+                if (definitiveField != f) {
+                    // If they refer to the _same_ instance, just keep the one we already have
+                    if (alternativeField.get(optionalInstance) == f.get(optionalInstance)) skip = true;
+                }
+                if (skip) {
+                    //nothing
+                } else if (definitiveField == f) {
+                    result.put(k.getName(), k);
+                    configFields.put(k.getName(), f);
+                } else if (definitiveField != null) {
+                    if (LOG.isDebugEnabled()) LOG.debug("multiple definitions for config key {} on {}; preferring that in sub-class: {} to {}", new Object[] {
+                            k.getName(), optionalInstance!=null ? optionalInstance : clazz, alternativeField, f});
+                } else if (definitiveField == null) {
+                    LOG.warn("multiple definitions for config key {} on {}; preferring {} to {}", new Object[] {
+                            k.getName(), optionalInstance!=null ? optionalInstance : clazz, alternativeField, f});
+                }
+            }
+            
+            return result;
+        } catch (IllegalAccessException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+    
+    /**
+     * Gets the field that is in the sub-class; or null if one field does not come from a sub-class of the other field's class
+     */
+    // TODO Remove duplication from EntityDynamicType
+    private static Field inferSubbestField(Field f1, Field f2) {
+        Class<?> c1 = f1.getDeclaringClass();
+        Class<?> c2 = f2.getDeclaringClass();
+        boolean isSuper1 = c1.isAssignableFrom(c2);
+        boolean isSuper2 = c2.isAssignableFrom(c1);
+        return (isSuper1) ? (isSuper2 ? null : f2) : (isSuper2 ? f1 : null);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/org/apache/brooklyn/core/policy/basic/ConfigMapImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/policy/basic/ConfigMapImpl.java b/core/src/main/java/org/apache/brooklyn/core/policy/basic/ConfigMapImpl.java
new file mode 100644
index 0000000..5cdc279
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/policy/basic/ConfigMapImpl.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import static brooklyn.util.GroovyJavaMethods.elvis;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.internal.ConfigKeySelfExtracting;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Maps;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.config.internal.AbstractConfigMapImpl;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.basic.Sanitizer;
+import brooklyn.event.basic.StructuredConfigKey;
+import brooklyn.util.guava.Maybe;
+
+public class ConfigMapImpl extends AbstractConfigMapImpl {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ConfigMapImpl.class);
+
+    /** policy against which config resolution / task execution will occur */
+    private final AbstractEntityAdjunct adjunct;
+
+    /*
+     * TODO An alternative implementation approach would be to have:
+     *   setParent(Entity o, Map<ConfigKey,Object> inheritedConfig=[:])
+     * The idea is that the parent could in theory decide explicitly what in its config
+     * would be shared.
+     * I (Aled) am undecided as to whether that would be better...
+     * 
+     * (Alex) i lean toward the config key getting to make the decision
+     */
+
+    public ConfigMapImpl(AbstractEntityAdjunct adjunct) {
+        this.adjunct = Preconditions.checkNotNull(adjunct, "AbstractEntityAdjunct must be specified");
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T getConfig(ConfigKey<T> key, T defaultValue) {
+        // FIXME What about inherited task in config?!
+        //              alex says: think that should work, no?
+        // FIXME What if someone calls getConfig on a task, before setting parent app?
+        //              alex says: not supported (throw exception, or return the task)
+        
+        // In case this entity class has overridden the given key (e.g. to set default), then retrieve this entity's key
+        // TODO If ask for a config value that's not in our configKeys, should we really continue with rest of method and return key.getDefaultValue?
+        //      e.g. SshBasedJavaAppSetup calls setAttribute(JMX_USER), which calls getConfig(JMX_USER)
+        //           but that example doesn't have a default...
+        ConfigKey<T> ownKey = adjunct!=null ? (ConfigKey<T>)elvis(adjunct.getAdjunctType().getConfigKey(key.getName()), key) : key;
+        
+        // Don't use groovy truth: if the set value is e.g. 0, then would ignore set value and return default!
+        if (ownKey instanceof ConfigKeySelfExtracting) {
+            if (((ConfigKeySelfExtracting<T>)ownKey).isSet(ownConfig)) {
+                // FIXME Should we support config from futures? How to get execution context before setEntity?
+                EntityLocal entity = adjunct.entity;
+                ExecutionContext exec = (entity != null) ? ((EntityInternal)entity).getExecutionContext() : null;
+                return ((ConfigKeySelfExtracting<T>)ownKey).extractValue(ownConfig, exec);
+            }
+        } else {
+            LOG.warn("Config key {} of {} is not a ConfigKeySelfExtracting; cannot retrieve value; returning default", ownKey, this);
+        }
+        return TypeCoercions.coerce((defaultValue != null) ? defaultValue : ownKey.getDefaultValue(), key.getTypeToken());
+    }
+    
+    @Override
+    public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) {
+        if (ownConfig.containsKey(key)) return Maybe.of(ownConfig.get(key));
+        return Maybe.absent();
+    }
+    
+    /** returns the config of this policy */
+    @Override
+    public Map<ConfigKey<?>,Object> getAllConfig() {
+        // Don't use ImmutableMap because valide for values to be null
+        return Collections.unmodifiableMap(Maps.newLinkedHashMap(ownConfig));
+    }
+
+    public Object setConfig(ConfigKey<?> key, Object v) {
+        Object val = coerceConfigVal(key, v);
+        if (key instanceof StructuredConfigKey) {
+            return ((StructuredConfigKey)key).applyValueToMap(val, ownConfig);
+        } else {
+            return ownConfig.put(key, val);
+        }
+    }
+    
+    public void addToLocalBag(Map<String, ?> vals) {
+        for (Map.Entry<String, ?> entry : vals.entrySet()) {
+            setConfig(ConfigKeys.newConfigKey(Object.class, entry.getKey()), entry.getValue());
+        }
+    }
+
+    public void removeFromLocalBag(String key) {
+        ownConfig.remove(key);
+    }
+
+    @Override
+    public ConfigMapImpl submap(Predicate<ConfigKey<?>> filter) {
+        ConfigMapImpl m = new ConfigMapImpl(adjunct);
+        for (Map.Entry<ConfigKey<?>,Object> entry: ownConfig.entrySet())
+            if (filter.apply(entry.getKey()))
+                m.ownConfig.put(entry.getKey(), entry.getValue());
+        return m;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString()+"[own="+Sanitizer.sanitize(ownConfig)+"]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/org/apache/brooklyn/core/policy/basic/GeneralPurposePolicy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/policy/basic/GeneralPurposePolicy.java b/core/src/main/java/org/apache/brooklyn/core/policy/basic/GeneralPurposePolicy.java
new file mode 100644
index 0000000..9d140cd
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/policy/basic/GeneralPurposePolicy.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @deprecated since 0.7.0; will be either deleted or moved to tests
+ */
+@Deprecated
+public class GeneralPurposePolicy extends AbstractPolicy {
+    public GeneralPurposePolicy() {
+        this(Collections.emptyMap());
+    }
+    public GeneralPurposePolicy(Map properties) {
+        super(properties);
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/org/apache/brooklyn/core/policy/basic/Policies.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/policy/basic/Policies.java b/core/src/main/java/org/apache/brooklyn/core/policy/basic/Policies.java
new file mode 100644
index 0000000..7ac8823
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/policy/basic/Policies.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.api.event.Sensor;
+import org.apache.brooklyn.api.event.SensorEvent;
+import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
+
+import groovy.lang.Closure;
+import brooklyn.entity.basic.Lifecycle;
+
+@SuppressWarnings({"rawtypes","unchecked"})
+public class Policies {
+
+    public static SensorEventListener listenerFromValueClosure(final Closure code) {
+        return new SensorEventListener() {
+            @Override
+            public void onEvent(SensorEvent event) {
+                code.call(event.getValue());
+            }
+        };
+    }
+    
+    public static <T> Policy newSingleSensorValuePolicy(final Sensor<T> sensor, final Closure code) {
+        return new AbstractPolicy() {
+            @Override
+            public void setEntity(EntityLocal entity) {
+                super.setEntity(entity);
+                entity.subscribe(entity, sensor, listenerFromValueClosure(code));
+            }
+        };
+    }
+    
+    public static <S,T> Policy newSingleSensorValuePolicy(final Entity remoteEntity, final Sensor<T> remoteSensor, 
+            final Closure code) {
+        return new AbstractPolicy() {
+            @Override
+            public void setEntity(EntityLocal entity) {
+                super.setEntity(entity);
+                entity.subscribe(remoteEntity, remoteSensor, listenerFromValueClosure(code));
+            }
+        };
+    }
+
+    public static Lifecycle getPolicyStatus(Policy p) {
+        if (p.isRunning()) return Lifecycle.RUNNING;
+        if (p.isDestroyed()) return Lifecycle.DESTROYED;
+        if (p.isSuspended()) return Lifecycle.STOPPED;
+        // TODO could policy be in an error state?
+        return Lifecycle.CREATED;        
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/org/apache/brooklyn/core/policy/basic/PolicyDynamicType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/policy/basic/PolicyDynamicType.java b/core/src/main/java/org/apache/brooklyn/core/policy/basic/PolicyDynamicType.java
new file mode 100644
index 0000000..315d03d
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/policy/basic/PolicyDynamicType.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.policy.PolicyType;
+
+import brooklyn.basic.BrooklynDynamicType;
+
+public class PolicyDynamicType extends BrooklynDynamicType<Policy, AbstractPolicy> {
+
+    public PolicyDynamicType(Class<? extends Policy> type) {
+        super(type);
+    }
+    
+    public PolicyDynamicType(AbstractPolicy policy) {
+        super(policy);
+    }
+    
+    public PolicyType getSnapshot() {
+        return (PolicyType) super.getSnapshot();
+    }
+
+    @Override
+    protected PolicyTypeSnapshot newSnapshot() {
+        return new PolicyTypeSnapshot(name, value(configKeys));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/org/apache/brooklyn/core/policy/basic/PolicyTypeSnapshot.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/policy/basic/PolicyTypeSnapshot.java b/core/src/main/java/org/apache/brooklyn/core/policy/basic/PolicyTypeSnapshot.java
new file mode 100644
index 0000000..0e655b6
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/policy/basic/PolicyTypeSnapshot.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.policy.PolicyType;
+
+import brooklyn.basic.BrooklynTypeSnapshot;
+import brooklyn.config.ConfigKey;
+
+public class PolicyTypeSnapshot extends BrooklynTypeSnapshot implements PolicyType {
+    private static final long serialVersionUID = 4670930188951106009L;
+    
+    PolicyTypeSnapshot(String name, Map<String, ConfigKey<?>> configKeys) {
+        super(name, configKeys);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        return (obj instanceof PolicyTypeSnapshot) && super.equals(obj);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/entity/EntityPreManagementTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/EntityPreManagementTest.java b/core/src/test/java/brooklyn/entity/EntityPreManagementTest.java
index c29682e..3fc07ed 100644
--- a/core/src/test/java/brooklyn/entity/EntityPreManagementTest.java
+++ b/core/src/test/java/brooklyn/entity/EntityPreManagementTest.java
@@ -27,6 +27,7 @@ import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.management.EntityManager;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.test.TestUtils;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
@@ -41,7 +42,6 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.Entities;
-import brooklyn.policy.basic.AbstractPolicy;
 
 
 @SuppressWarnings({"rawtypes","unchecked"})

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java b/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
index 80ebfb7..35a2c74 100644
--- a/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
+++ b/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
@@ -27,6 +27,7 @@ import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityImpl;
@@ -41,7 +42,6 @@ import brooklyn.event.basic.BasicConfigKey;
 
 import org.apache.brooklyn.location.basic.SimulatedLocation;
 
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.test.Asserts;
 
 import com.google.common.collect.ImmutableSet;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/entity/basic/PolicyRegistrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/PolicyRegistrationTest.java b/core/src/test/java/brooklyn/entity/basic/PolicyRegistrationTest.java
index 63c6a71..2f21dd8 100644
--- a/core/src/test/java/brooklyn/entity/basic/PolicyRegistrationTest.java
+++ b/core/src/test/java/brooklyn/entity/basic/PolicyRegistrationTest.java
@@ -31,6 +31,7 @@ import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.test.TestUtils;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityNoEnrichersImpl;
@@ -38,7 +39,6 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.BrooklynAppUnitTestSupport;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableMap;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/entity/group/GroupPickUpEntitiesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/group/GroupPickUpEntitiesTest.java b/core/src/test/java/brooklyn/entity/group/GroupPickUpEntitiesTest.java
index 0ac1ef9..8b9fa5a 100644
--- a/core/src/test/java/brooklyn/entity/group/GroupPickUpEntitiesTest.java
+++ b/core/src/test/java/brooklyn/entity/group/GroupPickUpEntitiesTest.java
@@ -25,6 +25,7 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.testng.Assert;
@@ -36,7 +37,6 @@ import brooklyn.entity.basic.BasicGroup;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.trait.Startable;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.test.Asserts;
 import brooklyn.util.javalang.Boxing;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
index 92383fc..996c656 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
@@ -44,6 +44,7 @@ import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.catalog.internal.CatalogDto;
 import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.test.entity.TestEntity;
 
 import brooklyn.config.BrooklynProperties;
@@ -51,8 +52,6 @@ import brooklyn.config.BrooklynServerConfig;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 
-import brooklyn.policy.basic.AbstractPolicy;
-
 import com.google.common.base.Joiner;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
index 674d77e..ac2d4c7 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
@@ -40,6 +40,7 @@ import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.Assert;
@@ -53,7 +54,6 @@ import brooklyn.entity.basic.EntityFunctions;
 import brooklyn.entity.basic.EntityPredicates;
 import brooklyn.entity.rebind.RebindEntityTest.MyEntity;
 import brooklyn.entity.rebind.RebindEntityTest.MyEntityImpl;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.os.Os;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/entity/rebind/RebindPolicyTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindPolicyTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindPolicyTest.java
index d74a6fc..84413c2 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindPolicyTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindPolicyTest.java
@@ -33,6 +33,7 @@ import org.apache.brooklyn.api.mementos.BrooklynMementoManifest;
 import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -47,7 +48,6 @@ import brooklyn.entity.rebind.RebindEnricherTest.MyEnricher;
 
 import org.apache.brooklyn.location.basic.Locations;
 
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/policy/basic/BasicPolicyTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/policy/basic/BasicPolicyTest.java b/core/src/test/java/brooklyn/policy/basic/BasicPolicyTest.java
deleted file mode 100644
index 5ac58c9..0000000
--- a/core/src/test/java/brooklyn/policy/basic/BasicPolicyTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.policy.basic;
-
-import static org.testng.Assert.assertEquals;
-
-import java.util.Map;
-
-import org.apache.brooklyn.api.policy.PolicySpec;
-import org.apache.brooklyn.core.util.flags.SetFromFlag;
-import org.testng.annotations.Test;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.BrooklynAppUnitTestSupport;
-import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.collections.MutableSet;
-
-/**
- * Test that policy can be created and accessed, by construction and by spec
- */
-public class BasicPolicyTest extends BrooklynAppUnitTestSupport {
-    
-    public static class MyPolicy extends AbstractPolicy {
-        @SetFromFlag("intKey")
-        public static final BasicConfigKey<Integer> INT_KEY = new BasicConfigKey<Integer>(Integer.class, "bkey", "b key");
-        
-        @SetFromFlag("strKey")
-        public static final ConfigKey<String> STR_KEY = new BasicConfigKey<String>(String.class, "akey", "a key");
-        public static final ConfigKey<Integer> INT_KEY_WITH_DEFAULT = new BasicConfigKey<Integer>(Integer.class, "ckey", "c key", 1);
-        public static final ConfigKey<String> STR_KEY_WITH_DEFAULT = new BasicConfigKey<String>(String.class, "strKey", "str key", "str key default");
-        
-        MyPolicy(Map<?,?> flags) {
-            super(flags);
-        }
-        
-        public MyPolicy() {
-            super();
-        }
-    }
-    
-    @Test
-    public void testAddInstance() throws Exception {
-        MyPolicy policy = new MyPolicy();
-        policy.setDisplayName("Bob");
-        policy.config().set(MyPolicy.STR_KEY, "aval");
-        policy.config().set(MyPolicy.INT_KEY, 2);
-        app.addPolicy(policy);
-        
-        assertEquals(policy.getDisplayName(), "Bob");
-        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "aval");
-        assertEquals(policy.getConfig(MyPolicy.INT_KEY), (Integer)2);
-    }
-    
-    @Test
-    public void testAddSpec() throws Exception {
-        MyPolicy policy = app.addPolicy(PolicySpec.create(MyPolicy.class)
-            .displayName("Bob")
-            .configure(MyPolicy.STR_KEY, "aval").configure(MyPolicy.INT_KEY, 2));
-        
-        assertEquals(policy.getDisplayName(), "Bob");
-        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "aval");
-        assertEquals(policy.getConfig(MyPolicy.INT_KEY), (Integer)2);
-    }
-        
-    @Test
-    public void testTagsFromSpec() throws Exception {
-        MyPolicy policy = app.addPolicy(PolicySpec.create(MyPolicy.class).tag(99).uniqueTag("x"));
-
-        assertEquals(policy.tags().getTags(), MutableSet.of("x", 99));
-        assertEquals(policy.getUniqueTag(), "x");
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/policy/basic/EnricherTypeTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/policy/basic/EnricherTypeTest.java b/core/src/test/java/brooklyn/policy/basic/EnricherTypeTest.java
deleted file mode 100644
index 14773f5..0000000
--- a/core/src/test/java/brooklyn/policy/basic/EnricherTypeTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.policy.basic;
-
-import static org.testng.Assert.assertEquals;
-
-import org.apache.brooklyn.api.policy.EnricherType;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.enricher.basic.AbstractEnricher;
-import brooklyn.event.basic.BasicConfigKey;
-
-import com.google.common.collect.ImmutableSet;
-
-public class EnricherTypeTest {
-    private MyEnricher enricher;
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception{
-        enricher = new MyEnricher();
-    }
-
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        // nothing to tear down; no management context not started
-    }
-    
-    @Test
-    public void testGetConfig() throws Exception {
-        EnricherType enricherType = enricher.getEnricherType();
-        assertEquals(enricherType.getConfigKeys(), ImmutableSet.of(MyEnricher.CONF1, MyEnricher.CONF2, AbstractEnricher.SUPPRESS_DUPLICATES));
-        assertEquals(enricherType.getName(), MyEnricher.class.getCanonicalName());
-        assertEquals(enricherType.getConfigKey("test.conf1"), MyEnricher.CONF1);
-        assertEquals(enricherType.getConfigKey("test.conf2"), MyEnricher.CONF2);
-    }
-    
-    public static class MyEnricher extends AbstractEnricher {
-        public static final BasicConfigKey<String> CONF1 = new BasicConfigKey<String>(String.class, "test.conf1", "my descr, conf1", "defaultval1");
-        public static final BasicConfigKey<Integer> CONF2 = new BasicConfigKey<Integer>(Integer.class, "test.conf2", "my descr, conf2", 2);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/policy/basic/PolicyConfigTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/policy/basic/PolicyConfigTest.java b/core/src/test/java/brooklyn/policy/basic/PolicyConfigTest.java
deleted file mode 100644
index b68781d..0000000
--- a/core/src/test/java/brooklyn/policy/basic/PolicyConfigTest.java
+++ /dev/null
@@ -1,202 +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.policy.basic;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-
-import org.apache.brooklyn.test.entity.TestEntity;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.BrooklynAppUnitTestSupport;
-import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.event.basic.DependentConfiguration;
-import brooklyn.policy.basic.BasicPolicyTest.MyPolicy;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-
-import com.google.common.util.concurrent.Callables;
-
-/**
- * Test that configuration properties are usable and inherited correctly.
- */
-public class PolicyConfigTest extends BrooklynAppUnitTestSupport {
-    private static final int EARLY_RETURN_GRACE = 10;
-
-    private BasicConfigKey<String> differentKey = new BasicConfigKey<String>(String.class, "differentkey", "diffval");
-
-    @Test
-    public void testConfigFlagsPassedInAtConstructionIsAvailable() throws Exception {
-        MyPolicy policy = new MyPolicy(MutableMap.builder()
-                .put("strKey", "aval")
-                .put("intKey", 2)
-                .build());
-        app.addPolicy(policy);
-        
-        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "aval");
-        assertEquals(policy.getConfig(MyPolicy.INT_KEY), (Integer)2);
-        // this is set, because key name matches annotation on STR_KEY
-        assertEquals(policy.getConfig(MyPolicy.STR_KEY_WITH_DEFAULT), "aval");
-    }
-    
-    @Test
-    public void testUnknownConfigPassedInAtConstructionIsWarnedAndIgnored() throws Exception {
-        // TODO Also assert it's warned
-        MyPolicy policy = new MyPolicy(MutableMap.builder()
-                .put(differentKey, "aval")
-                .build());
-        app.addPolicy(policy);
-        
-        assertEquals(policy.getConfig(differentKey), null);
-        assertEquals(policy.getPolicyType().getConfigKey(differentKey.getName()), null);
-    }
-    
-    @Test
-    public void testConfigPassedInAtConstructionIsAvailable() throws Exception {
-        MyPolicy policy = new MyPolicy(MutableMap.builder()
-                .put(MyPolicy.STR_KEY, "aval")
-                .put(MyPolicy.INT_KEY, 2)
-                .build());
-        app.addPolicy(policy);
-        
-        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "aval");
-        assertEquals(policy.getConfig(MyPolicy.INT_KEY), (Integer)2);
-        // this is not set (contrast with above)
-        assertEquals(policy.getConfig(MyPolicy.STR_KEY_WITH_DEFAULT), MyPolicy.STR_KEY_WITH_DEFAULT.getDefaultValue());
-    }
-    
-    @Test
-    public void testConfigSetToGroovyTruthFalseIsAvailable() throws Exception {
-        MyPolicy policy = new MyPolicy(MutableMap.builder()
-                .put(MyPolicy.INT_KEY_WITH_DEFAULT, 0)
-                .build());
-        app.addPolicy(policy);
-        
-        assertEquals(policy.getConfig(MyPolicy.INT_KEY_WITH_DEFAULT), (Integer)0);
-    }
-    
-    @Test
-    public void testConfigSetToNullIsAvailable() throws Exception {
-        MyPolicy policy = new MyPolicy(MutableMap.builder()
-                .put(MyPolicy.STR_KEY_WITH_DEFAULT, null)
-                .build());
-        app.addPolicy(policy);
-        
-        assertEquals(policy.getConfig(MyPolicy.STR_KEY_WITH_DEFAULT), null);
-    }
-    
-    @Test
-    public void testConfigCanBeSetOnPolicy() throws Exception {
-        MyPolicy policy = new MyPolicy();
-        policy.config().set(MyPolicy.STR_KEY, "aval");
-        policy.config().set(MyPolicy.INT_KEY, 2);
-        app.addPolicy(policy);
-        
-        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "aval");
-        assertEquals(policy.getConfig(MyPolicy.INT_KEY), (Integer)2);
-    }
-    
-    @Test
-    public void testConfigSetterOverridesConstructorValue() throws Exception {
-        MyPolicy policy = new MyPolicy(MutableMap.builder()
-                .put(MyPolicy.STR_KEY, "aval")
-                .build());
-        policy.config().set(MyPolicy.STR_KEY, "diffval");
-        app.addPolicy(policy);
-        
-        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "diffval");
-    }
-
-    @Test
-    public void testConfigCannotBeSetAfterApplicationIsStarted() throws Exception {
-        MyPolicy policy = new MyPolicy(MutableMap.builder()
-                .put(MyPolicy.STR_KEY, "origval")
-                .build());
-        app.addPolicy(policy);
-        
-        try {
-            policy.config().set(MyPolicy.STR_KEY,"newval");
-            fail();
-        } catch (UnsupportedOperationException e) {
-            // success
-        }
-        
-        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "origval");
-    }
-    
-    @Test
-    public void testConfigReturnsDefaultValueIfNotSet() throws Exception {
-        MyPolicy policy = new MyPolicy();
-        app.addPolicy(policy);
-        
-        assertEquals(policy.getConfig(MyPolicy.STR_KEY_WITH_DEFAULT), "str key default");
-    }
-    
-    // FIXME Should we support this now?
-    @Test(enabled=false)
-    public void testGetFutureConfigWhenReady() throws Exception {
-        MyPolicy policy = new MyPolicy(MutableMap.builder()
-                .put(TestEntity.CONF_NAME, DependentConfiguration.whenDone(Callables.returning("aval")))
-                .build());
-        app.addPolicy(policy);
-        
-        assertEquals(policy.getConfig(TestEntity.CONF_NAME), "aval");
-    }
-    
-    // FIXME Should we support this now?
-    @Test(enabled=false)
-    public void testGetFutureConfigBlocksUntilReady() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        MyPolicy policy = new MyPolicy(MutableMap.builder()
-                .put(TestEntity.CONF_NAME, DependentConfiguration.whenDone(new Callable<String>() {
-                        public String call() {
-                            try {
-                                latch.await(); return "aval";
-                            } catch (InterruptedException e) {
-                                throw Exceptions.propagate(e);
-                            }
-                        }}))
-                .build());
-        app.addPolicy(policy);
-        
-        Thread t = new Thread(new Runnable() {
-                public void run() {
-                    try {
-                        Thread.sleep(10+EARLY_RETURN_GRACE); latch.countDown();
-                    } catch (InterruptedException e) {
-                        throw Exceptions.propagate(e);
-                    }
-                }});
-        try {
-            long starttime = System.currentTimeMillis();
-            t.start();
-            assertEquals(policy.getConfig(TestEntity.CONF_NAME), "aval");
-            long endtime = System.currentTimeMillis();
-            
-            assertTrue((endtime - starttime) >= 10, "starttime="+starttime+"; endtime="+endtime);
-            
-        } finally {
-            t.interrupt();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/policy/basic/PolicySubscriptionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/policy/basic/PolicySubscriptionTest.java b/core/src/test/java/brooklyn/policy/basic/PolicySubscriptionTest.java
deleted file mode 100644
index e3b2dc8..0000000
--- a/core/src/test/java/brooklyn/policy/basic/PolicySubscriptionTest.java
+++ /dev/null
@@ -1,125 +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.policy.basic;
-
-import static org.testng.Assert.assertEquals;
-
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.BrooklynAppUnitTestSupport;
-
-import org.apache.brooklyn.api.entity.proxying.EntitySpec;
-import org.apache.brooklyn.api.management.SubscriptionHandle;
-import org.apache.brooklyn.entity.basic.RecordingSensorEventListener;
-import org.apache.brooklyn.test.entity.TestEntity;
-
-import brooklyn.event.basic.BasicSensorEvent;
-import org.apache.brooklyn.location.basic.SimulatedLocation;
-import brooklyn.test.Asserts;
-
-import com.google.common.collect.ImmutableList;
-
-public class PolicySubscriptionTest extends BrooklynAppUnitTestSupport {
-
-    // TODO Duplication between this and EntitySubscriptionTest
-    
-    private static final long SHORT_WAIT_MS = 100;
-    
-    private SimulatedLocation loc;
-    private TestEntity entity;
-    private TestEntity otherEntity;
-    private AbstractPolicy policy;
-    private RecordingSensorEventListener<Object> listener;
-    
-    @BeforeMethod(alwaysRun=true)
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        loc = app.newSimulatedLocation();
-        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        otherEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        listener = new RecordingSensorEventListener<>();
-        policy = new AbstractPolicy() {};
-        entity.addPolicy(policy);
-        app.start(ImmutableList.of(loc));
-    }
-
-    @Test
-    public void testSubscriptionReceivesEvents() throws Exception {
-        policy.subscribe(entity, TestEntity.SEQUENCE, listener);
-        policy.subscribe(entity, TestEntity.NAME, listener);
-        policy.subscribe(entity, TestEntity.MY_NOTIF, listener);
-        
-        otherEntity.setAttribute(TestEntity.SEQUENCE, 456);
-        entity.setAttribute(TestEntity.SEQUENCE, 123);
-        entity.setAttribute(TestEntity.NAME, "myname");
-        entity.emit(TestEntity.MY_NOTIF, 789);
-        
-        Asserts.succeedsEventually(new Runnable() {
-            @Override public void run() {
-                assertEquals(listener.getEvents(), ImmutableList.of(
-                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, entity, 123),
-                        new BasicSensorEvent<String>(TestEntity.NAME, entity, "myname"),
-                        new BasicSensorEvent<Integer>(TestEntity.MY_NOTIF, entity, 789)));
-            }});
-    }
-    
-    @Test
-    public void testUnsubscribeRemovesAllSubscriptionsForThatEntity() throws Exception {
-        policy.subscribe(entity, TestEntity.SEQUENCE, listener);
-        policy.subscribe(entity, TestEntity.NAME, listener);
-        policy.subscribe(entity, TestEntity.MY_NOTIF, listener);
-        policy.subscribe(otherEntity, TestEntity.SEQUENCE, listener);
-        policy.unsubscribe(entity);
-        
-        entity.setAttribute(TestEntity.SEQUENCE, 123);
-        entity.setAttribute(TestEntity.NAME, "myname");
-        entity.emit(TestEntity.MY_NOTIF, 456);
-        otherEntity.setAttribute(TestEntity.SEQUENCE, 789);
-        
-        Thread.sleep(SHORT_WAIT_MS);
-        Asserts.succeedsEventually(new Runnable() {
-            @Override public void run() {
-                assertEquals(listener.getEvents(), ImmutableList.of(
-                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, otherEntity, 789)));
-            }});
-    }
-    
-    @Test
-    public void testUnsubscribeUsingHandleStopsEvents() throws Exception {
-        SubscriptionHandle handle1 = policy.subscribe(entity, TestEntity.SEQUENCE, listener);
-        SubscriptionHandle handle2 = policy.subscribe(entity, TestEntity.NAME, listener);
-        SubscriptionHandle handle3 = policy.subscribe(otherEntity, TestEntity.SEQUENCE, listener);
-        
-        policy.unsubscribe(entity, handle2);
-        
-        entity.setAttribute(TestEntity.SEQUENCE, 123);
-        entity.setAttribute(TestEntity.NAME, "myname");
-        otherEntity.setAttribute(TestEntity.SEQUENCE, 456);
-        
-        Asserts.succeedsEventually(new Runnable() {
-            @Override public void run() {
-                assertEquals(listener.getEvents(), ImmutableList.of(
-                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, entity, 123),
-                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, otherEntity, 456)));
-            }});
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/policy/basic/PolicyTypeTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/policy/basic/PolicyTypeTest.java b/core/src/test/java/brooklyn/policy/basic/PolicyTypeTest.java
deleted file mode 100644
index cd9b6a7..0000000
--- a/core/src/test/java/brooklyn/policy/basic/PolicyTypeTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.policy.basic;
-
-import static org.testng.Assert.assertEquals;
-
-import org.apache.brooklyn.api.policy.PolicyType;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.event.basic.BasicConfigKey;
-
-import com.google.common.collect.ImmutableSet;
-
-public class PolicyTypeTest {
-    private MyPolicy policy;
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUpTestEntity() throws Exception{
-        policy = new MyPolicy();
-    }
-
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        // nothing to tear down; no management context not started
-    }
-    
-    @Test
-    public void testGetConfig() throws Exception {
-        PolicyType policyType = policy.getPolicyType();
-        assertEquals(policyType.getConfigKeys(), ImmutableSet.of(MyPolicy.CONF1, MyPolicy.CONF2));
-        assertEquals(policyType.getName(), MyPolicy.class.getCanonicalName());
-        assertEquals(policyType.getConfigKey("test.conf1"), MyPolicy.CONF1);
-        assertEquals(policyType.getConfigKey("test.conf2"), MyPolicy.CONF2);
-    }
-    
-    public static class MyPolicy extends AbstractPolicy {
-        public static final BasicConfigKey<String> CONF1 = new BasicConfigKey<String>(String.class, "test.conf1", "my descr, conf1", "defaultval1");
-        public static final BasicConfigKey<Integer> CONF2 = new BasicConfigKey<Integer>(Integer.class, "test.conf2", "my descr, conf2", 2);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/brooklyn/test/policy/TestPolicy.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/test/policy/TestPolicy.java b/core/src/test/java/brooklyn/test/policy/TestPolicy.java
index 184eb4e..e30fd2c 100644
--- a/core/src/test/java/brooklyn/test/policy/TestPolicy.java
+++ b/core/src/test/java/brooklyn/test/policy/TestPolicy.java
@@ -22,12 +22,12 @@ import java.util.Collections;
 import java.util.Map;
 
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.policy.basic.AbstractPolicy;
 
 import com.google.common.reflect.TypeToken;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/org/apache/brooklyn/core/policy/basic/BasicPolicyTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/policy/basic/BasicPolicyTest.java b/core/src/test/java/org/apache/brooklyn/core/policy/basic/BasicPolicyTest.java
new file mode 100644
index 0000000..852e658
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/policy/basic/BasicPolicyTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.testng.annotations.Test;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.BrooklynAppUnitTestSupport;
+import brooklyn.event.basic.BasicConfigKey;
+import brooklyn.util.collections.MutableSet;
+
+/**
+ * Test that policy can be created and accessed, by construction and by spec
+ */
+public class BasicPolicyTest extends BrooklynAppUnitTestSupport {
+    
+    public static class MyPolicy extends AbstractPolicy {
+        @SetFromFlag("intKey")
+        public static final BasicConfigKey<Integer> INT_KEY = new BasicConfigKey<Integer>(Integer.class, "bkey", "b key");
+        
+        @SetFromFlag("strKey")
+        public static final ConfigKey<String> STR_KEY = new BasicConfigKey<String>(String.class, "akey", "a key");
+        public static final ConfigKey<Integer> INT_KEY_WITH_DEFAULT = new BasicConfigKey<Integer>(Integer.class, "ckey", "c key", 1);
+        public static final ConfigKey<String> STR_KEY_WITH_DEFAULT = new BasicConfigKey<String>(String.class, "strKey", "str key", "str key default");
+        
+        MyPolicy(Map<?,?> flags) {
+            super(flags);
+        }
+        
+        public MyPolicy() {
+            super();
+        }
+    }
+    
+    @Test
+    public void testAddInstance() throws Exception {
+        MyPolicy policy = new MyPolicy();
+        policy.setDisplayName("Bob");
+        policy.config().set(MyPolicy.STR_KEY, "aval");
+        policy.config().set(MyPolicy.INT_KEY, 2);
+        app.addPolicy(policy);
+        
+        assertEquals(policy.getDisplayName(), "Bob");
+        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "aval");
+        assertEquals(policy.getConfig(MyPolicy.INT_KEY), (Integer)2);
+    }
+    
+    @Test
+    public void testAddSpec() throws Exception {
+        MyPolicy policy = app.addPolicy(PolicySpec.create(MyPolicy.class)
+            .displayName("Bob")
+            .configure(MyPolicy.STR_KEY, "aval").configure(MyPolicy.INT_KEY, 2));
+        
+        assertEquals(policy.getDisplayName(), "Bob");
+        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "aval");
+        assertEquals(policy.getConfig(MyPolicy.INT_KEY), (Integer)2);
+    }
+        
+    @Test
+    public void testTagsFromSpec() throws Exception {
+        MyPolicy policy = app.addPolicy(PolicySpec.create(MyPolicy.class).tag(99).uniqueTag("x"));
+
+        assertEquals(policy.tags().getTags(), MutableSet.of("x", 99));
+        assertEquals(policy.getUniqueTag(), "x");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/org/apache/brooklyn/core/policy/basic/EnricherTypeTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/policy/basic/EnricherTypeTest.java b/core/src/test/java/org/apache/brooklyn/core/policy/basic/EnricherTypeTest.java
new file mode 100644
index 0000000..380192d
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/policy/basic/EnricherTypeTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.policy.EnricherType;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.enricher.basic.AbstractEnricher;
+import brooklyn.event.basic.BasicConfigKey;
+
+import com.google.common.collect.ImmutableSet;
+
+public class EnricherTypeTest {
+    private MyEnricher enricher;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception{
+        enricher = new MyEnricher();
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        // nothing to tear down; no management context not started
+    }
+    
+    @Test
+    public void testGetConfig() throws Exception {
+        EnricherType enricherType = enricher.getEnricherType();
+        assertEquals(enricherType.getConfigKeys(), ImmutableSet.of(MyEnricher.CONF1, MyEnricher.CONF2, AbstractEnricher.SUPPRESS_DUPLICATES));
+        assertEquals(enricherType.getName(), MyEnricher.class.getCanonicalName());
+        assertEquals(enricherType.getConfigKey("test.conf1"), MyEnricher.CONF1);
+        assertEquals(enricherType.getConfigKey("test.conf2"), MyEnricher.CONF2);
+    }
+    
+    public static class MyEnricher extends AbstractEnricher {
+        public static final BasicConfigKey<String> CONF1 = new BasicConfigKey<String>(String.class, "test.conf1", "my descr, conf1", "defaultval1");
+        public static final BasicConfigKey<Integer> CONF2 = new BasicConfigKey<Integer>(Integer.class, "test.conf2", "my descr, conf2", 2);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicyConfigTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicyConfigTest.java b/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicyConfigTest.java
new file mode 100644
index 0000000..295d621
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicyConfigTest.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+
+import org.apache.brooklyn.core.policy.basic.BasicPolicyTest.MyPolicy;
+import org.apache.brooklyn.test.entity.TestEntity;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.BrooklynAppUnitTestSupport;
+import brooklyn.event.basic.BasicConfigKey;
+import brooklyn.event.basic.DependentConfiguration;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+
+import com.google.common.util.concurrent.Callables;
+
+/**
+ * Test that configuration properties are usable and inherited correctly.
+ */
+public class PolicyConfigTest extends BrooklynAppUnitTestSupport {
+    private static final int EARLY_RETURN_GRACE = 10;
+
+    private BasicConfigKey<String> differentKey = new BasicConfigKey<String>(String.class, "differentkey", "diffval");
+
+    @Test
+    public void testConfigFlagsPassedInAtConstructionIsAvailable() throws Exception {
+        MyPolicy policy = new MyPolicy(MutableMap.builder()
+                .put("strKey", "aval")
+                .put("intKey", 2)
+                .build());
+        app.addPolicy(policy);
+        
+        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "aval");
+        assertEquals(policy.getConfig(MyPolicy.INT_KEY), (Integer)2);
+        // this is set, because key name matches annotation on STR_KEY
+        assertEquals(policy.getConfig(MyPolicy.STR_KEY_WITH_DEFAULT), "aval");
+    }
+    
+    @Test
+    public void testUnknownConfigPassedInAtConstructionIsWarnedAndIgnored() throws Exception {
+        // TODO Also assert it's warned
+        MyPolicy policy = new MyPolicy(MutableMap.builder()
+                .put(differentKey, "aval")
+                .build());
+        app.addPolicy(policy);
+        
+        assertEquals(policy.getConfig(differentKey), null);
+        assertEquals(policy.getPolicyType().getConfigKey(differentKey.getName()), null);
+    }
+    
+    @Test
+    public void testConfigPassedInAtConstructionIsAvailable() throws Exception {
+        MyPolicy policy = new MyPolicy(MutableMap.builder()
+                .put(MyPolicy.STR_KEY, "aval")
+                .put(MyPolicy.INT_KEY, 2)
+                .build());
+        app.addPolicy(policy);
+        
+        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "aval");
+        assertEquals(policy.getConfig(MyPolicy.INT_KEY), (Integer)2);
+        // this is not set (contrast with above)
+        assertEquals(policy.getConfig(MyPolicy.STR_KEY_WITH_DEFAULT), MyPolicy.STR_KEY_WITH_DEFAULT.getDefaultValue());
+    }
+    
+    @Test
+    public void testConfigSetToGroovyTruthFalseIsAvailable() throws Exception {
+        MyPolicy policy = new MyPolicy(MutableMap.builder()
+                .put(MyPolicy.INT_KEY_WITH_DEFAULT, 0)
+                .build());
+        app.addPolicy(policy);
+        
+        assertEquals(policy.getConfig(MyPolicy.INT_KEY_WITH_DEFAULT), (Integer)0);
+    }
+    
+    @Test
+    public void testConfigSetToNullIsAvailable() throws Exception {
+        MyPolicy policy = new MyPolicy(MutableMap.builder()
+                .put(MyPolicy.STR_KEY_WITH_DEFAULT, null)
+                .build());
+        app.addPolicy(policy);
+        
+        assertEquals(policy.getConfig(MyPolicy.STR_KEY_WITH_DEFAULT), null);
+    }
+    
+    @Test
+    public void testConfigCanBeSetOnPolicy() throws Exception {
+        MyPolicy policy = new MyPolicy();
+        policy.config().set(MyPolicy.STR_KEY, "aval");
+        policy.config().set(MyPolicy.INT_KEY, 2);
+        app.addPolicy(policy);
+        
+        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "aval");
+        assertEquals(policy.getConfig(MyPolicy.INT_KEY), (Integer)2);
+    }
+    
+    @Test
+    public void testConfigSetterOverridesConstructorValue() throws Exception {
+        MyPolicy policy = new MyPolicy(MutableMap.builder()
+                .put(MyPolicy.STR_KEY, "aval")
+                .build());
+        policy.config().set(MyPolicy.STR_KEY, "diffval");
+        app.addPolicy(policy);
+        
+        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "diffval");
+    }
+
+    @Test
+    public void testConfigCannotBeSetAfterApplicationIsStarted() throws Exception {
+        MyPolicy policy = new MyPolicy(MutableMap.builder()
+                .put(MyPolicy.STR_KEY, "origval")
+                .build());
+        app.addPolicy(policy);
+        
+        try {
+            policy.config().set(MyPolicy.STR_KEY,"newval");
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // success
+        }
+        
+        assertEquals(policy.getConfig(MyPolicy.STR_KEY), "origval");
+    }
+    
+    @Test
+    public void testConfigReturnsDefaultValueIfNotSet() throws Exception {
+        MyPolicy policy = new MyPolicy();
+        app.addPolicy(policy);
+        
+        assertEquals(policy.getConfig(MyPolicy.STR_KEY_WITH_DEFAULT), "str key default");
+    }
+    
+    // FIXME Should we support this now?
+    @Test(enabled=false)
+    public void testGetFutureConfigWhenReady() throws Exception {
+        MyPolicy policy = new MyPolicy(MutableMap.builder()
+                .put(TestEntity.CONF_NAME, DependentConfiguration.whenDone(Callables.returning("aval")))
+                .build());
+        app.addPolicy(policy);
+        
+        assertEquals(policy.getConfig(TestEntity.CONF_NAME), "aval");
+    }
+    
+    // FIXME Should we support this now?
+    @Test(enabled=false)
+    public void testGetFutureConfigBlocksUntilReady() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        MyPolicy policy = new MyPolicy(MutableMap.builder()
+                .put(TestEntity.CONF_NAME, DependentConfiguration.whenDone(new Callable<String>() {
+                        public String call() {
+                            try {
+                                latch.await(); return "aval";
+                            } catch (InterruptedException e) {
+                                throw Exceptions.propagate(e);
+                            }
+                        }}))
+                .build());
+        app.addPolicy(policy);
+        
+        Thread t = new Thread(new Runnable() {
+                public void run() {
+                    try {
+                        Thread.sleep(10+EARLY_RETURN_GRACE); latch.countDown();
+                    } catch (InterruptedException e) {
+                        throw Exceptions.propagate(e);
+                    }
+                }});
+        try {
+            long starttime = System.currentTimeMillis();
+            t.start();
+            assertEquals(policy.getConfig(TestEntity.CONF_NAME), "aval");
+            long endtime = System.currentTimeMillis();
+            
+            assertTrue((endtime - starttime) >= 10, "starttime="+starttime+"; endtime="+endtime);
+            
+        } finally {
+            t.interrupt();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicySubscriptionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicySubscriptionTest.java b/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicySubscriptionTest.java
new file mode 100644
index 0000000..b775598
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicySubscriptionTest.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.BrooklynAppUnitTestSupport;
+
+import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.api.management.SubscriptionHandle;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
+import org.apache.brooklyn.entity.basic.RecordingSensorEventListener;
+import org.apache.brooklyn.test.entity.TestEntity;
+
+import brooklyn.event.basic.BasicSensorEvent;
+
+import org.apache.brooklyn.location.basic.SimulatedLocation;
+
+import brooklyn.test.Asserts;
+
+import com.google.common.collect.ImmutableList;
+
+public class PolicySubscriptionTest extends BrooklynAppUnitTestSupport {
+
+    // TODO Duplication between this and EntitySubscriptionTest
+    
+    private static final long SHORT_WAIT_MS = 100;
+    
+    private SimulatedLocation loc;
+    private TestEntity entity;
+    private TestEntity otherEntity;
+    private AbstractPolicy policy;
+    private RecordingSensorEventListener<Object> listener;
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        loc = app.newSimulatedLocation();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        otherEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        listener = new RecordingSensorEventListener<>();
+        policy = new AbstractPolicy() {};
+        entity.addPolicy(policy);
+        app.start(ImmutableList.of(loc));
+    }
+
+    @Test
+    public void testSubscriptionReceivesEvents() throws Exception {
+        policy.subscribe(entity, TestEntity.SEQUENCE, listener);
+        policy.subscribe(entity, TestEntity.NAME, listener);
+        policy.subscribe(entity, TestEntity.MY_NOTIF, listener);
+        
+        otherEntity.setAttribute(TestEntity.SEQUENCE, 456);
+        entity.setAttribute(TestEntity.SEQUENCE, 123);
+        entity.setAttribute(TestEntity.NAME, "myname");
+        entity.emit(TestEntity.MY_NOTIF, 789);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of(
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, entity, 123),
+                        new BasicSensorEvent<String>(TestEntity.NAME, entity, "myname"),
+                        new BasicSensorEvent<Integer>(TestEntity.MY_NOTIF, entity, 789)));
+            }});
+    }
+    
+    @Test
+    public void testUnsubscribeRemovesAllSubscriptionsForThatEntity() throws Exception {
+        policy.subscribe(entity, TestEntity.SEQUENCE, listener);
+        policy.subscribe(entity, TestEntity.NAME, listener);
+        policy.subscribe(entity, TestEntity.MY_NOTIF, listener);
+        policy.subscribe(otherEntity, TestEntity.SEQUENCE, listener);
+        policy.unsubscribe(entity);
+        
+        entity.setAttribute(TestEntity.SEQUENCE, 123);
+        entity.setAttribute(TestEntity.NAME, "myname");
+        entity.emit(TestEntity.MY_NOTIF, 456);
+        otherEntity.setAttribute(TestEntity.SEQUENCE, 789);
+        
+        Thread.sleep(SHORT_WAIT_MS);
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of(
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, otherEntity, 789)));
+            }});
+    }
+    
+    @Test
+    public void testUnsubscribeUsingHandleStopsEvents() throws Exception {
+        SubscriptionHandle handle1 = policy.subscribe(entity, TestEntity.SEQUENCE, listener);
+        SubscriptionHandle handle2 = policy.subscribe(entity, TestEntity.NAME, listener);
+        SubscriptionHandle handle3 = policy.subscribe(otherEntity, TestEntity.SEQUENCE, listener);
+        
+        policy.unsubscribe(entity, handle2);
+        
+        entity.setAttribute(TestEntity.SEQUENCE, 123);
+        entity.setAttribute(TestEntity.NAME, "myname");
+        otherEntity.setAttribute(TestEntity.SEQUENCE, 456);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of(
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, entity, 123),
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, otherEntity, 456)));
+            }});
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicyTypeTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicyTypeTest.java b/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicyTypeTest.java
new file mode 100644
index 0000000..40c4df3
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/policy/basic/PolicyTypeTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.policy.PolicyType;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.event.basic.BasicConfigKey;
+
+import com.google.common.collect.ImmutableSet;
+
+public class PolicyTypeTest {
+    private MyPolicy policy;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUpTestEntity() throws Exception{
+        policy = new MyPolicy();
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        // nothing to tear down; no management context not started
+    }
+    
+    @Test
+    public void testGetConfig() throws Exception {
+        PolicyType policyType = policy.getPolicyType();
+        assertEquals(policyType.getConfigKeys(), ImmutableSet.of(MyPolicy.CONF1, MyPolicy.CONF2));
+        assertEquals(policyType.getName(), MyPolicy.class.getCanonicalName());
+        assertEquals(policyType.getConfigKey("test.conf1"), MyPolicy.CONF1);
+        assertEquals(policyType.getConfigKey("test.conf2"), MyPolicy.CONF2);
+    }
+    
+    public static class MyPolicy extends AbstractPolicy {
+        public static final BasicConfigKey<String> CONF1 = new BasicConfigKey<String>(String.class, "test.conf1", "my descr, conf1", "defaultval1");
+        public static final BasicConfigKey<Integer> CONF2 = new BasicConfigKey<Integer>(Integer.class, "test.conf2", "my descr, conf2", 2);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java b/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java
index 266f738..f5257e5 100644
--- a/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java
+++ b/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java
@@ -24,6 +24,7 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,8 +33,6 @@ import brooklyn.event.basic.Sensors;
 
 import org.apache.brooklyn.location.basic.WinRmMachineLocation;
 
-import brooklyn.policy.basic.AbstractPolicy;
-
 import com.google.common.annotations.Beta;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java b/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
index 8327cf1..dd33058 100644
--- a/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
+++ b/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
@@ -26,6 +26,7 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.core.util.internal.ssh.SshTool;
 import org.jclouds.compute.config.AdminAccessConfiguration;
@@ -43,7 +44,6 @@ import brooklyn.event.basic.Sensors;
 
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.text.Identifiers;
 
 import com.google.common.annotations.Beta;



[36/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjTool.java b/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjTool.java
deleted file mode 100644
index e069aa9..0000000
--- a/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjTool.java
+++ /dev/null
@@ -1,1091 +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.internal.ssh.sshj;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Throwables.getCausalChain;
-import static com.google.common.collect.Iterables.any;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicReference;
-
-import net.schmizz.sshj.connection.ConnectionException;
-import net.schmizz.sshj.connection.channel.direct.PTYMode;
-import net.schmizz.sshj.connection.channel.direct.Session;
-import net.schmizz.sshj.connection.channel.direct.Session.Command;
-import net.schmizz.sshj.connection.channel.direct.Session.Shell;
-import net.schmizz.sshj.connection.channel.direct.SessionChannel;
-import net.schmizz.sshj.sftp.FileAttributes;
-import net.schmizz.sshj.sftp.SFTPClient;
-import net.schmizz.sshj.transport.TransportException;
-import net.schmizz.sshj.xfer.InMemorySourceFile;
-
-import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
-import org.apache.commons.io.input.ProxyInputStream;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.exceptions.RuntimeTimeoutException;
-import brooklyn.util.internal.ssh.BackoffLimitedRetryHandler;
-import brooklyn.util.internal.ssh.ShellTool;
-import brooklyn.util.internal.ssh.SshAbstractTool;
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.io.FileUtil;
-import brooklyn.util.repeat.Repeater;
-import brooklyn.util.stream.KnownSizeInputStream;
-import brooklyn.util.stream.StreamGobbler;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.text.Strings;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Joiner;
-import com.google.common.base.Predicate;
-import com.google.common.base.Stopwatch;
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.CountingOutputStream;
-import com.google.common.net.HostAndPort;
-import com.google.common.primitives.Ints;
-
-/**
- * For ssh and scp-style commands, using the sshj library.
- */
-public class SshjTool extends SshAbstractTool implements SshTool {
-
-    /*
-     * TODO synchronization of connect/disconnect needs revisited!
-     * Saw SshjToolIntegrationTest.testExecBigConcurrentCommand fail with:
-     *     Caused by: java.lang.AssertionError
-     *         at net.schmizz.sshj.SSHClient.auth(SSHClient.java:204)
-     * i.e. another thread had called disconnect just before the failing thread
-     * did SSHClient.auth.
-     * Having multiple threads call connect/disconnect is going to be brittle. With
-     * our retries we can get away with it usually, but it's not good!
-     *
-     * TODO need to upgrade sshj version from 0.8.1 to 0.9, but jclouds 1.7.2 still 
-     * relies on 0.8.1. In 0.9, it fixes the https://github.com/shikhar/sshj/issues/89
-     * so does not throw AssertionError.
-     */
-
-    private static final Logger LOG = LoggerFactory.getLogger(SshjTool.class);
-
-    protected final int sshTries;
-    protected final long sshTriesTimeout;
-    protected final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
-
-    /** Terminal type name for {@code allocatePTY} option. */
-    final static String TERM = "vt100"; // "dumb"
-    
-    private class CloseFtpChannelOnCloseInputStream extends ProxyInputStream {
-        private final SFTPClient sftp;
-
-        private CloseFtpChannelOnCloseInputStream(InputStream proxy, SFTPClient sftp) {
-            super(proxy);
-            this.sftp = sftp;
-        }
-
-        @Override
-        public void close() throws IOException {
-            super.close();
-            closeWhispering(sftp, this);
-        }
-    }
-
-    private final SshjClientConnection sshClientConnection;
-
-    public static SshjToolBuilder builder() {
-        return new SshjToolBuilder();
-    }
-    
-    public static class SshjToolBuilder extends Builder<SshjTool, SshjToolBuilder> {
-    }
-    
-    public static class Builder<T extends SshjTool, B extends Builder<T,B>> extends AbstractSshToolBuilder<T,B> {
-        protected long connectTimeout;
-        protected long sessionTimeout;
-        protected int sshTries = 4;  //allow 4 tries by default, much safer
-        protected long sshTriesTimeout = 2*60*1000;  //allow 2 minutes by default (so if too slow trying sshTries times, abort anyway)
-        protected long sshRetryDelay = 50L;
-        
-        @Override
-        public B from(Map<String,?> props) {
-            super.from(props);
-            sshTries = getOptionalVal(props, PROP_SSH_TRIES);
-            sshTriesTimeout = getOptionalVal(props, PROP_SSH_TRIES_TIMEOUT);
-            sshRetryDelay = getOptionalVal(props, PROP_SSH_RETRY_DELAY);
-            connectTimeout = getOptionalVal(props, PROP_CONNECT_TIMEOUT);
-            sessionTimeout = getOptionalVal(props, PROP_SESSION_TIMEOUT);
-            return self();
-        }
-        public B connectTimeout(int val) {
-            this.connectTimeout = val; return self();
-        }
-        public B sessionTimeout(int val) {
-            this.sessionTimeout = val; return self();
-        }
-        public B sshRetries(int val) {
-            this.sshTries = val; return self();
-        }
-        public B sshRetriesTimeout(int val) {
-            this.sshTriesTimeout = val; return self();
-        }
-        public B sshRetryDelay(long val) {
-            this.sshRetryDelay = val; return self();
-        }
-        @Override
-        @SuppressWarnings("unchecked")
-        public T build() {
-            return (T) new SshjTool(this);
-        }
-    }
-
-    public SshjTool(Map<String,?> map) {
-        this(builder().from(map));
-    }
-    
-    protected SshjTool(Builder<?,?> builder) {
-        super(builder);
-        
-        sshTries = builder.sshTries;
-        sshTriesTimeout = builder.sshTriesTimeout;
-        backoffLimitedRetryHandler = new BackoffLimitedRetryHandler(sshTries, builder.sshRetryDelay);
-
-        sshClientConnection = SshjClientConnection.builder()
-                .hostAndPort(HostAndPort.fromParts(host, port))
-                .username(user)
-                .password(password)
-                .privateKeyPassphrase(privateKeyPassphrase)
-                .privateKeyData(privateKeyData)
-                .privateKeyFile(privateKeyFile)
-                .strictHostKeyChecking(strictHostKeyChecking)
-                .connectTimeout(builder.connectTimeout)
-                .sessionTimeout(builder.sessionTimeout)
-                .build();
-        
-        if (LOG.isTraceEnabled()) LOG.trace("Created SshTool {} ({})", this, System.identityHashCode(this));
-    }
-    
-    @Override
-    public void connect() {
-        try {
-            if (LOG.isTraceEnabled()) LOG.trace("Connecting SshjTool {} ({})", this, System.identityHashCode(this));
-            acquire(sshClientConnection);
-        } catch (Exception e) {
-            if (LOG.isDebugEnabled()) LOG.debug(toString()+" failed to connect (rethrowing)", e);
-            throw propagate(e, "failed to connect");
-        }
-    }
-
-    @Override
-    @Deprecated // see super
-    public void connect(int maxAttempts) {
-        connect(); // FIXME Should callers instead configure sshTries? But that would apply to all ssh attempts
-    }
-
-    @Override
-    public void disconnect() {
-        if (LOG.isTraceEnabled()) LOG.trace("Disconnecting SshjTool {} ({})", this, System.identityHashCode(this));
-        try {
-            Stopwatch perfStopwatch = Stopwatch.createStarted();
-            sshClientConnection.clear();
-            if (LOG.isTraceEnabled()) LOG.trace("SSH Performance: {} disconnect took {}", sshClientConnection.getHostAndPort(), Time.makeTimeStringRounded(perfStopwatch));
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    @Override
-    public boolean isConnected() {
-        return sshClientConnection.isConnected() && sshClientConnection.isAuthenticated();
-    }
-    
-    @Override
-    public int copyToServer(java.util.Map<String,?> props, byte[] contents, String pathAndFileOnRemoteServer) {
-        return copyToServer(props, newInputStreamSupplier(contents), contents.length, pathAndFileOnRemoteServer);
-    }
-    
-    @Override
-    public int copyToServer(Map<String,?> props, InputStream contents, String pathAndFileOnRemoteServer) {
-        /* sshj needs to:
-         *   1) to know the length of the InputStream to copy the file to perform copy; and
-         *   2) re-read the input stream on retry if the first attempt fails.
-         * For now, write it to a file, unless caller supplies a KnownSizeInputStream
-         * 
-         * (We could have a switch where we hold it in memory if less than some max size,
-         * but most the routines should supply a string or byte array or similar,
-         * so we probably don't come here too often.)
-         */
-        if (contents instanceof KnownSizeInputStream) {
-            return copyToServer(props, Suppliers.ofInstance(contents), ((KnownSizeInputStream)contents).length(), pathAndFileOnRemoteServer);
-        } else {
-            File tempFile = writeTempFile(contents);
-            try {
-                return copyToServer(props, tempFile, pathAndFileOnRemoteServer);
-            } finally {
-                tempFile.delete();
-            }
-        }
-    }
-    
-    @Override
-    public int copyToServer(Map<String,?> props, File localFile, String pathAndFileOnRemoteServer) {
-        return copyToServer(props, newInputStreamSupplier(localFile), (int)localFile.length(), pathAndFileOnRemoteServer);
-    }
-    
-    private int copyToServer(Map<String,?> props, Supplier<InputStream> contentsSupplier, long length, String pathAndFileOnRemoteServer) {
-        acquire(new PutFileAction(props, pathAndFileOnRemoteServer, contentsSupplier, length));
-        return 0; // TODO Can we assume put will have thrown exception if failed? Rather than exit code != 0?
-    }
-
-
-    @Override
-    public int copyFromServer(Map<String,?> props, String pathAndFileOnRemoteServer, File localFile) {
-        InputStream contents = acquire(new GetFileAction(pathAndFileOnRemoteServer));
-        try {
-            FileUtil.copyTo(contents, localFile);
-            return 0; // TODO Can we assume put will have thrown exception if failed? Rather than exit code != 0?
-        } finally {
-            Streams.closeQuietly(contents);
-        }
-    }
-
-    /**
-     * This creates a script containing the user's commands, copies it to the remote server, and
-     * executes the script. The script is then deleted.
-     * <p>
-     * Executing commands directly is fraught with dangers! Here are other options, and their problems:
-     * <ul>
-     *   <li>Use execCommands, rather than shell.
-     *       The user's environment will not be setup normally (e.g. ~/.bash_profile will not have been sourced)
-     *       so things like wget may not be on the PATH.
-     *   <li>Send the stream of commands to the shell.
-     *       But characters being sent can be lost.
-     *       Try the following (e.g. in an OS X terminal):
-     *        - sleep 5
-     *        - <paste a command that is 1000s of characters long>
-     *       Only the first 1024 characters appear. The rest are lost.
-     *       If sending a stream of commands, you need to be careful not send the next (big) command while the
-     *       previous one is still executing.
-     *   <li>Send a stream to the shell, but spot when the previous command has completed.
-     *       e.g. by looking for the prompt (but what if the commands being executed change the prompt?)
-     *       e.g. by putting every second command as "echo <uid>", and waiting for the stdout.
-     *       This gets fiddly...
-     * </ul>
-     * 
-     * So on balance, the script-based approach seems most reliable, even if there is an overhead
-     * of separate message(s) for copying the file!
-     * 
-     * Another consideration is long-running scripts. On some clouds when executing a script that takes 
-     * several minutes, we have seen it fail with -1 (e.g. 1 in 20 times). This suggests the ssh connection
-     * is being dropped. To avoid this problem, we can execute the script asynchronously, writing to files
-     * the stdout/stderr/pid/exitStatus. We then periodically poll to retrieve the contents of these files.
-     * Use {@link #PROP_EXEC_ASYNC} to force this mode of execution.
-     */
-    @Override
-    public int execScript(final Map<String,?> props, final List<String> commands, final Map<String,?> env) {
-        Boolean execAsync = getOptionalVal(props, PROP_EXEC_ASYNC);
-        if (Boolean.TRUE.equals(execAsync) && BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC)) {
-            return execScriptAsyncAndPoll(props, commands, env);
-        } else {
-            if (Boolean.TRUE.equals(execAsync)) {
-                if (LOG.isDebugEnabled()) LOG.debug("Ignoring ssh exec-async configuration, because feature is disabled");
-            }
-            return new ToolAbstractExecScript(props) {
-                public int run() {
-                    String scriptContents = toScript(props, commands, env);
-                    if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} as script: {}", host, scriptContents);
-                    copyToServer(ImmutableMap.of("permissions", "0700"), scriptContents.getBytes(), scriptPath);
-                    return asInt(acquire(new ShellAction(buildRunScriptCommand(), out, err, execTimeout)), -1);
-                }
-            }.run();
-        }
-    }
-
-    /**
-     * Executes the script in the background (`nohup ... &`), and then executes other ssh commands to poll for the
-     * stdout, stderr and exit code of that original process (which will each have been written to separate files).
-     * 
-     * The polling is a "long poll". That is, it executes a long-running ssh command to retrieve the stdout, etc.
-     * If that long-poll command fails, then we just execute another one to pick up from where it left off.
-     * This means we do not need to execute many ssh commands (which are expensive), but can still return promptly
-     * when the command completes.
-     * 
-     * Much of this was motivated by https://issues.apache.org/jira/browse/BROOKLYN-106, which is no longer
-     * an issue. The retries (e.g. in the upload-script) are arguably overkill given that {@link #acquire(SshAction)}
-     * will already retry. However, leaving this in place as it could prove useful when working with flakey
-     * networks in the future.
-     * 
-     * TODO There are (probably) issues with this method when using {@link ShellTool#PROP_RUN_AS_ROOT}.
-     * I (Aled) saw the .pid file having an owner of root:root, and a failure message in stderr of:
-     *   -bash: line 3: /tmp/brooklyn-20150113-161203056-XMEo-move_install_dir_from_user_to_.pid: Permission denied
-     */
-    protected int execScriptAsyncAndPoll(final Map<String,?> props, final List<String> commands, final Map<String,?> env) {
-        return new ToolAbstractAsyncExecScript(props) {
-            private int maxConsecutiveSshFailures = 3;
-            private Duration maxDelayBetweenPolls = Duration.seconds(20);
-            private Duration pollTimeout = getOptionalVal(props, PROP_EXEC_ASYNC_POLLING_TIMEOUT, Duration.FIVE_MINUTES);
-            private int iteration = 0;
-            private int consecutiveSshFailures = 0;
-            private int stdoutCount = 0;
-            private int stderrCount = 0;
-            private Stopwatch timer;
-            
-            public int run() {
-                timer = Stopwatch.createStarted();
-                final String scriptContents = toScript(props, commands, env);
-                if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} as async script: {}", host, scriptContents);
-                
-                // Upload script; try repeatedly because have seen timeout intermittently on vcloud-director (BROOKLYN-106 related).
-                boolean uploadSuccess = Repeater.create("async script upload on "+SshjTool.this.toString()+" (for "+getSummary()+")")
-                        .backoffTo(maxDelayBetweenPolls)
-                        .limitIterationsTo(3)
-                        .rethrowException()
-                        .until(new Callable<Boolean>() {
-                            @Override
-                            public Boolean call() throws Exception {
-                                iteration++;
-                                if (LOG.isDebugEnabled()) {
-                                    String msg = "Uploading (iteration="+iteration+") for async script on "+SshjTool.this.toString()+" (for "+getSummary()+")";
-                                    if (iteration == 1) {
-                                        LOG.trace(msg);
-                                    } else {
-                                        LOG.debug(msg);
-                                    }
-                                }
-                                copyToServer(ImmutableMap.of("permissions", "0700"), scriptContents.getBytes(), scriptPath);
-                                return true;
-                            }})
-                        .run();
-                
-                if (!uploadSuccess) {
-                    // Unexpected! Should have either returned true or have rethrown the exception; should never get false.
-                    String msg = "Unexpected state: repeated failure for async script upload on "+SshjTool.this.toString()+" ("+getSummary()+")";
-                    LOG.warn(msg+"; rethrowing");
-                    throw new IllegalStateException(msg);
-                }
-                
-                // Execute script asynchronously
-                int execResult = asInt(acquire(new ShellAction(buildRunScriptCommand(), out, err, execTimeout)), -1);
-                if (execResult != 0) return execResult;
-
-                // Long polling to get the status
-                try {
-                    final AtomicReference<Integer> result = new AtomicReference<Integer>();
-                    boolean success = Repeater.create("async script long-poll on "+SshjTool.this.toString()+" (for "+getSummary()+")")
-                            .backoffTo(maxDelayBetweenPolls)
-                            .limitTimeTo(execTimeout)
-                            .until(new Callable<Boolean>() {
-                                @Override
-                                public Boolean call() throws Exception {
-                                    iteration++;
-                                    if (LOG.isDebugEnabled()) LOG.debug("Doing long-poll (iteration="+iteration+") for async script to complete on "+SshjTool.this.toString()+" (for "+getSummary()+")");
-                                    Integer exitstatus = longPoll();
-                                    result.set(exitstatus);
-                                    return exitstatus != null;
-                                }})
-                            .run();
-                    
-                    if (!success) {
-                        // Timed out
-                        String msg = "Timeout for async script to complete on "+SshjTool.this.toString()+" ("+getSummary()+")";
-                        LOG.warn(msg+"; rethrowing");
-                        throw new TimeoutException(msg);
-                    }
-                    
-                    return result.get();
-                    
-                } catch (Exception e) {
-                    LOG.debug("Problem polling for async script on "+SshjTool.this.toString()+" (for "+getSummary()+"); rethrowing after deleting temporary files", e);
-                    throw Exceptions.propagate(e);
-                } finally {
-                    // Delete the temporary files created (and the `tail -c` commands that might have been left behind by long-polls).
-                    // Using pollTimeout so doesn't wait forever, but waits for a reasonable (configurable) length of time.
-                    // TODO also execute this if the `buildRunScriptCommand` fails, as that might have left files behind?
-                    try {
-                        int execDeleteResult = asInt(acquire(new ShellAction(deleteTemporaryFilesCommand(), out, err, pollTimeout)), -1);
-                        if (execDeleteResult != 0) {
-                            LOG.debug("Problem deleting temporary files of async script on "+SshjTool.this.toString()+" (for "+getSummary()+"): exit status "+execDeleteResult);
-                        }
-                    } catch (Exception e) {
-                        Exceptions.propagateIfFatal(e);
-                        LOG.debug("Problem deleting temporary files of async script on "+SshjTool.this.toString()+" (for "+getSummary()+"); continuing", e);
-                    }
-                }
-            }
-            
-            Integer longPoll() throws IOException {
-                // Long-polling to get stdout, stderr + exit status of async task.
-                // If our long-poll disconnects, we will just re-execute.
-                // We wrap the stdout/stderr so that we can get the size count. 
-                // If we disconnect, we will pick up from that char of the stream.
-                // TODO Additional stdout/stderr written by buildLongPollCommand() could interfere, 
-                //      causing us to miss some characters.
-                Duration nextPollTimeout = Duration.min(pollTimeout, Duration.millis(execTimeout.toMilliseconds()-timer.elapsed(TimeUnit.MILLISECONDS)));
-                CountingOutputStream countingOut = (out == null) ? null : new CountingOutputStream(out);
-                CountingOutputStream countingErr = (err == null) ? null : new CountingOutputStream(err);
-                List<String> pollCommand = buildLongPollCommand(stdoutCount, stderrCount, nextPollTimeout);
-                Duration sshJoinTimeout = nextPollTimeout.add(Duration.TEN_SECONDS);
-                ShellAction action = new ShellAction(pollCommand, countingOut, countingErr, sshJoinTimeout);
-                
-                int longPollResult;
-                try {
-                    longPollResult = asInt(acquire(action, 3, nextPollTimeout), -1);
-                } catch (RuntimeTimeoutException e) {
-                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll timed out on "+SshjTool.this.toString()+" (for "+getSummary()+"): "+e);
-                    return null;
-                }
-                stdoutCount += (countingOut == null) ? 0 : countingOut.getCount();
-                stderrCount += (countingErr == null) ? 0 : countingErr.getCount();
-                
-                if (longPollResult == 0) {
-                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll succeeded (exit status 0) on "+SshjTool.this.toString()+" (for "+getSummary()+")");
-                    return longPollResult; // success
-                    
-                } else if (longPollResult == -1) {
-                    // probably a connection failure; try again
-                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll received exit status -1; will retry on "+SshjTool.this.toString()+" (for "+getSummary()+")");
-                    return null;
-
-                } else if (longPollResult == 125) {
-                    // 125 is the special code for timeout in long-poll (see buildLongPollCommand).
-                    // However, there is a tiny chance that the underlying command might have returned that exact exit code!
-                    // Don't treat a timeout as a "consecutiveSshFailure".
-                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll received exit status "+longPollResult+"; most likely timeout; retrieving actual status on "+SshjTool.this.toString()+" (for "+getSummary()+")");
-                    return retrieveStatusCommand();
-
-                } else {
-                    // want to double-check whether this is the exit-code from the async process, or
-                    // some unexpected failure in our long-poll command.
-                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll received exit status "+longPollResult+"; retrieving actual status on "+SshjTool.this.toString()+" (for "+getSummary()+")");
-                    Integer result = retrieveStatusCommand();
-                    if (result != null) {
-                        return result;
-                    }
-                }
-                    
-                consecutiveSshFailures++;
-                if (consecutiveSshFailures > maxConsecutiveSshFailures) {
-                    LOG.warn("Aborting on "+consecutiveSshFailures+" consecutive ssh connection errors (return -1) when polling for async script to complete on "+SshjTool.this.toString()+" ("+getSummary()+")");
-                    return -1;
-                } else {
-                    LOG.info("Retrying after ssh connection error when polling for async script to complete on "+SshjTool.this.toString()+" ("+getSummary()+")");
-                    return null;
-                }
-            }
-            
-            Integer retrieveStatusCommand() throws IOException {
-                // want to double-check whether this is the exit-code from the async process, or
-                // some unexpected failure in our long-poll command.
-                ByteArrayOutputStream statusOut = new ByteArrayOutputStream();
-                ByteArrayOutputStream statusErr = new ByteArrayOutputStream();
-                int statusResult = asInt(acquire(new ShellAction(buildRetrieveStatusCommand(), statusOut, statusErr, execTimeout)), -1);
-                
-                if (statusResult == 0) {
-                    // The status we retrieved really is valid; return it.
-                    // TODO How to ensure no additional output in stdout/stderr when parsing below?
-                    String statusOutStr = new String(statusOut.toByteArray()).trim();
-                    if (Strings.isEmpty(statusOutStr)) {
-                        // suggests not yet completed; will retry with long-poll
-                        if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieved status directly; command successful but no result available on "+SshjTool.this.toString()+" (for "+getSummary()+")");
-                        return null;
-                    } else {
-                        if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieved status directly; returning '"+statusOutStr+"' on "+SshjTool.this.toString()+" (for "+getSummary()+")");
-                        int result = Integer.parseInt(statusOutStr);
-                        return result;
-                    }
-
-                } else if (statusResult == -1) {
-                    // probably a connection failure; try again with long-poll
-                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieving status directly received exit status -1; will retry on "+SshjTool.this.toString()+" (for "+getSummary()+")");
-                    return null;
-                    
-                } else {
-                    if (out != null) {
-                        out.write(toUTF8ByteArray("retrieving status failed with exit code "+statusResult+" (stdout follow)"));
-                        out.write(statusOut.toByteArray());
-                    }
-                    if (err != null) {
-                        err.write(toUTF8ByteArray("retrieving status failed with exit code "+statusResult+" (stderr follow)"));
-                        err.write(statusErr.toByteArray());
-                    }
-                    
-                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieving status failed; returning "+statusResult+" on "+SshjTool.this.toString()+" (for "+getSummary()+")");
-                    return statusResult;
-                }
-            }
-        }.run();
-    }
-    
-    public int execShellDirect(Map<String,?> props, List<String> commands, Map<String,?> env) {
-        OutputStream out = getOptionalVal(props, PROP_OUT_STREAM);
-        OutputStream err = getOptionalVal(props, PROP_ERR_STREAM);
-        Duration execTimeout = getOptionalVal(props, PROP_EXEC_TIMEOUT);
-        
-        List<String> cmdSequence = toCommandSequence(commands, env);
-        List<String> allcmds = ImmutableList.<String>builder()
-                .add(getOptionalVal(props, PROP_DIRECT_HEADER))
-                .addAll(cmdSequence)
-                .add("exit $?")
-                .build();
-        
-        if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {}: {}", host, allcmds);
-        
-        Integer result = acquire(new ShellAction(allcmds, out, err, execTimeout));
-        if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} completed: return status {}", host, result);
-        return asInt(result, -1);
-    }
-
-    @Override
-    public int execCommands(Map<String,?> props, List<String> commands, Map<String,?> env) {
-        if (Boolean.FALSE.equals(props.get("blocks"))) {
-            throw new IllegalArgumentException("Cannot exec non-blocking: command="+commands);
-        }
-        
-        // If async is set, then do it as execScript
-        Boolean execAsync = getOptionalVal(props, PROP_EXEC_ASYNC);
-        if (Boolean.TRUE.equals(execAsync) && BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC)) {
-            return execScriptAsyncAndPoll(props, commands, env);
-        }
-
-        OutputStream out = getOptionalVal(props, PROP_OUT_STREAM);
-        OutputStream err = getOptionalVal(props, PROP_ERR_STREAM);
-        String separator = getOptionalVal(props, PROP_SEPARATOR);
-        Duration execTimeout = getOptionalVal(props, PROP_EXEC_TIMEOUT);
-
-        List<String> allcmds = toCommandSequence(commands, env);
-        String singlecmd = Joiner.on(separator).join(allcmds);
-
-        if (Boolean.TRUE.equals(getOptionalVal(props, PROP_RUN_AS_ROOT))) {
-            LOG.warn("Cannot run as root when executing as command; run as a script instead (will run as normal user): "+singlecmd);
-        }
-        
-        if (LOG.isTraceEnabled()) LOG.trace("Running command at {}: {}", host, singlecmd);
-        
-        Command result = acquire(new ExecAction(singlecmd, out, err, execTimeout));
-        if (LOG.isTraceEnabled()) LOG.trace("Running command at {} completed: exit code {}", host, result.getExitStatus());
-        // can be null if no exit status is received (observed on kill `ps aux | grep thing-to-grep-for | awk {print $2}`
-        if (result.getExitStatus()==null) LOG.warn("Null exit status running at {}: {}", host, singlecmd);
-        
-        return asInt(result.getExitStatus(), -1);
-    }
-
-    protected void checkConnected() {
-        if (!isConnected()) {
-            throw new IllegalStateException(String.format("(%s) ssh not connected!", toString()));
-        }
-    }
-
-    protected void backoffForAttempt(int retryAttempt, String message) {
-        backoffLimitedRetryHandler.imposeBackoffExponentialDelay(retryAttempt, message);
-    }
-
-    protected <T, C extends SshAction<T>> T acquire(C action) {
-        return acquire(action, sshTries, sshTriesTimeout == 0 ? Duration.PRACTICALLY_FOREVER : Duration.millis(sshTriesTimeout));
-    }
-    
-    protected <T, C extends SshAction<T>> T acquire(C action, int sshTries, Duration sshTriesTimeout) {
-        Stopwatch stopwatch = Stopwatch.createStarted();
-        
-        for (int i = 0; i < sshTries; i++) {
-            try {
-                action.clear();
-                if (LOG.isTraceEnabled()) LOG.trace(">> ({}) acquiring {}", toString(), action);
-                Stopwatch perfStopwatch = Stopwatch.createStarted();
-                
-                T returnVal;
-                try {
-                    returnVal = action.create();
-                } catch (AssertionError e) {
-                    /*
-                     * TODO In net.schmizz.sshj.SSHClient.auth(SSHClient.java:204) throws AssertionError
-                     * if not connected. This can happen if another thread has called disconnect
-                     * concurrently. This is changed in sshj v0.9.0 to instead throw an IllegalStateException.
-                     * 
-                     * For now, we'll retry. See "TODO" at top of class about synchronization.
-                     */
-                    throw new IllegalStateException("Problem in "+toString()+" for "+action, e);
-                }
-                
-                if (LOG.isTraceEnabled()) LOG.trace("<< ({}) acquired {}", toString(), returnVal);
-                if (LOG.isTraceEnabled()) LOG.trace("SSH Performance: {} {} took {}", new Object[] {
-                        sshClientConnection.getHostAndPort(), 
-                        action.getClass().getSimpleName() != null ? action.getClass().getSimpleName() : action, 
-                        Time.makeTimeStringRounded(perfStopwatch)});
-                return returnVal;
-            } catch (Exception e) {
-                // uninformative net.schmizz.sshj.connection.ConnectionException: 
-                //    Request failed (reason=UNKNOWN) may mean remote Subsytem is disabled (e.g. for FTP)
-                // if key is missing, get a UserAuth error
-                String errorMessage = String.format("(%s) error acquiring %s", toString(), action);
-                String fullMessage = String.format("%s (attempt %s/%s, in time %s/%s)", 
-                        errorMessage, (i+1), sshTries, Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)), 
-                        (sshTriesTimeout.equals(Duration.PRACTICALLY_FOREVER) ? "unlimited" : Time.makeTimeStringRounded(sshTriesTimeout)));
-                try {
-                    disconnect();
-                } catch (Exception e2) {
-                    LOG.debug("<< ("+toString()+") error closing connection: "+e+" / "+e2, e);
-                }
-                if (i + 1 == sshTries) {
-                    LOG.debug("<< {} (rethrowing, out of retries): {}", fullMessage, e.getMessage());
-                    throw propagate(e, fullMessage + "; out of retries");
-                } else if (sshTriesTimeout.isShorterThan(stopwatch)) {
-                    LOG.debug("<< {} (rethrowing, out of time - max {}): {}", new Object[] { fullMessage, Time.makeTimeStringRounded(sshTriesTimeout), e.getMessage() });
-                    throw new RuntimeTimeoutException(fullMessage + "; out of time", e);
-                } else {
-                    if (LOG.isDebugEnabled()) LOG.debug("<< {}: {}", fullMessage, e.getMessage());
-                    backoffForAttempt(i + 1, errorMessage + ": " + e.getMessage());
-                    if (action != sshClientConnection)
-                        connect();
-                    continue;
-                }
-            }
-        }
-        assert false : "should not reach here";
-        return null;
-    }
-
-    private final SshAction<SFTPClient> sftpConnection = new SshAction<SFTPClient>() {
-
-        private SFTPClient sftp;
-
-        @Override
-        public void clear() {
-            closeWhispering(sftp, this);
-            sftp = null;
-        }
-
-        @Override
-        public SFTPClient create() throws IOException {
-            checkConnected();
-            sftp = sshClientConnection.ssh.newSFTPClient();
-            return sftp;
-        }
-
-        @Override
-        public String toString() {
-            return "SFTPClient()";
-        }
-    };
-
-    private class GetFileAction implements SshAction<InputStream> {
-        private final String path;
-        private SFTPClient sftp;
-
-        GetFileAction(String path) {
-            this.path = checkNotNull(path, "path");
-        }
-
-        @Override
-        public void clear() throws IOException {
-            closeWhispering(sftp, this);
-            sftp = null;
-        }
-
-        @Override
-        public InputStream create() throws Exception {
-            sftp = acquire(sftpConnection);
-            return new CloseFtpChannelOnCloseInputStream(
-                    sftp.getSFTPEngine().open(path).getInputStream(), sftp);
-        }
-
-        @Override
-        public String toString() {
-            return "Payload(path=[" + path + "])";
-        }
-    }
-
-    private class PutFileAction implements SshAction<Void> {
-        // TODO support backup as a property?
-        
-        private SFTPClient sftp;
-        private final String path;
-        private final int permissionsMask;
-        private final long lastModificationDate;
-        private final long lastAccessDate;
-        private final int uid;
-        private final Supplier<InputStream> contentsSupplier;
-        private final Integer length;
-        
-        PutFileAction(Map<String,?> props, String path, Supplier<InputStream> contentsSupplier, long length) {
-            String permissions = getOptionalVal(props, PROP_PERMISSIONS);
-            long lastModificationDateVal = getOptionalVal(props, PROP_LAST_MODIFICATION_DATE);
-            long lastAccessDateVal = getOptionalVal(props, PROP_LAST_ACCESS_DATE);
-            if (lastAccessDateVal <= 0 ^ lastModificationDateVal <= 0) {
-                lastAccessDateVal = Math.max(lastAccessDateVal, lastModificationDateVal);
-                lastModificationDateVal = Math.max(lastAccessDateVal, lastModificationDateVal);
-            }
-            this.permissionsMask = Integer.parseInt(permissions, 8);
-            this.lastAccessDate = lastAccessDateVal;
-            this.lastModificationDate = lastModificationDateVal;
-            this.uid = getOptionalVal(props, PROP_OWNER_UID);
-            this.path = checkNotNull(path, "path");
-            this.contentsSupplier = checkNotNull(contentsSupplier, "contents");
-            this.length = Ints.checkedCast(checkNotNull((long)length, "size"));
-        }
-
-        @Override
-        public void clear() {
-            closeWhispering(sftp, this);
-            sftp = null;
-        }
-
-        @Override
-        public Void create() throws Exception {
-            final AtomicReference<InputStream> inputStreamRef = new AtomicReference<InputStream>();
-            sftp = acquire(sftpConnection);
-            try {
-                sftp.put(new InMemorySourceFile() {
-                    @Override public String getName() {
-                        return path;
-                    }
-                    @Override public long getLength() {
-                        return length;
-                    }
-                    @Override public InputStream getInputStream() throws IOException {
-                        InputStream contents = contentsSupplier.get();
-                        inputStreamRef.set(contents);
-                        return contents;
-                    }
-                }, path);
-                sftp.chmod(path, permissionsMask);
-                if (uid != -1) {
-                    sftp.chown(path, uid);
-                }
-                if (lastAccessDate > 0) {
-                    sftp.setattr(path, new FileAttributes.Builder()
-                            .withAtimeMtime(lastAccessDate, lastModificationDate)
-                            .build());
-                }
-            } finally {
-                closeWhispering(inputStreamRef.get(), this);
-            }
-            return null;
-        }
-
-        @Override
-        public String toString() {
-            return "Put(path=[" + path + " "+length+"])";
-        }
-    }
-
-    // TODO simpler not to use predicates
-    @VisibleForTesting
-    Predicate<String> causalChainHasMessageContaining(final Exception from) {
-        return new Predicate<String>() {
-            @Override
-            public boolean apply(final String input) {
-                return any(getCausalChain(from), new Predicate<Throwable>() {
-                    @Override
-                    public boolean apply(Throwable throwable) {
-                        return (throwable.toString().contains(input))
-                                || (throwable.getMessage() != null && throwable.getMessage().contains(input));
-                    }
-                });
-            }
-        };
-    }
-    
-    protected SshAction<Session> newSessionAction() {
-
-        return new SshAction<Session>() {
-
-            private Session session = null;
-
-            @Override
-            public void clear() throws TransportException, ConnectionException {
-                closeWhispering(session, this);
-                session = null;
-            }
-
-            @Override
-            public Session create() throws Exception {
-                checkConnected();
-                session = sshClientConnection.ssh.startSession();
-                if (allocatePTY) {
-                    session.allocatePTY(TERM, 80, 24, 0, 0, Collections.<PTYMode, Integer> emptyMap());
-                }
-                return session;
-            }
-
-            @Override
-            public String toString() {
-                return "Session()";
-            }
-        };
-
-    }
-
-    class ExecAction implements SshAction<Command> {
-        private final String command;
-        private final OutputStream out;
-        private final OutputStream err;
-        private final Duration timeout;
-        
-        private Session session;
-        private Shell shell;
-        private StreamGobbler outgobbler;
-        private StreamGobbler errgobbler;
-        
-        ExecAction(String command, OutputStream out, OutputStream err, Duration timeout) {
-            this.command = checkNotNull(command, "command");
-            this.out = out;
-            this.err = err;
-            Duration sessionTimeout = (sshClientConnection.getSessionTimeout() == 0) 
-                    ? Duration.PRACTICALLY_FOREVER 
-                    : Duration.millis(sshClientConnection.getSessionTimeout());
-            this.timeout = (timeout == null) ? sessionTimeout : Duration.min(timeout, sessionTimeout);
-        }
-
-        @Override
-        public void clear() throws TransportException, ConnectionException {
-            closeWhispering(session, this);
-            closeWhispering(shell, this);
-            closeWhispering(outgobbler, this);
-            closeWhispering(errgobbler, this);
-            session = null;
-            shell = null;
-        }
-
-        @Override
-        public Command create() throws Exception {
-            try {
-                session = acquire(newSessionAction());
-                
-                Command output = session.exec(checkNotNull(command, "command"));
-                
-                if (out != null) {
-                    outgobbler = new StreamGobbler(output.getInputStream(), out, (Logger)null);
-                    outgobbler.start();
-                }
-                if (err != null) {
-                    errgobbler = new StreamGobbler(output.getErrorStream(), err, (Logger)null);
-                    errgobbler.start();
-                }
-                try {
-                    output.join((int)Math.min(timeout.toMilliseconds(), Integer.MAX_VALUE), TimeUnit.MILLISECONDS);
-                    return output;
-                    
-                } finally {
-                    // wait for all stdout/stderr to have been re-directed
-                    try {
-                        // Don't use forever (i.e. 0) because BROOKLYN-106: ssh hangs
-                        long joinTimeout = 10*1000;
-                        if (outgobbler != null) outgobbler.join(joinTimeout);
-                        if (errgobbler != null) errgobbler.join(joinTimeout);
-                    } catch (InterruptedException e) {
-                        LOG.warn("Interrupted gobbling streams from ssh: "+command, e);
-                        Thread.currentThread().interrupt();
-                    }
-                }
-                
-            } finally {
-                clear();
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "Exec(command=[" + command + "])";
-        }
-    }
-
-    class ShellAction implements SshAction<Integer> {
-        @VisibleForTesting
-        final List<String> commands;
-        @VisibleForTesting
-        final OutputStream out;
-        @VisibleForTesting
-        final OutputStream err;
-        
-        private Session session;
-        private Shell shell;
-        private StreamGobbler outgobbler;
-        private StreamGobbler errgobbler;
-        private Duration timeout;
-
-        ShellAction(List<String> commands, OutputStream out, OutputStream err, Duration timeout) {
-            this.commands = checkNotNull(commands, "commands");
-            this.out = out;
-            this.err = err;
-            Duration sessionTimeout = (sshClientConnection.getSessionTimeout() == 0) 
-                    ? Duration.PRACTICALLY_FOREVER 
-                    : Duration.millis(sshClientConnection.getSessionTimeout());
-            this.timeout = (timeout == null) ? sessionTimeout : Duration.min(timeout, sessionTimeout);
-        }
-
-        @Override
-        public void clear() throws TransportException, ConnectionException {
-            closeWhispering(session, this);
-            closeWhispering(shell, this);
-            closeWhispering(outgobbler, this);
-            closeWhispering(errgobbler, this);
-            session = null;
-            shell = null;
-        }
-
-        @Override
-        public Integer create() throws Exception {
-            try {
-                session = acquire(newSessionAction());
-                
-                shell = session.startShell();
-                
-                if (out != null) {
-                    InputStream outstream = shell.getInputStream();
-                    outgobbler = new StreamGobbler(outstream, out, (Logger)null);
-                    outgobbler.start();
-                }
-                if (err != null) {
-                    InputStream errstream = shell.getErrorStream();
-                    errgobbler = new StreamGobbler(errstream, err, (Logger)null);
-                    errgobbler.start();
-                }
-                
-                OutputStream output = shell.getOutputStream();
-
-                for (CharSequence cmd : commands) {
-                    try {
-                        output.write(toUTF8ByteArray(cmd+"\n"));
-                        output.flush();
-                    } catch (ConnectionException e) {
-                        if (!shell.isOpen()) {
-                            // shell is closed; presumably the user command did `exit`
-                            if (LOG.isDebugEnabled()) LOG.debug("Shell closed to {} when executing {}", SshjTool.this.toString(), commands);
-                            break;
-                        } else {
-                            throw e;
-                        }
-                    }
-                }
-                // workaround attempt for SSHJ deadlock - https://github.com/shikhar/sshj/issues/105
-                synchronized (shell.getOutputStream()) {
-                    shell.sendEOF();
-                }
-                closeWhispering(output, this);
-                
-                boolean timedOut = false;
-                try {
-                    long timeoutMillis = Math.min(timeout.toMilliseconds(), Integer.MAX_VALUE);
-                    long timeoutEnd = System.currentTimeMillis() + timeoutMillis;
-                    Exception last = null;
-                    do {
-                        if (!shell.isOpen() && ((SessionChannel)session).getExitStatus()!=null)
-                            // shell closed, and exit status returned
-                            break;
-                        boolean endBecauseReturned =
-                            // if either condition is satisfied, then wait 1s in hopes the other does, then return
-                            (!shell.isOpen() || ((SessionChannel)session).getExitStatus()!=null);
-                        try {
-                            shell.join(1000, TimeUnit.MILLISECONDS);
-                        } catch (ConnectionException e) {
-                            last = e;
-                        }
-                        if (endBecauseReturned) {
-                            // shell is still open, ie some process is running
-                            // but we have a result code, so main shell is finished
-                            // we waited one second extra to allow any background process 
-                            // which is nohupped to really be in the background (#162)
-                            // now let's bail out
-                            break;
-                        }
-                    } while (System.currentTimeMillis() < timeoutEnd);
-                    if (shell.isOpen() && ((SessionChannel)session).getExitStatus()==null) {
-                        LOG.debug("Timeout ({}) in SSH shell to {}", timeout, this);
-                        // we timed out, or other problem -- reproduce the error.
-                        // The shell.join should always have thrown ConnectionExceptoin (looking at code of
-                        // AbstractChannel), but javadoc of Channel doesn't explicity say that so play it safe.
-                        timedOut = true;
-                        throw (last != null) ? last : new TimeoutException("Timeout after "+timeout+" executing "+this);
-                    }
-                    return ((SessionChannel)session).getExitStatus();
-                } finally {
-                    // wait for all stdout/stderr to have been re-directed
-                    closeWhispering(shell, this);
-                    shell = null;
-                    try {
-                        // Don't use forever (i.e. 0) because BROOKLYN-106: ssh hangs
-                        long joinTimeout = (timedOut) ? 1000 : 10*1000;
-                        if (outgobbler != null) {
-                            outgobbler.join(joinTimeout);
-                            outgobbler.close();
-                        }
-                        if (errgobbler != null) {
-                            errgobbler.join(joinTimeout);
-                            errgobbler.close();
-                        }
-                    } catch (InterruptedException e) {
-                        LOG.warn("Interrupted gobbling streams from ssh: "+commands, e);
-                        Thread.currentThread().interrupt();
-                    }
-                }
-                
-            } finally {
-                clear();
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "Shell(command=[" + commands + "])";
-        }
-    }
-
-    private byte[] toUTF8ByteArray(String string) {
-        return org.bouncycastle.util.Strings.toUTF8ByteArray(string);
-    }
-    
-    private Supplier<InputStream> newInputStreamSupplier(final byte[] contents) {
-        return new Supplier<InputStream>() {
-            @Override public InputStream get() {
-                return new ByteArrayInputStream(contents);
-            }
-        };
-    }
-
-    private Supplier<InputStream> newInputStreamSupplier(final File file) {
-        return new Supplier<InputStream>() {
-            @Override public InputStream get() {
-                try {
-                    return new FileInputStream(file);
-                } catch (FileNotFoundException e) {
-                    throw Exceptions.propagate(e);
-                }
-            }
-        };
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/javalang/ReflectionScanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/javalang/ReflectionScanner.java b/core/src/main/java/brooklyn/util/javalang/ReflectionScanner.java
deleted file mode 100644
index 66a5a72..0000000
--- a/core/src/main/java/brooklyn/util/javalang/ReflectionScanner.java
+++ /dev/null
@@ -1,135 +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.javalang;
-
-import java.lang.annotation.Annotation;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
-import org.reflections.ReflectionUtils;
-import org.reflections.Reflections;
-import org.reflections.Store;
-import org.reflections.scanners.Scanner;
-import org.reflections.scanners.SubTypesScanner;
-import org.reflections.scanners.TypeAnnotationsScanner;
-import org.reflections.util.ClasspathHelper;
-import org.reflections.util.ConfigurationBuilder;
-import org.reflections.util.FilterBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-
-/** Facade on {@link Reflections} which logs warnings for unloadable classes but does not fail */
-public class ReflectionScanner {
-
-    private static final Logger log = LoggerFactory.getLogger(ReflectionScanner.class);
-    
-    protected final ClassLoader[] classLoaders;
-    protected final Reflections reflections;
-
-    /** scanner which will look in the given urls 
-     * (or if those are null attempt to infer from the first entry in the classloaders,
-     * although currently that seems to only pick up directories, not JAR's),
-     * optionally filtering for the given prefix;
-     * any or all arguments can be null to accept all (and use default classpath for classloading).
-     **/
-    public ReflectionScanner(
-            final Iterable<URL> urlsToScan, 
-            final String optionalPrefix,
-            final ClassLoader ...classLoaders) {
-        reflections = new Reflections(new ConfigurationBuilder() {
-            {
-                final Predicate<String> filter = 
-                        Strings.isNonEmpty(optionalPrefix) ? new FilterBuilder.Include(FilterBuilder.prefix(optionalPrefix)) : null;
-
-                if (urlsToScan!=null)
-                    setUrls(ImmutableSet.copyOf(urlsToScan));
-                else if (classLoaders.length>0 && classLoaders[0]!=null)
-                    setUrls(
-                            ClasspathHelper.forPackage(Strings.isNonEmpty(optionalPrefix) ? optionalPrefix : "",
-                                    asClassLoaderVarArgs(classLoaders[0])));
-                
-                if (filter!=null) filterInputsBy(filter);
-
-                Scanner typeScanner = new TypeAnnotationsScanner();
-                if (filter!=null) typeScanner = typeScanner.filterResultsBy(filter);
-                Scanner subTypeScanner = new SubTypesScanner();
-                if (filter!=null) subTypeScanner = subTypeScanner.filterResultsBy(filter);
-                setScanners(typeScanner, subTypeScanner);
-                
-                for (ClassLoader cl: classLoaders)
-                    if (cl!=null) addClassLoader(cl);
-            }
-        });
-        this.classLoaders = Iterables.toArray(Iterables.filter(Arrays.asList(classLoaders), Predicates.notNull()), ClassLoader.class);
-    }
-
-    private static ClassLoader[] asClassLoaderVarArgs(final ClassLoader classLoaderToSearch) {
-        return classLoaderToSearch==null ? new ClassLoader[0] : new ClassLoader[] { classLoaderToSearch };
-    }
-
-    public Store getStore() {
-        return reflections.getStore();
-    }
-    
-    /** overrides delegate so as to log rather than throw exception if a class cannot be loaded */
-    public <T> Set<Class<? extends T>> getSubTypesOf(final Class<T> type) {
-        Set<String> subTypes = getStore().getSubTypesOf(type.getName());
-        return ImmutableSet.copyOf(this.<T>forNames(subTypes, "sub-type of "+type));
-    }
-    
-    /** overrides delegate so as to log rather than throw exception if a class cannot be loaded */
-    public Set<Class<?>> getTypesAnnotatedWith(Class<? extends Annotation> annotation) {
-        Set<String> annotatedWith = getStore().getTypesAnnotatedWith(annotation.getName());
-        return ImmutableSet.copyOf(this.forNames(annotatedWith, "annotated "+annotation.getName()));
-    }
-
-    @SuppressWarnings("unchecked")
-    protected <T> List<Class<? extends T>> forNames(Set<String> classNames, final String context) {
-        List<Class<? extends T>> result = new ArrayList<Class<? extends T>>();
-        for (String className : classNames) {
-            //noinspection unchecked
-            try {
-                Class<? extends T> clazz = (Class<? extends T>) loadClass(className);
-                if (clazz != null) {
-                    result.add(clazz);
-                } else {
-                    log.warn("Unable to instantiate '"+className+"' ("+context+")");
-                }
-            } catch (Throwable e) {
-                log.warn("Unable to instantiate '"+className+"' ("+context+"): "+e);
-            }
-        }
-        return result;
-    }
-    
-    protected Class<?> loadClass(String className) {
-        return ReflectionUtils.forName(className, classLoaders);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/javalang/UrlClassLoader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/javalang/UrlClassLoader.java b/core/src/main/java/brooklyn/util/javalang/UrlClassLoader.java
deleted file mode 100644
index d847174..0000000
--- a/core/src/main/java/brooklyn/util/javalang/UrlClassLoader.java
+++ /dev/null
@@ -1,69 +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.javalang;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Arrays;
-
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.exceptions.Exceptions;
-
-/** like URLClassLoader (and delegates to it) but:
- * * has a nice toString
- * * supports var args constructor
- * * supports file://~/xxx semantics (remaps it to user.home); 
- *      ideally we'd also support mvn, classpath, osgi, etc
- */
-public class UrlClassLoader extends URLClassLoader {
-
-    private URL[] urls;
-
-    public UrlClassLoader(URL ...urls) {
-        super(tidy(urls));
-        this.urls = urls;
-    }
-
-    public UrlClassLoader(String ...urls) {
-        this(asUrls(urls));
-    }
-    
-    private static URL[] asUrls(String[] urls) {
-        URL[] urlo = new URL[urls.length];
-        try {
-        for (int i=0; i<urls.length; i++)
-            urlo[i] = new URL(urls[i]);
-        } catch (MalformedURLException e) {
-            throw Exceptions.propagate(e);
-        }
-        return urlo;
-    }
-
-    private static URL[] tidy(URL[] urls) {
-        for (int i=0; i<urls.length; i++)
-            urls[i] = ResourceUtils.tidy(urls[i]);
-        return urls;
-    }
-
-    @Override
-    public String toString() {
-        return "UrlClassLoader"+Arrays.asList(urls);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/mutex/MutexSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/mutex/MutexSupport.java b/core/src/main/java/brooklyn/util/mutex/MutexSupport.java
deleted file mode 100644
index c0794cf..0000000
--- a/core/src/main/java/brooklyn/util/mutex/MutexSupport.java
+++ /dev/null
@@ -1,120 +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.mutex;
-
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.task.Tasks;
-
-import com.google.common.collect.ImmutableMap;
-
-public class MutexSupport implements WithMutexes {
-
-    private static final Logger log = LoggerFactory.getLogger(MutexSupport.class);
-    private final Map<String,SemaphoreWithOwners> semaphores = new LinkedHashMap<String,SemaphoreWithOwners>();
-
-    protected synchronized SemaphoreWithOwners getSemaphore(String mutexId) {
-        return getSemaphore(mutexId, false);
-    }
-    // NB: the map could be "lock-striped" (hashed on mutexId) to avoid the central lock 
-    protected synchronized SemaphoreWithOwners getSemaphore(String mutexId, boolean requestBeforeReturning) {
-        SemaphoreWithOwners s = semaphores.get(mutexId);
-        if (s==null) {
-            s = new SemaphoreWithOwners(mutexId);
-            semaphores.put(mutexId, s);
-        }
-        if (requestBeforeReturning) s.indicateCallingThreadWillRequest();
-        return s;
-    }
-    /** forces deletion of the given mutex if it is unused; 
-     * normally not required as is done automatically on close
-     * (but possibly needed where there are cancelations and risk of memory leaks) */
-    public synchronized void cleanupMutex(String mutexId) {
-        SemaphoreWithOwners s = semaphores.get(mutexId);
-        if (!s.isInUse()) semaphores.remove(mutexId);
-    }
-    public synchronized void cleanup() {
-        Iterator<SemaphoreWithOwners> si = semaphores.values().iterator();
-        while (si.hasNext()) {
-            SemaphoreWithOwners s = si.next();
-            if (!s.isInUse()) si.remove();
-        }
-    }
-
-    @Override
-    public synchronized boolean hasMutex(String mutexId) {
-        SemaphoreWithOwners s = semaphores.get(mutexId);
-        if (s!=null) return s.isCallingThreadAnOwner();
-        return false;
-    }
-    
-    @Override
-    public void acquireMutex(String mutexId, String description) throws InterruptedException {
-        SemaphoreWithOwners s = getSemaphore(mutexId, true);
-        if (description!=null) Tasks.setBlockingDetails(description+" - waiting for "+mutexId);
-        if (log.isDebugEnabled())
-            log.debug("Acquiring mutex: "+mutexId+"@"+this+" - "+description);
-        s.acquire(); 
-        if (description!=null) Tasks.setBlockingDetails(null);
-        s.setDescription(description);
-        if (log.isDebugEnabled())
-            log.debug("Acquired mutex: "+mutexId+"@"+this+" - "+description);
-    }
-
-    @Override
-    public boolean tryAcquireMutex(String mutexId, String description) {
-        SemaphoreWithOwners s = getSemaphore(mutexId, true);
-        if (s.tryAcquire()) {
-            if (log.isDebugEnabled())
-                log.debug("Acquired mutex (opportunistic): "+mutexId+"@"+this+" - "+description);
-            s.setDescription(description);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public synchronized void releaseMutex(String mutexId) {
-        SemaphoreWithOwners s;
-        if (log.isDebugEnabled())
-            log.debug("Releasing mutex: "+mutexId+"@"+this);
-        synchronized (this) { s = semaphores.get(mutexId); }
-        if (s==null) throw new IllegalStateException("No mutex known for '"+mutexId+"'");
-        s.release();
-        cleanupMutex(mutexId);
-    }
-    
-    @Override
-    public synchronized String toString() {
-        return super.toString()+"["+semaphores.size()+" semaphores: "+semaphores.values()+"]";
-    }
-    
-    /** Returns the semaphores in use at the time the method is called, for inspection purposes (and testing).
-     * The semaphores used by this class may change over time so callers are strongly discouraged
-     * from manipulating the semaphore objects themselves. 
-     */
-    public synchronized Map<String,SemaphoreWithOwners> getAllSemaphores() {
-        return ImmutableMap.<String,SemaphoreWithOwners>copyOf(semaphores);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/mutex/SemaphoreForTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/mutex/SemaphoreForTasks.java b/core/src/main/java/brooklyn/util/mutex/SemaphoreForTasks.java
deleted file mode 100644
index 5bdfdfa..0000000
--- a/core/src/main/java/brooklyn/util/mutex/SemaphoreForTasks.java
+++ /dev/null
@@ -1,112 +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.mutex;
-
-import java.util.List;
-import java.util.Set;
-
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.Task;
-
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableSet;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-
-
-/** A subclass of {@link SemaphoreWithOwners} 
- * which additionally sets Task blocking information. 
- * <p>
- * TODO As tasks are distributed this should support distribution across the management context. */
-public class SemaphoreForTasks extends SemaphoreWithOwners {
-    
-    private static final long serialVersionUID = 7898283056223005952L;
-    
-    /** unused at present, but wanted on the API for when this may be federated */
-    @SuppressWarnings("unused")
-    private final ManagementContext mgmt;
-    
-    final private MutableList<Task<?>> owningTasks = new MutableList<Task<?>>();
-    final private MutableSet<Task<?>> requestingTasks = new MutableSet<Task<?>>();
-
-    public SemaphoreForTasks(String name, ManagementContext mgmt) {
-        super(name);
-        this.mgmt = Preconditions.checkNotNull(mgmt);
-    }
-    
-    public SemaphoreForTasks(String name, int permits, boolean fair, ManagementContext mgmt) {
-        super(name, permits, fair);
-        this.mgmt = Preconditions.checkNotNull(mgmt);
-    }
-    
-    public synchronized Set<Task<?>> getRequestingTasks() {
-        return ImmutableSet.copyOf(requestingTasks);
-    }
-    
-    public synchronized List<Task<?>> getOwningTasks() {
-        return ImmutableList.copyOf(owningTasks);
-    }
-
-    @Override
-    protected synchronized void onRequesting() {
-        if (!owningTasks.isEmpty() || !requestingTasks.isEmpty()) {
-            Tasks.setBlockingTask( !requestingTasks.isEmpty() ? Iterables.getLast(requestingTasks) : Iterables.getFirst(owningTasks, null) );
-            Tasks.setBlockingDetails("Waiting on semaphore "+getName()+" ("+getDescription()+"); "
-                + "queued at "+Time.makeDateString()+" when "+getRequestingThreads().size()+" ahead in queue");
-        }
-        requestingTasks.addIfNotNull(Tasks.current());
-        super.onRequesting();
-    }
-    
-    @Override
-    protected synchronized void onRequestFinished() {
-        super.onRequestFinished();
-        requestingTasks.removeIfNotNull(Tasks.current());
-        
-        Tasks.resetBlockingDetails();
-        Tasks.resetBlockingTask();
-    }
-    
-    @Override
-    protected synchronized void onAcquired(int permits) {
-        super.onAcquired(permits);
-        for (int i=0; i<permits; i++)
-            owningTasks.appendIfNotNull(Tasks.current());
-    }
-    
-    @Override
-    protected synchronized void onReleased(int permits) {
-        super.onReleased(permits);
-        for (int i=0; i<permits; i++)
-            owningTasks.removeIfNotNull(Tasks.current());
-    }
-    
-    @Override
-    public synchronized String toString() {
-        return super.toString()+"["
-            + "owningTasks="+owningTasks
-            + "; requestingTasks="+requestingTasks+"]";
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/mutex/SemaphoreWithOwners.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/mutex/SemaphoreWithOwners.java b/core/src/main/java/brooklyn/util/mutex/SemaphoreWithOwners.java
deleted file mode 100644
index 6f3132c..0000000
--- a/core/src/main/java/brooklyn/util/mutex/SemaphoreWithOwners.java
+++ /dev/null
@@ -1,231 +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.mutex;
-
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-import brooklyn.util.exceptions.Exceptions;
-
-import com.google.common.collect.ImmutableList;
-
-/** a subclass of {@link Semaphore} 
- * which tracks who created and released the semaphores,
- * and which requires the same thread to release as created it. */
-public class SemaphoreWithOwners extends Semaphore {
-    public SemaphoreWithOwners(String name) {
-        this(name, 1, true);
-    }
-    public SemaphoreWithOwners(String name, int permits, boolean fair) {
-        super(permits, fair);
-        this.name = name;
-    }
-    private static final long serialVersionUID = -5303474637353009454L;
-    final private List<Thread> owningThreads = new ArrayList<Thread>();
-    final private Set<Thread> requestingThreads = new LinkedHashSet<Thread>();
-    
-    @Override
-    public void acquire() throws InterruptedException {
-        try {
-            onRequesting();
-            super.acquire();
-            onAcquired(1);
-        } finally {
-            onRequestFinished();
-        }
-    }
-    @Override
-    public void acquire(int permits) throws InterruptedException {
-        try {
-            onRequesting();
-            super.acquire(permits);
-            onAcquired(permits);
-        } finally {
-            onRequestFinished();
-        }
-    }
-    @Override
-    public void acquireUninterruptibly() {
-        try {
-            onRequesting();
-            super.acquireUninterruptibly();
-            onAcquired(1);
-        } finally {
-            onRequestFinished();
-        }
-    }
-    @Override
-    public void acquireUninterruptibly(int permits) {
-        try {
-            onRequesting();
-            super.acquireUninterruptibly(permits);
-            onAcquired(permits);
-        } finally {
-            onRequestFinished();
-        }
-    }
-
-    public void acquireUnchecked() {
-        try {
-            acquire();
-        } catch (InterruptedException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-    public void acquireUnchecked(int numPermits) {
-        try {
-            acquire(numPermits);
-        } catch (InterruptedException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-    
-    @Override
-    public boolean tryAcquire() {
-        try {
-            onRequesting();
-            if (super.tryAcquire()) {
-                onAcquired(1);
-                return true;
-            }
-            return false;
-        } finally {
-            onRequestFinished();
-        }
-    }
-    @Override
-    public boolean tryAcquire(int permits) {
-        try {
-            onRequesting();
-            if (super.tryAcquire(permits)) {
-                onAcquired(permits);
-                return true;
-            }
-            return false;
-        } finally {
-            onRequestFinished();
-        }
-    }
-    @Override
-    public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException {
-        try {
-            onRequesting();
-            if (super.tryAcquire(permits, timeout, unit)) {
-                onAcquired(permits);
-                return true;
-            }
-            return false;
-        } finally {
-            onRequestFinished();
-        }
-    }
-    @Override
-    public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {
-        try {
-            onRequesting();
-            if (super.tryAcquire(timeout, unit)) {
-                onAcquired(1);
-                return true;
-            }
-            return false;
-        } finally {
-            onRequestFinished();
-        }
-    }
-
-    /** invoked when a caller successfully acquires a mutex, before {@link #onRequestFinished()} */
-    protected synchronized void onAcquired(int permits) {
-        for (int i=0; i<permits; i++) owningThreads.add(Thread.currentThread());
-    }
-    /** invoked when a caller is about to request a semaphore (before it might block);
-     * guaranteed to call {@link #onRequestFinished()} after the blocking,
-     * with a call to {@link #onAcquired(int)} beforehand if the acquisition was successful */
-    protected synchronized void onRequesting() {
-        requestingThreads.add(Thread.currentThread());
-    }
-    /** invoked when a caller has completed requesting a mutex, whether successful, aborted, or interrupted */
-    protected synchronized void onRequestFinished() {
-        requestingThreads.remove(Thread.currentThread());
-    }
-
-    @Override
-    public void release() {
-        super.release();
-        onReleased(1);
-    }
-    @Override
-    public void release(int permits) {
-        super.release(permits);
-        onReleased(permits);
-    }
-
-    /** invoked when a caller has released permits */
-    protected synchronized void onReleased(int permits) {
-        boolean result = true;
-        for (int i=0; i<permits; i++) result = owningThreads.remove(Thread.currentThread()) & result;
-        if (!result) throw new IllegalStateException("Thread "+Thread.currentThread()+" which released "+this+" did not own it.");  
-    }
-
-    /** true iff there are any owners or any requesters (callers blocked trying to acquire) */
-    public synchronized boolean isInUse() {
-        return !owningThreads.isEmpty() || !requestingThreads.isEmpty();
-    }
-
-    /** true iff the calling thread is one of the owners */ 
-    public synchronized boolean isCallingThreadAnOwner() {
-        return owningThreads.contains(Thread.currentThread());
-    }
-
-    private final String name;
-    /** constructor-time supplied name */
-    public String getName() { return name; }
-
-    private String description;
-    public void setDescription(String description) { this.description = description; }
-    /** caller supplied description */
-    public String getDescription() { return description; }
-
-    /** unmodifiable view of threads owning the permits; threads with multiple permits listed multiply */
-    public synchronized List<Thread> getOwningThreads() {
-        return ImmutableList.<Thread>copyOf(owningThreads);
-    }
-    /** unmodifiable view of threads requesting access (blocked or briefly trying to acquire);
-     * this is guaranteed to be cleared _after_ getOwners 
-     * (synchronizing on this class while reading both fields will give canonical access) */
-    public synchronized List<Thread> getRequestingThreads() {
-        return ImmutableList.<Thread>copyOf(requestingThreads);
-    }
-    
-    @Override
-    public synchronized String toString() {
-        return super.toString()+"["+name+"; description="+description+"; owning="+owningThreads+"; requesting="+requestingThreads+"]";
-    }
-    
-    /** Indicate that the calling thread is going to acquire or tryAcquire, 
-     * in order to set up the semaphore's isInUse() value appropriately for certain checks.
-     * It *must* do so after invoking this method. */ 
-    public void indicateCallingThreadWillRequest() {
-        requestingThreads.add(Thread.currentThread());
-    }
-    
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/mutex/WithMutexes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/mutex/WithMutexes.java b/core/src/main/java/brooklyn/util/mutex/WithMutexes.java
deleted file mode 100644
index e772df8..0000000
--- a/core/src/main/java/brooklyn/util/mutex/WithMutexes.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.mutex;
-
-/** interface which allows multiple callers to co-operate using named mutexes, inspectably,
- * and containing implementation as inner class
- * <p>
- * MutexSupport is a common implementation of this.
- * mixin code frequently delegates to this, 
- * as shown in the test case's WithMutexesTest.SampleWithMutexesDelegatingMixin class 
- **/
-public interface WithMutexes {
-
-    /** returns true if the calling thread has the mutex with the given ID */
-    public boolean hasMutex(String mutexId);
-    
-    /** acquires a mutex, if available, otherwise blocks on its becoming available;
-     * caller must release after use */
-    public void acquireMutex(String mutexId, String description) throws InterruptedException;
-
-    /** acquires a mutex and returns true, if available; otherwise immediately returns false;
-     * caller must release after use if this returns true */
-    public boolean tryAcquireMutex(String mutexId, String description);
-
-    /** releases a mutex, triggering another thread to use it or cleaning it up if no one else is waiting;
-     * this should only be called by the mutex owner (thread) */
-    public void releaseMutex(String mutexId);
-    
-}


[38/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/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
deleted file mode 100644
index 31f27be..0000000
--- a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
+++ /dev/null
@@ -1,879 +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.flags;
-
-import groovy.lang.Closure;
-import groovy.time.TimeDuration;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.URI;
-import java.net.URL;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.GuardedBy;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.event.AttributeSensor;
-import org.apache.brooklyn.api.event.Sensor;
-import org.apache.brooklyn.core.internal.BrooklynInitialization;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.basic.ClosureEntityFactory;
-import brooklyn.entity.basic.ConfigurableEntityFactory;
-import brooklyn.entity.basic.ConfigurableEntityFactoryFromEntityFactory;
-import brooklyn.event.basic.Sensors;
-import brooklyn.util.JavaGroovyEquivalents;
-import brooklyn.util.collections.MutableSet;
-import brooklyn.util.collections.QuorumCheck;
-import brooklyn.util.collections.QuorumCheck.QuorumChecks;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.javalang.Enums;
-import brooklyn.util.net.Cidr;
-import brooklyn.util.net.Networking;
-import brooklyn.util.net.UserAndHostAndPort;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.text.StringEscapes.JavaStringEscapes;
-import brooklyn.util.text.Strings;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-import brooklyn.util.yaml.Yamls;
-
-import com.google.common.base.CaseFormat;
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-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.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-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);
-    
-    private TypeCoercions() {}
-
-    /** Store the coercion {@link Function functions} in a {@link Table table}. */
-    @GuardedBy("TypeCoercions.class")
-    private static Table<Class, Class, Function> registry = HashBasedTable.create();
-
-    /**
-     * Attempts to coerce {@code value} to {@code targetType}.
-     * <p>
-     * Maintains a registry of adapter functions for type pairs in a {@link Table} which
-     * is searched after checking various strategies, including the following:
-     * <ul>
-     * <li>{@code value.asTargetType()}
-     * <li>{@code TargetType.fromType(value)} (if {@code value instanceof Type})
-     * <li>{@code value.targetTypeValue()} (handy for primitives)
-     * <li>{@code TargetType.valueOf(value)} (for enums)
-     * </ul>
-     * <p>
-     * A default set of adapters will handle most common Java-type coercions
-     * as well as <code>String</code> coercion to:
-     * <ul>
-     * <li> {@link Set}, {@link List}, {@link Map} and similar -- parses as YAML
-     * <li> {@link Date} -- parses using {@link Time#parseDate(String)}
-     * <li> {@link Duration} -- parses using {@link Duration#parse(String)}
-     * </ul>
-     */
-    public static <T> T coerce(Object value, Class<T> targetType) {
-        return coerce(value, TypeToken.of(targetType));
-    }
-
-    /** @see #coerce(Object, Class) */
-    public static <T> Maybe<T> tryCoerce(Object value, TypeToken<T> targetTypeToken) {
-        try {
-            return Maybe.of( coerce(value, targetTypeToken) );
-        } catch (Throwable t) {
-            Exceptions.propagateIfFatal(t);
-            return Maybe.absent(t); 
-        }
-    }
-    
-    /** @see #coerce(Object, Class) */
-    @SuppressWarnings({ "unchecked" })
-    public static <T> T coerce(Object value, TypeToken<T> targetTypeToken) {
-        if (value==null) return null;
-        Class<? super T> targetType = targetTypeToken.getRawType();
-
-        //recursive coercion of parameterized collections and map entries
-        if (targetTypeToken.getType() instanceof ParameterizedType) {
-            if (value instanceof Collection && Collection.class.isAssignableFrom(targetType)) {
-                Type[] arguments = ((ParameterizedType) targetTypeToken.getType()).getActualTypeArguments();
-                if (arguments.length != 1) {
-                    throw new IllegalStateException("Unexpected number of parameters in collection type: " + arguments);
-                }
-                Collection coerced = Lists.newLinkedList();
-                TypeToken<?> listEntryType = TypeToken.of(arguments[0]);
-                for (Object entry : (Iterable<?>) value) {
-                    coerced.add(coerce(entry, listEntryType));
-                }
-                if (Set.class.isAssignableFrom(targetType)) {
-                    return (T) Sets.newLinkedHashSet(coerced);
-                } else {
-                    return (T) Lists.newArrayList(coerced);
-                }
-            } else if (value instanceof Map && Map.class.isAssignableFrom(targetType)) {
-                Type[] arguments = ((ParameterizedType) targetTypeToken.getType()).getActualTypeArguments();
-                if (arguments.length != 2) {
-                    throw new IllegalStateException("Unexpected number of parameters in map type: " + arguments);
-                }
-                Map coerced = Maps.newLinkedHashMap();
-                TypeToken<?> mapKeyType = TypeToken.of(arguments[0]);
-                TypeToken<?> mapValueType = TypeToken.of(arguments[1]);
-                for (Map.Entry entry : ((Map<?,?>) value).entrySet()) {
-                    coerced.put(coerce(entry.getKey(), mapKeyType),  coerce(entry.getValue(), mapValueType));
-                }
-                return (T) Maps.newLinkedHashMap(coerced);
-            }
-        }
-
-        if (targetType.isInstance(value)) return (T) value;
-
-        // TODO use registry first?
-
-        //deal with primitive->primitive casting
-        if (isPrimitiveOrBoxer(targetType) && isPrimitiveOrBoxer(value.getClass())) {
-            // Don't just rely on Java to do its normal casting later; if caller writes
-            // long `l = coerce(new Integer(1), Long.class)` then letting java do its casting will fail,
-            // because an Integer will not automatically be unboxed and cast to a long
-            return castPrimitive(value, (Class<T>)targetType);
-        }
-
-        //deal with string->primitive
-        if (value instanceof String && isPrimitiveOrBoxer(targetType)) {
-            return stringToPrimitive((String)value, (Class<T>)targetType);
-        }
-
-        //deal with primitive->string
-        if (isPrimitiveOrBoxer(value.getClass()) && targetType.equals(String.class)) {
-            return (T) value.toString();
-        }
-
-        //look for value.asType where Type is castable to targetType
-        String targetTypeSimpleName = getVerySimpleName(targetType);
-        if (targetTypeSimpleName!=null && targetTypeSimpleName.length()>0) {
-            for (Method m: value.getClass().getMethods()) {
-                if (m.getName().startsWith("as") && m.getParameterTypes().length==0 &&
-                        targetType.isAssignableFrom(m.getReturnType()) ) {
-                    if (m.getName().equals("as"+getVerySimpleName(m.getReturnType()))) {
-                        try {
-                            return (T) m.invoke(value);
-                        } catch (Exception e) {
-                            throw new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): "+m.getName()+" adapting failed, "+e);
-                        }
-                    }
-                }
-            }
-        }
-        
-        //now look for static TargetType.fromType(Type t) where value instanceof Type  
-        for (Method m: targetType.getMethods()) {
-            if (((m.getModifiers()&Modifier.STATIC)==Modifier.STATIC) && 
-                    m.getName().startsWith("from") && m.getParameterTypes().length==1 &&
-                    m.getParameterTypes()[0].isInstance(value)) {
-                if (m.getName().equals("from"+getVerySimpleName(m.getParameterTypes()[0]))) {
-                    try {
-                        return (T) m.invoke(null, value);
-                    } catch (Exception e) {
-                        throw new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): "+m.getName()+" adapting failed, "+e);
-                    }
-                }
-            }
-        }
-        
-       //ENHANCEMENT could look in type hierarchy of both types for a conversion method...
-        
-        //primitives get run through again boxed up
-        Class boxedT = UNBOXED_TO_BOXED_TYPES.get(targetType);
-        Class boxedVT = UNBOXED_TO_BOXED_TYPES.get(value.getClass());
-        if (boxedT!=null || boxedVT!=null) {
-            try {
-                if (boxedT==null) boxedT=targetType;
-                Object boxedV;
-                if (boxedVT==null) { boxedV = value; }
-                else { boxedV = boxedVT.getConstructor(value.getClass()).newInstance(value); }
-                return (T) coerce(boxedV, boxedT);
-            } catch (Exception e) {
-                throw new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): unboxing failed, "+e);
-            }
-        }
-
-        //for enums call valueOf with the string representation of the value
-        if (targetType.isEnum()) {
-            T result = (T) stringToEnum((Class<Enum>) targetType, null).apply(String.valueOf(value));
-            if (result != null) return result;
-        }
-
-        //now look in registry
-        synchronized (TypeCoercions.class) {
-            Map<Class, Function> adapters = registry.row(targetType);
-            for (Map.Entry<Class, Function> entry : adapters.entrySet()) {
-                if (entry.getKey().isInstance(value)) {
-                    T result = (T) entry.getValue().apply(value);
-                    
-                    // Check if need to unwrap again (e.g. if want List<Integer> and are given a String "1,2,3"
-                    // then we'll have so far converted to List.of("1", "2", "3"). Call recursively.
-                    // First check that value has changed, to avoid stack overflow!
-                    if (!Objects.equal(value, result) && targetTypeToken.getType() instanceof ParameterizedType) {
-                        // Could duplicate check for `result instanceof Collection` etc; but recursive call
-                        // will be fine as if that doesn't match we'll safely reach `targetType.isInstance(value)`
-                        // and just return the result.
-                        return coerce(result, targetTypeToken);
-                    }
-                    return result;
-                }
-            }
-        }
-
-        //not found
-        throw new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): no adapter known");
-    }
-
-    /**
-     * Returns a function that does a type coercion to the given type. For example,
-     * {@code TypeCoercions.function(Double.class)} will return a function that will
-     * coerce its input value to a {@link Double} (or throw a {@link ClassCoercionException}
-     * if that is not possible).
-     */
-    public static <T> Function<Object, T> function(final Class<T> type) {
-        return new CoerceFunction<T>(type);
-    }
-    
-    private static class CoerceFunction<T> implements Function<Object, T> {
-        private final Class<T> type;
-
-        public CoerceFunction(Class<T> type) {
-            this.type = type;
-        }
-        @Override
-        public T apply(Object input) {
-            return coerce(input, type);
-        }
-    }
-
-    /**
-     * Type coercion {@link Function function} for {@link Enum enums}.
-     * <p>
-     * Tries to convert the string to {@link CaseFormat#UPPER_UNDERSCORE} first,
-     * handling all of the different {@link CaseFormat format} possibilites. Failing 
-     * that, it tries a case-insensitive comparison with the valid enum values.
-     * <p>
-     * Returns {@code defaultValue} if the string cannot be converted.
-     *
-     * @see TypeCoercions#coerce(Object, Class)
-     * @see Enum#valueOf(Class, String)
-     */
-    public static <E extends Enum<E>> Function<String, E> stringToEnum(final Class<E> type, @Nullable final E defaultValue) {
-        return new StringToEnumFunction<E>(type, defaultValue);
-    }
-    
-    private static class StringToEnumFunction<E extends Enum<E>> implements Function<String, E> {
-        private final Class<E> type;
-        private final E defaultValue;
-        
-        public StringToEnumFunction(Class<E> type, @Nullable E defaultValue) {
-            this.type = type;
-            this.defaultValue = defaultValue;
-        }
-        @Override
-        public E apply(String input) {
-            Preconditions.checkNotNull(input, "input");
-            List<String> options = ImmutableList.of(
-                    input,
-                    CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, input),
-                    CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, input),
-                    CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, input),
-                    CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, input));
-            for (String value : options) {
-                try {
-                    return Enum.valueOf(type, value);
-                } catch (IllegalArgumentException iae) {
-                    continue;
-                }
-            }
-            Maybe<E> result = Enums.valueOfIgnoreCase(type, input);
-            return (result.isPresent()) ? result.get() : defaultValue;
-        }
-    }
-
-    /**
-     * Sometimes need to explicitly cast primitives, rather than relying on Java casting.
-     * For example, when using generics then type-erasure means it doesn't actually cast,
-     * which causes tests to fail with 0 != 0.0
-     */
-    @SuppressWarnings("unchecked")
-    public static <T> T castPrimitive(Object value, Class<T> targetType) {
-        if (value==null) return null;
-        assert isPrimitiveOrBoxer(targetType) : "targetType="+targetType;
-        assert isPrimitiveOrBoxer(value.getClass()) : "value="+targetType+"; valueType="+value.getClass();
-
-        Class<?> sourceWrapType = Primitives.wrap(value.getClass());
-        Class<?> targetWrapType = Primitives.wrap(targetType);
-        
-        // optimization, for when already correct type
-        if (sourceWrapType == targetWrapType) {
-            return (T) value;
-        }
-        
-        if (targetWrapType == Boolean.class) {
-            // only char can be mapped to boolean
-            // (we could say 0=false, nonzero=true, but there is no compelling use case so better
-            // to encourage users to write as boolean)
-            if (sourceWrapType == Character.class)
-                return (T) stringToPrimitive(value.toString(), targetType);
-            
-            throw new ClassCoercionException("Cannot cast "+sourceWrapType+" ("+value+") to "+targetType);
-        } else if (sourceWrapType == Boolean.class) {
-            // boolean can't cast to anything else
-            
-            throw new ClassCoercionException("Cannot cast "+sourceWrapType+" ("+value+") to "+targetType);
-        }
-        
-        // for whole-numbers (where casting to long won't lose anything)...
-        long v = 0;
-        boolean islong = true;
-        if (sourceWrapType == Character.class) {
-            v = (long) ((Character)value).charValue();
-        } else if (sourceWrapType == Byte.class) {
-            v = (long) ((Byte)value).byteValue();
-        } else if (sourceWrapType == Short.class) {
-            v = (long) ((Short)value).shortValue();
-        } else if (sourceWrapType == Integer.class) {
-            v = (long) ((Integer)value).intValue();
-        } else if (sourceWrapType == Long.class) {
-            v = ((Long)value).longValue();
-        } else {
-            islong = false;
-        }
-        if (islong) {
-            if (targetWrapType == Character.class) return (T) Character.valueOf((char)v); 
-            if (targetWrapType == Byte.class) return (T) Byte.valueOf((byte)v); 
-            if (targetWrapType == Short.class) return (T) Short.valueOf((short)v); 
-            if (targetWrapType == Integer.class) return (T) Integer.valueOf((int)v); 
-            if (targetWrapType == Long.class) return (T) Long.valueOf((long)v); 
-            if (targetWrapType == Float.class) return (T) Float.valueOf((float)v); 
-            if (targetWrapType == Double.class) return (T) Double.valueOf((double)v);
-            throw new IllegalStateException("Unexpected: sourceType="+sourceWrapType+"; targetType="+targetWrapType);
-        }
-        
-        // for real-numbers (cast to double)...
-        double d = 0;
-        boolean isdouble = true;
-        if (sourceWrapType == Float.class) {
-            d = (double) ((Float)value).floatValue();
-        } else if (sourceWrapType == Double.class) {
-            d = (double) ((Double)value).doubleValue();
-        } else {
-            isdouble = false;
-        }
-        if (isdouble) {
-            if (targetWrapType == Character.class) return (T) Character.valueOf((char)d); 
-            if (targetWrapType == Byte.class) return (T) Byte.valueOf((byte)d); 
-            if (targetWrapType == Short.class) return (T) Short.valueOf((short)d); 
-            if (targetWrapType == Integer.class) return (T) Integer.valueOf((int)d); 
-            if (targetWrapType == Long.class) return (T) Long.valueOf((long)d); 
-            if (targetWrapType == Float.class) return (T) Float.valueOf((float)d); 
-            if (targetWrapType == Double.class) return (T) Double.valueOf((double)d);
-            throw new IllegalStateException("Unexpected: sourceType="+sourceWrapType+"; targetType="+targetWrapType);
-        } else {
-            throw new IllegalStateException("Unexpected: sourceType="+sourceWrapType+"; targetType="+targetWrapType);
-        }
-    }
-    
-    public static boolean isPrimitiveOrBoxer(Class<?> type) {
-        return Primitives.allPrimitiveTypes().contains(type) || Primitives.allWrapperTypes().contains(type);
-    }
-    
-    @SuppressWarnings("unchecked")
-    public static <T> T stringToPrimitive(String value, Class<T> targetType) {
-        assert Primitives.allPrimitiveTypes().contains(targetType) || Primitives.allWrapperTypes().contains(targetType) : "targetType="+targetType;
-        // If char, then need to do explicit conversion
-        if (targetType == Character.class || targetType == char.class) {
-            if (value.length() == 1) {
-                return (T) (Character) value.charAt(0);
-            } else if (value.length() != 1) {
-                throw new ClassCoercionException("Cannot coerce type String to "+targetType.getCanonicalName()+" ("+value+"): adapting failed");
-            }
-        }
-        value = value.trim();
-        // For boolean we could use valueOf, but that returns false whereas we'd rather throw errors on bad values
-        if (targetType == Boolean.class || targetType == boolean.class) {
-            if ("true".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
-            if ("false".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
-            if ("yes".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
-            if ("no".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
-            if ("t".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
-            if ("f".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
-            if ("y".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
-            if ("n".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
-            
-            throw new ClassCoercionException("Cannot coerce type String to "+targetType.getCanonicalName()+" ("+value+"): adapting failed"); 
-        }
-        
-        // Otherwise can use valueOf reflectively
-        Class<?> wrappedType;
-        if (Primitives.allPrimitiveTypes().contains(targetType)) {
-            wrappedType = Primitives.wrap(targetType);
-        } else {
-            wrappedType = targetType;
-        }
-        
-        try {
-            return (T) wrappedType.getMethod("valueOf", String.class).invoke(null, value);
-        } catch (Exception e) {
-            ClassCoercionException tothrow = new ClassCoercionException("Cannot coerce "+JavaStringEscapes.wrapJavaString(value)+" to "+targetType.getCanonicalName()+" ("+value+"): adapting failed");
-            tothrow.initCause(e);
-            throw tothrow;
-        }
-    }
-    
-    /** returns the simple class name, and for any inner class the portion after the $ */
-    public static String getVerySimpleName(Class c) {
-        String s = c.getSimpleName();
-        if (s.indexOf('$')>=0)
-            s = s.substring(s.lastIndexOf('$')+1);
-        return s;
-    }
-    public static final Map<Class,Class> BOXED_TO_UNBOXED_TYPES = ImmutableMap.<Class,Class>builder().
-            put(Integer.class, Integer.TYPE).
-            put(Long.class, Long.TYPE).
-            put(Boolean.class, Boolean.TYPE).
-            put(Byte.class, Byte.TYPE).
-            put(Double.class, Double.TYPE).
-            put(Float.class, Float.TYPE).
-            put(Character.class, Character.TYPE).
-            put(Short.class, Short.TYPE).
-            build();
-    public static final Map<Class,Class> UNBOXED_TO_BOXED_TYPES = ImmutableMap.<Class,Class>builder().
-            put(Integer.TYPE, Integer.class).
-            put(Long.TYPE, Long.class).
-            put(Boolean.TYPE, Boolean.class).
-            put(Byte.TYPE, Byte.class).
-            put(Double.TYPE, Double.class).
-            put(Float.TYPE, Float.class).
-            put(Character.TYPE, Character.class).
-            put(Short.TYPE, Short.class).
-            build();
-    
-    /** for automatic conversion */
-    public static Object getMatchingConstructor(Class target, Object ...arguments) {
-        Constructor[] cc = target.getConstructors();
-        for (Constructor c: cc) {
-            if (c.getParameterTypes().length != arguments.length)
-                continue;
-            boolean matches = true;
-            Class[] tt = c.getParameterTypes();
-            for (int i=0; i<tt.length; i++) {
-                if (arguments[i]!=null && !tt[i].isInstance(arguments[i])) {
-                    matches=false;
-                    break;
-                }
-            }
-            if (matches) 
-                return c;
-        }
-        return null;
-    }
-
-    /** Registers an adapter for use with type coercion. Returns any old adapter. */
-    public synchronized static <A,B> Function registerAdapter(Class<A> sourceType, Class<B> targetType, Function<? super A,B> fn) {
-        return registry.put(targetType, sourceType, fn);
-    }
-
-    static { BrooklynInitialization.initTypeCoercionStandardAdapters(); }
-    
-    public static void initStandardAdapters() {
-        registerAdapter(CharSequence.class, String.class, new Function<CharSequence,String>() {
-            @Override
-            public String apply(CharSequence input) {
-                return input.toString();
-            }
-        });
-        registerAdapter(byte[].class, String.class, new Function<byte[],String>() {
-            @Override
-            public String apply(byte[] input) {
-                return new String(input);
-            }
-        });
-        registerAdapter(Collection.class, Set.class, new Function<Collection,Set>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public Set apply(Collection input) {
-                return Sets.newLinkedHashSet(input);
-            }
-        });
-        registerAdapter(Collection.class, List.class, new Function<Collection,List>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public List apply(Collection input) {
-                return Lists.newArrayList(input);
-            }
-        });
-        registerAdapter(String.class, InetAddress.class, new Function<String,InetAddress>() {
-            @Override
-            public InetAddress apply(String input) {
-                return Networking.getInetAddressWithFixedName(input);
-            }
-        });
-        registerAdapter(String.class, HostAndPort.class, new Function<String,HostAndPort>() {
-            @Override
-            public HostAndPort apply(String input) {
-                return HostAndPort.fromString(input);
-            }
-        });
-        registerAdapter(String.class, UserAndHostAndPort.class, new Function<String,UserAndHostAndPort>() {
-            @Override
-            public UserAndHostAndPort apply(String input) {
-                return UserAndHostAndPort.fromString(input);
-            }
-        });
-        registerAdapter(String.class, Cidr.class, new Function<String,Cidr>() {
-            @Override
-            public Cidr apply(String input) {
-                return new Cidr(input);
-            }
-        });
-        registerAdapter(String.class, URL.class, new Function<String,URL>() {
-            @Override
-            public URL apply(String input) {
-                try {
-                    return new URL(input);
-                } catch (Exception e) {
-                    throw Exceptions.propagate(e);
-                }
-            }
-        });
-        registerAdapter(String.class, URI.class, new Function<String,URI>() {
-            @Override
-            public URI apply(String input) {
-                return URI.create(input);
-            }
-        });
-        registerAdapter(Closure.class, ConfigurableEntityFactory.class, new Function<Closure,ConfigurableEntityFactory>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public ConfigurableEntityFactory apply(Closure input) {
-                return new ClosureEntityFactory(input);
-            }
-        });
-        @SuppressWarnings({"unused", "deprecation"})
-        Function<?,?> ignoredVarHereToAllowSuppressDeprecationWarning1 = registerAdapter(brooklyn.entity.basic.EntityFactory.class, ConfigurableEntityFactory.class, new Function<brooklyn.entity.basic.EntityFactory,ConfigurableEntityFactory>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public ConfigurableEntityFactory apply(brooklyn.entity.basic.EntityFactory input) {
-                if (input instanceof ConfigurableEntityFactory) return (ConfigurableEntityFactory)input;
-                return new ConfigurableEntityFactoryFromEntityFactory(input);
-            }
-        });
-        @SuppressWarnings({"unused", "deprecation"})
-        Function<?,?> ignoredVarHereToAllowSuppressDeprecationWarning2 = registerAdapter(Closure.class, brooklyn.entity.basic.EntityFactory.class, new Function<Closure,brooklyn.entity.basic.EntityFactory>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public brooklyn.entity.basic.EntityFactory apply(Closure input) {
-                return new ClosureEntityFactory(input);
-            }
-        });
-        registerAdapter(Closure.class, Predicate.class, new Function<Closure,Predicate>() {
-            @Override
-            public Predicate<?> apply(final Closure closure) {
-                return new Predicate<Object>() {
-                    @Override public boolean apply(Object input) {
-                        return (Boolean) closure.call(input);
-                    }
-                };
-            }
-        });
-        registerAdapter(Closure.class, Function.class, new Function<Closure,Function>() {
-            @Override
-            public Function apply(final Closure closure) {
-                return new Function() {
-                    @Override public Object apply(Object input) {
-                        return closure.call(input);
-                    }
-                };
-            }
-        });
-        registerAdapter(Object.class, Duration.class, new Function<Object,Duration>() {
-            @Override
-            public Duration apply(final Object input) {
-                return brooklyn.util.time.Duration.of(input);
-            }
-        });
-        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)");
-                return JavaGroovyEquivalents.toTimeDuration(input);
-            }
-        });
-        registerAdapter(TimeDuration.class, Long.class, new Function<TimeDuration,Long>() {
-            @Override
-            public Long apply(final TimeDuration input) {
-                log.warn("deprecated automatic coercion of TimeDuration to Long (set breakpoint in TypeCoercions to inspect, use Duration instead of Long!)");
-                return input.toMilliseconds();
-            }
-        });
-        registerAdapter(Integer.class, AtomicLong.class, new Function<Integer,AtomicLong>() {
-            @Override public AtomicLong apply(final Integer input) {
-                return new AtomicLong(input);
-            }
-        });
-        registerAdapter(Long.class, AtomicLong.class, new Function<Long,AtomicLong>() {
-            @Override public AtomicLong apply(final Long input) {
-                return new AtomicLong(input);
-            }
-        });
-        registerAdapter(String.class, AtomicLong.class, new Function<String,AtomicLong>() {
-            @Override public AtomicLong apply(final String input) {
-                return new AtomicLong(Long.parseLong(input.trim()));
-            }
-        });
-        registerAdapter(Integer.class, AtomicInteger.class, new Function<Integer,AtomicInteger>() {
-            @Override public AtomicInteger apply(final Integer input) {
-                return new AtomicInteger(input);
-            }
-        });
-        registerAdapter(String.class, AtomicInteger.class, new Function<String,AtomicInteger>() {
-            @Override public AtomicInteger apply(final String input) {
-                return new AtomicInteger(Integer.parseInt(input.trim()));
-            }
-        });
-        /** This always returns a {@link Double}, cast as a {@link Number}; 
-         * however primitives and boxers get exact typing due to call in #stringToPrimitive */
-        registerAdapter(String.class, Number.class, new Function<String,Number>() {
-            @Override
-            public Number apply(String input) {
-                return Double.valueOf(input);
-            }
-        });
-        registerAdapter(BigDecimal.class, Double.class, new Function<BigDecimal,Double>() {
-            @Override
-            public Double apply(BigDecimal input) {
-                return input.doubleValue();
-            }
-        });
-        registerAdapter(BigInteger.class, Long.class, new Function<BigInteger,Long>() {
-            @Override
-            public Long apply(BigInteger input) {
-                return input.longValue();
-            }
-        });
-        registerAdapter(BigInteger.class, Integer.class, new Function<BigInteger,Integer>() {
-            @Override
-            public Integer apply(BigInteger input) {
-                return input.intValue();
-            }
-        });
-        registerAdapter(String.class, BigDecimal.class, new Function<String,BigDecimal>() {
-            @Override
-            public BigDecimal apply(String input) {
-                return new BigDecimal(input);
-            }
-        });
-        registerAdapter(Double.class, BigDecimal.class, new Function<Double,BigDecimal>() {
-            @Override
-            public BigDecimal apply(Double input) {
-                return BigDecimal.valueOf(input);
-            }
-        });
-        registerAdapter(String.class, BigInteger.class, new Function<String,BigInteger>() {
-            @Override
-            public BigInteger apply(String input) {
-                return new BigInteger(input);
-            }
-        });
-        registerAdapter(Long.class, BigInteger.class, new Function<Long,BigInteger>() {
-            @Override
-            public BigInteger apply(Long input) {
-                return BigInteger.valueOf(input);
-            }
-        });
-        registerAdapter(Integer.class, BigInteger.class, new Function<Integer,BigInteger>() {
-            @Override
-            public BigInteger apply(Integer input) {
-                return BigInteger.valueOf(input);
-            }
-        });
-        registerAdapter(String.class, Date.class, new Function<String,Date>() {
-            @Override
-            public Date apply(final String input) {
-                return Time.parseDate(input);
-            }
-        });
-        registerAdapter(String.class, Class.class, new Function<String,Class>() {
-            @Override
-            public Class apply(final String input) {
-                try {
-                    return Class.forName(input);
-                } catch (ClassNotFoundException e) {
-                    throw Exceptions.propagate(e);
-                }
-            }
-        });
-        registerAdapter(String.class, AttributeSensor.class, new Function<String,AttributeSensor>() {
-            @Override
-            public AttributeSensor apply(final String input) {
-                Entity entity = BrooklynTaskTags.getContextEntity(Tasks.current());
-                if (entity!=null) {
-                    Sensor<?> result = entity.getEntityType().getSensor(input);
-                    if (result instanceof AttributeSensor) 
-                        return (AttributeSensor) result;
-                }
-                return Sensors.newSensor(Object.class, input);
-            }
-        });
-        registerAdapter(String.class, Sensor.class, new Function<String,Sensor>() {
-            @Override
-            public AttributeSensor apply(final String input) {
-                Entity entity = BrooklynTaskTags.getContextEntity(Tasks.current());
-                if (entity!=null) {
-                    Sensor<?> result = entity.getEntityType().getSensor(input);
-                    if (result != null) 
-                        return (AttributeSensor) result;
-                }
-                return Sensors.newSensor(Object.class, input);
-            }
-        });
-        registerAdapter(String.class, List.class, new Function<String,List>() {
-            @Override
-            public List<String> apply(final String input) {
-                return JavaStringEscapes.unwrapJsonishListIfPossible(input);
-            }
-        });
-        registerAdapter(String.class, Set.class, new Function<String,Set>() {
-            @Override
-            public Set<String> apply(final String input) {
-                return MutableSet.copyOf(JavaStringEscapes.unwrapJsonishListIfPossible(input)).asUnmodifiable();
-            }
-        });
-        registerAdapter(String.class, QuorumCheck.class, new Function<String,QuorumCheck>() {
-            @Override
-            public QuorumCheck apply(final String input) {
-                return QuorumChecks.of(input);
-            }
-        });
-        registerAdapter(Iterable.class, String[].class, new Function<Iterable, String[]>() {
-            @Nullable
-            @Override
-            public String[] apply(@Nullable Iterable list) {
-                if (list == null) return null;
-                String[] result = new String[Iterables.size(list)];
-                int count = 0;
-                for (Object element : list) {
-                    result[count++] = coerce(element, String.class);
-                }
-                return result;
-            }
-        });
-        registerAdapter(Iterable.class, Integer[].class, new Function<Iterable, Integer[]>() {
-            @Nullable
-            @Override
-            public Integer[] apply(@Nullable Iterable list) {
-                if (list == null) return null;
-                Integer[] result = new Integer[Iterables.size(list)];
-                int count = 0;
-                for (Object element : list) {
-                    result[count++] = coerce(element, Integer.class);
-                }
-                return result;
-            }
-        });
-        registerAdapter(Iterable.class, int[].class, new Function<Iterable, int[]>() {
-            @Nullable
-            @Override
-            public int[] apply(@Nullable Iterable list) {
-                if (list == null) return null;
-                int[] result = new int[Iterables.size(list)];
-                int count = 0;
-                for (Object element : list) {
-                    result[count++] = coerce(element, int.class);
-                }
-                return result;
-            }
-        });
-        registerAdapter(String.class, Map.class, new Function<String,Map>() {
-            @Override
-            public Map apply(final String input) {
-                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 {
-                    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;
-                    }
-                    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) );
-                }
-
-                // 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/a4c0e5fd/core/src/main/java/brooklyn/util/http/HttpTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/http/HttpTool.java b/core/src/main/java/brooklyn/util/http/HttpTool.java
deleted file mode 100644
index a812cfc..0000000
--- a/core/src/main/java/brooklyn/util/http/HttpTool.java
+++ /dev/null
@@ -1,387 +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.http;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.net.URI;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.http.ConnectionReuseStrategy;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.NameValuePair;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.Credentials;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.HttpDelete;
-import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpHead;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpPut;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeSocketFactory;
-import org.apache.http.conn.ssl.SSLSocketFactory;
-import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
-import org.apache.http.conn.ssl.TrustStrategy;
-import org.apache.http.conn.ssl.X509HostnameVerifier;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.client.LaxRedirectStrategy;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
-import org.apache.http.util.EntityUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.crypto.SslTrustUtils;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.net.URLParamEncoder;
-import brooklyn.util.text.Strings;
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
-
-public class HttpTool {
-
-    private static final Logger LOG = LoggerFactory.getLogger(HttpTool.class);
-
-    /** Apache HTTP commons utility for trusting all.
-     * <p>
-     * For generic java HTTP usage, see {@link SslTrustUtils#trustAll(java.net.URLConnection)} 
-     * and static constants in the same class. */
-    public static class TrustAllStrategy implements TrustStrategy {
-        @Override
-        public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
-            return true;
-        }
-    }
-
-    public static HttpClientBuilder httpClientBuilder() {
-        return new HttpClientBuilder();
-    }
-    
-    public static class HttpClientBuilder {
-        private ClientConnectionManager clientConnectionManager;
-        private HttpParams httpParams;
-        private URI uri;
-        private Integer port;
-        private Credentials credentials;
-        private boolean laxRedirect;
-        private Boolean https;
-        private SchemeSocketFactory socketFactory;
-        private ConnectionReuseStrategy reuseStrategy;
-        private boolean trustAll;
-        private boolean trustSelfSigned;
-
-        public HttpClientBuilder clientConnectionManager(ClientConnectionManager val) {
-            this.clientConnectionManager = checkNotNull(val, "clientConnectionManager");
-            return this;
-        }
-        public HttpClientBuilder httpParams(HttpParams val) {
-            checkState(httpParams == null, "Must not call httpParams multiple times, or after other methods like connectionTimeout");
-            this.httpParams = checkNotNull(val, "httpParams");
-            return this;
-        }
-        public HttpClientBuilder connectionTimeout(Duration val) {
-            if (httpParams == null) httpParams = new BasicHttpParams();
-            long millis = checkNotNull(val, "connectionTimeout").toMilliseconds();
-            if (millis > Integer.MAX_VALUE) throw new IllegalStateException("HttpClient only accepts upto max-int millis for connectionTimeout, but given "+val);
-            HttpConnectionParams.setConnectionTimeout(httpParams, (int) millis);
-            return this;
-        }
-        public HttpClientBuilder socketTimeout(Duration val) {
-            if (httpParams == null) httpParams = new BasicHttpParams();
-            long millis = checkNotNull(val, "socketTimeout").toMilliseconds();
-            if (millis > Integer.MAX_VALUE) throw new IllegalStateException("HttpClient only accepts upto max-int millis for socketTimeout, but given "+val);
-            HttpConnectionParams.setSoTimeout(httpParams, (int) millis);
-            return this;
-        }
-        public HttpClientBuilder reuseStrategy(ConnectionReuseStrategy val) {
-            this.reuseStrategy = checkNotNull(val, "reuseStrategy");
-            return this;
-        }
-        public HttpClientBuilder uri(String val) {
-            return uri(URI.create(checkNotNull(val, "uri")));
-        }
-        public HttpClientBuilder uri(URI val) {
-            this.uri = checkNotNull(val, "uri");
-            if (https == null) https = ("https".equalsIgnoreCase(uri.getScheme()));
-            return this;
-        }
-        public HttpClientBuilder port(int val) {
-            this.port = val;
-            return this;
-        }
-        public HttpClientBuilder credentials(Credentials val) {
-            this.credentials = checkNotNull(val, "credentials");
-            return this;
-        }
-        public void credential(Optional<Credentials> val) {
-            if (val.isPresent()) credentials = val.get();
-        }
-        /** similar to curl --post301 -L` */
-        public HttpClientBuilder laxRedirect(boolean val) {
-            this.laxRedirect = val;
-            return this;
-        }
-        public HttpClientBuilder https(boolean val) {
-            this.https = val;
-            return this;
-        }
-        public HttpClientBuilder socketFactory(SchemeSocketFactory val) {
-            this.socketFactory = checkNotNull(val, "socketFactory");
-            return this;
-        }
-        public HttpClientBuilder trustAll() {
-            this.trustAll = true;
-            return this;
-        }
-        public HttpClientBuilder trustSelfSigned() {
-            this.trustSelfSigned = true;
-            return this;
-        }
-        public HttpClient build() {
-            final DefaultHttpClient httpClient = new DefaultHttpClient(clientConnectionManager);
-            httpClient.setParams(httpParams);
-    
-            // support redirects for POST (similar to `curl --post301 -L`)
-            // http://stackoverflow.com/questions/3658721/httpclient-4-error-302-how-to-redirect
-            if (laxRedirect) {
-                httpClient.setRedirectStrategy(new LaxRedirectStrategy());
-            }
-            if (reuseStrategy != null) {
-                httpClient.setReuseStrategy(reuseStrategy);
-            }
-            if (https == Boolean.TRUE || (uri!=null && uri.toString().startsWith("https:"))) {
-                try {
-                    if (port == null) {
-                        port = (uri != null && uri.getPort() >= 0) ? uri.getPort() : 443;
-                    }
-                    if (socketFactory == null) {
-                        if (trustAll) {
-                            TrustStrategy trustStrategy = new TrustAllStrategy();
-                            X509HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
-                            socketFactory = new SSLSocketFactory(trustStrategy, hostnameVerifier);
-                        } else if (trustSelfSigned) {
-                            TrustStrategy trustStrategy = new TrustSelfSignedStrategy();
-                            X509HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
-                            socketFactory = new SSLSocketFactory(trustStrategy, hostnameVerifier);
-                        } else {
-                            // Using default https scheme: based on default java truststore, which is pretty strict!
-                        }
-                    }
-                    if (socketFactory != null) {
-                        Scheme sch = new Scheme("https", port, socketFactory);
-                        httpClient.getConnectionManager().getSchemeRegistry().register(sch);
-                    }
-                } catch (Exception e) {
-                    LOG.warn("Error setting trust for uri {}", uri);
-                    throw Exceptions.propagate(e);
-                }
-            }
-    
-            // Set credentials
-            if (uri != null && credentials != null) {
-                String hostname = uri.getHost();
-                int port = uri.getPort();
-                httpClient.getCredentialsProvider().setCredentials(new AuthScope(hostname, port), credentials);
-            }
-            if (uri==null && credentials!=null) {
-                LOG.warn("credentials have no effect in builder unless URI for host is specified");
-            }
-    
-            return httpClient;
-        }
-    }
-
-    protected static abstract class HttpRequestBuilder<B extends HttpRequestBuilder<B, R>, R extends HttpRequest> {
-        protected R req;
-        
-        protected HttpRequestBuilder(R req) {
-            this.req = req;
-        }
-        @SuppressWarnings("unchecked")
-        protected B self() {
-            return (B) this;
-        }
-        public B headers(Map<String,String> headers) {
-            if (headers!=null) {
-                for (Map.Entry<String,String> entry : headers.entrySet()) {
-                    req.addHeader(entry.getKey(), entry.getValue());
-                }
-            }
-            return self();
-        }
-        public B headers(Multimap<String,String> headers) {
-            if (headers!=null) {
-                for (Map.Entry<String,String> entry : headers.entries()) {
-                    req.addHeader(entry.getKey(), entry.getValue());
-                }
-            }
-            return self();
-        }
-        public R build() {
-            return req;
-        }
-    }
-    
-    protected static abstract class HttpEntityEnclosingRequestBaseBuilder<B extends HttpEntityEnclosingRequestBaseBuilder<B,R>, R extends HttpEntityEnclosingRequestBase> extends HttpRequestBuilder<B, R> {
-        protected HttpEntityEnclosingRequestBaseBuilder(R req) {
-            super(req);
-        }
-        public B body(byte[] body) {
-            if (body != null) {
-                HttpEntity httpEntity = new ByteArrayEntity(body);
-                req.setEntity(httpEntity);
-            }
-            return self();
-        }
-    }
-    
-    public static class HttpGetBuilder extends HttpRequestBuilder<HttpGetBuilder, HttpGet> {
-        public HttpGetBuilder(URI uri) {
-            super(new HttpGet(uri));
-        }
-    }
-    
-    public static class HttpHeadBuilder extends HttpRequestBuilder<HttpHeadBuilder, HttpHead> {
-        public HttpHeadBuilder(URI uri) {
-            super(new HttpHead(uri));
-        }
-    }
-    
-    public static class HttpDeleteBuilder extends HttpRequestBuilder<HttpDeleteBuilder, HttpDelete> {
-        public HttpDeleteBuilder(URI uri) {
-            super(new HttpDelete(uri));
-        }
-    }
-    
-    public static class HttpPostBuilder extends HttpEntityEnclosingRequestBaseBuilder<HttpPostBuilder, HttpPost> {
-        HttpPostBuilder(URI uri) {
-            super(new HttpPost(uri));
-        }
-    }
-
-    public static class HttpFormPostBuilder extends HttpRequestBuilder<HttpFormPostBuilder, HttpPost> {
-        HttpFormPostBuilder(URI uri) {
-            super(new HttpPost(uri));
-        }
-
-        public HttpFormPostBuilder params(Map<String, String> params) {
-            if (params != null) {
-                Collection<NameValuePair> httpParams = new ArrayList<NameValuePair>(params.size());
-                for (Entry<String, String> param : params.entrySet()) {
-                    httpParams.add(new BasicNameValuePair(param.getKey(), param.getValue()));
-                }
-                req.setEntity(new UrlEncodedFormEntity(httpParams));
-            }
-            return self();
-        }
-    }
-
-    public static class HttpPutBuilder extends HttpEntityEnclosingRequestBaseBuilder<HttpPutBuilder, HttpPut> {
-        public HttpPutBuilder(URI uri) {
-            super(new HttpPut(uri));
-        }
-    }
-    
-    public static HttpToolResponse httpGet(HttpClient httpClient, URI uri, Map<String,String> headers) {
-        HttpGet req = new HttpGetBuilder(uri).headers(headers).build();
-        return execAndConsume(httpClient, req);
-    }
-
-    public static HttpToolResponse httpPost(HttpClient httpClient, URI uri, Map<String,String> headers, byte[] body) {
-        HttpPost req = new HttpPostBuilder(uri).headers(headers).body(body).build();
-        return execAndConsume(httpClient, req);
-    }
-
-    public static HttpToolResponse httpPut(HttpClient httpClient, URI uri, Map<String, String> headers, byte[] body) {
-        HttpPut req = new HttpPutBuilder(uri).headers(headers).body(body).build();
-        return execAndConsume(httpClient, req);
-    }
-
-    public static HttpToolResponse httpPost(HttpClient httpClient, URI uri, Map<String,String> headers, Map<String, String> params) {
-        HttpPost req = new HttpFormPostBuilder(uri).headers(headers).params(params).build();
-        return execAndConsume(httpClient, req);
-    }
-
-    public static HttpToolResponse httpDelete(HttpClient httpClient, URI uri, Map<String,String> headers) {
-        HttpDelete req = new HttpDeleteBuilder(uri).headers(headers).build();
-        return execAndConsume(httpClient, req);
-    }
-    
-    public static HttpToolResponse httpHead(HttpClient httpClient, URI uri, Map<String,String> headers) {
-        HttpHead req = new HttpHeadBuilder(uri).headers(headers).build();
-        return execAndConsume(httpClient, req);
-    }
-    
-    public static HttpToolResponse execAndConsume(HttpClient httpClient, HttpUriRequest req) {
-        long startTime = System.currentTimeMillis();
-        try {
-            HttpResponse httpResponse = httpClient.execute(req);
-            
-            try {
-                return new HttpToolResponse(httpResponse, startTime);
-            } finally {
-                EntityUtils.consume(httpResponse.getEntity());
-            }
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-    
-    public static boolean isStatusCodeHealthy(int code) { return (code>=200 && code<=299); }
-
-    public static String toBasicAuthorizationValue(UsernamePasswordCredentials credentials) {
-        return "Basic "+Base64.encodeBase64String( (credentials.getUserName()+":"+credentials.getPassword()).getBytes() );
-    }
-
-    public static String encodeUrlParams(Map<?,?> data) {
-        if (data==null) return "";
-        Iterable<String> args = Iterables.transform(data.entrySet(), 
-            new Function<Map.Entry<?,?>,String>() {
-            @Override public String apply(Map.Entry<?,?> entry) {
-                Object k = entry.getKey();
-                Object v = entry.getValue();
-                return URLParamEncoder.encode(Strings.toString(k)) + (v != null ? "=" + URLParamEncoder.encode(Strings.toString(v)) : "");
-            }
-        });
-        return Joiner.on("&").join(args);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/http/HttpToolResponse.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/http/HttpToolResponse.java b/core/src/main/java/brooklyn/util/http/HttpToolResponse.java
deleted file mode 100644
index 1837a87..0000000
--- a/core/src/main/java/brooklyn/util/http/HttpToolResponse.java
+++ /dev/null
@@ -1,185 +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.http;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.event.feed.http.HttpPollValue;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.google.common.io.ByteStreams;
-
-public class HttpToolResponse implements HttpPollValue {
-
-    private static final Logger log = LoggerFactory.getLogger(HttpToolResponse.class);
-    
-    private final Object mutex = new Object();
-    private final HttpResponse response;
-    private final long startTime;
-    private final long durationMillisOfFirstResponse;
-    private final long durationMillisOfFullContent;
-    private int responseCode;
-    private String reasonPhrase;
-    private Map<String,List<String>> headerLists;
-    private byte[] content;
-
-
-    public HttpToolResponse(HttpResponse response, long startTime) {
-        this.response = response;
-        this.startTime = startTime; 
-        
-        try {
-            ByteArrayOutputStream out = new ByteArrayOutputStream();
-            HttpEntity entity = response.getEntity();
-            if (entity != null) {
-                entity.getContentLength();
-                durationMillisOfFirstResponse = Duration.sinceUtc(startTime).toMilliseconds();
-
-                ByteStreams.copy(entity.getContent(), out);
-                content = out.toByteArray();
-
-                entity.getContentLength();
-            } else {
-                durationMillisOfFirstResponse = Duration.sinceUtc(startTime).toMilliseconds();
-                content = new byte[0];
-            }
-            durationMillisOfFullContent = Duration.sinceUtc(startTime).toMilliseconds();
-            if (log.isTraceEnabled())
-                log.trace("HttpPollValue latency "+Time.makeTimeStringRounded(durationMillisOfFirstResponse)+" / "+Time.makeTimeStringRounded(durationMillisOfFullContent)+", content size "+content.length);
-        } catch (IOException e) {
-            throw Throwables.propagate(e);
-        }
-    }
-
-    public HttpToolResponse(int responseCode, Map<String,? extends List<String>> headers, byte[] content,
-            long startTime, long durationMillisOfFirstResponse, long durationMillisOfFullContent) {
-        this.response = null;
-        this.responseCode = responseCode;
-        this.headerLists = ImmutableMap.copyOf(headers);
-        this.content = content;
-        this.startTime = startTime;
-        this.durationMillisOfFirstResponse = durationMillisOfFirstResponse;
-        this.durationMillisOfFullContent = durationMillisOfFullContent;
-    }
-    
-    public int getResponseCode() {
-        synchronized (mutex) {
-            if (responseCode == 0) {
-                responseCode = response.getStatusLine().getStatusCode();
-            }
-        }
-        return responseCode;
-    }
-
-    public String getReasonPhrase() {
-        synchronized (mutex) {
-            if (reasonPhrase == null) {
-                reasonPhrase = response.getStatusLine().getReasonPhrase();
-            }
-        }
-        return reasonPhrase;
-    }
-
-    /** returns the timestamp (millis since 1970) when this request was started */ 
-    public long getStartTime() {
-        return startTime;
-    }
-    
-    /** returns latency, in milliseconds, if value was initialized with a start time */
-    public long getLatencyFullContent() {
-        return durationMillisOfFullContent;
-    }
-    
-    /** returns latency, in milliseconds, before response started coming in */
-    public long getLatencyFirstResponse() {
-        return durationMillisOfFirstResponse;
-    }
-    
-    public Map<String, List<String>> getHeaderLists() {
-        synchronized (mutex) {
-            if (headerLists == null) {
-                Map<String, List<String>> headerListsMutable = Maps.newLinkedHashMap();
-                for (Header header : response.getAllHeaders()) {
-                    List<String> vals = headerListsMutable.get(header.getName());
-                    if (vals == null) {
-                        vals = new ArrayList<String>();
-                        headerListsMutable.put(header.getName(), vals);
-                    }
-                    vals.add(header.getValue());
-                }
-                headerLists = Collections.unmodifiableMap(headerListsMutable);
-            }
-        }
-        return headerLists;
-    }
-    
-    public byte[] getContent() {
-        synchronized (mutex) {
-            if (content == null) {
-                InputStream in = null;
-                try {
-                    in = response.getEntity().getContent();
-                    ByteArrayOutputStream out = new ByteArrayOutputStream();
-                    ByteStreams.copy(in, out);
-                    content = out.toByteArray();
-                } catch (IOException e) {
-                    throw Throwables.propagate(e);
-                } finally {
-                    Streams.closeQuietly(in);
-                }
-            }
-        }
-        return content;
-    }
-
-    public String getContentAsString() {
-        return new String(getContent());
-    }
-    
-    public Maybe<HttpResponse> getResponse() {
-        return Maybe.fromNullable(response);
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(getClass())
-                .add("responseCode", responseCode)
-                .toString();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/ConfigKeySelfExtracting.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ConfigKeySelfExtracting.java b/core/src/main/java/brooklyn/util/internal/ConfigKeySelfExtracting.java
deleted file mode 100644
index 5048b0e..0000000
--- a/core/src/main/java/brooklyn/util/internal/ConfigKeySelfExtracting.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.internal;
-
-import java.util.Map;
-
-import org.apache.brooklyn.api.management.ExecutionContext;
-
-import brooklyn.config.ConfigKey;
-
-/** Interface for resolving key values; typically implemented by the config key,
- * but discouraged for external usage.
- */
-public interface ConfigKeySelfExtracting<T> extends ConfigKey<T> {
-
-    /**
-     * Extracts the value for this config key from the given map.
-     */
-    T extractValue(Map<?,?> configMap, ExecutionContext exec);
-
-    /**
-     * @return True if there is an entry in the configMap that could be extracted
-     */
-    boolean isSet(Map<?,?> configMap);
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/Repeater.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/Repeater.java b/core/src/main/java/brooklyn/util/internal/Repeater.java
deleted file mode 100644
index ef149ba..0000000
--- a/core/src/main/java/brooklyn/util/internal/Repeater.java
+++ /dev/null
@@ -1,369 +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.internal;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.JavaGroovyEquivalents;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.Callables;
-
-/**
- * Simple DSL to repeat a fragment of code periodically until a condition is satisfied.
- *
- * In its simplest case, it is passed two {@link groovy.lang.Closure}s / {@link Callable} - 
- * the first is executed, then the second. If the second closure returns false, the loop
- * is repeated; if true, it finishes. Further customization can be applied to set the period 
- * between loops and place a maximum limit on how long the loop should run for.
- * <p>
- * It is configured in a <em>fluent</em> manner. For example, in Groovy:
- * <pre>
- * {@code
- * Repeater.create("Wait until the Frobnitzer is ready")
- *     .repeat {
- *         status = frobnitzer.getStatus()
- *     }
- *     .until {
- *         status == "Ready" || status == "Failed"
- *     }
- *     .limitIterationsTo(30)
- *     .run()
- * }
- * </pre>
- * 
- * Or in Java:
- * <pre>
- * {@code
- * Repeater.create("Wait until the Frobnitzer is ready")
- *     .until(new Callable<Boolean>() {
- *              public Boolean call() {
- *                  String status = frobnitzer.getStatus()
- *                  return "Ready".equals(status) || "Failed".equals(status);
- *              }})
- *     .limitIterationsTo(30)
- *     .run()
- * }
- * </pre>
- * 
- * @deprecated since 0.7.0, use {@link brooklyn.util.repeat.Repeater} instead
- */
-@Deprecated
-public class Repeater {
-    
-    // TODO Was converted to Java, from groovy. Needs thorough review and improvements
-    // to use idiomatic java
-    
-    private static final Logger log = LoggerFactory.getLogger(Repeater.class);
-
-    static { TimeExtras.init(); }
-
-    @SetFromFlag
-    private String description;
-    private Callable<?> body = Callables.returning(null);
-    private Callable<Boolean> exitCondition;
-    @SetFromFlag
-    private Long period = null;
-    @SetFromFlag("timeout")
-    private Long durationLimit = null;
-    private int iterationLimit = 0;
-    private boolean rethrowException = false;
-    private boolean rethrowExceptionImmediately = false;
-    private boolean warnOnUnRethrownException = true;
-
-    public Repeater() {
-        this(MutableMap.of(), null);
-    }
-
-    public Repeater(Map<?,?> flags) {
-        this(flags, null);
-    }
-
-    public Repeater(String description) {
-        this(MutableMap.of(), description);
-    }
-    
-    /**
-     * Construct a new instance of Repeater.
-     *
-     * @param flags       can include period, timeout, description
-     * @param description a description of the operation that will appear in debug logs.
-     */
-    public Repeater(Map<?,?> flags, String description) {
-        setFromFlags(flags);
-        this.description = JavaGroovyEquivalents.elvis(description, this.description, "Repeater");
-    }
-
-    public void setFromFlags(Map<?,?> flags) {
-        FlagUtils.setFieldsFromFlags(flags, this);
-    }
-    
-    public static Repeater create() {
-        return create(MutableMap.of());
-    }
-    public static Repeater create(Map<?,?> flags) {
-        return create(flags, null);
-    }
-    public static Repeater create(String description) {
-        return create(MutableMap.of(), description);
-    }
-    public static Repeater create(Map<?,?> flags, String description) {
-        return new Repeater(flags, description);
-    }
-
-    /**
-     * Sets the main body of the loop to be a no-op.
-     * 
-     * @return {@literal this} to aid coding in a fluent style.
-     */
-    public Repeater repeat() {
-        return repeat(Callables.returning(null));
-    }
-    
-    /**
-     * Sets the main body of the loop.
-     *
-     * @param body a closure or other Runnable that is executed in the main body of the loop.
-     * @return {@literal this} to aid coding in a fluent style.
-     */
-    public Repeater repeat(Runnable body) {
-        checkNotNull(body, "body must not be null");
-        this.body = (body instanceof Callable) ? (Callable<?>)body : Executors.callable(body);
-        return this;
-    }
-    
-    /**
-     * Sets the main body of the loop.
-     *
-     * @param body a closure or other Callable that is executed in the main body of the loop.
-     * @return {@literal this} to aid coding in a fluent style.
-     */
-    public Repeater repeat(Callable<?> body) {
-        checkNotNull(body, "body must not be null");
-        this.body = body;
-        return this;
-    }
-
-    /**
-     * Set how long to wait between loop iterations.
-     *
-     * @param period how long to wait between loop iterations.
-     * @param unit the unit of measurement of the period.
-     * @return {@literal this} to aid coding in a fluent style.
-     */
-    public Repeater every(long period, TimeUnit unit) {
-        Preconditions.checkArgument(period > 0, "period must be positive: %s", period);
-        checkNotNull(unit, "unit must not be null");
-        this.period = unit.toMillis(period);
-        return this;
-    }
-
-    /**
-     * @see #every(long, TimeUnit)
-     */
-    public Repeater every(Duration duration) {
-        Preconditions.checkNotNull(duration, "duration must not be null");
-        Preconditions.checkArgument(duration.toMilliseconds()>0, "period must be positive: %s", duration);
-        this.period = duration.toMilliseconds();
-        return this;
-    }
-    
-    public Repeater every(groovy.time.Duration duration) {
-        return every(Duration.of(duration));
-    }
-
-    /**
-     * @see #every(long, TimeUnit)
-     * @deprecated specify unit
-     */
-    public Repeater every(long duration) {
-        return every(duration, TimeUnit.MILLISECONDS);
-    }
-
-    /**
-     * Set code fragment that tests if the loop has completed.
-     *
-     * @param exitCondition a closure or other Callable that returns a boolean. If this code returns {@literal true} then the
-     * loop will stop executing.
-     * @return {@literal this} to aid coding in a fluent style.
-     */
-    public Repeater until(Callable<Boolean> exitCondition) {
-        Preconditions.checkNotNull(exitCondition, "exitCondition must not be null");
-        this.exitCondition = exitCondition;
-        return this;
-    }
-
-    /**
-     * If the exit condition check throws an exception, it will be recorded and the last exception will be thrown on failure.
-     *
-     * @return {@literal this} to aid coding in a fluent style.
-     */
-    public Repeater rethrowException() {
-        this.rethrowException = true;
-        return this;
-    }
-
-    /**
-     * If the repeated body or the exit condition check throws an exception, then propagate that exception immediately.
-     *
-     * @return {@literal this} to aid coding in a fluent style.
-     */
-    public Repeater rethrowExceptionImmediately() {
-        this.rethrowExceptionImmediately = true;
-        return this;
-    }
-
-    public Repeater suppressWarnings() {
-        this.warnOnUnRethrownException = false;
-        return this;
-    }
-
-    /**
-     * Set the maximum number of iterations.
-     *
-     * The loop will exit if the condition has not been satisfied after this number of iterations.
-     *
-     * @param iterationLimit the maximum number of iterations.
-     * @return {@literal this} to aid coding in a fluent style.
-     */
-    public Repeater limitIterationsTo(int iterationLimit) {
-        Preconditions.checkArgument(iterationLimit > 0, "iterationLimit must be positive: %s", iterationLimit);
-        this.iterationLimit = iterationLimit;
-        return this;
-    }
-
-    /**
-     * Set the amount of time to wait for the condition.
-     * The repeater will wait at least this long for the condition to be true,
-     * and will exit soon after even if the condition is false.
-     *
-     * @param deadline the time that the loop should wait.
-     * @param unit the unit of measurement of the period.
-     * @return {@literal this} to aid coding in a fluent style.
-     */
-    public Repeater limitTimeTo(long deadline, TimeUnit unit) {
-        Preconditions.checkArgument(deadline > 0, "deadline must be positive: %s", deadline);
-        Preconditions.checkNotNull(unit, "unit must not be null");
-        this.durationLimit = unit.toMillis(deadline);
-        return this;
-    }
-
-    /**
-     * @see #limitTimeTo(long, TimeUnit)
-     */
-    public Repeater limitTimeTo(Duration duration) {
-        Preconditions.checkNotNull(duration, "duration must not be null");
-        Preconditions.checkArgument(duration.toMilliseconds() > 0, "deadline must be positive: %s", duration);
-        this.durationLimit = duration.toMilliseconds();
-        return this;
-    }
-
-    /**
-     * Run the loop.
-     *
-     * @return true if the exit condition was satisfied; false if the loop terminated for any other reason.
-     */
-    public boolean run() {
-        Preconditions.checkState(body != null, "repeat() method has not been called to set the body");
-        Preconditions.checkState(exitCondition != null, "until() method has not been called to set the exit condition");
-        Preconditions.checkState(period != null, "every() method has not been called to set the loop period time units");
-
-        Throwable lastError = null;
-        int iterations = 0;
-        long endTime = -1;
-        if (durationLimit != null) {
-            endTime = System.currentTimeMillis() + durationLimit;
-        }
-
-        while (true) {
-            iterations++;
-
-            try {
-                body.call();
-            } catch (Exception e) {
-                log.warn(description, e);
-                if (rethrowExceptionImmediately) throw Exceptions.propagate(e);
-            }
-
-            boolean done = false;
-            try {
-                lastError = null;
-                done = exitCondition.call();
-            } catch (Exception e) {
-                if (log.isDebugEnabled()) log.debug(description, e);
-                lastError = e;
-                if (rethrowExceptionImmediately) throw Exceptions.propagate(e);
-            }
-            if (done) {
-                if (log.isDebugEnabled()) log.debug("{}: condition satisfied", description);
-                return true;
-            } else {
-                if (log.isDebugEnabled()) {
-                    String msg = String.format("%s: unsatisfied during iteration %s %s", description, iterations,
-                            (iterationLimit > 0 ? "(max "+iterationLimit+" attempts)" : "") + 
-                            (endTime > 0 ? "("+Time.makeTimeStringRounded(endTime - System.currentTimeMillis())+" remaining)" : ""));
-                    if (iterations == 1) {
-                        log.debug(msg);
-                    } else {
-                        log.trace(msg);
-                    }
-                }
-            }
-
-            if (iterationLimit > 0 && iterations == iterationLimit) {
-                if (log.isDebugEnabled()) log.debug("{}: condition not satisfied and exceeded iteration limit", description);
-                if (rethrowException && lastError != null) {
-                    log.warn("{}: error caught checking condition (rethrowing): {}", description, lastError.getMessage());
-                    throw Exceptions.propagate(lastError);
-                }
-                if (warnOnUnRethrownException && lastError != null)
-                    log.warn("{}: error caught checking condition: {}", description, lastError.getMessage());
-                return false;
-            }
-
-            if (endTime > 0) {
-                if (System.currentTimeMillis() > endTime) {
-                    if (log.isDebugEnabled()) log.debug("{}: condition not satisfied and deadline {} passed", 
-                            description, Time.makeTimeStringRounded(endTime - System.currentTimeMillis()));
-                    if (rethrowException && lastError != null) {
-                        log.error("{}: error caught checking condition: {}", description, lastError.getMessage());
-                        throw Exceptions.propagate(lastError);
-                    }
-                    return false;
-                }
-            }
-
-            Time.sleep(period);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/ssh/BackoffLimitedRetryHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/BackoffLimitedRetryHandler.java b/core/src/main/java/brooklyn/util/internal/ssh/BackoffLimitedRetryHandler.java
deleted file mode 100644
index 1e2b435..0000000
--- a/core/src/main/java/brooklyn/util/internal/ssh/BackoffLimitedRetryHandler.java
+++ /dev/null
@@ -1,74 +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.internal.ssh;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.exceptions.Exceptions;
-
-/**
- * Allow replayable request to be retried a limited number of times, and impose an exponential back-off
- * delay before returning.
- * <p>
- * Copied and modified from jclouds; original author was James Murty
- */
-public class BackoffLimitedRetryHandler {
-
-    private static final Logger LOG = LoggerFactory.getLogger(BackoffLimitedRetryHandler.class);
-
-    private final int retryCountLimit;
-
-    private final long delayStart;
-
-    public BackoffLimitedRetryHandler() {
-        this(5, 50L);
-    }
-    
-    public BackoffLimitedRetryHandler(int retryCountLimit, long delayStart) {
-        this.retryCountLimit = retryCountLimit;
-        this.delayStart = delayStart;
-    }
-    
-    public void imposeBackoffExponentialDelay(int failureCount, String commandDescription) {
-        imposeBackoffExponentialDelay(delayStart, 2, failureCount, retryCountLimit, commandDescription);
-    }
-
-    public void imposeBackoffExponentialDelay(long period, int pow, int failureCount, int max, String commandDescription) {
-        imposeBackoffExponentialDelay(period, period * 10l, pow, failureCount, max, commandDescription);
-    }
-
-    public void imposeBackoffExponentialDelay(long period,
-            long maxPeriod,
-            int pow,
-            int failureCount,
-            int max,
-            String commandDescription) {
-        long delayMs = (long) (period * Math.pow(failureCount, pow));
-        delayMs = (delayMs > maxPeriod) ? maxPeriod : delayMs;
-        if (LOG.isDebugEnabled()) LOG.debug("Retry {}/{}: delaying for {} ms: {}", 
-                new Object[] {failureCount, max, delayMs, commandDescription});
-        try {
-            Thread.sleep(delayMs);
-        } catch (InterruptedException e) {
-            Exceptions.propagate(e);
-        }
-    }
-
-}


[13/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/file/ArchiveBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/file/ArchiveBuilderTest.java b/core/src/test/java/org/apache/brooklyn/core/util/file/ArchiveBuilderTest.java
new file mode 100644
index 0000000..0ce8298
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/file/ArchiveBuilderTest.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.file;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.core.util.file.ArchiveBuilder;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import brooklyn.util.collections.MutableSet;
+import brooklyn.util.os.Os;
+import brooklyn.util.text.Identifiers;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+
+/**
+ * Test the operation of the {@link ArchiveBuilder} class.
+ */
+@Test
+public class ArchiveBuilderTest {
+
+    private File parentDir, tmpDir, tmpDir2;
+    private Predicate<ZipEntry> isDirectory = new Predicate<ZipEntry>() {
+                @Override
+                public boolean apply(@Nullable ZipEntry input) {
+                    return input.isDirectory();
+                }
+            };
+
+    @BeforeClass
+    public void createTmpDirAndFiles() throws IOException {
+        parentDir = Os.newTempDir(getClass().getSimpleName());
+        Os.deleteOnExitRecursively(parentDir);
+        tmpDir = new File(parentDir, Identifiers.makeRandomId(4));
+        Os.mkdirs(tmpDir);
+        Files.write("abcdef", new File(tmpDir, "data01.txt"), Charsets.US_ASCII);
+        Files.write("123456", new File(tmpDir, "data02.txt"), Charsets.US_ASCII);
+        Files.write("qqqqqq", new File(tmpDir, "data03.txt"), Charsets.US_ASCII);
+        
+        tmpDir2 = new File(parentDir, Identifiers.makeRandomId(4));
+        Os.mkdirs(tmpDir2);
+        Files.write("zzzzzz", new File(tmpDir2, "data04.txt"), Charsets.US_ASCII);
+    }
+    
+    @Test
+    public void testCreateZipFromDir() throws Exception {
+        File archive = ArchiveBuilder.zip().addDirContentsAt(tmpDir, ".").create();
+        archive.deleteOnExit();
+
+        List<ZipEntry> entries = Lists.newArrayList();
+        ZipInputStream input = new ZipInputStream(new FileInputStream(archive));
+        ZipEntry entry = input.getNextEntry();
+        while (entry != null) {
+            entries.add(entry);
+            entry = input.getNextEntry();
+        }
+        assertEquals(entries.size(), 4);
+        Iterable<ZipEntry> directories = Iterables.filter(entries, isDirectory);
+        Iterable<ZipEntry> files = Iterables.filter(entries, Predicates.not(isDirectory));
+        assertEquals(Iterables.size(directories), 1);
+        assertEquals(Iterables.size(files), 3);
+        String dirName = Iterables.getOnlyElement(directories).getName();
+        assertEquals(dirName, "./");
+        
+        Set<String> names = MutableSet.of();
+        for (ZipEntry file : files) {
+            assertTrue(file.getName().startsWith(dirName));
+            names.add(file.getName());
+        }
+        assertTrue(names.contains("./data01.txt"));
+        assertFalse(names.contains("./data04.txt"));
+        input.close();
+    }
+
+    @Test
+    public void testCreateZipFromTwoDirs() throws Exception {
+        File archive = ArchiveBuilder.zip().addDirContentsAt(tmpDir, ".").addDirContentsAt(tmpDir2, ".").create();
+        archive.deleteOnExit();
+
+        List<ZipEntry> entries = Lists.newArrayList();
+        ZipInputStream input = new ZipInputStream(new FileInputStream(archive));
+        ZipEntry entry = input.getNextEntry();
+        while (entry != null) {
+            entries.add(entry);
+            entry = input.getNextEntry();
+        }
+        assertEquals(entries.size(), 5);
+        Iterable<ZipEntry> directories = Iterables.filter(entries, isDirectory);
+        Iterable<ZipEntry> files = Iterables.filter(entries, Predicates.not(isDirectory));
+        assertEquals(Iterables.size(directories), 1);
+        assertEquals(Iterables.size(files), 4);
+        String dirName = Iterables.getOnlyElement(directories).getName();
+        assertEquals(dirName, "./");
+        
+        Set<String> names = MutableSet.of();
+        for (ZipEntry file : files) {
+            assertTrue(file.getName().startsWith(dirName));
+            names.add(file.getName());
+        }
+        assertTrue(names.contains("./data01.txt"));
+        assertTrue(names.contains("./data04.txt"));
+        input.close();
+    }
+    @Test
+    public void testCreateZipFromFiles() throws Exception {
+        ArchiveBuilder builder = ArchiveBuilder.zip();
+        for (String fileName : Arrays.asList("data01.txt", "data02.txt", "data03.txt")) {
+            builder.addAt(new File(tmpDir, fileName), ".");
+        }
+        File archive = builder.create();
+        archive.deleteOnExit();
+
+        List<ZipEntry> entries = Lists.newArrayList();
+        ZipInputStream input = new ZipInputStream(new FileInputStream(archive));
+        ZipEntry entry = input.getNextEntry();
+        while (entry != null) {
+            entries.add(entry);
+            entry = input.getNextEntry();
+        }
+        assertEquals(entries.size(), 3);
+        Iterable<ZipEntry> directories = Iterables.filter(entries, isDirectory);
+        Iterable<ZipEntry> files = Iterables.filter(entries, Predicates.not(isDirectory));
+        assertTrue(Iterables.isEmpty(directories));
+        assertEquals(Iterables.size(files), 3);
+        for (ZipEntry file : files) {
+            assertTrue(file.getName().startsWith(Os.mergePathsUnix(".", "data")));
+        }
+        input.close();
+    }
+
+    @Test
+    public void testCreateZipFromFilesWithBaseDir() throws Exception {
+        ArchiveBuilder builder = ArchiveBuilder.zip();
+        String baseDir = tmpDir.getName();
+        for (String fileName : Arrays.asList("data01.txt", "data02.txt", "data03.txt")) {
+            builder.addFromLocalBaseDir(parentDir, Os.mergePaths(baseDir, fileName));
+        }
+        File archive = builder.create();
+        archive.deleteOnExit();
+
+        List<ZipEntry> entries = Lists.newArrayList();
+        ZipInputStream input = new ZipInputStream(new FileInputStream(archive));
+        ZipEntry entry = input.getNextEntry();
+        while (entry != null) {
+            entries.add(entry);
+            entry = input.getNextEntry();
+        }
+        assertEquals(entries.size(), 3);
+        Iterable<ZipEntry> directories = Iterables.filter(entries, isDirectory);
+        Iterable<ZipEntry> files = Iterables.filter(entries, Predicates.not(isDirectory));
+        assertTrue(Iterables.isEmpty(directories));
+        assertEquals(Iterables.size(files), 3);
+        for (ZipEntry file : files) {
+            assertTrue(file.getName().startsWith(Os.mergePathsUnix(".", baseDir)));
+        }
+        input.close();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/file/ArchiveUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/file/ArchiveUtilsTest.java b/core/src/test/java/org/apache/brooklyn/core/util/file/ArchiveUtilsTest.java
new file mode 100644
index 0000000..a13ef63
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/file/ArchiveUtilsTest.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.file;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.io.File;
+import java.util.Map;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.BrooklynAppUnitTestSupport;
+
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.file.ArchiveBuilder;
+import org.apache.brooklyn.core.util.file.ArchiveUtils;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.os.Os;
+
+import com.google.api.client.repackaged.com.google.common.base.Joiner;
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+
+// Test are integration, because relies on ssh/scp via SshMachineLocation
+public class ArchiveUtilsTest extends BrooklynAppUnitTestSupport {
+    
+    private SshMachineLocation machine;
+    private ResourceUtils resourceUtils;
+
+    private Map<String, String> archiveContents = ImmutableMap.of("a.txt", "mya");
+    private File destDir;
+    private File origZip;
+    private File origJar;
+
+    @BeforeClass(alwaysRun=true)
+    public void setUpClass() throws Exception {
+        origZip = newZip(archiveContents);
+        origJar = Os.newTempFile(ArchiveUtilsTest.class, ".jar");
+        Files.copy(origZip, origJar);
+    }
+    
+    @AfterClass(alwaysRun=true)
+    public void tearDownClass() throws Exception {
+        if (origZip != null) origZip.delete();
+        if (origJar != null) origJar.delete();
+    }
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        machine = app.newLocalhostProvisioningLocation().obtain();
+        resourceUtils = ResourceUtils.create(ArchiveUtilsTest.class);
+        destDir = Os.newTempDir(getClass().getSimpleName());
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (destDir != null) Os.deleteRecursively(destDir);
+    }
+    
+    @Test(groups="Integration")
+    public void testDeployZipWithNoOptionalArgsSupplied() throws Exception {
+        boolean result = ArchiveUtils.deploy(resourceUtils, ImmutableMap.<String, Object>of(), origZip.getAbsolutePath(), machine, destDir.getAbsolutePath(), true, null, null);
+        assertTrue(result);
+        assertFilesEqual(new File(destDir, origZip.getName()), origZip);
+        assertSubFilesEqual(destDir, archiveContents);
+    }
+    
+    @Test(groups="Integration")
+    public void testDeployZipDeletingArchiveAfterUnpack() throws Exception {
+        boolean result = ArchiveUtils.deploy(resourceUtils, ImmutableMap.<String, Object>of(), origZip.getAbsolutePath(), machine, destDir.getAbsolutePath(), false, null, null);
+        assertTrue(result);
+        assertFalse(new File(destDir, origZip.getName()).exists());
+        assertSubFilesEqual(destDir, archiveContents);
+    }
+    
+    @Test(groups="Integration")
+    public void testDeployJarNotUnpacked() throws Exception {
+        ArchiveUtils.deploy(origJar.getAbsolutePath(), machine, destDir.getAbsolutePath());
+        assertFilesEqual(new File(destDir, origJar.getName()), origJar);
+    }
+    
+    @Test(groups="Integration")
+    public void testDeployExplicitDestFile() throws Exception {
+        String destFile = "custom-destFile.jar";
+        ArchiveUtils.deploy(origJar.getAbsolutePath(), machine, destDir.getAbsolutePath(), destFile);
+        assertFilesEqual(new File(destDir, destFile), origJar);
+    }
+    
+    private File newZip(Map<String, String> files) throws Exception {
+        File parentDir = Os.newTempDir(getClass().getSimpleName()+"-archive");
+        for (Map.Entry<String, String> entry : files.entrySet()) {
+            File subFile = new File(Os.mergePaths(parentDir.getAbsolutePath(), entry.getKey()));
+            subFile.getParentFile().mkdirs();
+            Files.write(entry.getValue(), subFile, Charsets.UTF_8);
+        }
+        return ArchiveBuilder.zip().addDirContentsAt(parentDir, ".").create();
+    }
+    
+    private void assertFilesEqual(File f1, File f2) throws Exception {
+        byte[] bytes1 = Files.asByteSource(f1).read();
+        byte[] bytes2 = Files.asByteSource(f1).read();
+        assertEquals(bytes1, bytes2, "f1="+f1+"; f2="+f2);
+    }
+    
+    private void assertSubFilesEqual(File parentDir, Map<String, String> files) throws Exception {
+        for (Map.Entry<String, String> entry : archiveContents.entrySet()) {
+            File subFile = new File(Os.mergePaths(parentDir.getAbsolutePath(), entry.getKey()));
+            assertEquals(Joiner.on("\n").join(Files.readLines(subFile, Charsets.UTF_8)), entry.getValue());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/flags/MethodCoercionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/flags/MethodCoercionsTest.java b/core/src/test/java/org/apache/brooklyn/core/util/flags/MethodCoercionsTest.java
new file mode 100644
index 0000000..2d1efa3
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/flags/MethodCoercionsTest.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.flags;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+
+import org.apache.brooklyn.core.util.flags.MethodCoercions;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import static org.testng.Assert.*;
+
+public class MethodCoercionsTest {
+
+    private Method singleParameterMethod;
+    private Method multiParameterMethod;
+    private Method singleCollectionParameterMethod;
+
+    @BeforeClass
+    public void testFixtureSetUp() {
+        try {
+            singleParameterMethod = TestClass.class.getMethod("singleParameterMethod", int.class);
+            multiParameterMethod = TestClass.class.getMethod("multiParameterMethod", boolean.class, int.class);
+            singleCollectionParameterMethod = TestClass.class.getMethod("singleCollectionParameterMethod", List.class);
+        } catch (NoSuchMethodException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    @Test
+    public void testMatchSingleParameterMethod() throws Exception {
+        Predicate<Method> predicate = MethodCoercions.matchSingleParameterMethod("singleParameterMethod", "42");
+        assertTrue(predicate.apply(singleParameterMethod));
+        assertFalse(predicate.apply(multiParameterMethod));
+        assertFalse(predicate.apply(singleCollectionParameterMethod));
+    }
+
+    @Test
+    public void testTryFindAndInvokeSingleParameterMethod() throws Exception {
+        TestClass instance = new TestClass();
+        Maybe<?> maybe = MethodCoercions.tryFindAndInvokeSingleParameterMethod(instance, "singleParameterMethod", "42");
+        assertTrue(maybe.isPresent());
+        assertTrue(instance.wasSingleParameterMethodCalled());
+    }
+
+    @Test
+    public void testMatchMultiParameterMethod() throws Exception {
+        Predicate<Method> predicate = MethodCoercions.matchMultiParameterMethod("multiParameterMethod", ImmutableList.of("true", "42"));
+        assertFalse(predicate.apply(singleParameterMethod));
+        assertTrue(predicate.apply(multiParameterMethod));
+        assertFalse(predicate.apply(singleCollectionParameterMethod));
+    }
+
+    @Test
+    public void testTryFindAndInvokeMultiParameterMethod() throws Exception {
+        TestClass instance = new TestClass();
+        Maybe<?> maybe = MethodCoercions.tryFindAndInvokeMultiParameterMethod(instance, "multiParameterMethod", ImmutableList.of("true", "42"));
+        assertTrue(maybe.isPresent());
+        assertTrue(instance.wasMultiParameterMethodCalled());
+    }
+
+    @Test
+    public void testTryFindAndInvokeBestMatchingMethod() throws Exception {
+        TestClass instance = new TestClass();
+        Maybe<?> maybe = MethodCoercions.tryFindAndInvokeBestMatchingMethod(instance, "singleParameterMethod", "42");
+        assertTrue(maybe.isPresent());
+        assertTrue(instance.wasSingleParameterMethodCalled());
+
+        instance = new TestClass();
+        maybe = MethodCoercions.tryFindAndInvokeBestMatchingMethod(instance, "multiParameterMethod", ImmutableList.of("true", "42"));
+        assertTrue(maybe.isPresent());
+        assertTrue(instance.wasMultiParameterMethodCalled());
+
+        instance = new TestClass();
+        maybe = MethodCoercions.tryFindAndInvokeBestMatchingMethod(instance, "singleCollectionParameterMethod", ImmutableList.of("fred", "joe"));
+        assertTrue(maybe.isPresent());
+        assertTrue(instance.wasSingleCollectionParameterMethodCalled());
+    }
+/*
+    @Test
+    public void testMatchSingleCollectionParameterMethod() throws Exception {
+        Predicate<Method> predicate = MethodCoercions.matchSingleCollectionParameterMethod("singleCollectionParameterMethod", ImmutableList.of("42"));
+        assertFalse(predicate.apply(singleParameterMethod));
+        assertFalse(predicate.apply(multiParameterMethod));
+        assertTrue(predicate.apply(singleCollectionParameterMethod));
+    }
+
+    @Test
+    public void testTryFindAndInvokeSingleCollectionParameterMethod() throws Exception {
+        TestClass instance = new TestClass();
+        Maybe<?> maybe = MethodCoercions.tryFindAndInvokeSingleCollectionParameterMethod(instance, "singleCollectionParameterMethod", ImmutableList.of("42"));
+        assertTrue(maybe.isPresent());
+        assertTrue(instance.wasSingleCollectionParameterMethodCalled());
+    }
+*/
+    public static class TestClass {
+
+        private boolean singleParameterMethodCalled;
+        private boolean multiParameterMethodCalled;
+        private boolean singleCollectionParameterMethodCalled;
+
+        public void singleParameterMethod(int parameter) {
+            singleParameterMethodCalled = true;
+        }
+
+        public void multiParameterMethod(boolean parameter1, int parameter2) {
+            multiParameterMethodCalled = true;
+        }
+
+        public void singleCollectionParameterMethod(List<String> parameter) {
+            singleCollectionParameterMethodCalled = true;
+        }
+
+        public boolean wasSingleParameterMethodCalled() {
+            return singleParameterMethodCalled;
+        }
+
+        public boolean wasMultiParameterMethodCalled() {
+            return multiParameterMethodCalled;
+        }
+
+        public boolean wasSingleCollectionParameterMethodCalled() {
+            return singleCollectionParameterMethodCalled;
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/http/BetterMockWebServer.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/http/BetterMockWebServer.java b/core/src/test/java/org/apache/brooklyn/core/util/http/BetterMockWebServer.java
new file mode 100644
index 0000000..f02689b
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/http/BetterMockWebServer.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.http;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.URL;
+
+import javax.net.ssl.SSLSocketFactory;
+
+import com.google.common.base.Throwables;
+import com.google.mockwebserver.Dispatcher;
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.RecordedRequest;
+
+/** like MockWebServer (and delegating) but:
+ * <li> allows subclassing
+ * <li> easy way to create instance which returns localhost for {@link #getHostName()}
+ *      (since otherwise you can get failures on networks which misconfigure hostname) 
+ * */
+public class BetterMockWebServer {
+
+    final MockWebServer delegate = new MockWebServer();
+    String hostname = null;
+    boolean isHttps = false;
+    
+    public static BetterMockWebServer newInstanceLocalhost() {
+        return new BetterMockWebServer().setHostName("localhost");
+    }
+    
+    /** use {@link #newInstanceLocalhost()} or subclass */
+    protected BetterMockWebServer() {}
+
+    public BetterMockWebServer setHostName(String hostname) {
+        this.hostname = hostname;
+        return this;
+    }
+
+
+    // --- delegate methods (unchanged)
+    
+    public void enqueue(MockResponse response) {
+        delegate.enqueue(response);
+    }
+
+    public boolean equals(Object obj) {
+        return delegate.equals(obj);
+    }
+
+    public String getCookieDomain() {
+        return delegate.getCookieDomain();
+    }
+
+    public String getHostName() {
+        if (hostname!=null) return hostname;
+        return delegate.getHostName();
+    }
+
+    public int getPort() {
+        return delegate.getPort();
+    }
+
+    public int getRequestCount() {
+        return delegate.getRequestCount();
+    }
+
+    public URL getUrl(String path) {
+        try {
+            return isHttps
+                ? new URL("https://" + getHostName() + ":" + getPort() + path)
+                : new URL("http://" + getHostName() + ":" + getPort() + path);
+        } catch (MalformedURLException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+    public void play() throws IOException {
+        delegate.play();
+    }
+
+    public void play(int port) throws IOException {
+        delegate.play(port);
+    }
+
+    public void setBodyLimit(int maxBodyLength) {
+        delegate.setBodyLimit(maxBodyLength);
+    }
+
+    public void setDispatcher(Dispatcher dispatcher) {
+        delegate.setDispatcher(dispatcher);
+    }
+
+    public void shutdown() throws IOException {
+        delegate.shutdown();
+    }
+
+    public RecordedRequest takeRequest() throws InterruptedException {
+        return delegate.takeRequest();
+    }
+
+    public Proxy toProxyAddress() {
+        return delegate.toProxyAddress();
+    }
+
+    public String toString() {
+        return delegate.toString();
+    }
+
+    public void useHttps(SSLSocketFactory sslSocketFactory, boolean tunnelProxy) {
+        isHttps = true;
+        delegate.useHttps(sslSocketFactory, tunnelProxy);
+    }
+    
+    
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/http/HttpToolIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/http/HttpToolIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/util/http/HttpToolIntegrationTest.java
new file mode 100644
index 0000000..f557955
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/http/HttpToolIntegrationTest.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.http;
+
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+
+import org.apache.http.client.HttpClient;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
+import org.apache.brooklyn.location.basic.PortRanges;
+
+import brooklyn.test.HttpService;
+
+import com.google.common.collect.ImmutableMap;
+
+public class HttpToolIntegrationTest {
+
+    // TODO Expand test coverage for credentials etc
+    
+    private HttpService httpService;
+    private HttpService httpsService;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        httpService = new HttpService(PortRanges.fromString("9000+"), false).start();
+        httpsService = new HttpService(PortRanges.fromString("9000+"), true).start();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (httpService != null) httpService.shutdown();
+        if (httpsService != null) httpsService.shutdown();
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testHttpGet() throws Exception {
+        URI baseUri = new URI(httpService.getUrl());
+
+        HttpClient client = HttpTool.httpClientBuilder().build();
+        HttpToolResponse result = HttpTool.httpGet(client, baseUri, ImmutableMap.<String,String>of());
+        assertTrue(new String(result.getContent()).contains("Hello, World"), "val="+new String(result.getContent()));
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testHttpRedirect() throws Exception {
+        URI baseUri = new URI(httpService.getUrl() + "hello/redirectAbsolute");
+
+        HttpClient client = HttpTool.httpClientBuilder().laxRedirect(true).build();
+        HttpToolResponse result = HttpTool.httpGet(client, baseUri, ImmutableMap.<String,String>of());
+        assertTrue(new String(result.getContent()).contains("Hello, World"), "val="+new String(result.getContent()));
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testHttpPost() throws Exception {
+        URI baseUri = new URI(httpService.getUrl());
+
+        HttpClient client = HttpTool.httpClientBuilder().build();
+        HttpToolResponse result = HttpTool.httpPost(client, baseUri, ImmutableMap.<String,String>of(), new byte[0]);
+        assertTrue(new String(result.getContent()).contains("Hello, World"), "val="+new String(result.getContent()));
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testHttpsGetWithTrustAll() throws Exception {
+        URI baseUri = new URI(httpsService.getUrl());
+
+        HttpClient client = HttpTool.httpClientBuilder().https(true).trustAll().build();
+        HttpToolResponse result = HttpTool.httpGet(client, baseUri, ImmutableMap.<String,String>of());
+        assertTrue(new String(result.getContent()).contains("Hello, World"), "val="+new String(result.getContent()));
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testHttpsPostWithTrustSelfSigned() throws Exception {
+        URI baseUri = new URI(httpsService.getUrl());
+
+        HttpClient client = HttpTool.httpClientBuilder().https(true).trustSelfSigned().build();
+        HttpToolResponse result = HttpTool.httpPost(client, baseUri, ImmutableMap.<String,String>of(), new byte[0]);
+        assertTrue(new String(result.getContent()).contains("Hello, World"), "val="+new String(result.getContent()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/FlagUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/FlagUtilsTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/FlagUtilsTest.java
new file mode 100644
index 0000000..02becf6
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/FlagUtilsTest.java
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.trait.Configurable;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.config.ConfigKey.HasConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.event.basic.BasicConfigKey;
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class FlagUtilsTest {
+
+    public static final Logger log = LoggerFactory.getLogger(FlagUtilsTest.class);
+    
+    @Test
+    public void testGetAllFields() {
+        log.info("types {}", FlagUtils.getAllAssignableTypes(Baz.class));
+        assertEquals(FlagUtils.getAllAssignableTypes(Baz.class), ImmutableList.of(Baz.class, Foo.class, Bar.class));
+        List<Field> fs = FlagUtils.getAllFields(Baz.class);
+        for (Field f : fs) {
+            log.info("field {}    {}", f.getName(), f);
+        }
+        List<String> fsn = ImmutableList.copyOf(Iterables.transform(fs, new Function<Field, String>() {
+            @Override public String apply(Field f) {
+                return f.getName();
+            }}));
+        assertTrue(fsn.indexOf("A") >= 0);
+        assertTrue(fsn.indexOf("w") > fsn.indexOf("A")); 
+        assertTrue(fsn.indexOf("x") > fsn.indexOf("A") );
+        assertTrue(fsn.indexOf("yNotY") > fsn.indexOf("A")); 
+        assertTrue(fsn.indexOf("Z") > fsn.indexOf("yNotY") );
+    }    
+    
+    @Test
+    public void testSetFieldsFromFlags() {
+        Foo f = new Foo();
+        Map<?,?> m = MutableMap.of("w", 3, "x", 1, "y", 7, "z", 9);
+        Map<?, ?> unused = FlagUtils.setFieldsFromFlags(m, f);
+        assertEquals(f.w, 3);
+        assertEquals(f.x, 1);
+        assertEquals(f.yNotY, 7);
+        assertEquals(unused, ImmutableMap.of("z", 9));
+        Map<?,?> m2 = FlagUtils.getFieldsWithValues(f);
+        m.remove("z");
+        assertEquals(m2, m);
+    }
+    
+    @Test
+    public void testCollectionCoercionOnSetFromFlags() {
+        WithSpecialFieldTypes s = new WithSpecialFieldTypes();
+        Map<?,?> m = ImmutableMap.of("set", ImmutableSet.of(1));
+        FlagUtils.setFieldsFromFlags(m, s);
+        assertEquals(s.set, ImmutableSet.of(1));
+    }
+
+    @Test
+    public void testInetAddressCoercionOnSetFromFlags() {
+        WithSpecialFieldTypes s = new WithSpecialFieldTypes();
+        Map<?,?> m = ImmutableMap.of("inet", "127.0.0.1");
+        FlagUtils.setFieldsFromFlags(m, s);
+        assertEquals(s.inet.getHostAddress(), "127.0.0.1");
+    }
+
+    @Test
+    public void testNonImmutableField() {
+        Foo f = new Foo();
+        FlagUtils.setFieldsFromFlags(ImmutableMap.of("w", 8), f);
+        assertEquals(f.w, 8);
+        FlagUtils.setFieldsFromFlags(ImmutableMap.of("w", 9), f);
+        assertEquals(f.w, 9);
+    }
+
+    @Test
+    public void testImmutableIntField() {
+        Foo f = new Foo();
+        FlagUtils.setFieldsFromFlags(ImmutableMap.of("x", 8), f);
+        assertEquals(f.x, 8);
+        boolean succeededWhenShouldntHave = false; 
+        try {
+            FlagUtils.setFieldsFromFlags(ImmutableMap.of("x", 9), f);
+            succeededWhenShouldntHave = true;
+        } catch (IllegalStateException e) {
+            //expected
+        }
+        assertFalse(succeededWhenShouldntHave);
+        assertEquals(f.x, 8);
+    }
+
+    @Test
+    public void testImmutableObjectField() {
+        WithImmutableNonNullableObject o = new WithImmutableNonNullableObject();
+        FlagUtils.setFieldsFromFlags(ImmutableMap.of("a", "a", "b", "b"), o);
+        assertEquals(o.a, "a");
+        assertEquals(o.b, "b");
+        
+        FlagUtils.setFieldsFromFlags(ImmutableMap.of("a", "a2"), o);
+        assertEquals(o.a, "a2");
+        
+        boolean succeededWhenShouldntHave = false;
+        try {
+            FlagUtils.setFieldsFromFlags(ImmutableMap.of("b", "b2"), o);
+            succeededWhenShouldntHave = true;
+        } catch (IllegalStateException e) {
+            //expected
+        }
+        assertFalse(succeededWhenShouldntHave);
+        assertEquals(o.b, "b");
+    }
+
+    @Test
+    public void testNonNullable() {
+        WithImmutableNonNullableObject o = new WithImmutableNonNullableObject();
+        //allowed
+        FlagUtils.setFieldsFromFlags(MutableMap.of("a", null), o);
+        assertEquals(o.a, null);
+        assertEquals(o.b, null);
+        //not allowed
+        boolean succeededWhenShouldntHave = false;
+        try {
+            FlagUtils.setFieldsFromFlags(MutableMap.of("b", null), o);
+            succeededWhenShouldntHave = true;
+        } catch (IllegalArgumentException e) {
+            //expected
+        }
+        assertFalse(succeededWhenShouldntHave);
+        assertEquals(o.b, null);
+    }
+    
+    @Test
+    public void testGetAnnotatedFields() throws Exception {
+        Map<Field, SetFromFlag> fm = FlagUtils.getAnnotatedFields(WithImmutableNonNullableObject.class);
+        assertEquals(fm.keySet().size(), 2);
+        assertTrue(fm.get(WithImmutableNonNullableObject.class.getDeclaredField("b")).immutable());
+    }
+
+    @Test
+    public void testCheckRequired() {
+        WithImmutableNonNullableObject f = new WithImmutableNonNullableObject();
+        FlagUtils.setFieldsFromFlags(ImmutableMap.of("a", "a is a"), f);
+        assertEquals(f.a, "a is a");
+        assertEquals(f.b, null);
+        int exceptions = 0;
+        try {
+            FlagUtils.checkRequiredFields(f);
+        } catch (IllegalStateException e) {
+            exceptions++;
+        }
+        assertEquals(exceptions, 1);
+    }
+
+    @Test
+    public void testSetConfigKeys() {
+        FooCK f = new FooCK();
+        Map<?,?> unused = FlagUtils.setFieldsFromFlags(ImmutableMap.of("f1", 9, "ck1", "do-set", "ck2", "dont-set", "c3", "do-set"), f);
+        assertEquals(f.bag.get(FooCK.CK1), "do-set");
+        assertEquals(f.bag.get(FooCK.CK3), "do-set");
+        assertEquals(f.f1, 9);
+        assertEquals(f.bag.containsKey(FooCK.CK2), false);
+        assertEquals(unused, ImmutableMap.of("ck2", "dont-set"));
+    }
+    
+    @Test
+    public void testSetAllConfigKeys() {
+        FooCK f = new FooCK();
+        Map<?,?> unused = FlagUtils.setAllConfigKeys(ImmutableMap.of("f1", 9, "ck1", "do-set", "ck2", "do-set-2", "c3", "do-set"), f, true);
+        assertEquals(f.bag.get(FooCK.CK1), "do-set");
+        assertEquals(f.bag.get(FooCK.CK3), "do-set");
+        assertEquals(f.bag.containsKey(FooCK.CK2), true);
+        assertEquals(f.bag.get(FooCK.CK2), "do-set-2");
+        assertEquals(unused, ImmutableMap.of("f1", 9));
+    }
+
+    @Test
+    public void testSetFromConfigKeys() {
+        FooCK f = new FooCK();
+        Map<?, ?> unused = FlagUtils.setFieldsFromFlags(ImmutableMap.of(new BasicConfigKey<Integer>(Integer.class, "f1"), 9, "ck1", "do-set", "ck2", "dont-set"), f);
+        assertEquals(f.bag.get(FooCK.CK1), "do-set");
+        assertEquals(f.f1, 9);
+        assertEquals(f.bag.containsKey(FooCK.CK2), false);
+        assertEquals(unused, ImmutableMap.of("ck2", "dont-set"));
+    }
+
+    public static class Foo {
+        @SetFromFlag
+        int w;
+        
+        @SetFromFlag(immutable=true)
+        private int x;
+        
+        @SetFromFlag("y")
+        public int yNotY;
+    }
+    
+    public static interface Bar {
+        static final String Z = "myzval";
+    }
+    
+    public static class Baz extends Foo implements Bar {
+        @SuppressWarnings("unused")  //inspected by reflection
+        private static int A;
+    }
+    
+    public static class WithImmutableNonNullableObject {
+        @SetFromFlag
+        Object a;
+        @SetFromFlag(immutable=true, nullable=false)
+        public Object b;
+    }
+    
+    public static class WithSpecialFieldTypes {
+        @SetFromFlag Set<?> set;
+        @SetFromFlag InetAddress inet;
+    }
+    
+    public static class FooCK implements Configurable {
+        @SetFromFlag
+        public static ConfigKey<String> CK1 = ConfigKeys.newStringConfigKey("ck1");
+        
+        public static ConfigKey<String> CK2 = ConfigKeys.newStringConfigKey("ck2");
+
+        @SetFromFlag("c3")
+        public static ConfigKey<String> CK3 = ConfigKeys.newStringConfigKey("ck3");
+
+        @SetFromFlag
+        int f1;
+        
+        ConfigBag bag = new ConfigBag();
+        BasicConfigurationSupport configSupport = new BasicConfigurationSupport();
+        
+        @Override
+        public ConfigurationSupport config() {
+            return configSupport;
+        }
+        
+        public <T> T setConfig(ConfigKey<T> key, T val) {
+            return config().set(key, val);
+        }
+        
+        private class BasicConfigurationSupport implements ConfigurationSupport {
+            @Override
+            public <T> T get(ConfigKey<T> key) {
+                return bag.get(key);
+            }
+
+            @Override
+            public <T> T get(HasConfigKey<T> key) {
+                return get(key.getConfigKey());
+            }
+
+            @Override
+            public <T> T set(ConfigKey<T> key, T val) {
+                T old = bag.get(key);
+                bag.configure(key, val);
+                return old;
+            }
+
+            @Override
+            public <T> T set(HasConfigKey<T> key, T val) {
+                return set(key.getConfigKey(), val);
+            }
+
+            @Override
+            public <T> T set(ConfigKey<T> key, Task<T> val) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public <T> T set(HasConfigKey<T> key, Task<T> val) {
+                return set(key.getConfigKey(), val);
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/RepeaterTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/RepeaterTest.groovy b/core/src/test/java/org/apache/brooklyn/core/util/internal/RepeaterTest.groovy
new file mode 100644
index 0000000..08ef0da
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/RepeaterTest.groovy
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal
+
+import static java.util.concurrent.TimeUnit.*
+import static org.testng.Assert.*
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit
+
+import org.apache.brooklyn.core.util.internal.Repeater;
+import org.testng.annotations.Test
+
+import brooklyn.util.internal.TimeExtras;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Stopwatch
+
+public class RepeaterTest {
+    static { TimeExtras.init() }
+
+    @Test
+    public void sanityTest() {
+        new Repeater("Sanity test")
+            .repeat()
+            .until { true }
+            .every(10 * MILLISECONDS);
+    }
+
+    @Test
+    public void sanityTestDescription() {
+        new Repeater()
+            .repeat()
+            .until { true }
+            .every(10 * MILLISECONDS);
+    }
+
+    @Test
+    public void sanityTestBuilder() {
+        Repeater.create("Sanity test")
+            .repeat()
+            .until { true }
+            .every(10 * MILLISECONDS);
+    }
+
+    @Test
+    public void sanityTestBuilderDescription() {
+        Repeater.create()
+            .repeat()
+            .until { true }
+            .every(10 * MILLISECONDS);
+    }
+
+    @Test(expectedExceptions = [ NullPointerException.class ])
+    public void repeatFailsIfClosureIsNull() {
+        new Repeater("repeatFailsIfClosureIsNull").repeat((Callable<?>)null);
+        fail "Expected exception was not thrown"
+    }
+
+    @Test
+    public void repeatSucceedsIfClosureIsNonNull() {
+        new Repeater("repeatSucceedsIfClosureIsNonNull").repeat { true };
+    }
+
+    @Test(expectedExceptions = [ NullPointerException.class ])
+    public void untilFailsIfClosureIsNull() {
+        new Repeater("untilFailsIfClosureIsNull").until(null);
+        fail "Expected exception was not thrown"
+    }
+
+    @Test
+    public void untilSucceedsIfClosureIsNonNull() {
+        new Repeater("untilSucceedsIfClosureIsNonNull").until { true };
+    }
+
+    @Test(expectedExceptions = [ IllegalArgumentException.class ])
+    public void everyFailsIfPeriodIsZero() {
+        new Repeater("everyFailsIfPeriodIsZero").every(0 * MILLISECONDS);
+        fail "Expected exception was not thrown"
+    }
+
+    @Test(expectedExceptions = [ IllegalArgumentException.class ])
+    public void everyFailsIfPeriodIsNegative() {
+        new Repeater("everyFailsIfPeriodIsNegative").every(-1 * MILLISECONDS);
+        fail "Expected exception was not thrown"
+    }
+
+    @Test(expectedExceptions = [ NullPointerException.class ])
+    public void everyFailsIfUnitsIsNull() {
+        new Repeater("everyFailsIfUnitsIsNull").every(10, null);
+        fail "Expected exception was not thrown"
+    }
+
+    @Test
+    public void everySucceedsIfPeriodIsPositiveAndUnitsIsNonNull() {
+        new Repeater("repeatSucceedsIfClosureIsNonNull").every(10 * MILLISECONDS);
+    }
+
+    @Test(expectedExceptions = [ IllegalArgumentException.class ])
+    public void limitTimeToFailsIfPeriodIsZero() {
+        new Repeater("limitTimeToFailsIfPeriodIsZero").limitTimeTo(0, TimeUnit.MILLISECONDS);
+        fail "Expected exception was not thrown"
+    }
+
+    @Test(expectedExceptions = [ IllegalArgumentException.class ])
+    public void limitTimeToFailsIfPeriodIsNegative() {
+        new Repeater("limitTimeToFailsIfPeriodIsNegative").limitTimeTo(-1, TimeUnit.MILLISECONDS);
+        fail "Expected exception was not thrown"
+    }
+
+    @Test(expectedExceptions = [ NullPointerException.class ])
+    public void limitTimeToFailsIfUnitsIsNull() {
+        new Repeater("limitTimeToFailsIfUnitsIsNull").limitTimeTo(10, null);
+        fail "Expected exception was not thrown"
+    }
+
+    @Test
+    public void limitTimeToSucceedsIfPeriodIsPositiveAndUnitsIsNonNull() {
+        new Repeater("limitTimeToSucceedsIfClosureIsNonNull").limitTimeTo(10, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    public void everyAcceptsDuration() {
+        new Repeater("everyAcceptsDuration").every(Duration.ONE_SECOND);
+    }
+
+    @Test
+    public void everyAcceptsLong() {
+        new Repeater("everyAcceptsLong").every(1000L);
+    }
+
+    @Test
+    public void everyAcceptsTimeUnit() {
+        new Repeater("everyAcceptsTimeUnit").every(1000000L, TimeUnit.MICROSECONDS);
+    }
+
+    @Test
+    public void runReturnsTrueIfExitConditionIsTrue() {
+        assertTrue new Repeater("runReturnsTrueIfExitConditionIsTrue")
+            .repeat()
+            .every(1 * MILLISECONDS)
+            .until { true }
+            .run();
+    }
+
+    @Test
+    public void runRespectsMaximumIterationLimitAndReturnsFalseIfReached() {
+        int iterations = 0;
+        assertFalse new Repeater("runRespectsMaximumIterationLimitAndReturnsFalseIfReached")
+            .repeat { iterations++ }
+            .every(1 * MILLISECONDS)
+            .until { false }
+            .limitIterationsTo(5)
+            .run();
+        assertEquals 5, iterations;
+    }
+
+    /**
+     * Check that the {@link Repeater} will stop after a time limit.
+     *
+     * The repeater is configured to run every 100ms and never stop until the limit is reached.
+     * This is given as {@link Repeater#limitTimeTo(groovy.time.Duration)} and the execution time
+     * is then checked to ensure it is between 100% and 400% of the specified value. Due to scheduling
+     * delays and other factors in a non RTOS system it is expected that the repeater will take much
+     * longer to exit occasionally.
+     *
+     * @see #runRespectsMaximumIterationLimitAndReturnsFalseIfReached()
+     */
+    @Test(groups="Integration")
+    public void runRespectsTimeLimitAndReturnsFalseIfReached() {
+        final long LIMIT = 2000l;
+        Repeater repeater = new Repeater("runRespectsTimeLimitAndReturnsFalseIfReached")
+            .repeat()
+            .every(100 * MILLISECONDS)
+            .until { false }
+            .limitTimeTo(LIMIT, TimeUnit.MILLISECONDS);
+
+        Stopwatch stopwatch = new Stopwatch().start();
+        boolean result = repeater.run();
+        stopwatch.stop();
+
+        assertFalse result;
+
+        long difference = stopwatch.elapsed(TimeUnit.MILLISECONDS);
+        assertTrue(difference >= LIMIT, "Difference was: " + difference);
+        assertTrue(difference < 4 * LIMIT, "Difference was: " + difference);
+    }
+
+    @Test(expectedExceptions = [ IllegalStateException.class ])
+    public void runFailsIfUntilWasNotSet() {
+        new Repeater("runFailsIfUntilWasNotSet")
+            .repeat()
+            .every(10 * MILLISECONDS)
+            .run();
+        fail "Expected exception was not thrown"
+    }
+
+    @Test(expectedExceptions = [ IllegalStateException.class ])
+    public void runFailsIfEveryWasNotSet() {
+        new Repeater("runFailsIfEveryWasNotSet")
+            .repeat()
+            .until { true }
+            .run();
+        fail "Expected exception was not thrown"
+    }
+
+    @Test(expectedExceptions = [ UnsupportedOperationException.class ])
+    public void testRethrowsException() {
+        boolean result = new Repeater("throwRuntimeException")
+            .repeat()
+            .every(10 * MILLISECONDS)
+            .until { throw new UnsupportedOperationException("fail") }
+            .rethrowException()
+            .limitIterationsTo(2)
+            .run();
+        fail "Expected exception was not thrown"
+    }
+
+    @Test
+    public void testNoRethrowsException() {
+        try {
+	        boolean result = new Repeater("throwRuntimeException")
+	            .repeat()
+	            .every(10 * MILLISECONDS)
+	            .until { throw new UnsupportedOperationException("fail") }
+	            .limitIterationsTo(2)
+	            .run();
+	        assertFalse result
+        } catch (RuntimeException re) {
+            fail "Exception should not have been thrown: " + re.getMessage()
+        }
+    }
+	
+	public void testFlags() {
+		int count=0;
+		new Repeater(period: 5*MILLISECONDS, timeout: 100*MILLISECONDS).repeat({ count++ }).until({ count>100}).run();
+		assertTrue count>10
+		assertTrue count<30
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/TypeCoercionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/TypeCoercionsTest.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/TypeCoercionsTest.java
new file mode 100644
index 0000000..f1bb3f9
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/TypeCoercionsTest.java
@@ -0,0 +1,360 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal;
+
+import static org.testng.Assert.assertEquals;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.core.util.flags.ClassCoercionException;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.codehaus.groovy.runtime.GStringImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.util.collections.MutableSet;
+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;
+
+public class TypeCoercionsTest {
+
+    private static final Logger log = LoggerFactory.getLogger(TypeCoercionsTest.class);
+    
+    @Test
+    public void testCoerceCharSequenceToString() {
+        assertEquals(TypeCoercions.coerce(new StringBuilder("abc"), String.class), "abc");
+        assertEquals(TypeCoercions.coerce(new GStringImpl(new Object[0], new String[0]), String.class), "");
+    }
+    
+    @Test
+    public void testCoerceStringToPrimitive() {
+        assertEquals(TypeCoercions.coerce("1", Character.class), (Character)'1');
+        assertEquals(TypeCoercions.coerce(" ", Character.class), (Character)' ');
+        assertEquals(TypeCoercions.coerce("1", Short.class), (Short)((short)1));
+        assertEquals(TypeCoercions.coerce("1", Integer.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce("1", Long.class), (Long)1l);
+        assertEquals(TypeCoercions.coerce("1", Float.class), (Float)1f);
+        assertEquals(TypeCoercions.coerce("1", Double.class), (Double)1d);
+        assertEquals(TypeCoercions.coerce("true", Boolean.class), (Boolean)true);
+        assertEquals(TypeCoercions.coerce("False", Boolean.class), (Boolean)false);
+        assertEquals(TypeCoercions.coerce("true ", Boolean.class), (Boolean)true);
+
+        assertEquals(TypeCoercions.coerce("1", char.class), (Character)'1');
+        assertEquals(TypeCoercions.coerce("1", short.class), (Short)((short)1));
+        assertEquals(TypeCoercions.coerce("1", int.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce("1", long.class), (Long)1l);
+        assertEquals(TypeCoercions.coerce("1", float.class), (Float)1f);
+        assertEquals(TypeCoercions.coerce("1", double.class), (Double)1d);
+        assertEquals(TypeCoercions.coerce("TRUE", boolean.class), (Boolean)true);
+        assertEquals(TypeCoercions.coerce("false", boolean.class), (Boolean)false);
+    }
+
+    @Test
+    public void testCoercePrimitivesToSameType() {
+        assertEquals(TypeCoercions.coerce('1', Character.class), (Character)'1');
+        assertEquals(TypeCoercions.coerce((short)1, Short.class), (Short)((short)1));
+        assertEquals(TypeCoercions.coerce(1, Integer.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce(1l, Long.class), (Long)1l);
+        assertEquals(TypeCoercions.coerce(1f, Float.class), (Float)1f);
+        assertEquals(TypeCoercions.coerce(1d, Double.class), (Double)1d);
+        assertEquals(TypeCoercions.coerce(true, Boolean.class), (Boolean)true);
+    }
+    
+    @Test
+    public void testCastPrimitives() {
+        assertEquals(TypeCoercions.coerce(1L, Character.class), (Character)(char)1);
+        assertEquals(TypeCoercions.coerce(1L, Byte.class), (Byte)(byte)1);
+        assertEquals(TypeCoercions.coerce(1L, Short.class), (Short)(short)1);
+        assertEquals(TypeCoercions.coerce(1L, Integer.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce(1L, Long.class), (Long)(long)1);
+        assertEquals(TypeCoercions.coerce(1L, Float.class), (Float)(float)1);
+        assertEquals(TypeCoercions.coerce(1L, Double.class), (Double)(double)1);
+        
+        assertEquals(TypeCoercions.coerce(1L, char.class), (Character)(char)1);
+        assertEquals(TypeCoercions.coerce(1L, byte.class), (Byte)(byte)1);
+        assertEquals(TypeCoercions.coerce(1L, short.class), (Short)(short)1);
+        assertEquals(TypeCoercions.coerce(1L, int.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce(1L, long.class), (Long)(long)1);
+        assertEquals(TypeCoercions.coerce(1L, float.class), (Float)(float)1);
+        assertEquals(TypeCoercions.coerce(1L, double.class), (Double)(double)1);
+        
+        assertEquals(TypeCoercions.coerce((char)1, Integer.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce((byte)1, Integer.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce((short)1, Integer.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce((int)1, Integer.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce((long)1, Integer.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce((float)1, Integer.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce((double)1, Integer.class), (Integer)1);
+    }
+    
+    @Test
+    public void testCoercePrimitiveFailures() {
+        // error messages don't have to be this exactly, but they should include sufficient information...
+        assertCoercionFailsWithErrorMatching("maybe", boolean.class, StringPredicates.containsAllLiterals("String", "boolean", "maybe"));
+        assertCoercionFailsWithErrorMatching("NaN", int.class, StringPredicates.containsAllLiterals("int", "NaN"));
+        assertCoercionFailsWithErrorMatching('c', boolean.class, StringPredicates.containsAllLiterals("boolean", "(c)"));  // will say 'string' rather than 'char'
+        assertCoercionFailsWithErrorMatching(0, boolean.class, StringPredicates.containsAllLiterals("Integer", "boolean", "0"));
+    }
+    
+    protected void assertCoercionFailsWithErrorMatching(Object input, Class<?> type, Predicate<? super String> errorMessageRequirement) {
+        try {
+            Object result = TypeCoercions.coerce(input, type);
+            Assert.fail("Should have failed type coercion of "+input+" to "+type+", instead got: "+result);
+        } catch (Exception e) {
+            if (errorMessageRequirement==null || errorMessageRequirement.apply(e.toString()))
+                log.info("Primitive coercion failed as expected, with: "+e);
+            else
+                Assert.fail("Error from type coercion of "+input+" to "+type+" failed with wrong exception; expected match of "+errorMessageRequirement+" but got: "+e);
+        }
+        
+    }
+
+    @Test
+    public void testCastToNumericPrimitives() {
+        assertEquals(TypeCoercions.coerce(BigInteger.ONE, Integer.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce(BigInteger.ONE, int.class), (Integer)1);
+        assertEquals(TypeCoercions.coerce(BigInteger.valueOf(Long.MAX_VALUE), Long.class), (Long)Long.MAX_VALUE);
+        assertEquals(TypeCoercions.coerce(BigInteger.valueOf(Long.MAX_VALUE), long.class), (Long)Long.MAX_VALUE);
+        
+        assertEquals(TypeCoercions.coerce(BigDecimal.valueOf(0.5), Double.class), 0.5d, 0.00001d);
+        assertEquals(TypeCoercions.coerce(BigDecimal.valueOf(0.5), double.class), 0.5d, 0.00001d);
+    }
+
+    @Test
+    public void testCoerceStringToBigNumber() {
+    	assertEquals(TypeCoercions.coerce("0.5", BigDecimal.class), BigDecimal.valueOf(0.5));
+    	assertEquals(TypeCoercions.coerce("1", BigInteger.class), BigInteger.valueOf(1));
+    }
+
+    @Test
+    public void testCoerceStringToEnum() {
+        assertEquals(TypeCoercions.coerce("STARTING", Lifecycle.class), Lifecycle.STARTING);
+        assertEquals(TypeCoercions.coerce("Starting", Lifecycle.class), Lifecycle.STARTING);
+        assertEquals(TypeCoercions.coerce("starting", Lifecycle.class), Lifecycle.STARTING);
+        
+        assertEquals(TypeCoercions.coerce("LOWERCASE", PerverseEnum.class), PerverseEnum.lowercase);
+        assertEquals(TypeCoercions.coerce("CAMELCASE", PerverseEnum.class), PerverseEnum.camelCase);
+        assertEquals(TypeCoercions.coerce("upper", PerverseEnum.class), PerverseEnum.UPPER);
+        assertEquals(TypeCoercions.coerce("upper_with_underscore", PerverseEnum.class), PerverseEnum.UPPER_WITH_UNDERSCORE);
+        assertEquals(TypeCoercions.coerce("LOWER_WITH_UNDERSCORE", PerverseEnum.class), PerverseEnum.lower_with_underscore);
+    }
+    public static enum PerverseEnum {
+        lowercase,
+        camelCase,
+        UPPER,
+        UPPER_WITH_UNDERSCORE,
+        lower_with_underscore;
+    }
+    
+    @Test(expectedExceptions = ClassCoercionException.class)
+    public void testCoerceStringToEnumFailure() {
+        TypeCoercions.coerce("scrambled-eggs", Lifecycle.class);
+    }
+
+    @Test
+    public void testListToSetCoercion() {
+        Set<?> s = TypeCoercions.coerce(ImmutableList.of(1), Set.class);
+        Assert.assertEquals(s, ImmutableSet.of(1));
+    }
+    
+    @Test
+    public void testSetToListCoercion() {
+        List<?> s = TypeCoercions.coerce(ImmutableSet.of(1), List.class);
+        Assert.assertEquals(s, ImmutableList.of(1));
+    }
+    
+    @Test
+    public void testIterableToArrayCoercion() {
+        String[] s = TypeCoercions.coerce(ImmutableList.of("a", "b"), String[].class);
+        Assert.assertTrue(Arrays.equals(s, new String[] {"a", "b"}), "result="+Arrays.toString(s));
+        
+        Integer[] i = TypeCoercions.coerce(ImmutableList.of(1, 2), Integer[].class);
+        Assert.assertTrue(Arrays.equals(i, new Integer[] {1, 2}), "result="+Arrays.toString(i));
+        
+        int[] i2 = TypeCoercions.coerce(ImmutableList.of(1, 2), int[].class);
+        Assert.assertTrue(Arrays.equals(i2, new int[] {1, 2}), "result="+Arrays.toString(i2));
+        
+        int[] i3 = TypeCoercions.coerce(MutableSet.of("1", 2), int[].class);
+        Assert.assertTrue(Arrays.equals(i3, new int[] {1, 2}), "result="+Arrays.toString(i3));
+    }
+
+    @Test
+    public void testListEntryCoercion() {
+        List<?> s = TypeCoercions.coerce(ImmutableList.of("java.lang.Integer", "java.lang.Double"), new TypeToken<List<Class<?>>>() { });
+        Assert.assertEquals(s, ImmutableList.of(Integer.class, Double.class));
+    }
+    
+    @Test
+    public void testListEntryToSetCoercion() {
+        Set<?> s = TypeCoercions.coerce(ImmutableList.of("java.lang.Integer", "java.lang.Double"), new TypeToken<Set<Class<?>>>() { });
+        Assert.assertEquals(s, ImmutableSet.of(Integer.class, Double.class));
+    }
+    
+    @Test
+    public void testListEntryToCollectionCoercion() {
+        Collection<?> s = TypeCoercions.coerce(ImmutableList.of("java.lang.Integer", "java.lang.Double"), new TypeToken<Collection<Class<?>>>() { });
+        Assert.assertEquals(s, ImmutableList.of(Integer.class, Double.class));
+    }
+
+    @Test
+    public void testMapValueCoercion() {
+        Map<?,?> s = TypeCoercions.coerce(ImmutableMap.of("int", "java.lang.Integer", "double", "java.lang.Double"), new TypeToken<Map<String, Class<?>>>() { });
+        Assert.assertEquals(s, ImmutableMap.of("int", Integer.class, "double", Double.class));
+    }
+    
+    @Test
+    public void testMapKeyCoercion() {
+        Map<?,?> s = TypeCoercions.coerce(ImmutableMap.of("java.lang.Integer", "int", "java.lang.Double", "double"), new TypeToken<Map<Class<?>, String>>() { });
+        Assert.assertEquals(s, ImmutableMap.of(Integer.class, "int", Double.class, "double"));
+    }
+
+    @Test
+    public void testStringToListCoercion() {
+        List<?> s = TypeCoercions.coerce("a,b,c", List.class);
+        Assert.assertEquals(s, ImmutableList.of("a", "b", "c"));
+    }
+
+    @Test
+    @SuppressWarnings("serial")
+    public void testCoerceRecursivelyStringToGenericsCollection() {
+        assertEquals(TypeCoercions.coerce("1,2", new TypeToken<List<Integer>>() {}), ImmutableList.of(1, 2));
+    }
+    
+    @Test
+    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 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
+    public void testAs() {
+        Integer x = TypeCoercions.coerce(new WithAs("3"), Integer.class);
+        Assert.assertEquals(x, (Integer)3);
+    }
+
+    @Test
+    public void testFrom() {
+        WithFrom x = TypeCoercions.coerce("3", WithFrom.class);
+        Assert.assertEquals(x.value, 3);
+    }
+
+    @Test
+    public void testCoerceStringToNumber() {
+        assertEquals(TypeCoercions.coerce("1", Number.class), (Number) Double.valueOf(1));
+        assertEquals(TypeCoercions.coerce("1.0", Number.class), (Number) Double.valueOf(1.0));
+    }
+
+    @Test(expectedExceptions = ClassCoercionException.class)
+    public void testInvalidCoercionThrowsClassCoercionException() {
+        TypeCoercions.coerce(new Object(), TypeToken.of(Integer.class));
+    }
+
+    @Test
+    public void testCoercionFunction() {
+        assertEquals(TypeCoercions.function(Double.class).apply("1"), Double.valueOf(1));
+    }
+
+    public static class WithAs {
+        String value;
+        public WithAs(Object x) { value = ""+x; }
+        public Integer asInteger() {
+            return Integer.parseInt(value);
+        }
+    }
+
+    public static class WithFrom {
+        int value;
+        public static WithFrom fromString(String s) {
+            WithFrom result = new WithFrom();
+            result.value = Integer.parseInt(s);
+            return result;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/RecordingSshTool.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/RecordingSshTool.java b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/RecordingSshTool.java
new file mode 100644
index 0000000..fdf3e73
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/internal/ssh/RecordingSshTool.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+package org.apache.brooklyn.core.util.internal.ssh;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+
+/** Mock tool */
+public class RecordingSshTool implements SshTool {
+    
+    public static class ExecCmd {
+        public final Map<String,?> props;
+        public final String summaryForLogging;
+        public final List<String> commands;
+        public final Map<?,?> env;
+        
+        ExecCmd(Map<String,?> props, String summaryForLogging, List<String> commands, Map env) {
+            this.props = props;
+            this.summaryForLogging = summaryForLogging;
+            this.commands = commands;
+            this.env = env;
+        }
+        
+        @Override
+        public String toString() {
+            return "ExecCmd["+summaryForLogging+": "+commands+"; "+props+"; "+env+"]";
+        }
+    }
+    
+    public static List<ExecCmd> execScriptCmds = Lists.newCopyOnWriteArrayList();
+    
+    private boolean connected;
+    
+    public RecordingSshTool(Map<?,?> props) {
+    }
+    @Override public void connect() {
+        connected = true;
+    }
+    @Override public void connect(int maxAttempts) {
+        connected = true;
+    }
+    @Override public void disconnect() {
+        connected = false;
+    }
+    @Override public boolean isConnected() {
+        return connected;
+    }
+    @Override public int execScript(Map<String, ?> props, List<String> commands, Map<String, ?> env) {
+        execScriptCmds.add(new ExecCmd(props, "", commands, env));
+        return 0;
+    }
+    @Override public int execScript(Map<String, ?> props, List<String> commands) {
+        return execScript(props, commands, ImmutableMap.<String,Object>of());
+    }
+    @Override public int execCommands(Map<String, ?> props, List<String> commands, Map<String, ?> env) {
+        execScriptCmds.add(new ExecCmd(props, "", commands, env));
+        return 0;
+    }
+    @Override public int execCommands(Map<String, ?> props, List<String> commands) {
+        return execCommands(props, commands, ImmutableMap.<String,Object>of());
+    }
+    @Override public int copyToServer(Map<String, ?> props, File localFile, String pathAndFileOnRemoteServer) {
+        return 0;
+    }
+    @Override public int copyToServer(Map<String, ?> props, InputStream contents, String pathAndFileOnRemoteServer) {
+        return 0;
+    }
+    @Override public int copyToServer(Map<String, ?> props, byte[] contents, String pathAndFileOnRemoteServer) {
+        return 0;
+    }
+    @Override public int copyFromServer(Map<String, ?> props, String pathAndFileOnRemoteServer, File local) {
+        return 0;
+    }
+}
\ No newline at end of file


[07/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/service/InitdServiceInstaller.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/service/InitdServiceInstaller.java b/software/base/src/main/java/brooklyn/entity/service/InitdServiceInstaller.java
index 6cde4d3..04048af 100644
--- a/software/base/src/main/java/brooklyn/entity/service/InitdServiceInstaller.java
+++ b/software/base/src/main/java/brooklyn/entity/service/InitdServiceInstaller.java
@@ -27,6 +27,12 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.trait.HasShortName;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.policy.Enricher;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ssh.SshPutTaskWrapper;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Attributes;
@@ -38,15 +44,9 @@ import brooklyn.entity.effector.EffectorTasks;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 import org.apache.brooklyn.location.cloud.names.AbstractCloudMachineNamer;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ssh.SshPutTaskWrapper;
-import brooklyn.util.task.ssh.SshTasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-import brooklyn.util.text.TemplateProcessor;
 
 
 public class InitdServiceInstaller implements SystemServiceInstaller {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/service/SystemServiceEnricher.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/service/SystemServiceEnricher.java b/software/base/src/main/java/brooklyn/entity/service/SystemServiceEnricher.java
index 7c4ada5..bbad039 100644
--- a/software/base/src/main/java/brooklyn/entity/service/SystemServiceEnricher.java
+++ b/software/base/src/main/java/brooklyn/entity/service/SystemServiceEnricher.java
@@ -25,6 +25,13 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.management.ExecutionContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.policy.Enricher;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.ssh.SshPutTaskWrapper;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.basic.AbstractEnricher;
@@ -39,13 +46,6 @@ import brooklyn.entity.effector.EffectorTasks;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.util.net.Urls;
-import brooklyn.util.task.BasicExecutionManager;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.ssh.SshPutTaskWrapper;
-import brooklyn.util.task.ssh.SshTasks;
-import brooklyn.util.task.system.ProcessTaskFactory;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 
 import com.google.common.collect.ImmutableSet;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java b/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java
index 2e68f6f..3173aa4 100644
--- a/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java
+++ b/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java
@@ -22,6 +22,9 @@ import java.util.List;
 import java.util.concurrent.Callable;
 
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,15 +35,14 @@ import com.google.common.collect.Lists;
 
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.EntityInternal;
+
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.net.Protocol;
 import brooklyn.util.ssh.BashCommands;
 import brooklyn.util.ssh.IptablesCommands;
 import brooklyn.util.ssh.IptablesCommands.Chain;
 import brooklyn.util.ssh.IptablesCommands.Policy;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ssh.SshTasks;
 import brooklyn.util.text.Strings;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java
index 97add55..34cd86e 100644
--- a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java
+++ b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java
@@ -37,6 +37,10 @@ import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.location.MachineManagementMixins.SuspendsMachines;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -77,15 +81,11 @@ import org.apache.brooklyn.location.basic.SshMachineLocation;
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.net.UserAndHostAndPort;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/software/ProvidesProvisioningFlags.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/ProvidesProvisioningFlags.java b/software/base/src/main/java/brooklyn/entity/software/ProvidesProvisioningFlags.java
index 91c6666..8052ece 100644
--- a/software/base/src/main/java/brooklyn/entity/software/ProvidesProvisioningFlags.java
+++ b/software/base/src/main/java/brooklyn/entity/software/ProvidesProvisioningFlags.java
@@ -19,8 +19,7 @@
 package brooklyn.entity.software;
 
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
-
-import brooklyn.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import com.google.common.annotations.Beta;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/software/SshEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/SshEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/software/SshEffectorTasks.java
index e40170c..f5ca922 100644
--- a/software/base/src/main/java/brooklyn/entity/software/SshEffectorTasks.java
+++ b/software/base/src/main/java/brooklyn/entity/software/SshEffectorTasks.java
@@ -27,6 +27,18 @@ import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ssh.SshFetchTaskFactory;
+import org.apache.brooklyn.core.util.task.ssh.SshFetchTaskWrapper;
+import org.apache.brooklyn.core.util.task.ssh.SshPutTaskFactory;
+import org.apache.brooklyn.core.util.task.ssh.SshPutTaskWrapper;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.ssh.internal.AbstractSshExecTaskFactory;
+import org.apache.brooklyn.core.util.task.ssh.internal.PlainSshExecTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,19 +56,7 @@ import brooklyn.entity.effector.EffectorTasks.EffectorTaskFactory;
 import org.apache.brooklyn.location.basic.LocationInternal;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ssh.SshFetchTaskFactory;
-import brooklyn.util.task.ssh.SshFetchTaskWrapper;
-import brooklyn.util.task.ssh.SshPutTaskFactory;
-import brooklyn.util.task.ssh.SshPutTaskWrapper;
-import brooklyn.util.task.ssh.SshTasks;
-import brooklyn.util.task.ssh.internal.AbstractSshExecTaskFactory;
-import brooklyn.util.task.ssh.internal.PlainSshExecTaskFactory;
-import brooklyn.util.task.system.ProcessTaskFactory;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/software/StaticSensor.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/StaticSensor.java b/software/base/src/main/java/brooklyn/entity/software/StaticSensor.java
index a0c1b82..b0d8881 100644
--- a/software/base/src/main/java/brooklyn/entity/software/StaticSensor.java
+++ b/software/base/src/main/java/brooklyn/entity/software/StaticSensor.java
@@ -20,6 +20,9 @@ package brooklyn.entity.software;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ValueResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -27,10 +30,7 @@ import brooklyn.config.ConfigKey;
 import brooklyn.enricher.basic.Propagator;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.effector.AddSensor;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ValueResolver;
 
 import com.google.common.base.Supplier;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/software/http/HttpRequestSensor.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/http/HttpRequestSensor.java b/software/base/src/main/java/brooklyn/entity/software/http/HttpRequestSensor.java
index 8d0047a..c9f87a5 100644
--- a/software/base/src/main/java/brooklyn/entity/software/http/HttpRequestSensor.java
+++ b/software/base/src/main/java/brooklyn/entity/software/http/HttpRequestSensor.java
@@ -23,6 +23,7 @@ import java.net.URI;
 import net.minidev.json.JSONObject;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,7 +35,6 @@ import brooklyn.entity.software.ssh.SshCommandSensor;
 import brooklyn.event.feed.http.HttpFeed;
 import brooklyn.event.feed.http.HttpPollConfig;
 import brooklyn.event.feed.http.HttpValueFunctions;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Functions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/software/java/JmxAttributeSensor.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/java/JmxAttributeSensor.java b/software/base/src/main/java/brooklyn/entity/software/java/JmxAttributeSensor.java
index b866be3..0802b22 100644
--- a/software/base/src/main/java/brooklyn/entity/software/java/JmxAttributeSensor.java
+++ b/software/base/src/main/java/brooklyn/entity/software/java/JmxAttributeSensor.java
@@ -25,6 +25,9 @@ import javax.management.ObjectName;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -38,9 +41,6 @@ import brooklyn.event.basic.DependentConfiguration;
 import brooklyn.event.feed.jmx.JmxAttributePollConfig;
 import brooklyn.event.feed.jmx.JmxFeed;
 import brooklyn.event.feed.jmx.JmxHelper;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
index ca26baa..8d15270 100644
--- a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
+++ b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
@@ -22,6 +22,7 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.ParameterType;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -33,7 +34,6 @@ import brooklyn.entity.effector.Effectors.EffectorBuilder;
 import brooklyn.entity.software.SshEffectorTasks;
 import brooklyn.entity.software.SshEffectorTasks.SshEffectorTaskFactory;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.text.Strings;
 
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
index 4443ece..1b6e99b 100644
--- a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
+++ b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
@@ -22,6 +22,8 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntityInitializer;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,8 +37,6 @@ import brooklyn.event.feed.ssh.SshFeed;
 import brooklyn.event.feed.ssh.SshPollConfig;
 import brooklyn.event.feed.ssh.SshValueFunctions;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/software/winrm/WindowsPerformanceCounterSensors.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/winrm/WindowsPerformanceCounterSensors.java b/software/base/src/main/java/brooklyn/entity/software/winrm/WindowsPerformanceCounterSensors.java
index 1a3656f..310f855 100644
--- a/software/base/src/main/java/brooklyn/entity/software/winrm/WindowsPerformanceCounterSensors.java
+++ b/software/base/src/main/java/brooklyn/entity/software/winrm/WindowsPerformanceCounterSensors.java
@@ -23,6 +23,7 @@ import java.util.Set;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntityInitializer;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,7 +32,6 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.event.basic.Sensors;
 import brooklyn.event.feed.windows.WindowsPerformanceCounterFeed;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.text.Strings;
 
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/event/feed/jmx/JmxHelper.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/event/feed/jmx/JmxHelper.java b/software/base/src/main/java/brooklyn/event/feed/jmx/JmxHelper.java
index c39ca9f..f809d31 100644
--- a/software/base/src/main/java/brooklyn/event/feed/jmx/JmxHelper.java
+++ b/software/base/src/main/java/brooklyn/event/feed/jmx/JmxHelper.java
@@ -61,12 +61,12 @@ import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.java.JmxSupport;
 import brooklyn.entity.java.UsesJmx;
-import brooklyn.util.crypto.SecureKeys;
 import brooklyn.util.crypto.SslTrustUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.RuntimeInterruptedException;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityLatchTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityLatchTest.java b/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityLatchTest.java
index aa46dd1..ab59e39 100644
--- a/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityLatchTest.java
+++ b/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityLatchTest.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.TaskInternal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.BeforeMethod;
@@ -44,7 +45,6 @@ import org.apache.brooklyn.location.basic.FixedListMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.test.Asserts;
-import brooklyn.util.task.TaskInternal;
 import brooklyn.util.time.Duration;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityRebindTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityRebindTest.java b/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityRebindTest.java
index 7ae7b54..ee9723a 100644
--- a/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityRebindTest.java
+++ b/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityRebindTest.java
@@ -34,6 +34,7 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.annotations.AfterMethod;
@@ -48,8 +49,6 @@ import brooklyn.entity.rebind.RebindTestUtils;
 import org.apache.brooklyn.location.basic.AbstractLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.util.flags.SetFromFlag;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.io.Files;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityTest.java b/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityTest.java
index 3d8ddda..7a1deb4 100644
--- a/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityTest.java
+++ b/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessEntityTest.java
@@ -40,6 +40,9 @@ import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.management.EntityManager;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.jclouds.util.Throwables2;
@@ -74,13 +77,10 @@ import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.PropagatedRuntimeException;
 import brooklyn.util.net.UserAndHostAndPort;
 import brooklyn.util.os.Os;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessSshDriverIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessSshDriverIntegrationTest.java b/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessSshDriverIntegrationTest.java
index f528f7c..aadb1ea 100644
--- a/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessSshDriverIntegrationTest.java
+++ b/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessSshDriverIntegrationTest.java
@@ -34,6 +34,7 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.BrooklynNetworkUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
@@ -45,7 +46,6 @@ import brooklyn.entity.trait.Startable;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.util.BrooklynNetworkUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.os.Os;
 import brooklyn.util.stream.KnownSizeInputStream;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/basic/VanillaSoftwareProcessAndChildrenIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/basic/VanillaSoftwareProcessAndChildrenIntegrationTest.java b/software/base/src/test/java/brooklyn/entity/basic/VanillaSoftwareProcessAndChildrenIntegrationTest.java
index 8b516ad..f8fb978 100644
--- a/software/base/src/test/java/brooklyn/entity/basic/VanillaSoftwareProcessAndChildrenIntegrationTest.java
+++ b/software/base/src/test/java/brooklyn/entity/basic/VanillaSoftwareProcessAndChildrenIntegrationTest.java
@@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
@@ -33,7 +34,6 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.SoftwareProcess.ChildStartableMode;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/basic/lifecycle/MyEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/basic/lifecycle/MyEntityImpl.java b/software/base/src/test/java/brooklyn/entity/basic/lifecycle/MyEntityImpl.java
index c8eca43..9f7c290 100644
--- a/software/base/src/test/java/brooklyn/entity/basic/lifecycle/MyEntityImpl.java
+++ b/software/base/src/test/java/brooklyn/entity/basic/lifecycle/MyEntityImpl.java
@@ -26,11 +26,13 @@ import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.entity.basic.SoftwareProcessDriver;
 import brooklyn.entity.basic.SoftwareProcessImpl;
 import brooklyn.entity.java.JavaSoftwareProcessSshDriver;
+
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.ResourceUtils;
+
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.text.Identifiers;
 
 public class MyEntityImpl extends SoftwareProcessImpl implements MyEntity {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/basic/lifecycle/NaiveScriptRunnerTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/basic/lifecycle/NaiveScriptRunnerTest.java b/software/base/src/test/java/brooklyn/entity/basic/lifecycle/NaiveScriptRunnerTest.java
index 5ab4228..aedc116 100644
--- a/software/base/src/test/java/brooklyn/entity/basic/lifecycle/NaiveScriptRunnerTest.java
+++ b/software/base/src/test/java/brooklyn/entity/basic/lifecycle/NaiveScriptRunnerTest.java
@@ -26,6 +26,9 @@ import java.util.concurrent.Callable;
 
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -39,9 +42,6 @@ import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.repeat.Repeater;
-import brooklyn.util.task.BasicExecutionContext;
-import brooklyn.util.task.BasicExecutionManager;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Throwables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/basic/lifecycle/StartStopSshDriverTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/basic/lifecycle/StartStopSshDriverTest.java b/software/base/src/test/java/brooklyn/entity/basic/lifecycle/StartStopSshDriverTest.java
index 1ef3e94..83c7dec 100644
--- a/software/base/src/test/java/brooklyn/entity/basic/lifecycle/StartStopSshDriverTest.java
+++ b/software/base/src/test/java/brooklyn/entity/basic/lifecycle/StartStopSshDriverTest.java
@@ -31,6 +31,9 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.internal.ssh.cli.SshCliTool;
+import org.apache.brooklyn.core.util.internal.ssh.sshj.SshjTool;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestApplicationImpl;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -41,13 +44,12 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver;
 import brooklyn.entity.basic.BrooklynConfigKeys;
 import brooklyn.entity.basic.Entities;
+
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.internal.ssh.cli.SshCliTool;
-import brooklyn.util.internal.ssh.sshj.SshjTool;
 import brooklyn.util.stream.StreamGobbler;
 
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/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 a088996..e473f52 100644
--- a/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
+++ b/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
@@ -35,6 +35,9 @@ import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.HttpTestUtils;
 import org.apache.http.auth.UsernamePasswordCredentials;
@@ -58,17 +61,16 @@ import brooklyn.entity.brooklynnode.BrooklynNode.ExistingFileBehaviour;
 import brooklyn.entity.brooklynnode.BrooklynNode.StopNodeAndKillAppsEffector;
 import brooklyn.entity.proxying.EntityProxyImpl;
 import brooklyn.event.feed.http.JsonFunctions;
+
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.Locations;
 import org.apache.brooklyn.location.basic.PortRanges;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Functionals;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.net.Networking;
 import brooklyn.util.net.Urls;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/brooklynnode/CallbackEntityHttpClient.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/brooklynnode/CallbackEntityHttpClient.java b/software/base/src/test/java/brooklyn/entity/brooklynnode/CallbackEntityHttpClient.java
index ad5d1f2..e9a73bf 100644
--- a/software/base/src/test/java/brooklyn/entity/brooklynnode/CallbackEntityHttpClient.java
+++ b/software/base/src/test/java/brooklyn/entity/brooklynnode/CallbackEntityHttpClient.java
@@ -23,13 +23,13 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
+import org.apache.brooklyn.core.util.http.HttpTool.HttpClientBuilder;
 import org.apache.http.HttpStatus;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpPost;
 
 import brooklyn.entity.brooklynnode.EntityHttpClient;
-import brooklyn.util.http.HttpTool.HttpClientBuilder;
-import brooklyn.util.http.HttpToolResponse;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/chef/ChefLiveTestSupport.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/chef/ChefLiveTestSupport.java b/software/base/src/test/java/brooklyn/entity/chef/ChefLiveTestSupport.java
index a04d9fa..2b65f58 100644
--- a/software/base/src/test/java/brooklyn/entity/chef/ChefLiveTestSupport.java
+++ b/software/base/src/test/java/brooklyn/entity/chef/ChefLiveTestSupport.java
@@ -25,6 +25,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.BeforeMethod;
@@ -34,7 +35,6 @@ import brooklyn.entity.basic.EntityInternal;
 
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.io.FileUtil;
 import brooklyn.util.stream.InputStreamSupplier;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/chef/ChefServerTasksIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/chef/ChefServerTasksIntegrationTest.java b/software/base/src/test/java/brooklyn/entity/chef/ChefServerTasksIntegrationTest.java
index ff7dfb1..1d175b1 100644
--- a/software/base/src/test/java/brooklyn/entity/chef/ChefServerTasksIntegrationTest.java
+++ b/software/base/src/test/java/brooklyn/entity/chef/ChefServerTasksIntegrationTest.java
@@ -22,6 +22,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,7 +34,6 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
 import brooklyn.util.stream.StreamGobbler;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java b/software/base/src/test/java/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java
index 941ba0f..75133c7 100644
--- a/software/base/src/test/java/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java
+++ b/software/base/src/test/java/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java
@@ -20,12 +20,12 @@ package brooklyn.entity.chef.mysql;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.entity.software.SshEffectorTasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 
 public class ChefSoloDriverMySqlEntityLiveTest extends AbstractChefToyMySqlEntityLiveTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/java/JavaOptsTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/java/JavaOptsTest.java b/software/base/src/test/java/brooklyn/entity/java/JavaOptsTest.java
index b7034e0..f5213d9 100644
--- a/software/base/src/test/java/brooklyn/entity/java/JavaOptsTest.java
+++ b/software/base/src/test/java/brooklyn/entity/java/JavaOptsTest.java
@@ -31,6 +31,9 @@ import java.util.Set;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.core.util.internal.ssh.RecordingSshTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.internal.ssh.RecordingSshTool.ExecCmd;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
@@ -44,9 +47,6 @@ import org.apache.brooklyn.location.basic.SshMachineLocation;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.internal.ssh.RecordingSshTool;
-import brooklyn.util.internal.ssh.RecordingSshTool.ExecCmd;
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.jmx.jmxmp.JmxmpAgent;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/java/JmxSupportTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/java/JmxSupportTest.java b/software/base/src/test/java/brooklyn/entity/java/JmxSupportTest.java
index caaf757..97df90b 100644
--- a/software/base/src/test/java/brooklyn/entity/java/JmxSupportTest.java
+++ b/software/base/src/test/java/brooklyn/entity/java/JmxSupportTest.java
@@ -20,6 +20,8 @@ package brooklyn.entity.java;
 
 import static org.testng.Assert.assertEquals;
 
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,9 +31,7 @@ import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.java.UsesJmx.JmxAgentModes;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.maven.MavenRetriever;
 import brooklyn.util.stream.Streams;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/java/SslKeyConfigTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/java/SslKeyConfigTest.java b/software/base/src/test/java/brooklyn/entity/java/SslKeyConfigTest.java
index 01c2cfb..eab15ca 100644
--- a/software/base/src/test/java/brooklyn/entity/java/SslKeyConfigTest.java
+++ b/software/base/src/test/java/brooklyn/entity/java/SslKeyConfigTest.java
@@ -24,12 +24,11 @@ import java.security.Key;
 import java.security.KeyStore;
 import java.security.cert.Certificate;
 
+import org.apache.brooklyn.core.util.crypto.FluentKeySigner;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import brooklyn.util.crypto.FluentKeySigner;
-import brooklyn.util.crypto.SecureKeys;
-
 public class SslKeyConfigTest {
 
     @Test

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppRebindTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppRebindTest.java b/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppRebindTest.java
index dd2b9b0..eaabfed 100644
--- a/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppRebindTest.java
+++ b/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppRebindTest.java
@@ -25,6 +25,7 @@ import java.io.File;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
@@ -42,7 +43,6 @@ import brooklyn.event.basic.Sensors;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppTest.java b/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppTest.java
index 73a2675..067f86e 100644
--- a/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppTest.java
+++ b/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppTest.java
@@ -43,6 +43,9 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.crypto.FluentKeySigner;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -53,13 +56,12 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.Lifecycle;
 import brooklyn.event.feed.jmx.JmxHelper;
+
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.PortRanges;
+
 import brooklyn.test.Asserts;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.crypto.FluentKeySigner;
-import brooklyn.util.crypto.SecureKeys;
 import brooklyn.util.crypto.SslTrustUtils;
 import brooklyn.util.jmx.jmxmp.JmxmpAgent;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/software/MachineLifecycleEffectorTasksTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/software/MachineLifecycleEffectorTasksTest.java b/software/base/src/test/java/brooklyn/entity/software/MachineLifecycleEffectorTasksTest.java
index 2fcd1df..c143adf 100644
--- a/software/base/src/test/java/brooklyn/entity/software/MachineLifecycleEffectorTasksTest.java
+++ b/software/base/src/test/java/brooklyn/entity/software/MachineLifecycleEffectorTasksTest.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.TaskInternal;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
@@ -50,9 +51,10 @@ import brooklyn.entity.basic.SoftwareProcess.StopSoftwareParameters.StopMode;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.DependentConfiguration;
 import brooklyn.event.basic.Sensors;
+
 import org.apache.brooklyn.location.jclouds.BailOutJcloudsLocation;
+
 import brooklyn.test.Asserts;
-import brooklyn.util.task.TaskInternal;
 import brooklyn.util.time.Duration;
 
 public class MachineLifecycleEffectorTasksTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/software/SoftwareEffectorTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/software/SoftwareEffectorTest.java b/software/base/src/test/java/brooklyn/entity/software/SoftwareEffectorTest.java
index 599b7ac..61389c9 100644
--- a/software/base/src/test/java/brooklyn/entity/software/SoftwareEffectorTest.java
+++ b/software/base/src/test/java/brooklyn/entity/software/SoftwareEffectorTest.java
@@ -24,6 +24,8 @@ import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -39,9 +41,6 @@ import brooklyn.entity.software.SshEffectorTasks.SshEffectorBody;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-
 import com.google.common.base.Throwables;
 
 public class SoftwareEffectorTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/software/SshEffectorTasksTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/software/SshEffectorTasksTest.java b/software/base/src/test/java/brooklyn/entity/software/SshEffectorTasksTest.java
index 8494e36..f0a4364 100644
--- a/software/base/src/test/java/brooklyn/entity/software/SshEffectorTasksTest.java
+++ b/software/base/src/test/java/brooklyn/entity/software/SshEffectorTasksTest.java
@@ -26,6 +26,9 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.TaskAdaptable;
 import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.core.util.task.ssh.SshFetchTaskWrapper;
+import org.apache.brooklyn.core.util.task.ssh.SshPutTaskWrapper;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.commons.io.FileUtils;
 import org.slf4j.Logger;
@@ -42,9 +45,6 @@ import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.util.exceptions.PropagatedRuntimeException;
 import brooklyn.util.net.Urls;
-import brooklyn.util.task.ssh.SshFetchTaskWrapper;
-import brooklyn.util.task.ssh.SshPutTaskWrapper;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 
 import com.google.common.io.Files;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/software/StaticSensorTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/software/StaticSensorTest.java b/software/base/src/test/java/brooklyn/entity/software/StaticSensorTest.java
index 7ec8952..285122d 100644
--- a/software/base/src/test/java/brooklyn/entity/software/StaticSensorTest.java
+++ b/software/base/src/test/java/brooklyn/entity/software/StaticSensorTest.java
@@ -19,13 +19,13 @@
 package brooklyn.entity.software;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.BrooklynAppUnitTestSupport;
 import brooklyn.entity.basic.BasicEntity;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/software/http/HttpRequestSensorTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/software/http/HttpRequestSensorTest.java b/software/base/src/test/java/brooklyn/entity/software/http/HttpRequestSensorTest.java
index eb53f3e..8ae262b 100644
--- a/software/base/src/test/java/brooklyn/entity/software/http/HttpRequestSensorTest.java
+++ b/software/base/src/test/java/brooklyn/entity/software/http/HttpRequestSensorTest.java
@@ -22,6 +22,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -34,7 +35,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.event.basic.Sensors;
 import brooklyn.test.TestHttpRequestHandler;
 import brooklyn.test.TestHttpServer;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.time.Duration;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/software/mysql/AbstractToyMySqlEntityTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/software/mysql/AbstractToyMySqlEntityTest.java b/software/base/src/test/java/brooklyn/entity/software/mysql/AbstractToyMySqlEntityTest.java
index 98bae62..12266b5 100644
--- a/software/base/src/test/java/brooklyn/entity/software/mysql/AbstractToyMySqlEntityTest.java
+++ b/software/base/src/test/java/brooklyn/entity/software/mysql/AbstractToyMySqlEntityTest.java
@@ -21,6 +21,7 @@ package brooklyn.entity.software.mysql;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,7 +39,6 @@ import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/software/mysql/DynamicToyMySqlEntityBuilder.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/software/mysql/DynamicToyMySqlEntityBuilder.java b/software/base/src/test/java/brooklyn/entity/software/mysql/DynamicToyMySqlEntityBuilder.java
index df3e436..8e1ee2e 100644
--- a/software/base/src/test/java/brooklyn/entity/software/mysql/DynamicToyMySqlEntityBuilder.java
+++ b/software/base/src/test/java/brooklyn/entity/software/mysql/DynamicToyMySqlEntityBuilder.java
@@ -26,6 +26,10 @@ import org.apache.brooklyn.api.entity.proxying.EntityInitializer;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.OsDetails;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,10 +44,6 @@ import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation.L
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ssh.SshTasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.ComparableVersion;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/test/java/brooklyn/entity/software/ssh/SshCommandIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/software/ssh/SshCommandIntegrationTest.java b/software/base/src/test/java/brooklyn/entity/software/ssh/SshCommandIntegrationTest.java
index 2d142e4..6316d03 100644
--- a/software/base/src/test/java/brooklyn/entity/software/ssh/SshCommandIntegrationTest.java
+++ b/software/base/src/test/java/brooklyn/entity/software/ssh/SshCommandIntegrationTest.java
@@ -27,6 +27,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -43,7 +44,6 @@ import brooklyn.event.basic.Sensors;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.time.Duration;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/DatastoreMixins.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/DatastoreMixins.java b/software/database/src/main/java/brooklyn/entity/database/DatastoreMixins.java
index 90f8a62..4727473 100644
--- a/software/database/src/main/java/brooklyn/entity/database/DatastoreMixins.java
+++ b/software/database/src/main/java/brooklyn/entity/database/DatastoreMixins.java
@@ -25,13 +25,13 @@ import javax.annotation.Nullable;
 import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.stream.KnownSizeInputStream;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/crate/CrateNode.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/crate/CrateNode.java b/software/database/src/main/java/brooklyn/entity/database/crate/CrateNode.java
index 8d5fae3..8373648 100644
--- a/software/database/src/main/java/brooklyn/entity/database/crate/CrateNode.java
+++ b/software/database/src/main/java/brooklyn/entity/database/crate/CrateNode.java
@@ -20,6 +20,7 @@ package brooklyn.entity.database.crate;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Attributes;
@@ -33,8 +34,8 @@ import brooklyn.event.basic.AttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
+
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.flags.SetFromFlag;
 
 @ImplementedBy(CrateNodeImpl.class)
 public interface CrateNode extends SoftwareProcess, UsesJava,UsesJmx, UsesJavaMXBeans, DatastoreCommon {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbDriver.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbDriver.java b/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbDriver.java
index 3d92173..2db7f2ef4 100644
--- a/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbDriver.java
+++ b/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbDriver.java
@@ -18,8 +18,9 @@
  */
 package brooklyn.entity.database.mariadb;
 
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+
 import brooklyn.entity.basic.SoftwareProcessDriver;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 
 /**
  * The {@link SoftwareProcessDriver} for MariaDB.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbNode.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbNode.java b/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbNode.java
index d067625..ab77af5 100644
--- a/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbNode.java
+++ b/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbNode.java
@@ -22,6 +22,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.entity.trait.HasShortName;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Attributes;
@@ -34,8 +35,8 @@ import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.StringAttributeSens
 import brooklyn.event.basic.MapConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
+
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.flags.SetFromFlag;
 
 @Catalog(name="MariaDB Node", description="MariaDB is an open source relational database management system (RDBMS)", iconUrl="classpath:///mariadb-logo-180x119.png")
 @ImplementedBy(MariaDbNodeImpl.class)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbNodeImpl.java b/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbNodeImpl.java
index 0212749..a22af08 100644
--- a/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbNodeImpl.java
+++ b/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbNodeImpl.java
@@ -26,9 +26,11 @@ import brooklyn.entity.effector.EffectorBody;
 import brooklyn.event.feed.ssh.SshFeed;
 import brooklyn.event.feed.ssh.SshPollConfig;
 import brooklyn.event.feed.ssh.SshPollValue;
+
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.basic.Locations;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.config.ConfigBag;
+
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbSshDriver.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbSshDriver.java b/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbSshDriver.java
index b35a1cd..4d916b9 100644
--- a/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbSshDriver.java
+++ b/software/database/src/main/java/brooklyn/entity/database/mariadb/MariaDbSshDriver.java
@@ -41,14 +41,14 @@ import brooklyn.entity.database.DatastoreMixins;
 import brooklyn.entity.software.SshEffectorTasks;
 
 import org.apache.brooklyn.api.location.OsDetails;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.CountdownTimer;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlClusterImpl.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlClusterImpl.java b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlClusterImpl.java
index 683f13b..f86ad43 100644
--- a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlClusterImpl.java
+++ b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlClusterImpl.java
@@ -31,6 +31,8 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
 
 import com.google.common.base.Function;
 import com.google.common.base.Functions;
@@ -56,8 +58,6 @@ import brooklyn.event.feed.function.FunctionPollConfig;
 import brooklyn.util.collections.CollectionFunctionals;
 import brooklyn.util.guava.Functionals;
 import brooklyn.util.guava.IfFunctions;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskBuilder;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.StringPredicates;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlDriver.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlDriver.java b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlDriver.java
index ddfce38..2c90142 100644
--- a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlDriver.java
+++ b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlDriver.java
@@ -18,8 +18,9 @@
  */
 package brooklyn.entity.database.mysql;
 
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+
 import brooklyn.entity.basic.SoftwareProcessDriver;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 
 /**
  * The {@link SoftwareProcessDriver} for MySQL.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNode.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNode.java b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNode.java
index 0ab51d0..99b53c5 100644
--- a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNode.java
+++ b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNode.java
@@ -22,6 +22,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.entity.trait.HasShortName;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.annotation.Effector;
@@ -36,8 +37,8 @@ import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.StringAttributeSens
 import brooklyn.event.basic.MapConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
+
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.flags.SetFromFlag;
 
 @Catalog(name="MySql Node", description="MySql is an open source relational database management system (RDBMS)", iconUrl="classpath:///mysql-logo-110x57.png")
 @ImplementedBy(MySqlNodeImpl.class)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNodeImpl.java b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNodeImpl.java
index 00a591b..dfbcbf1 100644
--- a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNodeImpl.java
+++ b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNodeImpl.java
@@ -21,6 +21,7 @@ package brooklyn.entity.database.mysql;
 import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -29,10 +30,11 @@ import brooklyn.entity.effector.EffectorBody;
 import brooklyn.event.feed.ssh.SshFeed;
 import brooklyn.event.feed.ssh.SshPollConfig;
 import brooklyn.event.feed.ssh.SshPollValue;
+
 import org.apache.brooklyn.location.basic.Locations;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlSshDriver.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlSshDriver.java b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlSshDriver.java
index 2e748e8..32b530d 100644
--- a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlSshDriver.java
+++ b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlSshDriver.java
@@ -43,6 +43,8 @@ import brooklyn.entity.database.DatastoreMixins;
 import brooklyn.entity.software.SshEffectorTasks;
 
 import org.apache.brooklyn.api.location.OsDetails;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.apache.brooklyn.location.basic.BasicOsDetails.OsVersions;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
@@ -53,8 +55,6 @@ import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.ComparableVersion;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlDriver.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlDriver.java b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlDriver.java
index 9aaf7c2..02ad039 100644
--- a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlDriver.java
+++ b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlDriver.java
@@ -18,8 +18,9 @@
  */
 package brooklyn.entity.database.postgresql;
 
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+
 import brooklyn.entity.basic.SoftwareProcessDriver;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 
 /**
  * The {@link brooklyn.entity.basic.SoftwareProcessDriver} for PostgreSQL.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNode.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNode.java b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNode.java
index 793debf..20f4b76 100644
--- a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNode.java
+++ b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNode.java
@@ -22,6 +22,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.entity.trait.HasShortName;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -31,8 +32,8 @@ import brooklyn.entity.database.DatastoreMixins;
 import brooklyn.entity.database.DatastoreMixins.DatastoreCommon;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
+
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * PostgreSQL database node entity.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeChefImplFromScratch.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeChefImplFromScratch.java b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeChefImplFromScratch.java
index 7572845..d82e637 100644
--- a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeChefImplFromScratch.java
+++ b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeChefImplFromScratch.java
@@ -19,6 +19,9 @@
 package brooklyn.entity.database.postgresql;
 
 import org.apache.brooklyn.api.entity.Effector;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,14 +37,13 @@ import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.software.SshEffectorTasks;
 import brooklyn.event.feed.ssh.SshFeed;
 import brooklyn.event.feed.ssh.SshPollConfig;
+
 import org.apache.brooklyn.location.basic.Locations;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.ResourceUtils;
+
 import brooklyn.util.collections.Jsonya;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
 
 public class PostgreSqlNodeChefImplFromScratch extends EffectorStartableImpl implements PostgreSqlNode {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeImpl.java b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeImpl.java
index 71f63d9..c4b02de 100644
--- a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeImpl.java
+++ b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeImpl.java
@@ -18,12 +18,12 @@
  */
 package brooklyn.entity.database.postgresql;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.SoftwareProcessImpl;
 import brooklyn.entity.effector.EffectorBody;
-import brooklyn.util.config.ConfigBag;
 
 public class PostgreSqlNodeImpl extends SoftwareProcessImpl implements PostgreSqlNode {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlSshDriver.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlSshDriver.java b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlSshDriver.java
index a23f2bc..5ff0175 100644
--- a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlSshDriver.java
+++ b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlSshDriver.java
@@ -48,6 +48,10 @@ import brooklyn.entity.database.DatastoreMixins;
 import brooklyn.entity.software.SshEffectorTasks;
 
 import org.apache.brooklyn.api.location.OsDetails;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks.OnFailingTask;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.util.collections.MutableList;
@@ -56,10 +60,6 @@ import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.ssh.SshTasks;
-import brooklyn.util.task.ssh.SshTasks.OnFailingTask;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.StringFunctions;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/main/java/brooklyn/entity/database/rubyrep/RubyRepNode.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/rubyrep/RubyRepNode.java b/software/database/src/main/java/brooklyn/entity/database/rubyrep/RubyRepNode.java
index 8e990dd..edfbda5 100644
--- a/software/database/src/main/java/brooklyn/entity/database/rubyrep/RubyRepNode.java
+++ b/software/database/src/main/java/brooklyn/entity/database/rubyrep/RubyRepNode.java
@@ -21,6 +21,7 @@ package brooklyn.entity.database.rubyrep;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Attributes;
@@ -31,7 +32,6 @@ import brooklyn.entity.database.DatastoreMixins.DatastoreCommon;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.StringAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 @Catalog(name = "RubyRep Node", description = "RubyRep is a database replication system", iconUrl = "classpath:///rubyrep-logo.jpeg")
 @ImplementedBy(RubyRepNodeImpl.class)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/database/src/test/java/brooklyn/entity/database/postgresql/PostgreSqlChefTest.java
----------------------------------------------------------------------
diff --git a/software/database/src/test/java/brooklyn/entity/database/postgresql/PostgreSqlChefTest.java b/software/database/src/test/java/brooklyn/entity/database/postgresql/PostgreSqlChefTest.java
index 5d8e984..36eb963 100644
--- a/software/database/src/test/java/brooklyn/entity/database/postgresql/PostgreSqlChefTest.java
+++ b/software/database/src/test/java/brooklyn/entity/database/postgresql/PostgreSqlChefTest.java
@@ -33,10 +33,10 @@ import brooklyn.entity.effector.EffectorTasks;
 import brooklyn.entity.software.SshEffectorTasks;
 
 import org.apache.brooklyn.api.location.PortRange;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.apache.brooklyn.location.basic.PortRanges;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.time.Duration;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/activemq/ActiveMQBroker.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/activemq/ActiveMQBroker.java b/software/messaging/src/main/java/brooklyn/entity/messaging/activemq/ActiveMQBroker.java
index 2e59bd4..b8bbdca 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/activemq/ActiveMQBroker.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/activemq/ActiveMQBroker.java
@@ -20,6 +20,7 @@ package brooklyn.entity.messaging.activemq;
 
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Attributes;
@@ -33,7 +34,6 @@ import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.StringAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.time.Duration;
 /**
  * An {@link org.apache.brooklyn.api.entity.Entity} that represents a single ActiveMQ broker instance.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/amqp/AmqpExchange.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/amqp/AmqpExchange.java b/software/messaging/src/main/java/brooklyn/entity/messaging/amqp/AmqpExchange.java
index f2eeee3..0ddd854 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/amqp/AmqpExchange.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/amqp/AmqpExchange.java
@@ -19,9 +19,9 @@
 package brooklyn.entity.messaging.amqp;
 
 import org.apache.brooklyn.api.event.Sensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * An interface that describes an AMQP exchange.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/Kafka.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/Kafka.java b/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/Kafka.java
index ff7c368..64123b3 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/Kafka.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/Kafka.java
@@ -18,12 +18,13 @@
  */
 package brooklyn.entity.messaging.kafka;
 
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * Shared Kafka broker and zookeeper properties.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaBroker.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaBroker.java b/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaBroker.java
index 28b51c9..71b20c5 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaBroker.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaBroker.java
@@ -20,6 +20,7 @@ package brooklyn.entity.messaging.kafka;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -30,8 +31,9 @@ import brooklyn.entity.zookeeper.ZooKeeperNode;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
+
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.flags.SetFromFlag;
+
 import brooklyn.util.time.Duration;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaCluster.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaCluster.java b/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaCluster.java
index 8ebe328..3a24377 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaCluster.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaCluster.java
@@ -24,6 +24,7 @@ import org.apache.brooklyn.api.entity.Group;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynConfigKeys;
@@ -35,7 +36,6 @@ import brooklyn.entity.trait.Startable;
 import brooklyn.entity.zookeeper.ZooKeeperNode;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.time.Duration;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaZooKeeper.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaZooKeeper.java b/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaZooKeeper.java
index fa4e915..106690a 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaZooKeeper.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/kafka/KafkaZooKeeper.java
@@ -19,6 +19,7 @@
 package brooklyn.entity.messaging.kafka;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.annotation.Effector;
@@ -27,7 +28,6 @@ import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.entity.zookeeper.ZooKeeperNode;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.time.Duration;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/qpid/QpidBroker.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/qpid/QpidBroker.java b/software/messaging/src/main/java/brooklyn/entity/messaging/qpid/QpidBroker.java
index 876ade8..a2af8a4 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/qpid/QpidBroker.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/qpid/QpidBroker.java
@@ -22,6 +22,7 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Attributes;
@@ -34,7 +35,6 @@ import brooklyn.entity.messaging.jms.JMSBroker;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * An {@link org.apache.brooklyn.api.entity.Entity} that represents a single Qpid broker instance, using AMQP 0-10.



[42/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
[BROOKLYN-162] Refactor package in ./core/util


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

Branch: refs/heads/master
Commit: a4c0e5fdee499265f80cf6d8feb0e6159cd04c30
Parents: ff7f580
Author: Hadrian Zbarcea <ha...@apache.org>
Authored: Mon Aug 17 01:00:29 2015 -0400
Committer: Hadrian Zbarcea <ha...@apache.org>
Committed: Mon Aug 17 14:52:04 2015 -0400

----------------------------------------------------------------------
 .../src/main/java/brooklyn/BrooklynVersion.java |    6 +-
 .../brooklyn/basic/AbstractBrooklynObject.java  |    4 +-
 .../brooklyn/basic/BasicConfigurableObject.java |    4 +-
 .../brooklyn/basic/BrooklynDynamicType.java     |    2 +-
 .../brooklyn/basic/BrooklynObjectInternal.java  |    2 +-
 .../basic/internal/ApiObjectsFactoryImpl.java   |    2 +-
 .../brooklyn/config/BrooklynProperties.java     |    6 +-
 .../brooklyn/config/BrooklynServerPaths.java    |    2 +-
 .../config/internal/AbstractConfigMapImpl.java  |    4 +-
 .../enricher/CustomAggregatingEnricher.java     |    2 +-
 .../main/java/brooklyn/enricher/Enrichers.java  |    2 +-
 .../enricher/basic/AbstractEnricher.java        |    2 +-
 .../basic/AbstractMultipleSensorAggregator.java |    2 +-
 .../basic/AbstractTypeTransformingEnricher.java |    2 +-
 .../brooklyn/enricher/basic/Aggregator.java     |    2 +-
 .../java/brooklyn/enricher/basic/Joiner.java    |    2 +-
 .../brooklyn/enricher/basic/Propagator.java     |    6 +-
 .../brooklyn/enricher/basic/Transformer.java    |    4 +-
 .../brooklyn/enricher/basic/UpdatingMap.java    |    2 +-
 .../basic/YamlTimeWeightedDeltaEnricher.java    |    2 +-
 .../brooklyn/entity/basic/AbstractEffector.java |    4 +-
 .../brooklyn/entity/basic/AbstractEntity.java   |    8 +-
 .../java/brooklyn/entity/basic/BasicGroup.java  |    2 +-
 .../entity/basic/BrooklynConfigKeys.java        |    4 +-
 .../entity/basic/BrooklynShutdownHooks.java     |    2 +-
 .../brooklyn/entity/basic/BrooklynTaskTags.java |    6 +-
 .../java/brooklyn/entity/basic/ConfigKeys.java  |    2 +-
 .../java/brooklyn/entity/basic/DataEntity.java  |    2 +-
 .../brooklyn/entity/basic/DynamicGroup.java     |    2 +-
 .../brooklyn/entity/basic/DynamicGroupImpl.java |    2 +-
 .../entity/basic/EffectorStartableImpl.java     |    3 +-
 .../java/brooklyn/entity/basic/Entities.java    |   18 +-
 .../brooklyn/entity/basic/EntityConfigMap.java  |   10 +-
 .../brooklyn/entity/basic/EntityFunctions.java  |    2 +-
 .../brooklyn/entity/basic/EntityInternal.java   |    2 +-
 .../basic/EntityTransientCopyInternal.java      |    2 +-
 .../java/brooklyn/entity/basic/Lifecycle.java   |    2 +-
 .../brooklyn/entity/basic/MethodEffector.java   |    2 +-
 .../java/brooklyn/entity/basic/Sanitizer.java   |    2 +-
 .../entity/basic/ServiceStateLogic.java         |    2 +-
 .../entity/effector/AddChildrenEffector.java    |    2 +-
 .../brooklyn/entity/effector/AddEffector.java   |    2 +-
 .../brooklyn/entity/effector/AddSensor.java     |    2 +-
 .../brooklyn/entity/effector/EffectorBody.java  |   10 +-
 .../brooklyn/entity/effector/EffectorTasks.java |   10 +-
 .../brooklyn/entity/effector/Effectors.java     |    4 +-
 .../java/brooklyn/entity/group/Cluster.java     |    2 +-
 .../brooklyn/entity/group/DynamicCluster.java   |    2 +-
 .../entity/group/DynamicClusterImpl.java        |    8 +-
 .../brooklyn/entity/group/DynamicFabric.java    |    2 +-
 .../entity/group/DynamicMultiGroup.java         |    2 +-
 .../entity/group/QuarantineGroupImpl.java       |    4 +-
 .../entity/proxying/EntityProxyImpl.java        |    6 +-
 .../entity/proxying/InternalEntityFactory.java  |    4 +-
 .../proxying/InternalLocationFactory.java       |    4 +-
 .../entity/proxying/InternalPolicyFactory.java  |    2 +-
 .../rebind/BasicCatalogItemRebindSupport.java   |    2 +-
 .../rebind/BasicEnricherRebindSupport.java      |    4 +-
 .../entity/rebind/BasicFeedRebindSupport.java   |    4 +-
 .../rebind/BasicLocationRebindSupport.java      |    4 +-
 .../entity/rebind/BasicPolicyRebindSupport.java |    4 +-
 .../rebind/PeriodicDeltaChangeListener.java     |    4 +-
 .../brooklyn/entity/rebind/RebindIteration.java |    2 +-
 .../entity/rebind/RebindManagerImpl.java        |    6 +-
 .../entity/rebind/dto/BasicLocationMemento.java |    2 +-
 .../entity/rebind/dto/MementosGenerators.java   |    4 +-
 .../BrooklynMementoPersisterToObjectStore.java  |    2 +-
 .../persister/BrooklynPersistenceUtils.java     |    2 +-
 .../rebind/persister/FileBasedObjectStore.java  |    2 +-
 .../rebind/persister/XmlMementoSerializer.java  |    2 +-
 .../rebind/transformer/CompoundTransformer.java |    4 +-
 .../transformer/CompoundTransformerLoader.java  |    4 +-
 .../java/brooklyn/entity/trait/Startable.java   |    4 +-
 .../brooklyn/entity/trait/StartableMethods.java |    6 +-
 .../java/brooklyn/event/basic/AttributeMap.java |    2 +-
 .../basic/AttributeSensorAndConfigKey.java      |    2 +-
 .../brooklyn/event/basic/BasicConfigKey.java    |    4 +-
 .../event/basic/DependentConfiguration.java     |   16 +-
 .../basic/PortAttributeSensorAndConfigKey.java  |    2 +-
 ...platedStringAttributeSensorAndConfigKey.java |    2 +-
 .../event/feed/AttributePollHandler.java        |    4 +-
 .../main/java/brooklyn/event/feed/Poller.java   |    6 +-
 .../java/brooklyn/event/feed/http/HttpFeed.java |    6 +-
 .../event/feed/http/HttpPollConfig.java         |    4 +-
 .../brooklyn/event/feed/http/HttpPollValue.java |    2 +-
 .../brooklyn/event/feed/http/HttpPolls.java     |    5 +-
 .../event/feed/http/HttpValueFunctions.java     |    3 +-
 .../brooklyn/event/feed/shell/ShellFeed.java    |    6 +-
 .../java/brooklyn/event/feed/ssh/SshFeed.java   |    6 +-
 .../windows/WindowsPerformanceCounterFeed.java  |    4 +-
 .../policy/basic/AbstractEntityAdjunct.java     |    8 +-
 .../brooklyn/policy/basic/ConfigMapImpl.java    |    4 +-
 .../util/BrooklynLanguageExtensions.java        |   48 -
 .../brooklyn/util/BrooklynMavenArtifacts.java   |   58 -
 .../brooklyn/util/BrooklynNetworkUtils.java     |   41 -
 .../main/java/brooklyn/util/ResourceUtils.java  |  639 ----------
 .../java/brooklyn/util/config/ConfigBag.java    |  588 ----------
 .../brooklyn/util/crypto/FluentKeySigner.java   |  192 ---
 .../java/brooklyn/util/crypto/SecureKeys.java   |  184 ---
 .../java/brooklyn/util/file/ArchiveBuilder.java |  423 -------
 .../java/brooklyn/util/file/ArchiveTasks.java   |   58 -
 .../java/brooklyn/util/file/ArchiveUtils.java   |  351 ------
 .../util/flags/ClassCoercionException.java      |   39 -
 .../java/brooklyn/util/flags/FlagUtils.java     |  587 ----------
 .../brooklyn/util/flags/MethodCoercions.java    |  183 ---
 .../java/brooklyn/util/flags/SetFromFlag.java   |   71 --
 .../java/brooklyn/util/flags/TypeCoercions.java |  879 --------------
 .../main/java/brooklyn/util/http/HttpTool.java  |  387 -------
 .../brooklyn/util/http/HttpToolResponse.java    |  185 ---
 .../util/internal/ConfigKeySelfExtracting.java  |   41 -
 .../java/brooklyn/util/internal/Repeater.java   |  369 ------
 .../ssh/BackoffLimitedRetryHandler.java         |   74 --
 .../util/internal/ssh/ShellAbstractTool.java    |  442 -------
 .../brooklyn/util/internal/ssh/ShellTool.java   |  113 --
 .../util/internal/ssh/SshAbstractTool.java      |  172 ---
 .../util/internal/ssh/SshException.java         |   32 -
 .../brooklyn/util/internal/ssh/SshTool.java     |  174 ---
 .../util/internal/ssh/cli/SshCliTool.java       |  316 -----
 .../util/internal/ssh/process/ProcessTool.java  |  214 ----
 .../internal/ssh/sshj/SshjClientConnection.java |  282 -----
 .../util/internal/ssh/sshj/SshjTool.java        | 1091 ------------------
 .../util/javalang/ReflectionScanner.java        |  135 ---
 .../brooklyn/util/javalang/UrlClassLoader.java  |   69 --
 .../java/brooklyn/util/mutex/MutexSupport.java  |  120 --
 .../brooklyn/util/mutex/SemaphoreForTasks.java  |  112 --
 .../util/mutex/SemaphoreWithOwners.java         |  231 ----
 .../java/brooklyn/util/mutex/WithMutexes.java   |   45 -
 .../src/main/java/brooklyn/util/osgi/Osgis.java |  719 ------------
 .../util/task/AbstractExecutionContext.java     |   75 --
 .../util/task/BasicExecutionContext.java        |  221 ----
 .../util/task/BasicExecutionManager.java        |  755 ------------
 .../main/java/brooklyn/util/task/BasicTask.java |  892 --------------
 .../java/brooklyn/util/task/CanSetName.java     |   25 -
 .../java/brooklyn/util/task/CompoundTask.java   |  131 ---
 .../brooklyn/util/task/DeferredSupplier.java    |   38 -
 .../util/task/DynamicSequentialTask.java        |  480 --------
 .../java/brooklyn/util/task/DynamicTasks.java   |  337 ------
 .../brooklyn/util/task/ExecutionListener.java   |   31 -
 .../java/brooklyn/util/task/ExecutionUtils.java |   49 -
 .../java/brooklyn/util/task/ForwardingTask.java |  325 ------
 .../util/task/ListenableForwardingFuture.java   |   50 -
 .../java/brooklyn/util/task/ParallelTask.java   |   85 --
 .../java/brooklyn/util/task/ScheduledTask.java  |  185 ---
 .../java/brooklyn/util/task/SequentialTask.java |   58 -
 .../util/task/SingleThreadedScheduler.java      |  216 ----
 .../java/brooklyn/util/task/TaskBuilder.java    |  184 ---
 .../java/brooklyn/util/task/TaskInternal.java   |  125 --
 .../java/brooklyn/util/task/TaskScheduler.java  |   41 -
 .../main/java/brooklyn/util/task/TaskTags.java  |   71 --
 .../src/main/java/brooklyn/util/task/Tasks.java |  488 --------
 .../java/brooklyn/util/task/ValueResolver.java  |  426 -------
 .../util/task/ssh/SshFetchTaskFactory.java      |   89 --
 .../util/task/ssh/SshFetchTaskWrapper.java      |  135 ---
 .../util/task/ssh/SshPutTaskFactory.java        |  123 --
 .../brooklyn/util/task/ssh/SshPutTaskStub.java  |   69 --
 .../util/task/ssh/SshPutTaskWrapper.java        |  190 ---
 .../java/brooklyn/util/task/ssh/SshTasks.java   |  236 ----
 .../internal/AbstractSshExecTaskFactory.java    |   58 -
 .../ssh/internal/PlainSshExecTaskFactory.java   |   71 --
 .../util/task/system/ProcessTaskFactory.java    |   65 --
 .../util/task/system/ProcessTaskStub.java       |  101 --
 .../util/task/system/ProcessTaskWrapper.java    |  187 ---
 .../brooklyn/util/task/system/SystemTasks.java  |   29 -
 .../internal/AbstractProcessTaskFactory.java    |  214 ----
 .../system/internal/ExecWithLoggingHelpers.java |  200 ----
 .../internal/SystemProcessTaskFactory.java      |  131 ---
 .../brooklyn/util/text/DataUriSchemeParser.java |  267 -----
 .../brooklyn/util/text/TemplateProcessor.java   |  397 -------
 ...ompilerIndependentOuterClassFieldMapper.java |  166 ---
 .../xstream/EnumCaseForgivingConverter.java     |   60 -
 .../EnumCaseForgivingSingleValueConverter.java  |   35 -
 .../util/xstream/ImmutableListConverter.java    |   54 -
 .../util/xstream/ImmutableMapConverter.java     |   56 -
 .../util/xstream/ImmutableSetConverter.java     |   54 -
 .../util/xstream/Inet4AddressConverter.java     |   65 --
 .../brooklyn/util/xstream/MapConverter.java     |  104 --
 .../util/xstream/MutableSetConverter.java       |   44 -
 .../util/xstream/StringKeyMapConverter.java     |  134 ---
 .../brooklyn/util/xstream/XmlSerializer.java    |   97 --
 .../java/brooklyn/util/xstream/XmlUtil.java     |   59 -
 .../catalog/internal/BasicBrooklynCatalog.java  |    2 +-
 .../catalog/internal/CatalogClasspathDo.java    |    6 +-
 .../core/catalog/internal/CatalogDto.java       |    2 +-
 .../core/catalog/internal/CatalogDtoUtils.java  |    2 +-
 .../catalog/internal/CatalogInitialization.java |    4 +-
 .../internal/CatalogItemDtoAbstract.java        |    4 +-
 .../catalog/internal/CatalogXmlSerializer.java  |    4 +-
 .../internal/BrooklynFeatureEnablement.java     |    2 +-
 .../core/internal/BrooklynInitialization.java   |    9 +-
 .../management/entitlement/Entitlements.java    |    2 +-
 .../ha/HighAvailabilityManagerImpl.java         |    4 +-
 .../core/management/ha/OsgiManager.java         |    4 +-
 .../internal/AbstractManagementContext.java     |    8 +-
 .../internal/AsyncCollectionChangeAdapter.java  |    4 +-
 .../internal/BrooklynGarbageCollector.java      |    6 +-
 .../core/management/internal/EffectorUtils.java |    4 +-
 .../internal/EntityManagementUtils.java         |    4 +-
 .../management/internal/LocalEntityManager.java |    2 +-
 .../internal/LocalLocationManager.java          |    4 +-
 .../internal/LocalManagementContext.java        |   10 +-
 .../internal/LocalSubscriptionManager.java      |    4 +-
 .../management/internal/LocalUsageManager.java  |    2 +-
 .../internal/ManagementContextInternal.java     |    2 +-
 .../core/util/BrooklynLanguageExtensions.java   |   48 +
 .../core/util/BrooklynMavenArtifacts.java       |   58 +
 .../core/util/BrooklynNetworkUtils.java         |   44 +
 .../brooklyn/core/util/ResourceUtils.java       |  639 ++++++++++
 .../brooklyn/core/util/config/ConfigBag.java    |  589 ++++++++++
 .../core/util/crypto/FluentKeySigner.java       |  192 +++
 .../brooklyn/core/util/crypto/SecureKeys.java   |  186 +++
 .../brooklyn/core/util/file/ArchiveBuilder.java |  424 +++++++
 .../brooklyn/core/util/file/ArchiveTasks.java   |   58 +
 .../brooklyn/core/util/file/ArchiveUtils.java   |  351 ++++++
 .../core/util/flags/ClassCoercionException.java |   39 +
 .../brooklyn/core/util/flags/FlagUtils.java     |  587 ++++++++++
 .../core/util/flags/MethodCoercions.java        |  183 +++
 .../brooklyn/core/util/flags/SetFromFlag.java   |   71 ++
 .../brooklyn/core/util/flags/TypeCoercions.java |  879 ++++++++++++++
 .../brooklyn/core/util/http/HttpTool.java       |  387 +++++++
 .../core/util/http/HttpToolResponse.java        |  185 +++
 .../util/internal/ConfigKeySelfExtracting.java  |   41 +
 .../brooklyn/core/util/internal/Repeater.java   |  370 ++++++
 .../ssh/BackoffLimitedRetryHandler.java         |   74 ++
 .../util/internal/ssh/ShellAbstractTool.java    |  442 +++++++
 .../core/util/internal/ssh/ShellTool.java       |  113 ++
 .../core/util/internal/ssh/SshAbstractTool.java |  172 +++
 .../core/util/internal/ssh/SshException.java    |   32 +
 .../core/util/internal/ssh/SshTool.java         |  174 +++
 .../core/util/internal/ssh/cli/SshCliTool.java  |  317 +++++
 .../util/internal/ssh/process/ProcessTool.java  |  215 ++++
 .../internal/ssh/sshj/SshjClientConnection.java |  282 +++++
 .../core/util/internal/ssh/sshj/SshjTool.java   | 1091 ++++++++++++++++++
 .../core/util/javalang/ReflectionScanner.java   |  135 +++
 .../core/util/javalang/UrlClassLoader.java      |   70 ++
 .../brooklyn/core/util/mutex/MutexSupport.java  |  119 ++
 .../core/util/mutex/SemaphoreForTasks.java      |  112 ++
 .../core/util/mutex/SemaphoreWithOwners.java    |  231 ++++
 .../brooklyn/core/util/mutex/WithMutexes.java   |   45 +
 .../apache/brooklyn/core/util/osgi/Osgis.java   |  720 ++++++++++++
 .../util/task/AbstractExecutionContext.java     |   75 ++
 .../core/util/task/BasicExecutionContext.java   |  221 ++++
 .../core/util/task/BasicExecutionManager.java   |  755 ++++++++++++
 .../brooklyn/core/util/task/BasicTask.java      |  892 ++++++++++++++
 .../brooklyn/core/util/task/CanSetName.java     |   25 +
 .../brooklyn/core/util/task/CompoundTask.java   |  131 +++
 .../core/util/task/DeferredSupplier.java        |   38 +
 .../core/util/task/DynamicSequentialTask.java   |  480 ++++++++
 .../brooklyn/core/util/task/DynamicTasks.java   |  337 ++++++
 .../core/util/task/ExecutionListener.java       |   31 +
 .../brooklyn/core/util/task/ExecutionUtils.java |   49 +
 .../brooklyn/core/util/task/ForwardingTask.java |  325 ++++++
 .../util/task/ListenableForwardingFuture.java   |   50 +
 .../brooklyn/core/util/task/ParallelTask.java   |   85 ++
 .../brooklyn/core/util/task/ScheduledTask.java  |  185 +++
 .../brooklyn/core/util/task/SequentialTask.java |   58 +
 .../core/util/task/SingleThreadedScheduler.java |  216 ++++
 .../brooklyn/core/util/task/TaskBuilder.java    |  184 +++
 .../brooklyn/core/util/task/TaskInternal.java   |  125 ++
 .../brooklyn/core/util/task/TaskScheduler.java  |   41 +
 .../brooklyn/core/util/task/TaskTags.java       |   71 ++
 .../apache/brooklyn/core/util/task/Tasks.java   |  488 ++++++++
 .../brooklyn/core/util/task/ValueResolver.java  |  426 +++++++
 .../core/util/task/ssh/SshFetchTaskFactory.java |   88 ++
 .../core/util/task/ssh/SshFetchTaskWrapper.java |  135 +++
 .../core/util/task/ssh/SshPutTaskFactory.java   |  123 ++
 .../core/util/task/ssh/SshPutTaskStub.java      |   69 ++
 .../core/util/task/ssh/SshPutTaskWrapper.java   |  189 +++
 .../brooklyn/core/util/task/ssh/SshTasks.java   |  236 ++++
 .../internal/AbstractSshExecTaskFactory.java    |   58 +
 .../ssh/internal/PlainSshExecTaskFactory.java   |   71 ++
 .../util/task/system/ProcessTaskFactory.java    |   66 ++
 .../core/util/task/system/ProcessTaskStub.java  |  102 ++
 .../util/task/system/ProcessTaskWrapper.java    |  187 +++
 .../core/util/task/system/SystemTasks.java      |   29 +
 .../internal/AbstractProcessTaskFactory.java    |  216 ++++
 .../system/internal/ExecWithLoggingHelpers.java |  202 ++++
 .../internal/SystemProcessTaskFactory.java      |  131 +++
 .../core/util/text/DataUriSchemeParser.java     |  267 +++++
 .../core/util/text/TemplateProcessor.java       |  398 +++++++
 ...ompilerIndependentOuterClassFieldMapper.java |  166 +++
 .../xstream/EnumCaseForgivingConverter.java     |   60 +
 .../EnumCaseForgivingSingleValueConverter.java  |   35 +
 .../util/xstream/ImmutableListConverter.java    |   54 +
 .../util/xstream/ImmutableMapConverter.java     |   56 +
 .../util/xstream/ImmutableSetConverter.java     |   54 +
 .../util/xstream/Inet4AddressConverter.java     |   65 ++
 .../core/util/xstream/MapConverter.java         |  104 ++
 .../core/util/xstream/MutableSetConverter.java  |   44 +
 .../util/xstream/StringKeyMapConverter.java     |  134 +++
 .../core/util/xstream/XmlSerializer.java        |   97 ++
 .../brooklyn/core/util/xstream/XmlUtil.java     |   59 +
 .../location/access/BrooklynAccessUtils.java    |    8 +-
 .../PortForwardManagerLocationResolver.java     |    3 +-
 .../location/basic/AbstractLocation.java        |    6 +-
 .../basic/AbstractLocationResolver.java         |    2 +-
 .../AggregatingMachineProvisioningLocation.java |    2 +-
 .../location/basic/BasicLocationRegistry.java   |    2 +-
 .../location/basic/BasicMachineDetails.java     |   10 +-
 .../location/basic/ByonLocationResolver.java    |    4 +-
 .../FixedListMachineProvisioningLocation.java   |    4 +-
 .../location/basic/HostLocationResolver.java    |    2 +-
 .../basic/LocalhostLocationResolver.java        |    3 +-
 .../LocalhostMachineProvisioningLocation.java   |   10 +-
 ...calhostPropertiesFromBrooklynProperties.java |    3 +-
 .../location/basic/LocationConfigUtils.java     |    8 +-
 .../location/basic/LocationInternal.java        |    2 +-
 ...ocationPropertiesFromBrooklynProperties.java |    4 +-
 .../brooklyn/location/basic/MultiLocation.java  |    2 +-
 .../location/basic/NamedLocationResolver.java   |    2 +-
 .../brooklyn/location/basic/PortRanges.java     |    3 +-
 .../basic/SingleMachineLocationResolver.java    |    2 +-
 .../SingleMachineProvisioningLocation.java      |    3 +-
 .../location/basic/SshMachineLocation.java      |   32 +-
 ...bstractCloudMachineProvisioningLocation.java |    4 +-
 .../location/cloud/CloudLocationConfig.java     |    3 +-
 .../cloud/names/AbstractCloudMachineNamer.java  |    4 +-
 .../cloud/names/BasicCloudMachineNamer.java     |    4 +-
 .../location/cloud/names/CloudMachineNamer.java |    3 +-
 .../cloud/names/CustomMachineNamer.java         |    6 +-
 .../location/dynamic/DynamicLocation.java       |    2 +-
 .../location/dynamic/LocationOwner.java         |    2 +-
 .../brooklyn/location/geo/HostGeoInfo.java      |    2 +-
 .../location/geo/LocalhostExternalIpLoader.java |    2 +-
 .../brooklyn/camp/lite/CampYamlLiteTest.java    |    4 +-
 .../camp/lite/TestAppAssemblyInstantiator.java  |    2 +-
 .../enricher/basic/BasicEnricherTest.java       |    2 +-
 .../java/brooklyn/entity/EffectorSayHiTest.java |    2 +-
 .../java/brooklyn/entity/SetFromFlagTest.java   |    2 +-
 .../brooklyn/entity/basic/ConfigMapTest.java    |    4 +-
 .../basic/DependentConfigurationTest.java       |    2 +-
 .../brooklyn/entity/basic/EntityConfigTest.java |    2 +-
 .../brooklyn/entity/basic/EntitySpecTest.java   |    2 +-
 ...apListAndOtherStructuredConfigKeyTest.groovy |    2 +-
 .../brooklyn/entity/basic/SanitizerTest.java    |    3 +-
 .../entity/effector/EffectorBasicTest.java      |    2 +-
 .../effector/EffectorConcatenateTest.java       |    4 +-
 .../entity/effector/EffectorTaskTest.java       |   10 +-
 .../java/brooklyn/entity/rebind/Dumpers.java    |    2 +-
 .../entity/rebind/RebindCatalogEntityTest.java  |    3 +-
 .../entity/rebind/RebindEnricherTest.java       |    2 +-
 .../rebind/RebindEntityDynamicTypeInfoTest.java |    2 +-
 .../entity/rebind/RebindEntityTest.java         |    2 +-
 .../entity/rebind/RebindFailuresTest.java       |    2 +-
 .../brooklyn/entity/rebind/RebindFeedTest.java  |    4 +-
 .../entity/rebind/RebindFeedWithHaTest.java     |    4 +-
 .../entity/rebind/RebindLocationTest.java       |    2 +-
 .../entity/rebind/RebindManagerTest.java        |    5 +-
 .../entity/rebind/RebindPolicyTest.java         |    2 +-
 .../entity/rebind/RebindTestFixture.java        |    2 +-
 .../transformer/impl/XsltTransformerTest.java   |    4 +-
 .../brooklyn/entity/trait/FailingEntity.java    |    2 +-
 .../entity/trait/FailingEntityImpl.java         |    2 +-
 .../entity/trait/StartableMethodsTest.java      |    3 +-
 .../java/brooklyn/event/feed/PollerTest.java    |    2 +-
 .../brooklyn/event/feed/http/HttpFeedTest.java  |    4 +-
 .../event/feed/http/HttpValueFunctionsTest.java |    3 +-
 .../brooklyn/policy/basic/BasicPolicyTest.java  |    2 +-
 .../EntityCleanupLongevityTestFixture.java      |    4 +-
 .../FilePersistencePerformanceTest.java         |    2 +-
 .../qa/performance/TaskPerformanceTest.java     |    4 +-
 .../test/java/brooklyn/test/HttpService.java    |    4 +-
 .../java/brooklyn/test/policy/TestEnricher.java |    2 +-
 .../java/brooklyn/test/policy/TestPolicy.java   |    2 +-
 .../util/BrooklynMavenArtifactsTest.java        |   96 --
 .../brooklyn/util/ResourceUtilsHttpTest.java    |  196 ----
 .../java/brooklyn/util/ResourceUtilsTest.java   |  189 ---
 .../brooklyn/util/config/ConfigBagTest.java     |  191 ---
 .../util/crypto/SecureKeysAndSignerTest.java    |  166 ---
 .../brooklyn/util/file/ArchiveBuilderTest.java  |  193 ----
 .../brooklyn/util/file/ArchiveUtilsTest.java    |  135 ---
 .../util/flags/MethodCoercionsTest.java         |  146 ---
 .../brooklyn/util/http/BetterMockWebServer.java |  138 ---
 .../util/http/HttpToolIntegrationTest.java      |   98 --
 .../brooklyn/util/internal/FlagUtilsTest.java   |  314 -----
 .../brooklyn/util/internal/RepeaterTest.groovy  |  255 ----
 .../util/internal/TypeCoercionsTest.java        |  360 ------
 .../util/internal/ssh/RecordingSshTool.java     |   95 --
 .../internal/ssh/ShellToolAbstractTest.java     |  439 -------
 .../ssh/SshToolAbstractIntegrationTest.java     |  301 -----
 .../ssh/SshToolAbstractPerformanceTest.java     |  137 ---
 .../ssh/cli/SshCliToolIntegrationTest.java      |  119 --
 .../ssh/cli/SshCliToolPerformanceTest.java      |   44 -
 .../ssh/process/ProcessToolIntegrationTest.java |   69 --
 .../ssh/process/ProcessToolStaticsTest.java     |   79 --
 .../sshj/SshjToolAsyncStubIntegrationTest.java  |  177 ---
 .../ssh/sshj/SshjToolIntegrationTest.java       |  313 -----
 .../ssh/sshj/SshjToolPerformanceTest.java       |   44 -
 .../brooklyn/util/mutex/WithMutexesTest.java    |  126 --
 .../test/java/brooklyn/util/osgi/OsgisTest.java |   41 -
 .../util/ssh/BashCommandsIntegrationTest.java   |  501 --------
 .../task/BasicTaskExecutionPerformanceTest.java |  206 ----
 .../util/task/BasicTaskExecutionTest.java       |  460 --------
 .../util/task/BasicTasksFutureTest.java         |  224 ----
 .../util/task/CompoundTaskExecutionTest.java    |  252 ----
 .../util/task/DynamicSequentialTaskTest.java    |  365 ------
 .../util/task/NonBasicTaskExecutionTest.java    |  126 --
 .../util/task/ScheduledExecutionTest.java       |  287 -----
 .../util/task/SingleThreadedSchedulerTest.java  |  192 ---
 .../util/task/TaskFinalizationTest.java         |   62 -
 .../test/java/brooklyn/util/task/TasksTest.java |  181 ---
 .../brooklyn/util/task/ValueResolverTest.java   |  132 ---
 .../brooklyn/util/task/ssh/SshTasksTest.java    |  207 ----
 .../util/task/system/SystemTasksTest.java       |  134 ---
 .../util/text/DataUriSchemeParserTest.java      |   52 -
 .../util/text/TemplateProcessorTest.java        |  179 ---
 .../util/xstream/CompilerCompatibilityTest.java |  154 ---
 .../util/xstream/ConverterTestFixture.java      |   40 -
 .../xstream/EnumCaseForgivingConverterTest.java |   52 -
 .../xstream/ImmutableListConverterTest.java     |   59 -
 .../util/xstream/InetAddressConverterTest.java  |   41 -
 .../util/xstream/StringKeyMapConverterTest.java |   77 --
 .../java/brooklyn/util/xstream/XmlUtilTest.java |   33 -
 .../core/catalog/internal/CatalogDtoTest.java   |    2 +-
 .../core/catalog/internal/CatalogLoadTest.java  |    3 +-
 .../core/catalog/internal/CatalogScanTest.java  |    2 +-
 .../AcmeEntitlementManagerTestFixture.java      |    2 +-
 .../entitlement/EntityEntitlementTest.java      |    2 +-
 .../internal/EntityExecutionManagerTest.java    |    8 +-
 .../management/osgi/OsgiStandaloneTest.java     |    6 +-
 .../osgi/OsgiVersionMoreEntityTest.java         |    2 +-
 .../core/util/BrooklynMavenArtifactsTest.java   |   98 ++
 .../core/util/ResourceUtilsHttpTest.java        |  197 ++++
 .../brooklyn/core/util/ResourceUtilsTest.java   |  190 +++
 .../core/util/config/ConfigBagTest.java         |  193 ++++
 .../util/crypto/SecureKeysAndSignerTest.java    |  169 +++
 .../core/util/file/ArchiveBuilderTest.java      |  194 ++++
 .../core/util/file/ArchiveUtilsTest.java        |  139 +++
 .../core/util/flags/MethodCoercionsTest.java    |  149 +++
 .../core/util/http/BetterMockWebServer.java     |  138 +++
 .../core/util/http/HttpToolIntegrationTest.java |  100 ++
 .../core/util/internal/FlagUtilsTest.java       |  314 +++++
 .../core/util/internal/RepeaterTest.groovy      |  257 +++++
 .../core/util/internal/TypeCoercionsTest.java   |  360 ++++++
 .../util/internal/ssh/RecordingSshTool.java     |   97 ++
 .../internal/ssh/ShellToolAbstractTest.java     |  441 +++++++
 .../ssh/SshToolAbstractIntegrationTest.java     |  304 +++++
 .../ssh/SshToolAbstractPerformanceTest.java     |  138 +++
 .../ssh/cli/SshCliToolIntegrationTest.java      |  119 ++
 .../ssh/cli/SshCliToolPerformanceTest.java      |   44 +
 .../ssh/process/ProcessToolIntegrationTest.java |   69 ++
 .../ssh/process/ProcessToolStaticsTest.java     |   80 ++
 .../sshj/SshjToolAsyncStubIntegrationTest.java  |  178 +++
 .../ssh/sshj/SshjToolIntegrationTest.java       |  314 +++++
 .../ssh/sshj/SshjToolPerformanceTest.java       |   44 +
 .../core/util/mutex/WithMutexesTest.java        |  129 +++
 .../brooklyn/core/util/osgi/OsgisTest.java      |   41 +
 .../util/ssh/BashCommandsIntegrationTest.java   |  504 ++++++++
 .../task/BasicTaskExecutionPerformanceTest.java |  209 ++++
 .../core/util/task/BasicTaskExecutionTest.java  |  462 ++++++++
 .../core/util/task/BasicTasksFutureTest.java    |  227 ++++
 .../util/task/CompoundTaskExecutionTest.java    |  258 +++++
 .../util/task/DynamicSequentialTaskTest.java    |  371 ++++++
 .../util/task/NonBasicTaskExecutionTest.java    |  130 +++
 .../core/util/task/ScheduledExecutionTest.java  |  291 +++++
 .../util/task/SingleThreadedSchedulerTest.java  |  195 ++++
 .../core/util/task/TaskFinalizationTest.java    |   63 +
 .../brooklyn/core/util/task/TasksTest.java      |  184 +++
 .../core/util/task/ValueResolverTest.java       |  134 +++
 .../core/util/task/ssh/SshTasksTest.java        |  213 ++++
 .../core/util/task/system/SystemTasksTest.java  |  137 +++
 .../core/util/text/DataUriSchemeParserTest.java |   53 +
 .../core/util/text/TemplateProcessorTest.java   |  180 +++
 .../util/xstream/CompilerCompatibilityTest.java |  158 +++
 .../core/util/xstream/ConverterTestFixture.java |   40 +
 .../xstream/EnumCaseForgivingConverterTest.java |   53 +
 .../xstream/ImmutableListConverterTest.java     |   60 +
 .../util/xstream/InetAddressConverterTest.java  |   42 +
 .../util/xstream/StringKeyMapConverterTest.java |   78 ++
 .../brooklyn/core/util/xstream/XmlUtilTest.java |   34 +
 .../location/basic/AbstractLocationTest.java    |    2 +-
 .../basic/LegacyAbstractLocationTest.java       |    2 +-
 .../location/basic/LocationConfigTest.java      |    2 +-
 .../location/basic/LocationConfigUtilsTest.java |    3 +-
 .../brooklyn/location/basic/PortRangesTest.java |    3 +-
 .../SshMachineLocationIntegrationTest.java      |    9 +-
 .../SshMachineLocationPerformanceTest.java      |    2 +-
 .../SshMachineLocationReuseIntegrationTest.java |    4 +-
 .../location/basic/SshMachineLocationTest.java  |   14 +-
 .../location/cloud/CloudMachineNamerTest.java   |    4 +-
 .../location/cloud/CustomMachineNamerTest.java  |    3 +-
 .../brooklyn/test/entity/BlockingEntity.java    |    2 +-
 .../apache/brooklyn/test/entity/TestEntity.java |    2 +-
 .../rebind/compiler_compatibility_eclipse.xml   |   20 +-
 .../rebind/compiler_compatibility_oracle.xml    |   18 +-
 .../brooklyn/demo/GlobalWebFabricExample.java   |    6 +-
 .../brooklyn/demo/CumulusRDFApplication.java    |    8 +-
 .../demo/WebClusterDatabaseExampleApp.java      |    6 +-
 .../brooklyn/policy/os/CreateUserPolicy.java    |    4 +-
 .../location/jclouds/BrooklynMachinePool.java   |    2 +-
 .../jclouds/ComputeServiceRegistry.java         |    3 +-
 .../jclouds/ComputeServiceRegistryImpl.java     |    2 +-
 .../jclouds/JcloudsByonLocationResolver.java    |    2 +-
 .../location/jclouds/JcloudsLocation.java       |   18 +-
 .../location/jclouds/JcloudsLocationConfig.java |    3 +-
 .../jclouds/JcloudsLocationCustomizer.java      |    3 +-
 .../location/jclouds/JcloudsMachineNamer.java   |    2 +-
 ...JcloudsPropertiesFromBrooklynProperties.java |    4 +-
 .../jclouds/JcloudsSshMachineLocation.java      |    2 +-
 .../brooklyn/location/jclouds/JcloudsUtil.java  |    2 +-
 .../jclouds/JcloudsWinRmMachineLocation.java    |    2 +-
 .../jclouds/SudoTtyFixingCustomizer.java        |    7 +-
 .../JcloudsLocationSecurityGroupCustomizer.java |    4 +-
 .../persister/jclouds/BlobStoreExpiryTest.java  |    6 +-
 .../policy/os/CreateUserPolicyLiveTest.java     |    2 +-
 .../policy/os/CreateUserPolicyTest.java         |    3 +-
 .../jclouds/AbstractJcloudsStubbedLiveTest.java |    3 +-
 .../jclouds/BailOutJcloudsLocation.java         |    2 +-
 ...ationTemplateOptionsCustomisersLiveTest.java |    4 +-
 .../location/jclouds/JcloudsLocationTest.java   |    2 +-
 .../jclouds/JcloudsMachineNamerTest.java        |    2 +-
 .../jclouds/RebindJcloudsLocationLiveTest.java  |    2 +-
 .../jclouds/RebindJcloudsLocationTest.java      |    3 +-
 .../java/brooklyn/enricher/DeltaEnricher.java   |    2 +-
 .../brooklyn/enricher/HttpLatencyDetector.java  |    2 +-
 .../brooklyn/enricher/RollingMeanEnricher.java  |    2 +-
 .../enricher/RollingTimeWindowMeanEnricher.java |    2 +-
 .../enricher/TimeFractionDeltaEnricher.java     |    2 +-
 .../enricher/TimeWeightedDeltaEnricher.java     |    2 +-
 .../entity/brooklyn/BrooklynMetrics.java        |    2 +-
 .../entity/brooklyn/BrooklynMetricsImpl.java    |    2 +-
 .../policy/autoscaling/AutoScalerPolicy.java    |    6 +-
 .../policy/followthesun/FollowTheSunPolicy.java |    2 +-
 .../policy/ha/AbstractFailureDetector.java      |    6 +-
 .../policy/ha/ConditionalSuspendPolicy.java     |    2 +-
 .../policy/ha/ConnectionFailureDetector.java    |    2 +-
 .../policy/ha/ServiceFailureDetector.java       |    8 +-
 .../brooklyn/policy/ha/ServiceReplacer.java     |    4 +-
 .../brooklyn/policy/ha/ServiceRestarter.java    |    4 +-
 .../policy/ha/SshMachineFailureDetector.java    |    4 +-
 .../loadbalancing/ItemsInContainersGroup.java   |    2 +-
 .../loadbalancing/LoadBalancingPolicy.java      |    2 +-
 .../brooklyn/policy/loadbalancing/Movable.java  |    2 +-
 .../enricher/HttpLatencyDetectorTest.java       |    4 +-
 .../brooklyn/enricher/RebindEnricherTest.java   |    2 +-
 .../brooklyn/policy/ha/ServiceReplacerTest.java |    2 +-
 .../policy/ha/ServiceRestarterTest.java         |    2 +-
 .../loadbalancing/MockContainerEntity.java      |    2 +-
 .../basic/AbstractSoftwareProcessDriver.java    |    8 +-
 .../basic/AbstractSoftwareProcessSshDriver.java |   12 +-
 .../SameServerDriverLifecycleEffectorTasks.java |    4 +-
 .../brooklyn/entity/basic/SameServerEntity.java |    2 +-
 .../entity/basic/SameServerEntityImpl.java      |    6 +-
 .../brooklyn/entity/basic/SoftwareProcess.java  |    2 +-
 ...wareProcessDriverLifecycleEffectorTasks.java |    4 +-
 .../entity/basic/SoftwareProcessImpl.java       |    8 +-
 .../basic/VanillaSoftwareProcessSshDriver.java  |    4 +-
 .../basic/lifecycle/NaiveScriptRunner.java      |    2 +-
 .../entity/basic/lifecycle/ScriptHelper.java    |   12 +-
 .../brooklynnode/BrooklynEntityMirrorImpl.java  |    8 +-
 .../entity/brooklynnode/BrooklynNode.java       |    2 +-
 .../entity/brooklynnode/BrooklynNodeImpl.java   |   12 +-
 .../brooklynnode/BrooklynNodeSshDriver.java     |   10 +-
 .../entity/brooklynnode/EntityHttpClient.java   |    4 +-
 .../brooklynnode/EntityHttpClientImpl.java      |    6 +-
 .../brooklynnode/RemoteEffectorBuilder.java     |    2 +-
 .../BrooklynClusterUpgradeEffectorBody.java     |    6 +-
 .../BrooklynNodeUpgradeEffectorBody.java        |    6 +-
 .../effector/SelectMasterEffectorBody.java      |    4 +-
 .../SetHighAvailabilityModeEffectorBody.java    |    4 +-
 ...SetHighAvailabilityPriorityEffectorBody.java |    4 +-
 .../brooklyn/entity/chef/ChefAttributeFeed.java |    4 +-
 .../java/brooklyn/entity/chef/ChefConfig.java   |    3 +-
 .../entity/chef/ChefLifecycleEffectorTasks.java |   10 +-
 .../brooklyn/entity/chef/ChefServerTasks.java   |    2 +-
 .../brooklyn/entity/chef/ChefSoloDriver.java    |    3 +-
 .../java/brooklyn/entity/chef/ChefTasks.java    |   10 +-
 .../entity/chef/KnifeConvergeTaskFactory.java   |    4 +-
 .../brooklyn/entity/chef/KnifeTaskFactory.java  |   10 +-
 .../java/JavaSoftwareProcessSshDriver.java      |   16 +-
 .../java/brooklyn/entity/java/JmxSupport.java   |    8 +-
 .../brooklyn/entity/java/JmxmpSslSupport.java   |    7 +-
 .../java/brooklyn/entity/java/UsesJava.java     |    3 +-
 .../brooklyn/entity/java/UsesJavaMXBeans.java   |    2 +-
 .../main/java/brooklyn/entity/java/UsesJmx.java |    3 +-
 .../brooklyn/entity/java/VanillaJavaApp.java    |    2 +-
 .../entity/java/VanillaJavaAppImpl.java         |    2 +-
 .../entity/java/VanillaJavaAppSshDriver.java    |   10 +-
 .../entity/machine/MachineEntityImpl.java       |    6 +-
 .../brooklyn/entity/pool/ServerPoolImpl.java    |    2 +-
 .../entity/pool/ServerPoolLocation.java         |    3 +-
 .../entity/service/EntityLaunchListener.java    |    2 +-
 .../entity/service/InitdServiceInstaller.java   |   12 +-
 .../entity/service/SystemServiceEnricher.java   |   14 +-
 .../entity/software/MachineInitTasks.java       |    8 +-
 .../software/MachineLifecycleEffectorTasks.java |    8 +-
 .../software/ProvidesProvisioningFlags.java     |    3 +-
 .../entity/software/SshEffectorTasks.java       |   24 +-
 .../brooklyn/entity/software/StaticSensor.java  |    6 +-
 .../entity/software/http/HttpRequestSensor.java |    2 +-
 .../software/java/JmxAttributeSensor.java       |    6 +-
 .../entity/software/ssh/SshCommandEffector.java |    2 +-
 .../entity/software/ssh/SshCommandSensor.java   |    4 +-
 .../winrm/WindowsPerformanceCounterSensors.java |    2 +-
 .../java/brooklyn/event/feed/jmx/JmxHelper.java |    2 +-
 .../basic/SoftwareProcessEntityLatchTest.java   |    2 +-
 .../basic/SoftwareProcessEntityRebindTest.java  |    3 +-
 .../entity/basic/SoftwareProcessEntityTest.java |    6 +-
 ...SoftwareProcessSshDriverIntegrationTest.java |    2 +-
 ...ftwareProcessAndChildrenIntegrationTest.java |    2 +-
 .../entity/basic/lifecycle/MyEntityImpl.java    |    6 +-
 .../basic/lifecycle/NaiveScriptRunnerTest.java  |    6 +-
 .../basic/lifecycle/StartStopSshDriverTest.java |    8 +-
 .../BrooklynNodeIntegrationTest.java            |    8 +-
 .../brooklynnode/CallbackEntityHttpClient.java  |    4 +-
 .../entity/chef/ChefLiveTestSupport.java        |    2 +-
 .../chef/ChefServerTasksIntegrationTest.java    |    2 +-
 .../ChefSoloDriverMySqlEntityLiveTest.java      |    2 +-
 .../java/brooklyn/entity/java/JavaOptsTest.java |    6 +-
 .../brooklyn/entity/java/JmxSupportTest.java    |    4 +-
 .../brooklyn/entity/java/SslKeyConfigTest.java  |    5 +-
 .../entity/java/VanillaJavaAppRebindTest.java   |    2 +-
 .../entity/java/VanillaJavaAppTest.java         |    8 +-
 .../MachineLifecycleEffectorTasksTest.java      |    4 +-
 .../entity/software/SoftwareEffectorTest.java   |    5 +-
 .../entity/software/SshEffectorTasksTest.java   |    6 +-
 .../entity/software/StaticSensorTest.java       |    2 +-
 .../software/http/HttpRequestSensorTest.java    |    2 +-
 .../mysql/AbstractToyMySqlEntityTest.java       |    2 +-
 .../mysql/DynamicToyMySqlEntityBuilder.java     |    8 +-
 .../software/ssh/SshCommandIntegrationTest.java |    2 +-
 .../entity/database/DatastoreMixins.java        |    4 +-
 .../entity/database/crate/CrateNode.java        |    3 +-
 .../entity/database/mariadb/MariaDbDriver.java  |    3 +-
 .../entity/database/mariadb/MariaDbNode.java    |    3 +-
 .../database/mariadb/MariaDbNodeImpl.java       |    4 +-
 .../database/mariadb/MariaDbSshDriver.java      |    4 +-
 .../entity/database/mysql/MySqlClusterImpl.java |    4 +-
 .../entity/database/mysql/MySqlDriver.java      |    3 +-
 .../entity/database/mysql/MySqlNode.java        |    3 +-
 .../entity/database/mysql/MySqlNodeImpl.java    |    4 +-
 .../entity/database/mysql/MySqlSshDriver.java   |    4 +-
 .../database/postgresql/PostgreSqlDriver.java   |    3 +-
 .../database/postgresql/PostgreSqlNode.java     |    3 +-
 .../PostgreSqlNodeChefImplFromScratch.java      |    8 +-
 .../database/postgresql/PostgreSqlNodeImpl.java |    2 +-
 .../postgresql/PostgreSqlSshDriver.java         |    8 +-
 .../entity/database/rubyrep/RubyRepNode.java    |    2 +-
 .../database/postgresql/PostgreSqlChefTest.java |    2 +-
 .../messaging/activemq/ActiveMQBroker.java      |    2 +-
 .../entity/messaging/amqp/AmqpExchange.java     |    2 +-
 .../brooklyn/entity/messaging/kafka/Kafka.java  |    3 +-
 .../entity/messaging/kafka/KafkaBroker.java     |    4 +-
 .../entity/messaging/kafka/KafkaCluster.java    |    2 +-
 .../entity/messaging/kafka/KafkaZooKeeper.java  |    2 +-
 .../entity/messaging/qpid/QpidBroker.java       |    2 +-
 .../messaging/qpid/QpidDestinationImpl.java     |    2 +-
 .../entity/messaging/rabbit/RabbitBroker.java   |    2 +-
 .../brooklyn/entity/messaging/storm/Storm.java  |    2 +-
 .../entity/messaging/storm/StormDeployment.java |    2 +-
 .../messaging/storm/StormDeploymentImpl.java    |    2 +-
 .../entity/zookeeper/ZooKeeperEnsemble.java     |    2 +-
 .../entity/zookeeper/ZooKeeperNode.java         |    2 +-
 .../storm/StormAbstractCloudLiveTest.java       |    4 +-
 .../entity/monitoring/monit/MonitNode.java      |    2 +-
 .../entity/network/bind/BindDnsServer.java      |    4 +-
 .../nosql/cassandra/CassandraDatacenter.java    |    2 +-
 .../cassandra/CassandraDatacenterImpl.java      |    4 +-
 .../entity/nosql/cassandra/CassandraNode.java   |    4 +-
 .../nosql/cassandra/CassandraNodeDriver.java    |    3 +-
 .../nosql/cassandra/CassandraNodeImpl.java      |    4 +-
 .../nosql/cassandra/CassandraNodeSshDriver.java |    8 +-
 .../nosql/couchbase/CouchbaseCluster.java       |    2 +-
 .../nosql/couchbase/CouchbaseClusterImpl.java   |   10 +-
 .../entity/nosql/couchbase/CouchbaseNode.java   |    2 +-
 .../nosql/couchbase/CouchbaseNodeImpl.java      |    8 +-
 .../nosql/couchbase/CouchbaseNodeSshDriver.java |   12 +-
 .../nosql/couchbase/CouchbaseSyncGateway.java   |    2 +-
 .../entity/nosql/couchdb/CouchDBCluster.java    |    2 +-
 .../entity/nosql/couchdb/CouchDBNode.java       |    2 +-
 .../entity/nosql/couchdb/CouchDBNodeImpl.java   |    2 +-
 .../elasticsearch/ElasticSearchCluster.java     |    2 +-
 .../nosql/elasticsearch/ElasticSearchNode.java  |    3 +-
 .../elasticsearch/ElasticSearchNodeImpl.java    |    4 +-
 .../nosql/mongodb/AbstractMongoDBServer.java    |    2 +-
 .../entity/nosql/mongodb/MongoDBClient.java     |    3 +-
 .../entity/nosql/mongodb/MongoDBReplicaSet.java |    2 +-
 .../entity/nosql/mongodb/MongoDBServer.java     |    2 +-
 .../sharding/CoLocatedMongoDBRouter.java        |    2 +-
 .../sharding/MongoDBShardedDeployment.java      |    2 +-
 .../brooklyn/entity/nosql/redis/RedisSlave.java |    2 +-
 .../brooklyn/entity/nosql/redis/RedisStore.java |    2 +-
 .../brooklyn/entity/nosql/riak/RiakCluster.java |    2 +-
 .../entity/nosql/riak/RiakClusterImpl.java      |    2 +-
 .../brooklyn/entity/nosql/riak/RiakNode.java    |    2 +-
 .../entity/nosql/riak/RiakNodeImpl.java         |    2 +-
 .../entity/nosql/riak/RiakNodeSshDriver.java    |    4 +-
 .../brooklyn/entity/nosql/solr/SolrServer.java  |    4 +-
 .../entity/nosql/solr/SolrServerSshDriver.java  |    2 +-
 .../cassandra/CassandraDatacenterTest.java      |    4 +-
 .../ElasticSearchClusterIntegrationTest.java    |    4 +-
 .../ElasticSearchNodeIntegrationTest.java       |    5 +-
 .../entity/osgi/karaf/KarafContainer.java       |    2 +-
 .../entity/dns/AbstractGeoDnsServiceImpl.java   |    2 +-
 .../dns/geoscaling/GeoscalingDnsService.java    |    2 +-
 .../geoscaling/GeoscalingDnsServiceImpl.java    |    4 +-
 .../geoscaling/GeoscalingScriptGenerator.java   |    3 +-
 .../dns/geoscaling/GeoscalingWebClient.java     |    2 +-
 .../entity/proxy/AbstractController.java        |    2 +-
 .../entity/proxy/AbstractControllerImpl.java    |    2 +-
 .../brooklyn/entity/proxy/LoadBalancer.java     |    2 +-
 .../brooklyn/entity/proxy/ProxySslConfig.java   |    5 +-
 .../entity/proxy/nginx/NginxController.java     |    2 +-
 .../entity/proxy/nginx/NginxControllerImpl.java |    8 +-
 .../entity/proxy/nginx/NginxSshDriver.java      |    8 +-
 .../nginx/NginxTemplateConfigGenerator.java     |    4 +-
 .../brooklyn/entity/proxy/nginx/UrlMapping.java |    2 +-
 .../webapp/ControlledDynamicWebAppCluster.java  |    2 +-
 .../entity/webapp/DynamicWebAppClusterImpl.java |    8 +-
 .../entity/webapp/JavaWebAppService.java        |    2 +-
 .../entity/webapp/JavaWebAppSshDriver.java      |    8 +-
 .../entity/webapp/WebAppServiceConstants.java   |    2 +-
 .../entity/webapp/jboss/JBoss6Server.java       |    2 +-
 .../entity/webapp/jboss/JBoss7Server.java       |    2 +-
 .../entity/webapp/jetty/Jetty6Server.java       |    2 +-
 .../webapp/nodejs/NodeJsWebAppService.java      |    3 +-
 .../webapp/nodejs/NodeJsWebAppSshDriver.java    |    4 +-
 .../entity/webapp/tomcat/Tomcat8Server.java     |    2 +-
 .../entity/webapp/tomcat/TomcatServer.java      |    4 +-
 .../GeoscalingScriptGeneratorTest.java          |    3 +-
 .../dns/geoscaling/GeoscalingWebClientTest.java |    2 +-
 .../entity/proxy/AbstractControllerTest.java    |    2 +-
 .../entity/proxy/ProxySslConfigTest.java        |    2 +-
 .../nginx/NginxRebindWithHaIntegrationTest.java |    4 +-
 .../AbstractWebAppFixtureIntegrationTest.java   |    4 +-
 .../entity/webapp/HttpsSslConfigTest.java       |    2 +-
 .../webapp/WebAppConcurrentDeployTest.java      |    4 +-
 .../test/entity/TestJavaWebAppEntity.java       |    2 +-
 .../test/entity/TestJavaWebAppEntityImpl.java   |    2 +-
 .../app/SampleLocalhostIntegrationTest.java     |    2 +-
 .../camp/brooklyn/YamlLauncherAbstract.java     |    2 +-
 .../BrooklynAssemblyTemplateInstantiator.java   |    4 +-
 .../BrooklynComponentTemplateResolver.java      |   10 +-
 .../BrooklynEntityDecorationResolver.java       |    2 +-
 .../creation/BrooklynYamlTypeInstantiator.java  |    2 +-
 .../spi/dsl/BrooklynDslDeferredSupplier.java    |    2 +-
 .../camp/brooklyn/spi/dsl/DslUtils.java         |    2 +-
 .../spi/dsl/methods/BrooklynDslCommon.java      |   10 +-
 .../brooklyn/spi/dsl/methods/DslComponent.java  |    4 +-
 .../camp/brooklyn/AbstractYamlRebindTest.java   |    4 +-
 .../camp/brooklyn/AbstractYamlTest.java         |    4 +-
 .../camp/brooklyn/DslAndRebindYamlTest.java     |    2 +-
 .../camp/brooklyn/EntitiesYamlTest.java         |    2 +-
 ...aWebAppWithDslYamlRebindIntegrationTest.java |    2 +-
 .../brooklyn/JavaWebAppsIntegrationTest.java    |    2 +-
 .../camp/brooklyn/JavaWebAppsMatchingTest.java  |    4 +-
 .../camp/brooklyn/MapReferenceYamlTest.java     |    2 +-
 .../brooklyn/camp/brooklyn/ObjectsYamlTest.java |    6 +-
 .../brooklyn/ReloadBrooklynPropertiesTest.java  |    2 +-
 .../TestSensorAndEffectorInitializer.java       |    2 +-
 .../catalog/AbstractCatalogXmlTest.java         |    2 +-
 .../CatalogOsgiVersionMoreEntityTest.java       |    2 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java |    2 +-
 .../org/apache/brooklyn/cli/ItemLister.java     |    4 +-
 .../main/java/org/apache/brooklyn/cli/Main.java |    2 +-
 .../apache/brooklyn/cli/lister/ClassFinder.java |    4 +-
 .../java/org/apache/brooklyn/cli/CliTest.java   |    2 +-
 .../brooklyn/launcher/BrooklynWebServer.java    |   14 +-
 .../launcher/config/CustomResourceLocator.java  |    2 +-
 .../entity/basic/VanillaSoftwareYamlTest.java   |    2 +-
 .../brooklynnode/BrooklynNodeRestTest.java      |    6 +-
 .../database/mssql/MssqlBlueprintLiveTest.java  |    5 +-
 .../BrooklynLauncherRebindCatalogTest.java      |    2 +-
 .../launcher/BrooklynWebServerTest.java         |    4 +-
 .../blueprints/AbstractBlueprintTest.java       |    2 +-
 .../qa/load/SimulatedMySqlNodeImpl.java         |    6 +-
 .../brooklyn/qa/longevity/MonitorUtils.java     |    4 +-
 .../SoftlayerObtainPrivateLiveTest.java         |    2 +-
 .../resources/AbstractBrooklynRestResource.java |    2 +-
 .../rest/resources/ApplicationResource.java     |    2 +-
 .../rest/resources/CatalogResource.java         |    2 +-
 .../rest/resources/EntityConfigResource.java    |    4 +-
 .../brooklyn/rest/resources/EntityResource.java |    2 +-
 .../rest/resources/PolicyConfigResource.java    |    3 +-
 .../brooklyn/rest/resources/SensorResource.java |    2 +-
 .../brooklyn/rest/resources/ServerResource.java |    6 +-
 .../rest/transform/EffectorTransformer.java     |    4 +-
 .../rest/transform/LocationTransformer.java     |    2 +-
 .../rest/transform/TaskTransformer.java         |    2 +-
 .../rest/util/BrooklynRestResourceUtils.java    |    2 +-
 .../rest/util/DefaultExceptionMapper.java       |    2 +-
 .../BrooklynPropertiesSecurityFilterTest.java   |    6 +-
 .../brooklyn/rest/HaMasterCheckFilterTest.java  |    4 +-
 .../rest/resources/CatalogResetTest.java        |    2 +-
 .../SensorResourceIntegrationTest.java          |    4 +-
 .../ServerResourceIntegrationTest.java          |    7 +-
 .../testing/mocks/RestMockSimpleEntity.java     |    3 +-
 .../testing/mocks/RestMockSimplePolicy.java     |    2 +-
 .../json/BrooklynJacksonSerializerTest.java     |    2 +-
 .../util/jmx/jmxmp/JmxmpAgentSslTest.java       |    5 +-
 .../brooklyn/util/jmx/jmxmp/JmxmpClient.java    |    3 +-
 .../brooklyn/osgi/tests/SimpleLocation.java     |    3 +-
 .../java/brooklyn/osgi/tests/SimplePolicy.java  |    3 +-
 .../osgi/tests/more/MoreEntityImpl.java         |    3 +-
 .../osgi/tests/more/MoreEntityImpl.java         |    2 +-
 .../osgi/tests/more/MoreEntityImpl.java         |    2 +-
 795 files changed, 28078 insertions(+), 27902 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/BrooklynVersion.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/BrooklynVersion.java b/core/src/main/java/brooklyn/BrooklynVersion.java
index dbc7869..bf45a99 100644
--- a/core/src/main/java/brooklyn/BrooklynVersion.java
+++ b/core/src/main/java/brooklyn/BrooklynVersion.java
@@ -45,11 +45,11 @@ import com.google.common.collect.Maps;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.management.classloading.OsgiBrooklynClassLoadingContext;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.osgi.Osgis;
+import org.apache.brooklyn.core.util.osgi.Osgis.ManifestHelper;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.osgi.Osgis;
-import brooklyn.util.osgi.Osgis.ManifestHelper;
 import brooklyn.util.stream.Streams;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/basic/AbstractBrooklynObject.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/basic/AbstractBrooklynObject.java b/core/src/main/java/brooklyn/basic/AbstractBrooklynObject.java
index c990ed9..76641bf 100644
--- a/core/src/main/java/brooklyn/basic/AbstractBrooklynObject.java
+++ b/core/src/main/java/brooklyn/basic/AbstractBrooklynObject.java
@@ -25,14 +25,14 @@ import java.util.Set;
 import org.apache.brooklyn.api.basic.internal.ApiObjectsFactory;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.proxying.InternalFactory;
 import brooklyn.entity.rebind.RebindManagerImpl;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.text.Identifiers;
 
 import com.google.common.collect.ImmutableSet;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/basic/BasicConfigurableObject.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/basic/BasicConfigurableObject.java b/core/src/main/java/brooklyn/basic/BasicConfigurableObject.java
index d02f1bd..0604f3a 100644
--- a/core/src/main/java/brooklyn/basic/BasicConfigurableObject.java
+++ b/core/src/main/java/brooklyn/basic/BasicConfigurableObject.java
@@ -23,13 +23,13 @@ import org.apache.brooklyn.api.entity.trait.Identifiable;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.core.management.ManagementContextInjectable;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.camp.brooklyn.api.HasBrooklynManagementContext;
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.config.ConfigMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.text.Identifiers;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/basic/BrooklynDynamicType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/basic/BrooklynDynamicType.java b/core/src/main/java/brooklyn/basic/BrooklynDynamicType.java
index dbf8bbc..52d1413 100644
--- a/core/src/main/java/brooklyn/basic/BrooklynDynamicType.java
+++ b/core/src/main/java/brooklyn/basic/BrooklynDynamicType.java
@@ -34,13 +34,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.brooklyn.api.basic.BrooklynObject;
 import org.apache.brooklyn.api.basic.BrooklynType;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.event.basic.BasicConfigKey.BasicConfigKeyOverwriting;
-import brooklyn.util.flags.FlagUtils;
 import brooklyn.util.javalang.Reflections;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/basic/BrooklynObjectInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/basic/BrooklynObjectInternal.java b/core/src/main/java/brooklyn/basic/BrooklynObjectInternal.java
index 038013f..ef9212a 100644
--- a/core/src/main/java/brooklyn/basic/BrooklynObjectInternal.java
+++ b/core/src/main/java/brooklyn/basic/BrooklynObjectInternal.java
@@ -24,10 +24,10 @@ import org.apache.brooklyn.api.basic.BrooklynObject;
 import org.apache.brooklyn.api.entity.rebind.RebindSupport;
 import org.apache.brooklyn.api.entity.rebind.Rebindable;
 import org.apache.brooklyn.api.entity.trait.Configurable;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Maybe;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/basic/internal/ApiObjectsFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/basic/internal/ApiObjectsFactoryImpl.java b/core/src/main/java/brooklyn/basic/internal/ApiObjectsFactoryImpl.java
index 8e0f5a4..654c51a 100644
--- a/core/src/main/java/brooklyn/basic/internal/ApiObjectsFactoryImpl.java
+++ b/core/src/main/java/brooklyn/basic/internal/ApiObjectsFactoryImpl.java
@@ -21,9 +21,9 @@ package brooklyn.basic.internal;
 import org.apache.brooklyn.api.basic.internal.ApiObjectsFactoryInterface;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.Tasks;
 
 import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.util.task.Tasks;
 
 public class ApiObjectsFactoryImpl implements ApiObjectsFactoryInterface {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/config/BrooklynProperties.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/config/BrooklynProperties.java b/core/src/main/java/brooklyn/config/BrooklynProperties.java
index 2c76b21..921fe22 100644
--- a/core/src/main/java/brooklyn/config/BrooklynProperties.java
+++ b/core/src/main/java/brooklyn/config/BrooklynProperties.java
@@ -33,15 +33,15 @@ import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Properties;
 
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.StringFunctions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/config/BrooklynServerPaths.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/config/BrooklynServerPaths.java b/core/src/main/java/brooklyn/config/BrooklynServerPaths.java
index a865d5b..0e0a5c9 100644
--- a/core/src/main/java/brooklyn/config/BrooklynServerPaths.java
+++ b/core/src/main/java/brooklyn/config/BrooklynServerPaths.java
@@ -25,6 +25,7 @@ import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.apache.commons.io.FileUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,7 +37,6 @@ import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
 import brooklyn.util.time.Time;
 
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java b/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java
index defb5bc..c648654 100644
--- a/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java
+++ b/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java
@@ -23,6 +23,8 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.concurrent.Future;
 
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.task.DeferredSupplier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,8 +33,6 @@ import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.config.ConfigMap;
 import brooklyn.entity.basic.ConfigMapViewWithStringKeys;
 import brooklyn.event.basic.StructuredConfigKey;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.task.DeferredSupplier;
 
 public abstract class AbstractConfigMapImpl implements ConfigMap {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/enricher/CustomAggregatingEnricher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/CustomAggregatingEnricher.java b/core/src/main/java/brooklyn/enricher/CustomAggregatingEnricher.java
index 1089ae4..4924684 100644
--- a/core/src/main/java/brooklyn/enricher/CustomAggregatingEnricher.java
+++ b/core/src/main/java/brooklyn/enricher/CustomAggregatingEnricher.java
@@ -26,12 +26,12 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.enricher.basic.AbstractAggregatingEnricher;
 import brooklyn.util.GroovyJavaMethods;
-import brooklyn.util.flags.TypeCoercions;
 
 import com.google.common.base.Function;
 import com.google.common.base.Throwables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/enricher/Enrichers.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/Enrichers.java b/core/src/main/java/brooklyn/enricher/Enrichers.java
index 54af8d1..00aa03f 100644
--- a/core/src/main/java/brooklyn/enricher/Enrichers.java
+++ b/core/src/main/java/brooklyn/enricher/Enrichers.java
@@ -32,6 +32,7 @@ import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.EnricherSpec;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.enricher.basic.Aggregator;
@@ -43,7 +44,6 @@ import brooklyn.enricher.basic.UpdatingMap;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.text.StringPredicates;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java b/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java
index da5e822..5bca143 100644
--- a/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java
+++ b/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.mementos.EnricherMemento;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.EnricherType;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -36,7 +37,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.rebind.BasicEnricherRebindSupport;
 import brooklyn.policy.basic.AbstractEntityAdjunct;
-import brooklyn.util.flags.TypeCoercions;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.Maps;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/enricher/basic/AbstractMultipleSensorAggregator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/basic/AbstractMultipleSensorAggregator.java b/core/src/main/java/brooklyn/enricher/basic/AbstractMultipleSensorAggregator.java
index af47c59..84db77f 100644
--- a/core/src/main/java/brooklyn/enricher/basic/AbstractMultipleSensorAggregator.java
+++ b/core/src/main/java/brooklyn/enricher/basic/AbstractMultipleSensorAggregator.java
@@ -30,12 +30,12 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.BrooklynLogging;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.TypeCoercions;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/enricher/basic/AbstractTypeTransformingEnricher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/basic/AbstractTypeTransformingEnricher.java b/core/src/main/java/brooklyn/enricher/basic/AbstractTypeTransformingEnricher.java
index 7bf0ef6..cd9a33f 100644
--- a/core/src/main/java/brooklyn/enricher/basic/AbstractTypeTransformingEnricher.java
+++ b/core/src/main/java/brooklyn/enricher/basic/AbstractTypeTransformingEnricher.java
@@ -23,9 +23,9 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.event.basic.BasicSensorEvent;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * Convenience base for transforming a single sensor into a single new sensor of the same type

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/enricher/basic/Aggregator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/basic/Aggregator.java b/core/src/main/java/brooklyn/enricher/basic/Aggregator.java
index c14487b..5512c28 100644
--- a/core/src/main/java/brooklyn/enricher/basic/Aggregator.java
+++ b/core/src/main/java/brooklyn/enricher/basic/Aggregator.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,7 +41,6 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.text.StringPredicates;
 
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/enricher/basic/Joiner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/basic/Joiner.java b/core/src/main/java/brooklyn/enricher/basic/Joiner.java
index 8c65681..f9e9d4e 100644
--- a/core/src/main/java/brooklyn/enricher/basic/Joiner.java
+++ b/core/src/main/java/brooklyn/enricher/basic/Joiner.java
@@ -26,6 +26,7 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,7 +34,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicSensorEvent;
 import brooklyn.util.collections.MutableList;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.text.StringEscapes;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/enricher/basic/Propagator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/basic/Propagator.java b/core/src/main/java/brooklyn/enricher/basic/Propagator.java
index 187ad8d..f6d2ee8 100644
--- a/core/src/main/java/brooklyn/enricher/basic/Propagator.java
+++ b/core/src/main/java/brooklyn/enricher/basic/Propagator.java
@@ -28,6 +28,9 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ValueResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,9 +38,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ValueResolver;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/enricher/basic/Transformer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/basic/Transformer.java b/core/src/main/java/brooklyn/enricher/basic/Transformer.java
index 7e331ba..705c626 100644
--- a/core/src/main/java/brooklyn/enricher/basic/Transformer.java
+++ b/core/src/main/java/brooklyn/enricher/basic/Transformer.java
@@ -21,14 +21,14 @@ package brooklyn.enricher.basic;
 import static com.google.common.base.Preconditions.checkArgument;
 
 import org.apache.brooklyn.api.event.SensorEvent;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ValueResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ValueResolver;
 
 import com.google.common.base.Function;
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/enricher/basic/UpdatingMap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/basic/UpdatingMap.java b/core/src/main/java/brooklyn/enricher/basic/UpdatingMap.java
index a973774..a396e6c 100644
--- a/core/src/main/java/brooklyn/enricher/basic/UpdatingMap.java
+++ b/core/src/main/java/brooklyn/enricher/basic/UpdatingMap.java
@@ -25,6 +25,7 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,7 +34,6 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Maps;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/enricher/basic/YamlTimeWeightedDeltaEnricher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/basic/YamlTimeWeightedDeltaEnricher.java b/core/src/main/java/brooklyn/enricher/basic/YamlTimeWeightedDeltaEnricher.java
index 65df607..f94c203 100644
--- a/core/src/main/java/brooklyn/enricher/basic/YamlTimeWeightedDeltaEnricher.java
+++ b/core/src/main/java/brooklyn/enricher/basic/YamlTimeWeightedDeltaEnricher.java
@@ -19,13 +19,13 @@
 package brooklyn.enricher.basic;
 
 import org.apache.brooklyn.api.event.SensorEvent;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.basic.AbstractTransformer;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/AbstractEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/AbstractEffector.java b/core/src/main/java/brooklyn/entity/basic/AbstractEffector.java
index bf29732..2988aeb 100644
--- a/core/src/main/java/brooklyn/entity/basic/AbstractEffector.java
+++ b/core/src/main/java/brooklyn/entity/basic/AbstractEffector.java
@@ -27,14 +27,14 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.ParameterType;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.core.management.internal.EffectorUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicSequentialTask;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.effector.EffectorBase;
 import brooklyn.entity.effector.EffectorTasks.EffectorTaskFactory;
 import brooklyn.entity.effector.EffectorWithBody;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.task.DynamicSequentialTask;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java b/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
index 1d21a7e..4bdae46 100644
--- a/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
+++ b/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
@@ -60,6 +60,10 @@ import org.apache.brooklyn.core.management.internal.EffectorUtils;
 import org.apache.brooklyn.core.management.internal.EntityManagementSupport;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.management.internal.SubscriptionTracker;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.task.DeferredSupplier;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -91,12 +95,8 @@ import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.collections.SetFromLiveMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.Equals;
-import brooklyn.util.task.DeferredSupplier;
 import brooklyn.util.text.Strings;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/BasicGroup.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/BasicGroup.java b/core/src/main/java/brooklyn/entity/basic/BasicGroup.java
index 0bb67b6..05606af 100644
--- a/core/src/main/java/brooklyn/entity/basic/BasicGroup.java
+++ b/core/src/main/java/brooklyn/entity/basic/BasicGroup.java
@@ -19,10 +19,10 @@
 package brooklyn.entity.basic;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 @ImplementedBy(BasicGroupImpl.class)
 public interface BasicGroup extends AbstractGroup {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/BrooklynConfigKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/BrooklynConfigKeys.java b/core/src/main/java/brooklyn/entity/basic/BrooklynConfigKeys.java
index 7910267..d9e4903 100644
--- a/core/src/main/java/brooklyn/entity/basic/BrooklynConfigKeys.java
+++ b/core/src/main/java/brooklyn/entity/basic/BrooklynConfigKeys.java
@@ -26,9 +26,9 @@ import brooklyn.event.basic.AttributeSensorAndConfigKey;
 import brooklyn.event.basic.TemplatedStringAttributeSensorAndConfigKey;
 
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
 
-import brooklyn.util.internal.ssh.ShellTool;
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/BrooklynShutdownHooks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/BrooklynShutdownHooks.java b/core/src/main/java/brooklyn/entity/basic/BrooklynShutdownHooks.java
index 6032fff..79fe68f 100644
--- a/core/src/main/java/brooklyn/entity/basic/BrooklynShutdownHooks.java
+++ b/core/src/main/java/brooklyn/entity/basic/BrooklynShutdownHooks.java
@@ -27,6 +27,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,7 +37,6 @@ import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.RuntimeInterruptedException;
 import brooklyn.util.javalang.Threads;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.CountdownTimer;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java b/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
index 3cdfc39..2904ff5 100644
--- a/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
+++ b/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
@@ -33,16 +33,16 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.ExecutionManager;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.TaskTags;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.MemoryUsageTracker;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.TaskTags;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.StringEscapes.BashStringEscapes;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java b/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
index 2b22a90..a50c800 100644
--- a/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
+++ b/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
@@ -22,6 +22,7 @@ import java.util.Map;
 
 import javax.annotation.Nonnull;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,7 +33,6 @@ import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.TemplatedStringAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicConfigKey.BasicConfigKeyOverwriting;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/DataEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/DataEntity.java b/core/src/main/java/brooklyn/entity/basic/DataEntity.java
index 27f1c33..534e125 100644
--- a/core/src/main/java/brooklyn/entity/basic/DataEntity.java
+++ b/core/src/main/java/brooklyn/entity/basic/DataEntity.java
@@ -23,10 +23,10 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.trait.Startable;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.base.Supplier;
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/DynamicGroup.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/DynamicGroup.java b/core/src/main/java/brooklyn/entity/basic/DynamicGroup.java
index 078fc88..9ad8c83 100644
--- a/core/src/main/java/brooklyn/entity/basic/DynamicGroup.java
+++ b/core/src/main/java/brooklyn/entity/basic/DynamicGroup.java
@@ -23,6 +23,7 @@ import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import com.google.common.base.Predicate;
 import com.google.common.reflect.TypeToken;
@@ -31,7 +32,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.annotation.Effector;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 import groovy.lang.Closure;
 
 @ImplementedBy(DynamicGroupImpl.class)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/DynamicGroupImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/DynamicGroupImpl.java b/core/src/main/java/brooklyn/entity/basic/DynamicGroupImpl.java
index 7ffe8a0..3b1eb47 100644
--- a/core/src/main/java/brooklyn/entity/basic/DynamicGroupImpl.java
+++ b/core/src/main/java/brooklyn/entity/basic/DynamicGroupImpl.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.core.management.internal.CollectionChangeListener;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,7 +38,6 @@ import brooklyn.config.BrooklynLogging;
 import brooklyn.config.BrooklynLogging.LoggingLevel;
 import brooklyn.util.GroovyJavaMethods;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/EffectorStartableImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/EffectorStartableImpl.java b/core/src/main/java/brooklyn/entity/basic/EffectorStartableImpl.java
index 0abb90c..f60f408 100644
--- a/core/src/main/java/brooklyn/entity/basic/EffectorStartableImpl.java
+++ b/core/src/main/java/brooklyn/entity/basic/EffectorStartableImpl.java
@@ -29,8 +29,7 @@ import brooklyn.entity.annotation.EffectorParam;
 import brooklyn.entity.trait.Startable;
 
 import org.apache.brooklyn.api.location.Location;
-
-import brooklyn.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import com.google.common.reflect.TypeToken;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/Entities.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/Entities.java b/core/src/main/java/brooklyn/entity/basic/Entities.java
index c768726..635e99c 100644
--- a/core/src/main/java/brooklyn/entity/basic/Entities.java
+++ b/core/src/main/java/brooklyn/entity/basic/Entities.java
@@ -64,6 +64,15 @@ import org.apache.brooklyn.core.management.internal.EntityManagerInternal;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.management.internal.NonDeploymentManagementContext;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.ParallelTask;
+import org.apache.brooklyn.core.util.task.TaskTags;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.core.util.task.system.SystemTasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -80,20 +89,11 @@ import brooklyn.event.basic.DependentConfiguration;
 import org.apache.brooklyn.location.basic.LocationInternal;
 import org.apache.brooklyn.location.basic.Locations;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.FlagUtils;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.repeat.Repeater;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.ParallelTask;
-import brooklyn.util.task.TaskTags;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-import brooklyn.util.task.system.SystemTasks;
 import brooklyn.util.time.Duration;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java b/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java
index 988d6d7..8a7a9f0 100644
--- a/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java
+++ b/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java
@@ -28,6 +28,11 @@ import java.util.Set;
 
 import org.apache.brooklyn.api.management.ExecutionContext;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.internal.ConfigKeySelfExtracting;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,12 +41,7 @@ import brooklyn.config.ConfigKey;
 import brooklyn.config.internal.AbstractConfigMapImpl;
 import brooklyn.event.basic.StructuredConfigKey;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.internal.ConfigKeySelfExtracting;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Maps;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/EntityFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/EntityFunctions.java b/core/src/main/java/brooklyn/entity/basic/EntityFunctions.java
index 4e02948..d9afc44 100644
--- a/core/src/main/java/brooklyn/entity/basic/EntityFunctions.java
+++ b/core/src/main/java/brooklyn/entity/basic/EntityFunctions.java
@@ -30,9 +30,9 @@ import org.apache.brooklyn.api.entity.trait.Identifiable;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import brooklyn.config.ConfigKey;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Functionals;
 
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/EntityInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/EntityInternal.java b/core/src/main/java/brooklyn/entity/basic/EntityInternal.java
index 085079f..36e6907 100644
--- a/core/src/main/java/brooklyn/entity/basic/EntityInternal.java
+++ b/core/src/main/java/brooklyn/entity/basic/EntityInternal.java
@@ -33,10 +33,10 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.SubscriptionContext;
 import org.apache.brooklyn.api.mementos.EntityMemento;
 import org.apache.brooklyn.core.management.internal.EntityManagementSupport;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.basic.BrooklynObjectInternal;
 import brooklyn.config.ConfigKey;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.annotations.Beta;
 



[37/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/ssh/ShellAbstractTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/ShellAbstractTool.java b/core/src/main/java/brooklyn/util/internal/ssh/ShellAbstractTool.java
deleted file mode 100644
index 5c839a9..0000000
--- a/core/src/main/java/brooklyn/util/internal/ssh/ShellAbstractTool.java
+++ /dev/null
@@ -1,442 +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.internal.ssh;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import java.io.ByteArrayInputStream;
-import java.io.Closeable;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.os.Os;
-import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.text.Identifiers;
-import brooklyn.util.text.StringEscapes.BashStringEscapes;
-import brooklyn.util.text.Strings;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
-
-public abstract class ShellAbstractTool implements ShellTool {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ShellAbstractTool.class);
-
-    protected final File localTempDir;
-
-    public ShellAbstractTool(String localTempDir) {
-        this(localTempDir == null ? null : new File(Os.tidyPath(localTempDir)));
-    }
-    
-    public ShellAbstractTool(File localTempDir) {
-        if (localTempDir == null) {
-            localTempDir = new File(Os.tmp(), "tmpssh-"+Os.user());
-            if (!localTempDir.exists()) localTempDir.mkdir();
-            Os.deleteOnExitEmptyParentsUpTo(localTempDir, new File(Os.tmp()));
-        }
-        this.localTempDir = localTempDir;
-    }
-    
-    public ShellAbstractTool() {
-        this((File)null);
-    }
-    
-    protected static void warnOnDeprecated(Map<String, ?> props, String deprecatedKey, String correctKey) {
-        if (props.containsKey(deprecatedKey)) {
-            if (correctKey != null && props.containsKey(correctKey)) {
-                Object dv = props.get(deprecatedKey);
-                Object cv = props.get(correctKey);
-                if (!Objects.equal(cv, dv)) {
-                    LOG.warn("SshTool detected deprecated key '"+deprecatedKey+"' with different value ("+dv+") "+
-                            "than new key '"+correctKey+"' ("+cv+"); ambiguous which will be used");
-                } else {
-                    // ignore, the deprecated key populated for legacy reasons
-                }
-            } else {
-                Object dv = props.get(deprecatedKey);
-                LOG.warn("SshTool detected deprecated key '"+deprecatedKey+"' used, with value ("+dv+")");     
-            }
-        }
-    }
-
-    protected static Boolean hasVal(Map<String,?> map, ConfigKey<?> keyC) {
-        String key = keyC.getName();
-        return map.containsKey(key);
-    }
-    
-    protected static <T> T getMandatoryVal(Map<String,?> map, ConfigKey<T> keyC) {
-        String key = keyC.getName();
-        checkArgument(map.containsKey(key), "must contain key '"+keyC+"'");
-        return TypeCoercions.coerce(map.get(key), keyC.getTypeToken());
-    }
-    
-    public static <T> T getOptionalVal(Map<String,?> map, ConfigKey<T> keyC) {
-        if (keyC==null) return null;
-        String key = keyC.getName();
-        if (map!=null && map.containsKey(key) && map.get(key) != null) {
-            return TypeCoercions.coerce(map.get(key), keyC.getTypeToken());
-        } else {
-            return keyC.getDefaultValue();
-        }
-    }
-
-    /** returns the value of the key if specified, otherwise defaultValue */
-    protected static <T> T getOptionalVal(Map<String,?> map, ConfigKey<T> keyC, T defaultValue) {
-        String key = keyC.getName();
-        if (map!=null && map.containsKey(key) && map.get(key) != null) {
-            return TypeCoercions.coerce(map.get(key), keyC.getTypeToken());
-        } else {
-            return defaultValue;
-        }
-    }
-
-    protected void closeWhispering(Closeable closeable, Object context) {
-        closeWhispering(closeable, this, context);
-    }
-    
-    /**
-     * Similar to Guava's Closeables.closeQuitely, except logs exception at debug with context in message.
-     */
-    protected static void closeWhispering(Closeable closeable, Object context1, Object context2) {
-        if (closeable != null) {
-            try {
-                closeable.close();
-            } catch (IOException e) {
-                if (LOG.isDebugEnabled()) {
-                    String msg = String.format("<< exception during close, for %s -> %s (%s); continuing.", 
-                            context1, context2, closeable);
-                    if (LOG.isTraceEnabled())
-                        LOG.debug(msg + ": " + e);
-                    else
-                        LOG.trace(msg, e);
-                }
-            }
-        }
-    }
-
-    protected File writeTempFile(InputStream contents) {
-        File tempFile = Os.writeToTempFile(contents, localTempDir, "sshcopy", "data");
-        tempFile.setReadable(false, false);
-        tempFile.setReadable(true, true);
-        tempFile.setWritable(false);
-        tempFile.setExecutable(false);
-        return tempFile;
-    }
-
-    protected File writeTempFile(String contents) {
-        return writeTempFile(contents.getBytes());
-    }
-
-    protected File writeTempFile(byte[] contents) {
-        return writeTempFile(new ByteArrayInputStream(contents));
-    }
-
-    protected String toScript(Map<String,?> props, List<String> commands, Map<String,?> env) {
-        List<String> allcmds = toCommandSequence(commands, env);
-        StringBuilder result = new StringBuilder();
-        result.append(getOptionalVal(props, PROP_SCRIPT_HEADER)).append('\n');
-        
-        for (String cmd : allcmds) {
-            result.append(cmd).append('\n');
-        }
-        
-        return result.toString();
-    }
-
-    /**
-     * Merges the commands and env, into a single set of commands. Also escapes the commands as required.
-     * 
-     * Not all ssh servers handle "env", so instead convert env into exported variables
-     */
-    protected List<String> toCommandSequence(List<String> commands, Map<String,?> env) {
-        List<String> result = new ArrayList<String>((env!=null ? env.size() : 0) + commands.size());
-        
-        if (env!=null) {
-            for (Entry<String,?> entry : env.entrySet()) {
-                if (entry.getKey() == null || entry.getValue() == null) {
-                    LOG.warn("env key-values must not be null; ignoring: key="+entry.getKey()+"; value="+entry.getValue());
-                    continue;
-                }
-                String escapedVal = BashStringEscapes.escapeLiteralForDoubleQuotedBash(entry.getValue().toString());
-                result.add("export "+entry.getKey()+"=\""+escapedVal+"\"");
-            }
-        }
-        for (CharSequence cmd : commands) { // objects in commands can be groovy GString so can't treat as String here
-            result.add(cmd.toString());
-        }
-
-        return result;
-    }
-
-    @Override
-    public int execScript(Map<String,?> props, List<String> commands) {
-        return execScript(props, commands, Collections.<String,Object>emptyMap());
-    }
-
-    @Override
-    public int execCommands(Map<String,?> props, List<String> commands) {
-        return execCommands(props, commands, Collections.<String,Object>emptyMap());
-    }
-
-    protected static int asInt(Integer input, int valueIfInputNull) {
-        return input != null ? input : valueIfInputNull;
-    }
-
-    protected abstract class ToolAbstractExecScript {
-        protected final Map<String, ?> props;
-        protected final String separator;
-        protected final OutputStream out;
-        protected final OutputStream err;
-        protected final String scriptDir;
-        protected final Boolean runAsRoot;
-        protected final Boolean noExtraOutput;
-        protected final Boolean noDeleteAfterExec;
-        protected final String scriptNameWithoutExtension;
-        protected final String scriptPath;
-        protected final Duration execTimeout;
-
-        public ToolAbstractExecScript(Map<String,?> props) {
-            this.props = props;
-            this.separator = getOptionalVal(props, PROP_SEPARATOR);
-            this.out = getOptionalVal(props, PROP_OUT_STREAM);
-            this.err = getOptionalVal(props, PROP_ERR_STREAM);
-            
-            this.scriptDir = getOptionalVal(props, PROP_SCRIPT_DIR);
-            this.runAsRoot = getOptionalVal(props, PROP_RUN_AS_ROOT);
-            this.noExtraOutput = getOptionalVal(props, PROP_NO_EXTRA_OUTPUT);
-            this.noDeleteAfterExec = getOptionalVal(props, PROP_NO_DELETE_SCRIPT);
-            this.execTimeout = getOptionalVal(props, PROP_EXEC_TIMEOUT);
-            
-            String summary = getOptionalVal(props, PROP_SUMMARY);
-            if (summary!=null) {
-                summary = Strings.makeValidFilename(summary);
-                if (summary.length()>30) 
-                    summary = summary.substring(0,30);
-            }
-            this.scriptNameWithoutExtension = "brooklyn-"+
-                    Time.makeDateStampString()+"-"+Identifiers.makeRandomId(4)+
-                    (Strings.isBlank(summary) ? "" : "-"+summary);
-            this.scriptPath = Os.mergePathsUnix(scriptDir, scriptNameWithoutExtension+".sh");
-        }
-
-        /** builds the command to run the given script;
-         * note that some modes require \$RESULT passed in order to access a variable, whereas most just need $ */
-        protected List<String> buildRunScriptCommand() {
-            MutableList.Builder<String> cmds = MutableList.<String>builder()
-                    .add((runAsRoot ? BashCommands.sudo(scriptPath) : scriptPath) + " < /dev/null")
-                    .add("RESULT=$?");
-            if (noExtraOutput==null || !noExtraOutput)
-                cmds.add("echo Executed "+scriptPath+", result $RESULT"); 
-            if (noDeleteAfterExec!=Boolean.TRUE) {
-                // use "-f" because some systems have "rm" aliased to "rm -i"
-                // use "< /dev/null" to guarantee doesn't hang
-                cmds.add("rm -f "+scriptPath+" < /dev/null");
-            }
-            cmds.add("exit $RESULT");
-            return cmds.build();
-        }
-
-        protected String getSummary() {
-            String summary = getOptionalVal(props, PROP_SUMMARY);
-            return (summary != null) ? summary : scriptPath; 
-        }
-
-        public abstract int run();
-    }
-    
-    protected abstract class ToolAbstractAsyncExecScript extends ToolAbstractExecScript {
-        protected final String stdoutPath;
-        protected final String stderrPath;
-        protected final String exitStatusPath;
-        protected final String pidPath;
-
-        public ToolAbstractAsyncExecScript(Map<String,?> props) {
-            super(props);
-
-            stdoutPath = Os.mergePathsUnix(scriptDir, scriptNameWithoutExtension + ".stdout");
-            stderrPath = Os.mergePathsUnix(scriptDir, scriptNameWithoutExtension + ".stderr");
-            exitStatusPath = Os.mergePathsUnix(scriptDir, scriptNameWithoutExtension + ".exitstatus");
-            pidPath = Os.mergePathsUnix(scriptDir, scriptNameWithoutExtension + ".pid");
-        }
-
-        /**
-         * Builds the command to run the given script, asynchronously.
-         * The executed command will return immediately, but the output from the script
-         * will continue to be written 
-         * note that some modes require \$RESULT passed in order to access a variable, whereas most just need $ */
-        @Override
-        protected List<String> buildRunScriptCommand() {
-            String touchCmd = String.format("touch %s %s %s %s", stdoutPath, stderrPath, exitStatusPath, pidPath);
-            String cmd = String.format("nohup sh -c \"( %s > %s 2> %s < /dev/null ) ; echo \\$? > %s \" > /dev/null 2>&1 < /dev/null &", scriptPath, stdoutPath, stderrPath, exitStatusPath);
-            MutableList.Builder<String> cmds = MutableList.<String>builder()
-                    .add(runAsRoot ? BashCommands.sudo(touchCmd) : touchCmd)
-                    .add(runAsRoot ? BashCommands.sudo(cmd) : cmd)
-                    .add("echo $! > "+pidPath)
-                    .add("RESULT=$?");
-            if (noExtraOutput==null || !noExtraOutput) {
-                cmds.add("echo Executing async "+scriptPath);
-            }
-            cmds.add("exit $RESULT");
-            return cmds.build();
-        }
-
-        /**
-         * Builds the command to retrieve the exit status of the command, written to stdout.
-         */
-        protected List<String> buildRetrieveStatusCommand() {
-            // Retrieve exit status from file (writtent to stdout), if populated;
-            // if not found and pid still running, then return empty string; else exit code 1.
-            List<String> cmdParts = ImmutableList.of(
-                    "# Retrieve status", // comment is to aid testing - see SshjToolAsyncStubIntegrationTest
-                    "if test -s "+exitStatusPath+"; then",
-                    "    cat "+exitStatusPath,
-                    "elif test -s "+pidPath+"; then",
-                    "    pid=`cat "+pidPath+"`",
-                    "    if ! ps -p $pid > /dev/null < /dev/null; then",
-                    "        # no exit status, and not executing; give a few seconds grace in case just about to write exit status",
-                    "        sleep 3",
-                    "        if test -s "+exitStatusPath+"; then",
-                    "            cat "+exitStatusPath+"",
-                    "        else",
-                    "            echo \"No exit status in "+exitStatusPath+", and pid in "+pidPath+" ($pid) not executing\"",
-                    "            exit 1",
-                    "        fi",
-                    "    fi",
-                    "else",
-                    "    echo \"No exit status in "+exitStatusPath+", and "+pidPath+" is empty\"",
-                    "    exit 1",
-                    "fi"+"\n");
-            String cmd = Joiner.on("\n").join(cmdParts);
-
-            MutableList.Builder<String> cmds = MutableList.<String>builder()
-                    .add((runAsRoot ? BashCommands.sudo(cmd) : cmd))
-                    .add("RESULT=$?");
-            cmds.add("exit $RESULT");
-            return cmds.build();
-        }
-
-        /**
-         * Builds the command to retrieve the stdout and stderr of the async command.
-         * An offset can be given, to only retrieve data starting at a particular character (indexed from 0).
-         */
-        protected List<String> buildRetrieveStdoutAndStderrCommand(int stdoutPosition, int stderrPosition) {
-            // Note that `tail -c +1` means start at the *first* character (i.e. start counting from 1, not 0)
-            String catStdoutCmd = "tail -c +"+(stdoutPosition+1)+" "+stdoutPath+" 2> /dev/null";
-            String catStderrCmd = "tail -c +"+(stderrPosition+1)+" "+stderrPath+" 2>&1 > /dev/null";
-            MutableList.Builder<String> cmds = MutableList.<String>builder()
-                    .add((runAsRoot ? BashCommands.sudo(catStdoutCmd) : catStdoutCmd))
-                    .add((runAsRoot ? BashCommands.sudo(catStderrCmd) : catStderrCmd))
-                    .add("RESULT=$?");
-            cmds.add("exit $RESULT");
-            return cmds.build();
-        }
-
-        /**
-         * Builds the command to retrieve the stdout and stderr of the async command.
-         * An offset can be given, to only retrieve data starting at a particular character (indexed from 0).
-         */
-        protected List<String> buildLongPollCommand(int stdoutPosition, int stderrPosition, Duration timeout) {
-            long maxTime = Math.max(1, timeout.toSeconds());
-            
-            // Note that `tail -c +1` means start at the *first* character (i.e. start counting from 1, not 0)
-            List<String> waitForExitStatusParts = ImmutableList.of(
-                    //Should be careful here because any output will be part of the stdout/stderr streams
-                    "# Long poll", // comment is to aid testing - see SshjToolAsyncStubIntegrationTest
-                    // disown to avoid Terminated message after killing the process
-                    // redirect error output to avoid "file truncated" messages
-                    "tail -c +"+(stdoutPosition+1)+" -f "+stdoutPath+" 2> /dev/null & export TAIL_STDOUT_PID=$!; disown",
-                    "tail -c +"+(stderrPosition+1)+" -f "+stderrPath+" 1>&2 2> /dev/null & export TAIL_STDERR_PID=$!; disown",
-                    "EXIT_STATUS_PATH="+exitStatusPath,
-                    "PID_PATH="+pidPath,
-                    "MAX_TIME="+maxTime,
-                    "COUNTER=0",
-                    "while [ \"$COUNTER\" -lt $MAX_TIME ]; do",
-                    "    if test -s $EXIT_STATUS_PATH; then",
-                    "        EXIT_STATUS=`cat $EXIT_STATUS_PATH`",
-                    "        kill ${TAIL_STDERR_PID} ${TAIL_STDOUT_PID} 2> /dev/null",
-                    "        exit $EXIT_STATUS",
-                    "    elif test -s $PID_PATH; then",
-                    "        PID=`cat $PID_PATH`",
-                    "        if ! ps -p $PID > /dev/null 2>&1 < /dev/null; then",
-                    "            # no exit status, and not executing; give a few seconds grace in case just about to write exit status",
-                    "            sleep 3",
-                    "            if test -s $EXIT_STATUS_PATH; then",
-                    "                EXIT_STATUS=`cat $EXIT_STATUS_PATH`",
-                    "                kill ${TAIL_STDERR_PID} ${TAIL_STDOUT_PID} 2> /dev/null",
-                    "                exit $EXIT_STATUS",
-                    "            else",
-                    "                echo \"No exit status in $EXIT_STATUS_PATH, and pid in $PID_PATH ($PID) not executing\"",
-                    "                kill ${TAIL_STDERR_PID} ${TAIL_STDOUT_PID} 2> /dev/null",
-                    "                exit 126",
-                    "            fi",
-                    "        fi",
-                    "    fi",
-                    "    # No exit status in $EXIT_STATUS_PATH; keep waiting",
-                    "    sleep 1",
-                    "    COUNTER+=1",
-                    "done",
-                    "kill ${TAIL_STDERR_PID} ${TAIL_STDOUT_PID} 2> /dev/null",
-                    "exit 125"+"\n");
-            String waitForExitStatus = Joiner.on("\n").join(waitForExitStatusParts);
-
-            return ImmutableList.of(runAsRoot ? BashCommands.sudo(waitForExitStatus) : waitForExitStatus);
-        }
-
-        protected List<String> deleteTemporaryFilesCommand() {
-            ImmutableList.Builder<String> cmdParts = ImmutableList.builder();
-            
-            if (!Boolean.TRUE.equals(noDeleteAfterExec)) {
-                // use "-f" because some systems have "rm" aliased to "rm -i"
-                // use "< /dev/null" to guarantee doesn't hang
-                cmdParts.add(
-                        "rm -f "+scriptPath+" "+stdoutPath+" "+stderrPath+" "+exitStatusPath+" "+pidPath+" < /dev/null");
-            }
-            
-            // If the buildLongPollCommand didn't complete properly then it might have left tail command running;
-            // ensure they are killed.
-            cmdParts.add(
-                    //ignore error output for the case where there are no running processes and kill is called without arguments
-                    "ps aux | grep \"tail -c\" | grep \""+stdoutPath+"\" | grep -v grep | awk '{ printf $2 }' | xargs kill 2> /dev/null",
-                    "ps aux | grep \"tail -c\" | grep \""+stderrPath+"\" | grep -v grep | awk '{ printf $2 }' | xargs kill 2> /dev/null");
-
-            String cmd = Joiner.on("\n").join(cmdParts.build());
-            
-            return ImmutableList.of(runAsRoot ? BashCommands.sudo(cmd) : cmd);
-        }
-
-        @Override
-        public abstract int run();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/ssh/ShellTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/ShellTool.java b/core/src/main/java/brooklyn/util/internal/ssh/ShellTool.java
deleted file mode 100644
index 0555039..0000000
--- a/core/src/main/java/brooklyn/util/internal/ssh/ShellTool.java
+++ /dev/null
@@ -1,113 +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.internal.ssh;
-
-import static brooklyn.entity.basic.ConfigKeys.newConfigKey;
-import static brooklyn.entity.basic.ConfigKeys.newStringConfigKey;
-
-import java.io.OutputStream;
-import java.util.List;
-import java.util.Map;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.os.Os;
-import brooklyn.util.time.Duration;
-
-/** Methods for executing things in an environment (localhost process, or ssh) */
-public interface ShellTool {
-
-    // config which applies to sessions
-    
-    public static final ConfigKey<String> PROP_LOCAL_TEMP_DIR = newStringConfigKey(
-            "localTempDir", 
-            "The directory on the local machine (i.e. running brooklyn) for writing temp files", 
-            Os.mergePaths(Os.tmp(), "brooklyn-"+Os.user()+"-ssh-tmp"));
-    
-    // config which applies to calls:
-    
-    public static final ConfigKey<Boolean> PROP_RUN_AS_ROOT = newConfigKey("runAsRoot", "When running a script, whether to run as root", Boolean.FALSE);
-    
-    public static final ConfigKey<OutputStream> PROP_OUT_STREAM = newConfigKey(OutputStream.class, "out", "Stream to which to capture stdout");
-    public static final ConfigKey<OutputStream> PROP_ERR_STREAM = newConfigKey(OutputStream.class, "err", "Stream to which to capture stderr");
-    
-    public static final ConfigKey<Boolean> PROP_NO_EXTRA_OUTPUT = newConfigKey("noExtraOutput", "Suppresses any decorative output such as result code which some tool commands insert", false);
-    
-    public static final ConfigKey<String> PROP_SEPARATOR = newConfigKey("separator", "string to insert between caller-supplied commands being executed as commands", " ; ");
-    
-    public static final ConfigKey<String> PROP_SCRIPT_DIR = newConfigKey("scriptDir", "directory where scripts should be copied", "/tmp");
-    public static final ConfigKey<String> PROP_SCRIPT_HEADER = newConfigKey("scriptHeader", "lines to insert at the start of scripts generated for caller-supplied commands for script execution", "#!/bin/bash -e\n");
-    public static final ConfigKey<String> PROP_DIRECT_HEADER = newConfigKey("directHeader", "commands to run at the target before any caller-supplied commands for direct execution", "exec bash -e");
-
-    ConfigKey<Boolean> PROP_NO_DELETE_SCRIPT = newConfigKey("noDeleteAfterExec", "Retains the generated script file after executing the commands instead of deleting it", false);
-
-    ConfigKey<String> PROP_SUMMARY = ConfigKeys.newStringConfigKey("summary", "Provides a human-readable summary, used in file generation etc");
-    
-    ConfigKey<Duration> PROP_EXEC_TIMEOUT = newConfigKey("execTimeout", "Timeout when executing a script", Duration.PRACTICALLY_FOREVER);
-
-    ConfigKey<Boolean> PROP_EXEC_ASYNC = newConfigKey("execAsync", "Executes the script asynchronously, and then polls for the result (and for stdout/stderr)", false);
-
-    ConfigKey<Duration> PROP_EXEC_ASYNC_POLLING_TIMEOUT = newConfigKey("execAsyncPollTimeout", "Timeout per poll when executing a script asynchronously", Duration.ONE_MINUTE);
-
-    /**
-     * Executes the set of commands in a shell script. Blocks until completion.
-     * <p>
-     * 
-     * Optional properties are the same common ones as for {@link #execCommands(Map, List, Map)} with the addition of:
-     * <ul>
-     * <li>{@link #PROP_RUN_AS_ROOT}
-     * <li>{@link #PROP_SCRIPT_DIR}
-     * </ul>
-     * 
-     * @return exit status of script
-     */
-    public int execScript(Map<String,?> props, List<String> commands, Map<String,?> env);
-
-    /**
-     * @see #execScript(Map, List, Map)
-     */
-    public int execScript(Map<String,?> props, List<String> commands);
-
-    /**
-     * Executes the set of commands using ssh exec.
-     * 
-     * This is generally more efficient than ssh shell mode (cf {@link #execScript(Map, List, Map)}), 
-     * but is not suitable if you need env values which are only set on a fully-fledged shell,
-     * or if you want the entire block executed with root permission.
-     *
-     * Common optional properties (which also apply to {@link #execScript(Map, List, Map)}) are:
-     * <ul>
-     * <li>{@link #PROP_OUT_STREAM}
-     * <li>{@link #PROP_ERR_STREAM}
-     * <li>{@link #PROP_SEPARATOR} (for some modes)
-     * <li>{@link #PROP_NO_EXTRA_OUTPUT} (often there is no extra output here)
-     * </ul>
-     * 
-     * Note that {@link #PROP_RUN_AS_ROOT} is <i>not</i> typically supported here. Prefer {@link #execScript(Map, List, Map)}).
-     * 
-     * @return exit status of commands
-     */
-    public int execCommands(Map<String,?> properties, List<String> commands, Map<String,?> env);
-
-    /**
-     * @see #execCommands(Map, List, Map)
-     */
-    public int execCommands(Map<String,?> properties, List<String> commands);
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/ssh/SshAbstractTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/SshAbstractTool.java b/core/src/main/java/brooklyn/util/internal/ssh/SshAbstractTool.java
deleted file mode 100644
index eb0222a..0000000
--- a/core/src/main/java/brooklyn/util/internal/ssh/SshAbstractTool.java
+++ /dev/null
@@ -1,172 +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.internal.ssh;
-
-import static brooklyn.util.net.Networking.checkPortValid;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.io.File;
-import java.util.Map;
-import java.util.Set;
-
-import brooklyn.util.os.Os;
-
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-
-public abstract class SshAbstractTool extends ShellAbstractTool implements SshTool {
-
-    protected final String toString;
-
-    protected final String host;
-    protected final String user;
-    protected final String password;
-    protected final int port;
-    protected String privateKeyPassphrase;
-    protected String privateKeyData;
-    protected File privateKeyFile;
-    protected boolean strictHostKeyChecking;
-    protected boolean allocatePTY;
-
-    public static interface SshAction<T> {
-        void clear() throws Exception;
-        T create() throws Exception;
-    }
-
-    public static abstract class AbstractSshToolBuilder<T extends SshTool, B extends AbstractSshToolBuilder<T,B>> {
-        protected String host;
-        protected int port = 22;
-        protected String user = System.getProperty("user.name");
-        protected String password;
-        protected String privateKeyData;
-        protected String privateKeyPassphrase;
-        protected Set<String> privateKeyFiles = Sets.newLinkedHashSet();
-        protected boolean strictHostKeyChecking = false;
-        protected boolean allocatePTY = false;
-        protected File localTempDir = null;
-
-        @SuppressWarnings("unchecked")
-        protected B self() {
-           return (B) this;
-        }
-
-        public B from(Map<String,?> props) {
-            host = getMandatoryVal(props, PROP_HOST);
-            port = getOptionalVal(props, PROP_PORT);
-            user = getOptionalVal(props, PROP_USER);
-            
-            password = getOptionalVal(props, PROP_PASSWORD);
-            
-            warnOnDeprecated(props, "privateKey", "privateKeyData");
-            privateKeyData = getOptionalVal(props, PROP_PRIVATE_KEY_DATA);
-            privateKeyPassphrase = getOptionalVal(props, PROP_PRIVATE_KEY_PASSPHRASE);
-            
-            // for backwards compatibility accept keyFiles and privateKey
-            // but sshj accepts only a single privateKeyFile; leave blank to use defaults (i.e. ~/.ssh/id_rsa and id_dsa)
-            warnOnDeprecated(props, "keyFiles", null);
-            String privateKeyFile = getOptionalVal(props, PROP_PRIVATE_KEY_FILE);
-            if (privateKeyFile != null) privateKeyFiles.add(privateKeyFile);
-            
-            strictHostKeyChecking = getOptionalVal(props, PROP_STRICT_HOST_KEY_CHECKING);
-            allocatePTY = getOptionalVal(props, PROP_ALLOCATE_PTY);
-            
-            String localTempDirPath = getOptionalVal(props, PROP_LOCAL_TEMP_DIR);
-            localTempDir = (localTempDirPath == null) ? null : new File(Os.tidyPath(localTempDirPath));
-            
-            return self();
-        }
-        public B host(String val) {
-            this.host = val; return self();
-        }
-        public B user(String val) {
-            this.user = val; return self();
-        }
-        public B password(String val) {
-            this.password = val; return self();
-        }
-        public B port(int val) {
-            this.port = val; return self();
-        }
-        public B privateKeyPassphrase(String val) {
-            this.privateKeyPassphrase = val; return self();
-        }
-        /** @deprecated 1.4.0, use privateKeyData */
-        public B privateKey(String val) {
-            this.privateKeyData = val; return self();
-        }
-        public B privateKeyData(String val) {
-            this.privateKeyData = val; return self();
-        }
-        public B privateKeyFile(String val) {
-            this.privateKeyFiles.add(val); return self();
-        }
-        public B localTempDir(File val) {
-            this.localTempDir = val; return self();
-        }
-        public abstract T build();
-    }
-
-    protected SshAbstractTool(AbstractSshToolBuilder<?,?> builder) {
-        super(builder.localTempDir);
-        
-        host = checkNotNull(builder.host, "host");
-        port = builder.port;
-        user = builder.user;
-        password = builder.password;
-        strictHostKeyChecking = builder.strictHostKeyChecking;
-        allocatePTY = builder.allocatePTY;
-        privateKeyPassphrase = builder.privateKeyPassphrase;
-        privateKeyData = builder.privateKeyData;
-        
-        if (builder.privateKeyFiles.size() > 1) {
-            throw new IllegalArgumentException("sshj supports only a single private key-file; " +
-                    "for defaults of ~/.ssh/id_rsa and ~/.ssh/id_dsa leave blank");
-        } else if (builder.privateKeyFiles.size() == 1) {
-            String privateKeyFileStr = Iterables.get(builder.privateKeyFiles, 0);
-            String amendedKeyFile = privateKeyFileStr.startsWith("~") ? (System.getProperty("user.home")+privateKeyFileStr.substring(1)) : privateKeyFileStr;
-            privateKeyFile = new File(amendedKeyFile);
-        } else {
-            privateKeyFile = null;
-        }
-        
-        checkArgument(host.length() > 0, "host value must not be an empty string");
-        checkPortValid(port, "ssh port");
-        
-        toString = String.format("%s@%s:%d", user, host, port);
-    }
-
-    @Override
-    public String toString() {
-        return toString;
-    }
-
-    public String getHostAddress() {
-        return this.host;
-    }
-
-    public String getUsername() {
-        return this.user;
-    }
-
-    protected SshException propagate(Exception e, String message) throws SshException {
-        throw new SshException("(" + toString() + ") " + message + ": " + e.getMessage(), e);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/ssh/SshException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/SshException.java b/core/src/main/java/brooklyn/util/internal/ssh/SshException.java
deleted file mode 100644
index 20653bb..0000000
--- a/core/src/main/java/brooklyn/util/internal/ssh/SshException.java
+++ /dev/null
@@ -1,32 +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.internal.ssh;
-
-public class SshException extends RuntimeException {
-
-    private static final long serialVersionUID = -5690230838066860965L;
-
-    public SshException(String msg) {
-        super(msg);
-    }
-    
-    public SshException(String msg, Throwable cause) {
-        super(msg, cause);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/ssh/SshTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/SshTool.java b/core/src/main/java/brooklyn/util/internal/ssh/SshTool.java
deleted file mode 100644
index a676599..0000000
--- a/core/src/main/java/brooklyn/util/internal/ssh/SshTool.java
+++ /dev/null
@@ -1,174 +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.internal.ssh;
-
-import static brooklyn.entity.basic.ConfigKeys.newConfigKey;
-import static brooklyn.entity.basic.ConfigKeys.newStringConfigKey;
-
-import java.io.File;
-import java.io.InputStream;
-import java.util.List;
-import java.util.Map;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.stream.KnownSizeInputStream;
-import brooklyn.util.time.Duration;
-
-/**
- * Defines the methods available on the various different implementations of SSH,
- * and configuration options which are also generally available.
- * <p>
- * The config keys in this class can be supplied (or their string equivalents, where the flags/props take {@code Map<String,?>})
- * to influence configuration, either for the tool/session itself or for individual commands.
- * <p>
- * To specify some of these properties on a global basis, use the variants of the keys here
- * contained in {@link ConfigKeys}
- * (which are generally {@value #BROOKLYN_CONFIG_KEY_PREFIX} prefixed to the names of keys here).
- */
-public interface SshTool extends ShellTool {
-    
-    /** Public-facing global config keys for Brooklyn are defined in ConfigKeys, 
-     * and have this prefix pre-prended to the config keys in this class. 
-     * These keys are detected from entity/global config and automatically applied to ssh executions. */
-    public static final String BROOKLYN_CONFIG_KEY_PREFIX = "brooklyn.ssh.config.";
-    
-    public static final ConfigKey<String> PROP_TOOL_CLASS = newStringConfigKey("tool.class", "SshTool implementation to use", null);
-    
-    public static final ConfigKey<String> PROP_HOST = newStringConfigKey("host", "Host to connect to (required)", null);
-    public static final ConfigKey<Integer> PROP_PORT = newConfigKey("port", "Port on host to connect to", 22);
-    public static final ConfigKey<String> PROP_USER = newConfigKey("user", "User to connect as", System.getProperty("user.name"));
-    public static final ConfigKey<String> PROP_PASSWORD = newStringConfigKey("password", "Password to use to connect", null);
-    
-    public static final ConfigKey<String> PROP_PRIVATE_KEY_FILE = newStringConfigKey("privateKeyFile", "the path of an ssh private key file; leave blank to use defaults (i.e. ~/.ssh/id_rsa and id_dsa)", null);
-    public static final ConfigKey<String> PROP_PRIVATE_KEY_DATA = newStringConfigKey("privateKeyData", "the private ssh key (e.g. contents of an id_rsa or id_dsa file)", null);
-    public static final ConfigKey<String> PROP_PRIVATE_KEY_PASSPHRASE = newStringConfigKey("privateKeyPassphrase", "the passphrase for the ssh private key", null);
-    public static final ConfigKey<Boolean> PROP_STRICT_HOST_KEY_CHECKING = newConfigKey("strictHostKeyChecking", "whether to check the remote host's identification; defaults to false", false);
-    public static final ConfigKey<Boolean> PROP_ALLOCATE_PTY = newConfigKey("allocatePTY", "whether to allocate PTY (vt100); if true then stderr is sent to stdout, but sometimes required for sudo'ing due to requiretty", false);
-
-    public static final ConfigKey<Long> PROP_CONNECT_TIMEOUT = newConfigKey("connectTimeout", "Timeout in millis when establishing an SSH connection; if 0 then uses default (usually 30s)", 0L);
-    public static final ConfigKey<Long> PROP_SESSION_TIMEOUT = newConfigKey("sessionTimeout", "Timeout in millis for an ssh session; if 0 then uses default", 0L);
-    public static final ConfigKey<Integer> PROP_SSH_TRIES = newConfigKey("sshTries", "Max number of times to attempt ssh operations", 4);
-    public static final ConfigKey<Long> PROP_SSH_TRIES_TIMEOUT = newConfigKey("sshTriesTimeout", "Time limit for attempting retries; will not interrupt tasks, but stops retrying after a total amount of elapsed time", Duration.TWO_MINUTES.toMilliseconds());
-    public static final ConfigKey<Long> PROP_SSH_RETRY_DELAY = newConfigKey("sshRetryDelay", "Time (in milliseconds) before first ssh-retry, after which it will do exponential backoff", 50L);
-
-    // NB -- items above apply for _session_ (a tool), below apply for a _call_
-    // TODO would be nice to track which arguments are used, so we can indicate whether extras are supplied
-
-    public static final ConfigKey<String> PROP_PERMISSIONS = newConfigKey("permissions", "Default permissions for files copied/created on remote machine; must be four-digit octal string, default '0644'", "0644");
-    public static final ConfigKey<Long> PROP_LAST_MODIFICATION_DATE = newConfigKey("lastModificationDate", "Last-modification-date to be set on files copied/created (should be UTC/1000, ie seconds since 1970; default 0 usually means current)", 0L);
-    public static final ConfigKey<Long> PROP_LAST_ACCESS_DATE = newConfigKey("lastAccessDate", "Last-access-date to be set on files copied/created (should be UTC/1000, ie seconds since 1970; default 0 usually means lastModificationDate)", 0L);
-    public static final ConfigKey<Integer> PROP_OWNER_UID = newConfigKey("ownerUid", "Default owner UID (not username) for files created on remote machine; default is unset", -1);
-    
-    // TODO remove unnecessary "public static final" modifiers
-    
-    // TODO Could define the following in SshMachineLocation, or some such?
-    //public static ConfigKey<String> PROP_LOG_PREFIX = newStringKey("logPrefix", "???", ???);
-    //public static ConfigKey<Boolean> PROP_NO_STDOUT_LOGGING = newStringKey("noStdoutLogging", "???", ???);
-    //public static ConfigKey<Boolean> PROP_NO_STDOUT_LOGGING = newStringKey("noStdoutLogging", "???", ???);
-
-    /**
-     * @throws SshException
-     */
-    public void connect();
-
-    /**
-     * @deprecated since 0.7.0; (since much earlier) this ignores the argument in favour of {@link #PROP_SSH_TRIES}
-     * 
-     * @param maxAttempts
-     * @throws SshException
-     */
-    public void connect(int maxAttempts);
-
-    public void disconnect();
-
-    public boolean isConnected();
-
-    /**
-     * @see super{@link #execScript(Map, List, Map)}
-     * @throws SshException If failed to connect
-     */
-    @Override
-    public int execScript(Map<String,?> props, List<String> commands, Map<String,?> env);
-
-    /**
-     * @see #execScript(Map, List, Map)
-     */
-    @Override
-    public int execScript(Map<String,?> props, List<String> commands);
-
-    /**
-     * @see super{@link #execCommands(Map, List, Map)}
-     * @throws SshException If failed to connect
-     */
-    @Override
-    public int execCommands(Map<String,?> properties, List<String> commands, Map<String,?> env);
-
-    /**
-     * @see #execCommands(Map, List, Map)
-     */
-    @Override
-    public int execCommands(Map<String,?> properties, List<String> commands);
-
-    /**
-     * Copies the file to the server at the given path.
-     * If path is null, empty, '.', '..', or ends with '/' then file name is used.
-     * <p>
-     * The file will not preserve the permission of last _access_ date.
-     * 
-     * Optional properties are:
-     * <ul>
-     *   <li>'permissions' (e.g. "0644") - see {@link #PROP_PERMISSIONS}
-     *   <li>'lastModificationDate' see {@link #PROP_LAST_MODIFICATION_DATE}; not supported by all SshTool implementations
-     *   <li>'lastAccessDate' see {@link #PROP_LAST_ACCESS_DATE}; not supported by all SshTool implementations
-     * </ul>
-     * 
-     * @return exit code (not supported by all SshTool implementations, usually throwing on error;
-     * sometimes possibly returning 0 even on error (?) )
-     */
-    public int copyToServer(Map<String,?> props, File localFile, String pathAndFileOnRemoteServer);
-
-    /**
-     * Closes the given input stream before returning.
-     * Consider using {@link KnownSizeInputStream} for efficiency when the size of the stream is known.
-     * 
-     * @see #copyToServer(Map, File, String)
-     */
-    public int copyToServer(Map<String,?> props, InputStream contents, String pathAndFileOnRemoteServer);
-
-    /**
-     * @see #copyToServer(Map, File, String)
-     */
-    public int copyToServer(Map<String,?> props, byte[] contents, String pathAndFileOnRemoteServer);
-
-    /**
-     * Copies the file from the server at the given path.
-     *
-     * @return exit code (not supported by all SshTool implementations, usually throwing on error;
-     * sometimes possibly returning 0 even on error (?) )
-     */
-    public int copyFromServer(Map<String,?> props, String pathAndFileOnRemoteServer, File local);
-
-    // TODO might be more efficicent than copyFrom by way of temp file
-//    /**
-//     * Reads from the file at the given path on the remote server.
-//     */
-//    public InputStream streamFromServer(Map<String,?> props, String pathAndFileOnRemoteServer);
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/ssh/cli/SshCliTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/cli/SshCliTool.java b/core/src/main/java/brooklyn/util/internal/ssh/cli/SshCliTool.java
deleted file mode 100644
index 91b5d91..0000000
--- a/core/src/main/java/brooklyn/util/internal/ssh/cli/SshCliTool.java
+++ /dev/null
@@ -1,316 +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.internal.ssh.cli;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.io.File;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.internal.ssh.SshAbstractTool;
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.internal.ssh.process.ProcessTool;
-import brooklyn.util.text.StringEscapes.BashStringEscapes;
-import brooklyn.util.text.Strings;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-
-/**
- * For ssh and scp commands, delegating to system calls.
- */
-public class SshCliTool extends SshAbstractTool implements SshTool {
-
-    // TODO No retry support, with backoffLimitedRetryHandler
-    
-    private static final Logger LOG = LoggerFactory.getLogger(SshCliTool.class);
-
-    public static final ConfigKey<String> PROP_SSH_EXECUTABLE = ConfigKeys.newStringConfigKey("sshExecutable", "command to execute for ssh (defaults to \"ssh\", but could be overridden to sshg3 for Tectia for example)", "ssh");
-    public static final ConfigKey<String> PROP_SSH_FLAGS = ConfigKeys.newStringConfigKey("sshFlags", "flags to pass to ssh, as a space separated list", "");
-    public static final ConfigKey<String> PROP_SCP_EXECUTABLE = ConfigKeys.newStringConfigKey("scpExecutable", "command to execute for scp (defaults to \"scp\", but could be overridden to scpg3 for Tectia for example)", "scp");
-
-    public static Builder<SshCliTool,?> builder() {
-        return new ConcreteBuilder();
-    }
-    
-    private static class ConcreteBuilder extends Builder<SshCliTool, ConcreteBuilder> {
-    }
-    
-    public static class Builder<T extends SshCliTool, B extends Builder<T,B>> extends AbstractSshToolBuilder<T,B> {
-        private String sshExecutable;
-        private String sshFlags;
-        private String scpExecutable;
-
-        @Override
-        public B from(Map<String,?> props) {
-            super.from(props);
-            sshExecutable = getOptionalVal(props, PROP_SSH_EXECUTABLE);
-            sshFlags = getOptionalVal(props, PROP_SSH_FLAGS);
-            scpExecutable = getOptionalVal(props, PROP_SCP_EXECUTABLE);
-            return self();
-        }
-        public B sshExecutable(String val) {
-            this.sshExecutable = val; return self();
-        }
-        public B scpExecutable(String val) {
-            this.scpExecutable = val; return self();
-        }
-        @SuppressWarnings("unchecked")
-        public T build() {
-            return (T) new SshCliTool(this);
-        }
-    }
-
-    private final String sshExecutable;
-    private final String sshFlags;
-    private final String scpExecutable;
-
-    public SshCliTool(Map<String,?> map) {
-        this(builder().from(map));
-    }
-    
-    private SshCliTool(Builder<?,?> builder) {
-        super(builder);
-        sshExecutable = checkNotNull(builder.sshExecutable);
-        sshFlags = checkNotNull(builder.sshFlags);
-        scpExecutable = checkNotNull(builder.scpExecutable);
-        if (LOG.isTraceEnabled()) LOG.trace("Created SshCliTool {} ({})", this, System.identityHashCode(this));
-    }
-    
-    @Override
-    public void connect() {
-        // no-op
-    }
-
-    @Override
-    public void connect(int maxAttempts) {
-        // no-op
-    }
-
-    @Override
-    public void disconnect() {
-        if (LOG.isTraceEnabled()) LOG.trace("Disconnecting SshCliTool {} ({}) - no-op", this, System.identityHashCode(this));
-        // no-op
-    }
-
-    @Override
-    public boolean isConnected() {
-        // TODO Always pretends to be connected
-        return true;
-    }
-
-    @Override
-    public int copyToServer(java.util.Map<String,?> props, byte[] contents, String pathAndFileOnRemoteServer) {
-        return copyTempFileToServer(props, writeTempFile(contents), pathAndFileOnRemoteServer);
-    }
-    
-    @Override
-    public int copyToServer(java.util.Map<String,?> props, InputStream contents, String pathAndFileOnRemoteServer) {
-        return copyTempFileToServer(props, writeTempFile(contents), pathAndFileOnRemoteServer);
-    }
-    
-    @Override
-    public int copyToServer(Map<String,?> props, File f, String pathAndFileOnRemoteServer) {
-        if (hasVal(props, PROP_LAST_MODIFICATION_DATE)) {
-            LOG.warn("Unsupported ssh feature, setting lastModificationDate for {}:{}", this, pathAndFileOnRemoteServer);
-        }
-        if (hasVal(props, PROP_LAST_ACCESS_DATE)) {
-            LOG.warn("Unsupported ssh feature, setting lastAccessDate for {}:{}", this, pathAndFileOnRemoteServer);
-        }
-        String permissions = getOptionalVal(props, PROP_PERMISSIONS);
-        
-        int uid = getOptionalVal(props, PROP_OWNER_UID);
-        
-        int result = scpToServer(props, f, pathAndFileOnRemoteServer);
-        if (result == 0) {
-            result = chmodOnServer(props, permissions, pathAndFileOnRemoteServer);
-            if (result == 0) {
-                if (uid != -1) {
-                    result = chownOnServer(props, uid, pathAndFileOnRemoteServer);
-                    if (result != 0) {
-                        LOG.warn("Error setting file owner to {}, after copying file {} to {}:{}; exit code {}", new Object[] { uid, pathAndFileOnRemoteServer, this, f, result });
-                    }
-                }
-            } else {
-                LOG.warn("Error setting file permissions to {}, after copying file {} to {}:{}; exit code {}", new Object[] { permissions, pathAndFileOnRemoteServer, this, f, result });
-            }
-        } else {
-            LOG.warn("Error copying file {} to {}:{}; exit code {}", new Object[] {pathAndFileOnRemoteServer, this, f, result});
-        }
-        return result;
-    }
-
-    private int chownOnServer(Map<String,?> props, int uid, String remote) {
-        return sshExec(props, "chown "+uid+" "+remote);
-    }
-    
-    private int copyTempFileToServer(Map<String,?> props, File f, String pathAndFileOnRemoteServer) {
-        try {
-            return copyToServer(props, f, pathAndFileOnRemoteServer);
-        } finally {
-            f.delete();
-        }
-    }
-
-    @Override
-    public int copyFromServer(Map<String,?> props, String pathAndFileOnRemoteServer, File localFile) {
-        return scpFromServer(props, pathAndFileOnRemoteServer, localFile);
-    }
-
-    @Override
-    public int execScript(final Map<String,?> props, final List<String> commands, final Map<String,?> env) {
-        return new ToolAbstractExecScript(props) {
-            public int run() {
-                String scriptContents = toScript(props, commands, env);
-                if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} as script: {}", host, scriptContents);
-                copyTempFileToServer(ImmutableMap.of("permissions", "0700"), writeTempFile(scriptContents), scriptPath);
-
-                String cmd = Strings.join(buildRunScriptCommand(), separator);
-                return asInt(sshExec(props, cmd), -1);
-            }
-        }.run();
-    }
-
-    @Override
-    public int execCommands(Map<String,?> props, List<String> commands, Map<String,?> env) {
-        Map<String,Object> props2 = new MutableMap<String,Object>();
-        if (props!=null) props2.putAll(props);
-        props2.put(SshTool.PROP_NO_EXTRA_OUTPUT.getName(), true);
-        return execScript(props2, commands, env);
-    }
-    
-    private int scpToServer(Map<String,?> props, File local, String remote) {
-        String to = (Strings.isEmpty(getUsername()) ? "" : getUsername()+"@")+getHostAddress()+":"+remote;
-        return scpExec(props, local.getAbsolutePath(), to);
-    }
-
-    private int scpFromServer(Map<String,?> props, String remote, File local) {
-        String from = (Strings.isEmpty(getUsername()) ? "" : getUsername()+"@")+getHostAddress()+":"+remote;
-        return scpExec(props, from, local.getAbsolutePath());
-    }
-    
-    private int chmodOnServer(Map<String,?> props, String permissions, String remote) {
-        return sshExec(props, "chmod "+permissions+" "+remote);
-    }
-
-    private int scpExec(Map<String,?> props, String from, String to) {
-        File tempFile = null;
-        try {
-            List<String> cmd = Lists.newArrayList();
-            cmd.add(getOptionalVal(props, PROP_SCP_EXECUTABLE, scpExecutable));
-            if (privateKeyFile != null) {
-                cmd.add("-i");
-                cmd.add(privateKeyFile.getAbsolutePath());
-            } else if (privateKeyData != null) {
-                tempFile = writeTempFile(privateKeyData);
-                cmd.add("-i");
-                cmd.add(tempFile.getAbsolutePath());
-            }
-            if (!strictHostKeyChecking) {
-                cmd.add("-o");
-                cmd.add("StrictHostKeyChecking=no");
-            }
-            if (port != 22) {
-                cmd.add("-P");
-                cmd.add(""+port);
-            }
-            cmd.add(from);
-            cmd.add(to);
-            
-            if (LOG.isTraceEnabled()) LOG.trace("Executing with command: {}", cmd);
-            int result = execProcess(props, cmd);
-            
-            if (LOG.isTraceEnabled()) LOG.trace("Executed command: {}; exit code {}", cmd, result);
-            return result;
-
-        } finally {
-            if (tempFile != null) tempFile.delete();
-        }
-    }
-    
-    private int sshExec(Map<String,?> props, String command) {
-        File tempKeyFile = null;
-        try {
-            List<String> cmd = Lists.newArrayList();
-            cmd.add(getOptionalVal(props, PROP_SSH_EXECUTABLE, sshExecutable));
-            String propsFlags = getOptionalVal(props, PROP_SSH_FLAGS, sshFlags);
-            if (propsFlags!=null && propsFlags.trim().length()>0)
-                cmd.addAll(Arrays.asList(propsFlags.trim().split(" ")));
-            if (privateKeyFile != null) {
-                cmd.add("-i");
-                cmd.add(privateKeyFile.getAbsolutePath());
-            } else if (privateKeyData != null) {
-                tempKeyFile = writeTempFile(privateKeyData);
-                cmd.add("-i");
-                cmd.add(tempKeyFile.getAbsolutePath());
-            }
-            if (!strictHostKeyChecking) {
-                cmd.add("-o");
-                cmd.add("StrictHostKeyChecking=no");
-            }
-            if (port != 22) {
-                cmd.add("-P");
-                cmd.add(""+port);
-            }
-            if (allocatePTY) {
-                // have to be careful with double -tt as it can leave a shell session active
-                // when done from bash (ie  ssh -tt localhost < /tmp/myscript.sh);
-                // hover that doesn't seem to be a problem the way we use it from brooklyn
-                // (and note single -t doesn't work _programmatically_ since the input isn't a terminal)
-                cmd.add("-tt");
-            }
-            cmd.add((Strings.isEmpty(getUsername()) ? "" : getUsername()+"@")+getHostAddress());
-            
-            cmd.add("bash -c "+BashStringEscapes.wrapBash(command));
-            // previously we tried these approaches:
-            //cmd.add("$(<"+tempCmdFile.getAbsolutePath()+")");
-            // only pays attention to the first word; the "; echo Executing ..." get treated as arguments
-            // to the script in the first word, when invoked from java (when invoked from prompt the behaviour is as desired)
-            //cmd.add("\""+command+"\"");
-            // only works if command is a single word
-            //cmd.add(tempCmdFile.getAbsolutePath());
-            // above of course only works if the metafile is copied across
-            
-            if (LOG.isTraceEnabled()) LOG.trace("Executing ssh with command: {} (with {})", command, cmd);
-            int result = execProcess(props, cmd);
-            
-            if (LOG.isTraceEnabled()) LOG.trace("Executed command: {}; exit code {}", cmd, result);
-            return result;
-            
-        } finally {
-            if (tempKeyFile != null) tempKeyFile.delete();
-        }
-    }
-
-    private int execProcess(Map<String,?> props, List<String> cmdWords) {
-        OutputStream out = getOptionalVal(props, PROP_OUT_STREAM);
-        OutputStream err = getOptionalVal(props, PROP_ERR_STREAM);
-        return ProcessTool.execSingleProcess(cmdWords, null, (File)null, out, err, this);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/ssh/process/ProcessTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/process/ProcessTool.java b/core/src/main/java/brooklyn/util/internal/ssh/process/ProcessTool.java
deleted file mode 100644
index 90de9f2..0000000
--- a/core/src/main/java/brooklyn/util/internal/ssh/process/ProcessTool.java
+++ /dev/null
@@ -1,214 +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.internal.ssh.process;
-
-import static brooklyn.entity.basic.ConfigKeys.newConfigKey;
-import static brooklyn.entity.basic.ConfigKeys.newStringConfigKey;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.internal.ssh.ShellAbstractTool;
-import brooklyn.util.internal.ssh.ShellTool;
-import brooklyn.util.internal.ssh.SshException;
-import brooklyn.util.os.Os;
-import brooklyn.util.stream.StreamGobbler;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Throwables;
-import com.google.common.io.ByteSource;
-import com.google.common.io.ByteStreams;
-import com.google.common.io.Files;
-
-/** Implementation of {@link ShellTool} which runs locally. */
-public class ProcessTool extends ShellAbstractTool implements ShellTool {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ProcessTool.class);
-
-    // applies to calls
-    
-    public static final ConfigKey<Boolean> PROP_LOGIN_SHELL = newConfigKey("loginShell", "Causes the commands to be invoked with bash arguments to forcea  login shell", Boolean.FALSE);
-
-    public static final ConfigKey<String> PROP_DIRECTORY = newStringConfigKey("directory", "the working directory, for executing commands", null);
-    
-    public ProcessTool() {
-        this(null);
-    }
-    
-    public ProcessTool(Map<String,?> flags) {
-        super(getOptionalVal(flags, PROP_LOCAL_TEMP_DIR));
-        if (flags!=null) {
-            MutableMap<String, Object> flags2 = MutableMap.copyOf(flags);
-            // TODO should remember other flags here?  (e.g. NO_EXTRA_OUTPUT, RUN_AS_ROOT, etc)
-            flags2.remove(PROP_LOCAL_TEMP_DIR.getName());
-            if (!flags2.isEmpty())
-                LOG.warn(""+this+" ignoring unsupported constructor flags: "+flags);
-        }
-    }
-
-    @Override
-    public int execScript(final Map<String,?> props, final List<String> commands, final Map<String,?> env) {
-        return new ToolAbstractExecScript(props) {
-            public int run() {
-                try {
-                    String directory = getOptionalVal(props, PROP_DIRECTORY);
-                    File directoryDir = (directory != null) ? new File(Os.tidyPath(directory)) : null;
-                    
-                    String scriptContents = toScript(props, commands, env);
-
-                    if (LOG.isTraceEnabled()) LOG.trace("Running shell process (process) as script:\n{}", scriptContents);
-                    File to = new File(scriptPath);
-                    Files.createParentDirs(to);
-                    ByteSource.wrap(scriptContents.getBytes()).copyTo(Files.asByteSink(to));
-
-                    List<String> cmds = buildRunScriptCommand();
-                    cmds.add(0, "chmod +x "+scriptPath);
-                    return asInt(execProcesses(cmds, null, directoryDir, out, err, separator, getOptionalVal(props, PROP_LOGIN_SHELL), this), -1);
-                } catch (IOException e) {
-                    throw Throwables.propagate(e);
-                }
-            }
-        }.run();
-    }
-
-    @Override
-    public int execCommands(Map<String,?> props, List<String> commands, Map<String,?> env) {
-        if (Boolean.FALSE.equals(props.get("blocks"))) {
-            throw new IllegalArgumentException("Cannot exec non-blocking: command="+commands);
-        }
-        OutputStream out = getOptionalVal(props, PROP_OUT_STREAM);
-        OutputStream err = getOptionalVal(props, PROP_ERR_STREAM);
-        String separator = getOptionalVal(props, PROP_SEPARATOR);
-        String directory = getOptionalVal(props, PROP_DIRECTORY);
-        File directoryDir = (directory != null) ? new File(Os.tidyPath(directory)) : null;
-
-        List<String> allcmds = toCommandSequence(commands, null);
-
-        String singlecmd = Joiner.on(separator).join(allcmds);
-        if (Boolean.TRUE.equals(getOptionalVal(props, PROP_RUN_AS_ROOT))) {
-            LOG.warn("Cannot run as root when executing as command; run as a script instead (will run as normal user): "+singlecmd);
-        }
-        if (LOG.isTraceEnabled()) LOG.trace("Running shell command (process): {}", singlecmd);
-        
-        return asInt(execProcesses(allcmds, env, directoryDir, out, err, separator, getOptionalVal(props, PROP_LOGIN_SHELL), this), -1);
-    }
-
-    /**
-     * as {@link #execProcesses(List, Map, OutputStream, OutputStream, String, boolean, Object)} but not using a login shell
-     * @deprecated since 0.7; use {@link #execProcesses(List, Map, File, OutputStream, OutputStream, String, boolean, Object)}
-     */
-    @Deprecated
-    public static int execProcesses(List<String> cmds, Map<String,?> env, OutputStream out, OutputStream err, String separator, Object contextForLogging) {
-        return execProcesses(cmds, env, (File)null, out, err, separator, false, contextForLogging);
-    }
-
-    /**
-     * @deprecated since 0.7; use {@link #execProcesses(List, Map, File, OutputStream, OutputStream, String, boolean, Object)}
-     */
-    @Deprecated
-    public static int execProcesses(List<String> cmds, Map<String,?> env, OutputStream out, OutputStream err, String separator, boolean asLoginShell, Object contextForLogging) {
-        return execProcesses(cmds, env, (File)null, out, err, separator, asLoginShell, contextForLogging);
-    }
-    
-    /** executes a set of commands by sending them as a single process to `bash -c` 
-     * (single command argument of all the commands, joined with separator)
-     * <p>
-     * consequence of this is that you should not normally need to escape things oddly in your commands, 
-     * type them just as you would into a bash shell (if you find exceptions please note them here!)
-     */
-    public static int execProcesses(List<String> cmds, Map<String,?> env, File directory, OutputStream out, OutputStream err, String separator, boolean asLoginShell, Object contextForLogging) {
-        MutableList<String> commands = new MutableList<String>().append("bash");
-        if (asLoginShell) commands.append("-l");
-        commands.append("-c", Strings.join(cmds, Preconditions.checkNotNull(separator, "separator")));
-        return execSingleProcess(commands, env, directory, out, err, contextForLogging);
-    }
-    
-    /**
-     * @deprecated since 0.7; use {@link #execSingleProcess(List, Map, File, OutputStream, OutputStream, Object)}
-     */
-    @Deprecated
-    public static int execSingleProcess(List<String> cmdWords, Map<String,?> env, OutputStream out, OutputStream err, Object contextForLogging) {
-        return execSingleProcess(cmdWords, env, (File)null, out, err, contextForLogging);
-    }
-    
-    /** executes a single process made up of the given command words (*not* bash escaped);
-     * should be portable across OS's */
-    public static int execSingleProcess(List<String> cmdWords, Map<String,?> env, File directory, OutputStream out, OutputStream err, Object contextForLogging) {
-        StreamGobbler errgobbler = null;
-        StreamGobbler outgobbler = null;
-        
-        ProcessBuilder pb = new ProcessBuilder(cmdWords);
-        if (env!=null) {
-            for (Map.Entry<String,?> kv: env.entrySet()) pb.environment().put(kv.getKey(), String.valueOf(kv.getValue())); 
-        }
-        if (directory != null) {
-            pb.directory(directory);
-        }
-        
-        try {
-            Process p = pb.start();
-            
-            if (out != null) {
-                InputStream outstream = p.getInputStream();
-                outgobbler = new StreamGobbler(outstream, out, (Logger) null);
-                outgobbler.start();
-            }
-            if (err != null) {
-                InputStream errstream = p.getErrorStream();
-                errgobbler = new StreamGobbler(errstream, err, (Logger) null);
-                errgobbler.start();
-            }
-            
-            int result = p.waitFor();
-            
-            if (outgobbler != null) outgobbler.blockUntilFinished();
-            if (errgobbler != null) errgobbler.blockUntilFinished();
-            
-            if (result==255)
-                // this is not definitive, but tests (and code?) expects throw exception if can't connect;
-                // only return exit code when it is exit code from underlying process;
-                // we have no way to distinguish 255 from ssh failure from 255 from the command run through ssh ...
-                // but probably 255 is from CLI ssh
-                throw new SshException("exit code 255 from CLI ssh; probably failed to connect");
-            
-            return result;
-        } catch (InterruptedException e) {
-            throw Exceptions.propagate(e);
-        } catch (IOException e) {
-            throw Exceptions.propagate(e);
-        } finally {
-            closeWhispering(outgobbler, contextForLogging, "execProcess");
-            closeWhispering(errgobbler, contextForLogging, "execProcess");
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjClientConnection.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjClientConnection.java b/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjClientConnection.java
deleted file mode 100644
index 982022a..0000000
--- a/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjClientConnection.java
+++ /dev/null
@@ -1,282 +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.internal.ssh.sshj;
-
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.io.File;
-import java.io.IOException;
-
-import net.schmizz.sshj.SSHClient;
-import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
-import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
-import net.schmizz.sshj.userauth.password.PasswordUtils;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.GroovyJavaMethods;
-import brooklyn.util.internal.ssh.SshAbstractTool.SshAction;
-
-import com.google.common.base.Objects;
-import com.google.common.net.HostAndPort;
-
-/** based on code from jclouds */
-public class SshjClientConnection implements SshAction<SSHClient> {
-
-    private static final Logger LOG = LoggerFactory.getLogger(SshjClientConnection.class);
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-
-        protected HostAndPort hostAndPort;
-        protected String username;
-        protected String password;
-        protected String privateKeyPassphrase;
-        protected String privateKeyData;
-        protected File privateKeyFile;
-        protected long connectTimeout;
-        protected long sessionTimeout;
-        protected boolean strictHostKeyChecking;
-
-        public Builder hostAndPort(HostAndPort hostAndPort) {
-            this.hostAndPort = hostAndPort;
-            return this;
-        }
-
-        public Builder username(String username) {
-            this.username = username;
-            return this;
-        }
-
-        public Builder password(String val) {
-            this.password = val;
-            return this;
-        }
-
-        /** @deprecated use privateKeyData */
-        public Builder privateKey(String val) {
-            this.privateKeyData = val;
-            return this;
-        }
-
-        public Builder privateKeyPassphrase(String val) {
-            this.privateKeyPassphrase = val;
-            return this;
-        }
-        
-        public Builder privateKeyData(String val) {
-            this.privateKeyData = val;
-            return this;
-        }
-        
-        public Builder privateKeyFile(File val) {
-            this.privateKeyFile = val;
-            return this;
-        }
-        
-        public Builder strictHostKeyChecking(boolean val) {
-            this.strictHostKeyChecking = val;
-            return this;
-        }
-
-        public Builder connectTimeout(long connectTimeout) {
-            this.connectTimeout = connectTimeout;
-            return this;
-        }
-
-        public Builder sessionTimeout(long sessionTimeout) {
-            this.sessionTimeout = sessionTimeout;
-            return this;
-        }
-
-        public SshjClientConnection build() {
-            return new SshjClientConnection(this);
-        }
-
-        protected static Builder fromSSHClientConnection(SshjClientConnection in) {
-            return new Builder().hostAndPort(in.getHostAndPort()).connectTimeout(in.getConnectTimeout()).sessionTimeout(
-                    in.getSessionTimeout()).username(in.username).password(in.password).privateKey(in.privateKeyData).privateKeyFile(in.privateKeyFile);
-        }
-    }
-
-    private final HostAndPort hostAndPort;
-    private final String username;
-    private final String password;
-    private final String privateKeyPassphrase;
-    private final String privateKeyData;
-    private final File privateKeyFile;
-    private final boolean strictHostKeyChecking;
-    private final int connectTimeout;
-    private final int sessionTimeout;
-    
-    SSHClient ssh;
-
-    private SshjClientConnection(Builder builder) {
-        this.hostAndPort = checkNotNull(builder.hostAndPort);
-        this.username = builder.username;
-        this.password = builder.password;
-        this.privateKeyPassphrase = builder.privateKeyPassphrase;
-        this.privateKeyData = builder.privateKeyData;
-        this.privateKeyFile = builder.privateKeyFile;
-        this.strictHostKeyChecking = builder.strictHostKeyChecking;
-        this.connectTimeout = checkInt("connectTimeout", builder.connectTimeout, Integer.MAX_VALUE);
-        this.sessionTimeout = checkInt("sessionTimeout", builder.sessionTimeout, Integer.MAX_VALUE);
-    }
-
-    static Integer checkInt(String context, long value, Integer ifTooLarge) {
-        if (value > Integer.MAX_VALUE) {
-            LOG.warn("Value '"+value+"' for "+context+" too large in SshjClientConnection; using "+value);
-            return ifTooLarge;
-        }
-        return (int)value;
-    }
-
-    public boolean isConnected() {
-        return ssh != null && ssh.isConnected();
-    }
-
-    public boolean isAuthenticated() {
-        return ssh != null && ssh.isAuthenticated();
-    }
-
-    @Override
-    public void clear() {
-        if (ssh != null && ssh.isConnected()) {
-            try {
-                if (LOG.isTraceEnabled()) LOG.trace("Disconnecting SshjClientConnection {} ({})", this, System.identityHashCode(this));
-                ssh.disconnect();
-            } catch (IOException e) {
-                if (LOG.isDebugEnabled()) LOG.debug("<< exception disconnecting from {}: {}", e, e.getMessage());
-            }
-        }
-        ssh = null;
-    }
-
-    @Override
-    public SSHClient create() throws Exception {
-        if (LOG.isTraceEnabled()) LOG.trace("Connecting SshjClientConnection {} ({})", this, System.identityHashCode(this));
-        ssh = new net.schmizz.sshj.SSHClient();
-        if (!strictHostKeyChecking) {
-            ssh.addHostKeyVerifier(new PromiscuousVerifier());
-        }
-        if (connectTimeout != 0) {
-            ssh.setConnectTimeout(connectTimeout);
-        }
-        if (sessionTimeout != 0) {
-            ssh.setTimeout(sessionTimeout);
-        }
-        ssh.connect(hostAndPort.getHostText(), hostAndPort.getPortOrDefault(22));
-        
-        if (password != null) {
-            ssh.authPassword(username, password);
-        } else if (privateKeyData != null) {
-            OpenSSHKeyFile key = new OpenSSHKeyFile();
-            key.init(privateKeyData, null, 
-                    GroovyJavaMethods.truth(privateKeyPassphrase) ? 
-                            PasswordUtils.createOneOff(privateKeyPassphrase.toCharArray())
-                            : null);
-            ssh.authPublickey(username, key);
-        } else if (privateKeyFile != null) {
-            OpenSSHKeyFile key = new OpenSSHKeyFile();
-            key.init(privateKeyFile, 
-                    GroovyJavaMethods.truth(privateKeyPassphrase) ? 
-                            PasswordUtils.createOneOff(privateKeyPassphrase.toCharArray())
-                            : null);
-            ssh.authPublickey(username, key);
-        } else {
-            // Accept defaults (in ~/.ssh)
-            ssh.authPublickey(username);
-        }
-        
-        return ssh;
-    }
-
-    /**
-     * @return host and port, where port if not present defaults to {@code 22}
-     */
-    public HostAndPort getHostAndPort() {
-        return hostAndPort;
-    }
-
-    /**
-     * @return username used in this ssh
-     */
-    public String getUsername() {
-        return username;
-    }
-
-    /**
-     * 
-     * @return how long to wait for the initial connection to be made
-     */
-    public int getConnectTimeout() {
-        return connectTimeout;
-    }
-
-    /**
-     * 
-     * @return how long to keep the ssh open, or {@code 0} for indefinitely
-     */
-    public int getSessionTimeout() {
-        return sessionTimeout;
-    }
-
-    /**
-     * 
-     * @return the current ssh or {@code null} if not connected
-     */
-    public SSHClient getSSHClient() {
-        return ssh;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o)
-            return true;
-        if (o == null || getClass() != o.getClass())
-            return false;
-        SshjClientConnection that = SshjClientConnection.class.cast(o);
-        return equal(this.hostAndPort, that.hostAndPort) && equal(this.username, that.username) 
-                && equal(this.password, that.password) && equal(this.privateKeyData, that.privateKeyData)
-                && equal(this.privateKeyFile, that.privateKeyFile) && equal(this.ssh, that.ssh);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(hostAndPort, username, password, privateKeyData, ssh);
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper("")
-                .add("hostAndPort", hostAndPort)
-                .add("user", username)
-                .add("ssh", ssh != null ? ssh.hashCode() : null)
-                .add("password", (password != null ? "xxxxxx" : null))
-                .add("privateKeyFile", privateKeyFile)
-                .add("privateKey", (privateKeyData != null ? "xxxxxx" : null))
-                .add("connectTimeout", connectTimeout)
-                .add("sessionTimeout", sessionTimeout).toString();
-    }
-}


[17/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/RepeaterTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/RepeaterTest.groovy b/core/src/test/java/brooklyn/util/internal/RepeaterTest.groovy
deleted file mode 100644
index 65976e1..0000000
--- a/core/src/test/java/brooklyn/util/internal/RepeaterTest.groovy
+++ /dev/null
@@ -1,255 +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.internal
-
-import static java.util.concurrent.TimeUnit.*
-import static org.testng.Assert.*
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit
-
-import org.testng.annotations.Test
-
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Stopwatch
-
-public class RepeaterTest {
-    static { TimeExtras.init() }
-
-    @Test
-    public void sanityTest() {
-        new Repeater("Sanity test")
-            .repeat()
-            .until { true }
-            .every(10 * MILLISECONDS);
-    }
-
-    @Test
-    public void sanityTestDescription() {
-        new Repeater()
-            .repeat()
-            .until { true }
-            .every(10 * MILLISECONDS);
-    }
-
-    @Test
-    public void sanityTestBuilder() {
-        Repeater.create("Sanity test")
-            .repeat()
-            .until { true }
-            .every(10 * MILLISECONDS);
-    }
-
-    @Test
-    public void sanityTestBuilderDescription() {
-        Repeater.create()
-            .repeat()
-            .until { true }
-            .every(10 * MILLISECONDS);
-    }
-
-    @Test(expectedExceptions = [ NullPointerException.class ])
-    public void repeatFailsIfClosureIsNull() {
-        new Repeater("repeatFailsIfClosureIsNull").repeat((Callable<?>)null);
-        fail "Expected exception was not thrown"
-    }
-
-    @Test
-    public void repeatSucceedsIfClosureIsNonNull() {
-        new Repeater("repeatSucceedsIfClosureIsNonNull").repeat { true };
-    }
-
-    @Test(expectedExceptions = [ NullPointerException.class ])
-    public void untilFailsIfClosureIsNull() {
-        new Repeater("untilFailsIfClosureIsNull").until(null);
-        fail "Expected exception was not thrown"
-    }
-
-    @Test
-    public void untilSucceedsIfClosureIsNonNull() {
-        new Repeater("untilSucceedsIfClosureIsNonNull").until { true };
-    }
-
-    @Test(expectedExceptions = [ IllegalArgumentException.class ])
-    public void everyFailsIfPeriodIsZero() {
-        new Repeater("everyFailsIfPeriodIsZero").every(0 * MILLISECONDS);
-        fail "Expected exception was not thrown"
-    }
-
-    @Test(expectedExceptions = [ IllegalArgumentException.class ])
-    public void everyFailsIfPeriodIsNegative() {
-        new Repeater("everyFailsIfPeriodIsNegative").every(-1 * MILLISECONDS);
-        fail "Expected exception was not thrown"
-    }
-
-    @Test(expectedExceptions = [ NullPointerException.class ])
-    public void everyFailsIfUnitsIsNull() {
-        new Repeater("everyFailsIfUnitsIsNull").every(10, null);
-        fail "Expected exception was not thrown"
-    }
-
-    @Test
-    public void everySucceedsIfPeriodIsPositiveAndUnitsIsNonNull() {
-        new Repeater("repeatSucceedsIfClosureIsNonNull").every(10 * MILLISECONDS);
-    }
-
-    @Test(expectedExceptions = [ IllegalArgumentException.class ])
-    public void limitTimeToFailsIfPeriodIsZero() {
-        new Repeater("limitTimeToFailsIfPeriodIsZero").limitTimeTo(0, TimeUnit.MILLISECONDS);
-        fail "Expected exception was not thrown"
-    }
-
-    @Test(expectedExceptions = [ IllegalArgumentException.class ])
-    public void limitTimeToFailsIfPeriodIsNegative() {
-        new Repeater("limitTimeToFailsIfPeriodIsNegative").limitTimeTo(-1, TimeUnit.MILLISECONDS);
-        fail "Expected exception was not thrown"
-    }
-
-    @Test(expectedExceptions = [ NullPointerException.class ])
-    public void limitTimeToFailsIfUnitsIsNull() {
-        new Repeater("limitTimeToFailsIfUnitsIsNull").limitTimeTo(10, null);
-        fail "Expected exception was not thrown"
-    }
-
-    @Test
-    public void limitTimeToSucceedsIfPeriodIsPositiveAndUnitsIsNonNull() {
-        new Repeater("limitTimeToSucceedsIfClosureIsNonNull").limitTimeTo(10, TimeUnit.MILLISECONDS);
-    }
-
-    @Test
-    public void everyAcceptsDuration() {
-        new Repeater("everyAcceptsDuration").every(Duration.ONE_SECOND);
-    }
-
-    @Test
-    public void everyAcceptsLong() {
-        new Repeater("everyAcceptsLong").every(1000L);
-    }
-
-    @Test
-    public void everyAcceptsTimeUnit() {
-        new Repeater("everyAcceptsTimeUnit").every(1000000L, TimeUnit.MICROSECONDS);
-    }
-
-    @Test
-    public void runReturnsTrueIfExitConditionIsTrue() {
-        assertTrue new Repeater("runReturnsTrueIfExitConditionIsTrue")
-            .repeat()
-            .every(1 * MILLISECONDS)
-            .until { true }
-            .run();
-    }
-
-    @Test
-    public void runRespectsMaximumIterationLimitAndReturnsFalseIfReached() {
-        int iterations = 0;
-        assertFalse new Repeater("runRespectsMaximumIterationLimitAndReturnsFalseIfReached")
-            .repeat { iterations++ }
-            .every(1 * MILLISECONDS)
-            .until { false }
-            .limitIterationsTo(5)
-            .run();
-        assertEquals 5, iterations;
-    }
-
-    /**
-     * Check that the {@link Repeater} will stop after a time limit.
-     *
-     * The repeater is configured to run every 100ms and never stop until the limit is reached.
-     * This is given as {@link Repeater#limitTimeTo(groovy.time.Duration)} and the execution time
-     * is then checked to ensure it is between 100% and 400% of the specified value. Due to scheduling
-     * delays and other factors in a non RTOS system it is expected that the repeater will take much
-     * longer to exit occasionally.
-     *
-     * @see #runRespectsMaximumIterationLimitAndReturnsFalseIfReached()
-     */
-    @Test(groups="Integration")
-    public void runRespectsTimeLimitAndReturnsFalseIfReached() {
-        final long LIMIT = 2000l;
-        Repeater repeater = new Repeater("runRespectsTimeLimitAndReturnsFalseIfReached")
-            .repeat()
-            .every(100 * MILLISECONDS)
-            .until { false }
-            .limitTimeTo(LIMIT, TimeUnit.MILLISECONDS);
-
-        Stopwatch stopwatch = new Stopwatch().start();
-        boolean result = repeater.run();
-        stopwatch.stop();
-
-        assertFalse result;
-
-        long difference = stopwatch.elapsed(TimeUnit.MILLISECONDS);
-        assertTrue(difference >= LIMIT, "Difference was: " + difference);
-        assertTrue(difference < 4 * LIMIT, "Difference was: " + difference);
-    }
-
-    @Test(expectedExceptions = [ IllegalStateException.class ])
-    public void runFailsIfUntilWasNotSet() {
-        new Repeater("runFailsIfUntilWasNotSet")
-            .repeat()
-            .every(10 * MILLISECONDS)
-            .run();
-        fail "Expected exception was not thrown"
-    }
-
-    @Test(expectedExceptions = [ IllegalStateException.class ])
-    public void runFailsIfEveryWasNotSet() {
-        new Repeater("runFailsIfEveryWasNotSet")
-            .repeat()
-            .until { true }
-            .run();
-        fail "Expected exception was not thrown"
-    }
-
-    @Test(expectedExceptions = [ UnsupportedOperationException.class ])
-    public void testRethrowsException() {
-        boolean result = new Repeater("throwRuntimeException")
-            .repeat()
-            .every(10 * MILLISECONDS)
-            .until { throw new UnsupportedOperationException("fail") }
-            .rethrowException()
-            .limitIterationsTo(2)
-            .run();
-        fail "Expected exception was not thrown"
-    }
-
-    @Test
-    public void testNoRethrowsException() {
-        try {
-	        boolean result = new Repeater("throwRuntimeException")
-	            .repeat()
-	            .every(10 * MILLISECONDS)
-	            .until { throw new UnsupportedOperationException("fail") }
-	            .limitIterationsTo(2)
-	            .run();
-	        assertFalse result
-        } catch (RuntimeException re) {
-            fail "Exception should not have been thrown: " + re.getMessage()
-        }
-    }
-	
-	public void testFlags() {
-		int count=0;
-		new Repeater(period: 5*MILLISECONDS, timeout: 100*MILLISECONDS).repeat({ count++ }).until({ count>100}).run();
-		assertTrue count>10
-		assertTrue count<30
-	}
-	
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/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
deleted file mode 100644
index ecb8c7c..0000000
--- a/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java
+++ /dev/null
@@ -1,360 +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.internal;
-
-import static org.testng.Assert.assertEquals;
-
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.codehaus.groovy.runtime.GStringImpl;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.util.collections.MutableSet;
-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;
-
-public class TypeCoercionsTest {
-
-    private static final Logger log = LoggerFactory.getLogger(TypeCoercionsTest.class);
-    
-    @Test
-    public void testCoerceCharSequenceToString() {
-        assertEquals(TypeCoercions.coerce(new StringBuilder("abc"), String.class), "abc");
-        assertEquals(TypeCoercions.coerce(new GStringImpl(new Object[0], new String[0]), String.class), "");
-    }
-    
-    @Test
-    public void testCoerceStringToPrimitive() {
-        assertEquals(TypeCoercions.coerce("1", Character.class), (Character)'1');
-        assertEquals(TypeCoercions.coerce(" ", Character.class), (Character)' ');
-        assertEquals(TypeCoercions.coerce("1", Short.class), (Short)((short)1));
-        assertEquals(TypeCoercions.coerce("1", Integer.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce("1", Long.class), (Long)1l);
-        assertEquals(TypeCoercions.coerce("1", Float.class), (Float)1f);
-        assertEquals(TypeCoercions.coerce("1", Double.class), (Double)1d);
-        assertEquals(TypeCoercions.coerce("true", Boolean.class), (Boolean)true);
-        assertEquals(TypeCoercions.coerce("False", Boolean.class), (Boolean)false);
-        assertEquals(TypeCoercions.coerce("true ", Boolean.class), (Boolean)true);
-
-        assertEquals(TypeCoercions.coerce("1", char.class), (Character)'1');
-        assertEquals(TypeCoercions.coerce("1", short.class), (Short)((short)1));
-        assertEquals(TypeCoercions.coerce("1", int.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce("1", long.class), (Long)1l);
-        assertEquals(TypeCoercions.coerce("1", float.class), (Float)1f);
-        assertEquals(TypeCoercions.coerce("1", double.class), (Double)1d);
-        assertEquals(TypeCoercions.coerce("TRUE", boolean.class), (Boolean)true);
-        assertEquals(TypeCoercions.coerce("false", boolean.class), (Boolean)false);
-    }
-
-    @Test
-    public void testCoercePrimitivesToSameType() {
-        assertEquals(TypeCoercions.coerce('1', Character.class), (Character)'1');
-        assertEquals(TypeCoercions.coerce((short)1, Short.class), (Short)((short)1));
-        assertEquals(TypeCoercions.coerce(1, Integer.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce(1l, Long.class), (Long)1l);
-        assertEquals(TypeCoercions.coerce(1f, Float.class), (Float)1f);
-        assertEquals(TypeCoercions.coerce(1d, Double.class), (Double)1d);
-        assertEquals(TypeCoercions.coerce(true, Boolean.class), (Boolean)true);
-    }
-    
-    @Test
-    public void testCastPrimitives() {
-        assertEquals(TypeCoercions.coerce(1L, Character.class), (Character)(char)1);
-        assertEquals(TypeCoercions.coerce(1L, Byte.class), (Byte)(byte)1);
-        assertEquals(TypeCoercions.coerce(1L, Short.class), (Short)(short)1);
-        assertEquals(TypeCoercions.coerce(1L, Integer.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce(1L, Long.class), (Long)(long)1);
-        assertEquals(TypeCoercions.coerce(1L, Float.class), (Float)(float)1);
-        assertEquals(TypeCoercions.coerce(1L, Double.class), (Double)(double)1);
-        
-        assertEquals(TypeCoercions.coerce(1L, char.class), (Character)(char)1);
-        assertEquals(TypeCoercions.coerce(1L, byte.class), (Byte)(byte)1);
-        assertEquals(TypeCoercions.coerce(1L, short.class), (Short)(short)1);
-        assertEquals(TypeCoercions.coerce(1L, int.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce(1L, long.class), (Long)(long)1);
-        assertEquals(TypeCoercions.coerce(1L, float.class), (Float)(float)1);
-        assertEquals(TypeCoercions.coerce(1L, double.class), (Double)(double)1);
-        
-        assertEquals(TypeCoercions.coerce((char)1, Integer.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce((byte)1, Integer.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce((short)1, Integer.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce((int)1, Integer.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce((long)1, Integer.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce((float)1, Integer.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce((double)1, Integer.class), (Integer)1);
-    }
-    
-    @Test
-    public void testCoercePrimitiveFailures() {
-        // error messages don't have to be this exactly, but they should include sufficient information...
-        assertCoercionFailsWithErrorMatching("maybe", boolean.class, StringPredicates.containsAllLiterals("String", "boolean", "maybe"));
-        assertCoercionFailsWithErrorMatching("NaN", int.class, StringPredicates.containsAllLiterals("int", "NaN"));
-        assertCoercionFailsWithErrorMatching('c', boolean.class, StringPredicates.containsAllLiterals("boolean", "(c)"));  // will say 'string' rather than 'char'
-        assertCoercionFailsWithErrorMatching(0, boolean.class, StringPredicates.containsAllLiterals("Integer", "boolean", "0"));
-    }
-    
-    protected void assertCoercionFailsWithErrorMatching(Object input, Class<?> type, Predicate<? super String> errorMessageRequirement) {
-        try {
-            Object result = TypeCoercions.coerce(input, type);
-            Assert.fail("Should have failed type coercion of "+input+" to "+type+", instead got: "+result);
-        } catch (Exception e) {
-            if (errorMessageRequirement==null || errorMessageRequirement.apply(e.toString()))
-                log.info("Primitive coercion failed as expected, with: "+e);
-            else
-                Assert.fail("Error from type coercion of "+input+" to "+type+" failed with wrong exception; expected match of "+errorMessageRequirement+" but got: "+e);
-        }
-        
-    }
-
-    @Test
-    public void testCastToNumericPrimitives() {
-        assertEquals(TypeCoercions.coerce(BigInteger.ONE, Integer.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce(BigInteger.ONE, int.class), (Integer)1);
-        assertEquals(TypeCoercions.coerce(BigInteger.valueOf(Long.MAX_VALUE), Long.class), (Long)Long.MAX_VALUE);
-        assertEquals(TypeCoercions.coerce(BigInteger.valueOf(Long.MAX_VALUE), long.class), (Long)Long.MAX_VALUE);
-        
-        assertEquals(TypeCoercions.coerce(BigDecimal.valueOf(0.5), Double.class), 0.5d, 0.00001d);
-        assertEquals(TypeCoercions.coerce(BigDecimal.valueOf(0.5), double.class), 0.5d, 0.00001d);
-    }
-
-    @Test
-    public void testCoerceStringToBigNumber() {
-    	assertEquals(TypeCoercions.coerce("0.5", BigDecimal.class), BigDecimal.valueOf(0.5));
-    	assertEquals(TypeCoercions.coerce("1", BigInteger.class), BigInteger.valueOf(1));
-    }
-
-    @Test
-    public void testCoerceStringToEnum() {
-        assertEquals(TypeCoercions.coerce("STARTING", Lifecycle.class), Lifecycle.STARTING);
-        assertEquals(TypeCoercions.coerce("Starting", Lifecycle.class), Lifecycle.STARTING);
-        assertEquals(TypeCoercions.coerce("starting", Lifecycle.class), Lifecycle.STARTING);
-        
-        assertEquals(TypeCoercions.coerce("LOWERCASE", PerverseEnum.class), PerverseEnum.lowercase);
-        assertEquals(TypeCoercions.coerce("CAMELCASE", PerverseEnum.class), PerverseEnum.camelCase);
-        assertEquals(TypeCoercions.coerce("upper", PerverseEnum.class), PerverseEnum.UPPER);
-        assertEquals(TypeCoercions.coerce("upper_with_underscore", PerverseEnum.class), PerverseEnum.UPPER_WITH_UNDERSCORE);
-        assertEquals(TypeCoercions.coerce("LOWER_WITH_UNDERSCORE", PerverseEnum.class), PerverseEnum.lower_with_underscore);
-    }
-    public static enum PerverseEnum {
-        lowercase,
-        camelCase,
-        UPPER,
-        UPPER_WITH_UNDERSCORE,
-        lower_with_underscore;
-    }
-    
-    @Test(expectedExceptions = ClassCoercionException.class)
-    public void testCoerceStringToEnumFailure() {
-        TypeCoercions.coerce("scrambled-eggs", Lifecycle.class);
-    }
-
-    @Test
-    public void testListToSetCoercion() {
-        Set<?> s = TypeCoercions.coerce(ImmutableList.of(1), Set.class);
-        Assert.assertEquals(s, ImmutableSet.of(1));
-    }
-    
-    @Test
-    public void testSetToListCoercion() {
-        List<?> s = TypeCoercions.coerce(ImmutableSet.of(1), List.class);
-        Assert.assertEquals(s, ImmutableList.of(1));
-    }
-    
-    @Test
-    public void testIterableToArrayCoercion() {
-        String[] s = TypeCoercions.coerce(ImmutableList.of("a", "b"), String[].class);
-        Assert.assertTrue(Arrays.equals(s, new String[] {"a", "b"}), "result="+Arrays.toString(s));
-        
-        Integer[] i = TypeCoercions.coerce(ImmutableList.of(1, 2), Integer[].class);
-        Assert.assertTrue(Arrays.equals(i, new Integer[] {1, 2}), "result="+Arrays.toString(i));
-        
-        int[] i2 = TypeCoercions.coerce(ImmutableList.of(1, 2), int[].class);
-        Assert.assertTrue(Arrays.equals(i2, new int[] {1, 2}), "result="+Arrays.toString(i2));
-        
-        int[] i3 = TypeCoercions.coerce(MutableSet.of("1", 2), int[].class);
-        Assert.assertTrue(Arrays.equals(i3, new int[] {1, 2}), "result="+Arrays.toString(i3));
-    }
-
-    @Test
-    public void testListEntryCoercion() {
-        List<?> s = TypeCoercions.coerce(ImmutableList.of("java.lang.Integer", "java.lang.Double"), new TypeToken<List<Class<?>>>() { });
-        Assert.assertEquals(s, ImmutableList.of(Integer.class, Double.class));
-    }
-    
-    @Test
-    public void testListEntryToSetCoercion() {
-        Set<?> s = TypeCoercions.coerce(ImmutableList.of("java.lang.Integer", "java.lang.Double"), new TypeToken<Set<Class<?>>>() { });
-        Assert.assertEquals(s, ImmutableSet.of(Integer.class, Double.class));
-    }
-    
-    @Test
-    public void testListEntryToCollectionCoercion() {
-        Collection<?> s = TypeCoercions.coerce(ImmutableList.of("java.lang.Integer", "java.lang.Double"), new TypeToken<Collection<Class<?>>>() { });
-        Assert.assertEquals(s, ImmutableList.of(Integer.class, Double.class));
-    }
-
-    @Test
-    public void testMapValueCoercion() {
-        Map<?,?> s = TypeCoercions.coerce(ImmutableMap.of("int", "java.lang.Integer", "double", "java.lang.Double"), new TypeToken<Map<String, Class<?>>>() { });
-        Assert.assertEquals(s, ImmutableMap.of("int", Integer.class, "double", Double.class));
-    }
-    
-    @Test
-    public void testMapKeyCoercion() {
-        Map<?,?> s = TypeCoercions.coerce(ImmutableMap.of("java.lang.Integer", "int", "java.lang.Double", "double"), new TypeToken<Map<Class<?>, String>>() { });
-        Assert.assertEquals(s, ImmutableMap.of(Integer.class, "int", Double.class, "double"));
-    }
-
-    @Test
-    public void testStringToListCoercion() {
-        List<?> s = TypeCoercions.coerce("a,b,c", List.class);
-        Assert.assertEquals(s, ImmutableList.of("a", "b", "c"));
-    }
-
-    @Test
-    @SuppressWarnings("serial")
-    public void testCoerceRecursivelyStringToGenericsCollection() {
-        assertEquals(TypeCoercions.coerce("1,2", new TypeToken<List<Integer>>() {}), ImmutableList.of(1, 2));
-    }
-    
-    @Test
-    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 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
-    public void testAs() {
-        Integer x = TypeCoercions.coerce(new WithAs("3"), Integer.class);
-        Assert.assertEquals(x, (Integer)3);
-    }
-
-    @Test
-    public void testFrom() {
-        WithFrom x = TypeCoercions.coerce("3", WithFrom.class);
-        Assert.assertEquals(x.value, 3);
-    }
-
-    @Test
-    public void testCoerceStringToNumber() {
-        assertEquals(TypeCoercions.coerce("1", Number.class), (Number) Double.valueOf(1));
-        assertEquals(TypeCoercions.coerce("1.0", Number.class), (Number) Double.valueOf(1.0));
-    }
-
-    @Test(expectedExceptions = ClassCoercionException.class)
-    public void testInvalidCoercionThrowsClassCoercionException() {
-        TypeCoercions.coerce(new Object(), TypeToken.of(Integer.class));
-    }
-
-    @Test
-    public void testCoercionFunction() {
-        assertEquals(TypeCoercions.function(Double.class).apply("1"), Double.valueOf(1));
-    }
-
-    public static class WithAs {
-        String value;
-        public WithAs(Object x) { value = ""+x; }
-        public Integer asInteger() {
-            return Integer.parseInt(value);
-        }
-    }
-
-    public static class WithFrom {
-        int value;
-        public static WithFrom fromString(String s) {
-            WithFrom result = new WithFrom();
-            result.value = Integer.parseInt(s);
-            return result;
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/ssh/RecordingSshTool.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/RecordingSshTool.java b/core/src/test/java/brooklyn/util/internal/ssh/RecordingSshTool.java
deleted file mode 100644
index 8c556a6..0000000
--- a/core/src/test/java/brooklyn/util/internal/ssh/RecordingSshTool.java
+++ /dev/null
@@ -1,95 +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.internal.ssh;
-
-import java.io.File;
-import java.io.InputStream;
-import java.util.List;
-import java.util.Map;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-
-/** Mock tool */
-public class RecordingSshTool implements SshTool {
-    
-    public static class ExecCmd {
-        public final Map<String,?> props;
-        public final String summaryForLogging;
-        public final List<String> commands;
-        public final Map<?,?> env;
-        
-        ExecCmd(Map<String,?> props, String summaryForLogging, List<String> commands, Map env) {
-            this.props = props;
-            this.summaryForLogging = summaryForLogging;
-            this.commands = commands;
-            this.env = env;
-        }
-        
-        @Override
-        public String toString() {
-            return "ExecCmd["+summaryForLogging+": "+commands+"; "+props+"; "+env+"]";
-        }
-    }
-    
-    public static List<ExecCmd> execScriptCmds = Lists.newCopyOnWriteArrayList();
-    
-    private boolean connected;
-    
-    public RecordingSshTool(Map<?,?> props) {
-    }
-    @Override public void connect() {
-        connected = true;
-    }
-    @Override public void connect(int maxAttempts) {
-        connected = true;
-    }
-    @Override public void disconnect() {
-        connected = false;
-    }
-    @Override public boolean isConnected() {
-        return connected;
-    }
-    @Override public int execScript(Map<String, ?> props, List<String> commands, Map<String, ?> env) {
-        execScriptCmds.add(new ExecCmd(props, "", commands, env));
-        return 0;
-    }
-    @Override public int execScript(Map<String, ?> props, List<String> commands) {
-        return execScript(props, commands, ImmutableMap.<String,Object>of());
-    }
-    @Override public int execCommands(Map<String, ?> props, List<String> commands, Map<String, ?> env) {
-        execScriptCmds.add(new ExecCmd(props, "", commands, env));
-        return 0;
-    }
-    @Override public int execCommands(Map<String, ?> props, List<String> commands) {
-        return execCommands(props, commands, ImmutableMap.<String,Object>of());
-    }
-    @Override public int copyToServer(Map<String, ?> props, File localFile, String pathAndFileOnRemoteServer) {
-        return 0;
-    }
-    @Override public int copyToServer(Map<String, ?> props, InputStream contents, String pathAndFileOnRemoteServer) {
-        return 0;
-    }
-    @Override public int copyToServer(Map<String, ?> props, byte[] contents, String pathAndFileOnRemoteServer) {
-        return 0;
-    }
-    @Override public int copyFromServer(Map<String, ?> props, String pathAndFileOnRemoteServer, File local) {
-        return 0;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/ssh/ShellToolAbstractTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/ShellToolAbstractTest.java b/core/src/test/java/brooklyn/util/internal/ssh/ShellToolAbstractTest.java
deleted file mode 100644
index b8b394b..0000000
--- a/core/src/test/java/brooklyn/util/internal/ssh/ShellToolAbstractTest.java
+++ /dev/null
@@ -1,439 +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.internal.ssh;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotEquals;
-import static org.testng.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.text.Identifiers;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-
-public abstract class ShellToolAbstractTest {
-
-    protected List<ShellTool> tools = Lists.newArrayList();
-    protected List<String> filesCreated;
-    protected String localFilePath;
-    
-    protected ShellTool tool;
-    
-    protected ShellTool newTool() {
-        return newTool(MutableMap.<String,Object>of());
-    }
-    
-    protected ShellTool newTool(Map<String,?> flags) {
-        ShellTool t = newUnregisteredTool(flags);
-        tools.add(t);
-        return t;
-    }
-
-    protected abstract ShellTool newUnregisteredTool(Map<String,?> flags);
-    
-    protected ShellTool tool() { return tool; }
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        localFilePath = "/tmp/ssh-test-local-"+Identifiers.makeRandomId(8);
-        filesCreated = new ArrayList<String>();
-        filesCreated.add(localFilePath);
-
-        tool = newTool();
-        connect(tool);
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void afterMethod() throws Exception {
-        for (ShellTool t : tools) {
-            if (t instanceof SshTool) ((SshTool)t).disconnect();
-        }
-        for (String fileCreated : filesCreated) {
-            new File(fileCreated).delete();
-        }
-    }
-
-    protected static void connect(ShellTool tool) {
-        if (tool instanceof SshTool)
-            ((SshTool)tool).connect();
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecConsecutiveCommands() throws Exception {
-        String out = execScript("echo run1");
-        String out2 = execScript("echo run2");
-        
-        assertTrue(out.contains("run1"), "out="+out);
-        assertTrue(out2.contains("run2"), "out="+out);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecScriptChainOfCommands() throws Exception {
-        String out = execScript("export MYPROP=abc", "echo val is $MYPROP");
-
-        assertTrue(out.contains("val is abc"), "out="+out);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecScriptReturningNonZeroExitCode() throws Exception {
-        int exitcode = tool.execScript(MutableMap.<String,Object>of(), ImmutableList.of("exit 123"));
-        assertEquals(exitcode, 123);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecScriptReturningZeroExitCode() throws Exception {
-        int exitcode = tool.execScript(MutableMap.<String,Object>of(), ImmutableList.of("date"));
-        assertEquals(exitcode, 0);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecScriptCommandWithEnvVariables() throws Exception {
-        String out = execScript(ImmutableList.of("echo val is $MYPROP2"), ImmutableMap.of("MYPROP2", "myval"));
-
-        assertTrue(out.contains("val is myval"), "out="+out);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testScriptDataNotLost() throws Exception {
-        String out = execScript("echo `echo foo``echo bar`");
-
-        assertTrue(out.contains("foobar"), "out="+out);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecScriptWithSleepThenExit() throws Exception {
-        Stopwatch watch = Stopwatch.createStarted();
-        execScript("sleep 1", "exit 0");
-        assertTrue(watch.elapsed(TimeUnit.MILLISECONDS) > 900, "only slept "+Time.makeTimeStringRounded(watch));
-    }
-
-    // Really just tests that it returns; the command will be echo'ed automatically so this doesn't assert the command will have been executed
-    @Test(groups = {"Integration"})
-    public void testExecScriptBigCommand() throws Exception {
-        String bigstring = Strings.repeat("a", 10000);
-        String out = execScript("echo "+bigstring);
-        
-        assertTrue(out.contains(bigstring), "out="+out);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecScriptBigChainOfCommand() throws Exception {
-        String bigstring = Strings.repeat("abcdefghij", 100); // 1KB
-        List<String> cmds = Lists.newArrayList();
-        for (int i = 0; i < 10; i++) {
-            cmds.add("export MYPROP"+i+"="+bigstring);
-            cmds.add("echo val"+i+" is $MYPROP"+i);
-        }
-        String out = execScript(cmds);
-        
-        for (int i = 0; i < 10; i++) {
-            assertTrue(out.contains("val"+i+" is "+bigstring), "out="+out);
-        }
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecScriptAbortsOnCommandFailure() throws Exception {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        int exitcode = tool.execScript(ImmutableMap.of("out", out), ImmutableList.of("export MYPROP=myval", "acmdthatdoesnotexist", "echo val is $MYPROP"));
-        String outstr = new String(out.toByteArray());
-
-        assertFalse(outstr.contains("val is myval"), "out="+out);
-        assertNotEquals(exitcode,  0);
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testExecScriptWithSleepThenBigCommand() throws Exception {
-        String bigstring = Strings.repeat("abcdefghij", 1000); // 10KB
-        String out = execScript("sleep 2", "export MYPROP="+bigstring, "echo val is $MYPROP");
-        assertTrue(out.contains("val is "+bigstring), "out="+out);
-    }
-    
-    @Test(groups = {"WIP", "Integration"})
-    public void testExecScriptBigConcurrentCommand() throws Exception {
-        ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
-        List<ListenableFuture<?>> futures = new ArrayList<ListenableFuture<?>>();
-        try {
-            for (int i = 0; i < 10; i++) {
-                final ShellTool localtool = newTool();
-                connect(localtool);
-                
-                futures.add(executor.submit(new Runnable() {
-                        public void run() {
-                            String bigstring = Strings.repeat("abcdefghij", 1000); // 10KB
-                            String out = execScript(localtool, ImmutableList.of("export MYPROP="+bigstring, "echo val is $MYPROP"));
-                            assertTrue(out.contains("val is "+bigstring), "outSize="+out.length()+"; out="+out);
-                        }}));
-            }
-            Futures.allAsList(futures).get();
-        } finally {
-            executor.shutdownNow();
-        }
-    }
-
-    @Test(groups = {"WIP", "Integration"})
-    public void testExecScriptBigConcurrentSleepyCommand() throws Exception {
-        ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
-        List<ListenableFuture<?>> futures = new ArrayList<ListenableFuture<?>>();
-        try {
-            long starttime = System.currentTimeMillis();
-            for (int i = 0; i < 10; i++) {
-                final ShellTool localtool = newTool();
-                connect(localtool);
-                
-                futures.add(executor.submit(new Runnable() {
-                        public void run() {
-                            String bigstring = Strings.repeat("abcdefghij", 1000); // 10KB
-                            String out = execScript(localtool, ImmutableList.of("sleep 2", "export MYPROP="+bigstring, "echo val is $MYPROP"));
-                            assertTrue(out.contains("val is "+bigstring), "out="+out);
-                        }}));
-            }
-            Futures.allAsList(futures).get();
-            long runtime = System.currentTimeMillis() - starttime;
-            
-            long OVERHEAD = 20*1000;
-            assertTrue(runtime < 2000+OVERHEAD, "runtime="+runtime);
-            
-        } finally {
-            executor.shutdownNow();
-        }
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecChainOfCommands() throws Exception {
-        String out = execCommands("MYPROP=abc", "echo val is $MYPROP");
-
-        assertEquals(out, "val is abc\n");
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecReturningNonZeroExitCode() throws Exception {
-        int exitcode = tool.execCommands(MutableMap.<String,Object>of(), ImmutableList.of("exit 123"));
-        assertEquals(exitcode, 123);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecReturningZeroExitCode() throws Exception {
-        int exitcode = tool.execCommands(MutableMap.<String,Object>of(), ImmutableList.of("date"));
-        assertEquals(exitcode, 0);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecCommandWithEnvVariables() throws Exception {
-        String out = execCommands(ImmutableList.of("echo val is $MYPROP2"), ImmutableMap.of("MYPROP2", "myval"));
-
-        assertEquals(out, "val is myval\n");
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecBigCommand() throws Exception {
-        String bigstring = Strings.repeat("abcdefghij", 1000); // 10KB
-        String out = execCommands("echo "+bigstring);
-
-        assertEquals(out, bigstring+"\n", "actualSize="+out.length()+"; expectedSize="+bigstring.length());
-    }
-
-    @Test(groups = {"Integration"})
-    public void testExecBigConcurrentCommand() throws Exception {
-        runExecBigConcurrentCommand(10, 0L);
-    }
-    
-    // TODO Fails I believe due to synchronization model in SshjTool of calling connect/disconnect.
-    // Even with a retry-count of 4, it still fails because some commands are calling disconnect
-    // while another concurrently executing command expects to be still connected.
-    @Test(groups = {"Integration", "WIP"})
-    public void testExecBigConcurrentCommandWithStaggeredStart() throws Exception {
-        // This test is to vary the concurrency of concurrent actions
-        runExecBigConcurrentCommand(50, 100L);
-    }
-    
-    protected void runExecBigConcurrentCommand(int numCommands, long staggeredDelayBeforeStart) throws Exception {
-        ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
-        List<ListenableFuture<?>> futures = new ArrayList<ListenableFuture<?>>();
-        try {
-            for (int i = 0; i < numCommands; i++) {
-                long delay = (long) (Math.random() * staggeredDelayBeforeStart);
-                if (i > 0) Time.sleep(delay);
-                
-                futures.add(executor.submit(new Runnable() {
-                        public void run() {
-                            String bigstring = Strings.repeat("abcdefghij", 1000); // 10KB
-                            String out = execCommands("echo "+bigstring);
-                            assertEquals(out, bigstring+"\n", "actualSize="+out.length()+"; expectedSize="+bigstring.length());
-                        }}));
-            }
-            Futures.allAsList(futures).get();
-        } finally {
-            executor.shutdownNow();
-        }
-    }
-
-    // fails if terminal enabled
-    @Test(groups = {"Integration"})
-    @Deprecated // tests deprecated code
-    public void testExecScriptCapturesStderr() throws Exception {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        ByteArrayOutputStream err = new ByteArrayOutputStream();
-        String nonExistantCmd = "acmdthatdoesnotexist";
-        tool.execScript(ImmutableMap.of("out", out, "err", err), ImmutableList.of(nonExistantCmd));
-        assertTrue(new String(err.toByteArray()).contains(nonExistantCmd+": command not found"), "out="+out+"; err="+err);
-    }
-
-    // fails if terminal enabled
-    @Test(groups = {"Integration"})
-    @Deprecated // tests deprecated code
-    public void testExecCapturesStderr() throws Exception {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        ByteArrayOutputStream err = new ByteArrayOutputStream();
-        String nonExistantCmd = "acmdthatdoesnotexist";
-        tool.execCommands(ImmutableMap.of("out", out, "err", err), ImmutableList.of(nonExistantCmd));
-        String errMsg = new String(err.toByteArray());
-        assertTrue(errMsg.contains(nonExistantCmd+": command not found\n"), "errMsg="+errMsg+"; out="+out+"; err="+err);
-        
-    }
-
-    @Test(groups = {"Integration"})
-    public void testScriptHeader() {
-        final ShellTool localtool = newTool();
-        String out = execScript(MutableMap.of("scriptHeader", "#!/bin/bash -e\necho hello world\n"), 
-                localtool, Arrays.asList("echo goodbye world"), null);
-        assertTrue(out.contains("goodbye world"), "no goodbye in output: "+out);
-        assertTrue(out.contains("hello world"), "no hello in output: "+out);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testStdErr() {
-        final ShellTool localtool = newTool();
-        Map<String,Object> props = new LinkedHashMap<String, Object>();
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        ByteArrayOutputStream err = new ByteArrayOutputStream();
-        props.put("out", out);
-        props.put("err", err);
-        int exitcode = localtool.execScript(props, Arrays.asList("echo hello err > /dev/stderr"), null);
-        assertFalse(out.toString().contains("hello err"), "hello found where it shouldn't have been, in stdout: "+out);
-        assertTrue(err.toString().contains("hello err"), "no hello in stderr: "+err);
-        assertEquals(0, exitcode);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testRunAsRoot() {
-        final ShellTool localtool = newTool();
-        Map<String,Object> props = new LinkedHashMap<String, Object>();
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        ByteArrayOutputStream err = new ByteArrayOutputStream();
-        props.put("out", out);
-        props.put("err", err);
-        props.put(SshTool.PROP_RUN_AS_ROOT.getName(), true);
-        int exitcode = localtool.execScript(props, Arrays.asList("whoami"), null);
-        assertTrue(out.toString().contains("root"), "not running as root; whoami is: "+out+" (err is '"+err+"')");
-        assertEquals(0, exitcode);
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testExecScriptEchosExecute() throws Exception {
-        String out = execScript("date");
-        assertTrue(out.toString().contains("Executed"), "Executed did not display: "+out);
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testExecScriptEchosDontExecuteWhenToldNoExtraOutput() throws Exception {
-        final ShellTool localtool = newTool();
-        Map<String,Object> props = new LinkedHashMap<String, Object>();
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        ByteArrayOutputStream err = new ByteArrayOutputStream();
-        props.put("out", out);
-        props.put("err", err);
-        props.put(SshTool.PROP_NO_EXTRA_OUTPUT.getName(), true);
-        int exitcode = localtool.execScript(props, Arrays.asList("echo hello world"), null);
-        assertFalse(out.toString().contains("Executed"), "Executed should not have displayed: "+out);
-        assertEquals(out.toString().trim(), "hello world");
-        assertEquals(0, exitcode);
-    }
-    
-    protected String execCommands(String... cmds) {
-        return execCommands(Arrays.asList(cmds));
-    }
-    
-    protected String execCommands(List<String> cmds) {
-        return execCommands(cmds, ImmutableMap.<String,Object>of());
-    }
-
-    protected String execCommands(List<String> cmds, Map<String,?> env) {
-        return execCommands(null, cmds, env);
-    }
-
-    protected String execCommands(ConfigBag config, List<String> cmds, Map<String,?> env) {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        MutableMap<String,Object> flags = MutableMap.<String,Object>of("out", out);
-        if (config!=null) flags.add(config.getAllConfig());
-        tool.execCommands(flags, cmds, env);
-        return new String(out.toByteArray());
-    }
-
-    protected String execScript(String... cmds) {
-        return execScript(tool, Arrays.asList(cmds));
-    }
-
-    protected String execScript(ShellTool t, List<String> cmds) {
-        return execScript(ImmutableMap.<String,Object>of(), t, cmds, ImmutableMap.<String,Object>of());
-    }
-
-    protected String execScript(List<String> cmds) {
-        return execScript(cmds, ImmutableMap.<String,Object>of());
-    }
-    
-    protected String execScript(List<String> cmds, Map<String,?> env) {
-        return execScript(MutableMap.<String,Object>of(), tool, cmds, env);
-    }
-    
-    protected String execScript(Map<String, ?> props, ShellTool tool, List<String> cmds, Map<String,?> env) {
-        Map<String, Object> props2 = new LinkedHashMap<String, Object>(props);
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        props2.put("out", out);
-        int exitcode = tool.execScript(props2, cmds, env);
-        String outstr = new String(out.toByteArray());
-        assertEquals(exitcode, 0, outstr);
-        return outstr;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/ssh/SshToolAbstractIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/SshToolAbstractIntegrationTest.java b/core/src/test/java/brooklyn/util/internal/ssh/SshToolAbstractIntegrationTest.java
deleted file mode 100644
index 84b1029..0000000
--- a/core/src/test/java/brooklyn/util/internal/ssh/SshToolAbstractIntegrationTest.java
+++ /dev/null
@@ -1,301 +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.internal.ssh;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.os.Os;
-import brooklyn.util.text.Identifiers;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.Files;
-
-/**
- * Test the operation of the {@link SshTool} utility class; to be extended to test concrete implementations.
- * 
- * Requires keys set up, e.g. running:
- * 
- * <pre>
- * cd ~/.ssh
- * ssh-keygen
- * id_rsa_with_passphrase
- * mypassphrase
- * mypassphrase
- * </pre>
- * 
- */
-public abstract class SshToolAbstractIntegrationTest extends ShellToolAbstractTest {
-
-    private static final Logger log = LoggerFactory.getLogger(SshToolAbstractIntegrationTest.class);
-    
-    // FIXME need tests which take properties set in entities and brooklyn.properties;
-    // but not in this class because it is lower level than entities, Aled would argue.
-
-    // TODO No tests for retry logic and exception handing yet
-
-    public static final String SSH_KEY_WITH_PASSPHRASE = System.getProperty("sshPrivateKeyWithPassphrase", "~/.ssh/id_rsa_with_passphrase");
-    public static final String SSH_PASSPHRASE = System.getProperty("sshPrivateKeyPassphrase", "mypassphrase");
-
-    protected String remoteFilePath;
-
-    protected SshTool tool() { return (SshTool)tool; }
-    
-    protected abstract SshTool newUnregisteredTool(Map<String,?> flags);
-
-    @Override
-    protected SshTool newTool() {
-        return newTool(ImmutableMap.of("host", "localhost", "privateKeyFile", "~/.ssh/id_rsa"));
-    }
-    
-    @Override
-    protected SshTool newTool(Map<String,?> flags) {
-        return (SshTool) super.newTool(flags);
-    }
-    
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        super.setUp();
-        remoteFilePath = "/tmp/ssh-test-remote-"+Identifiers.makeRandomId(8);
-        filesCreated.add(remoteFilePath);
-    }
-    
-    protected void assertRemoteFileContents(String remotePath, String expectedContents) {
-        String catout = execCommands("cat "+remotePath);
-        assertEquals(catout, expectedContents);
-    }
-    
-    /**
-     * @param remotePath
-     * @param expectedPermissions Of the form, for example, "-rw-r--r--"
-     */
-    protected void assertRemoteFilePermissions(String remotePath, String expectedPermissions) {
-        String lsout = execCommands("ls -l "+remotePath);
-        assertTrue(lsout.contains(expectedPermissions), lsout);
-    }
-    
-    protected void assertRemoteFileLastModifiedIsNow(String remotePath) {
-        // Check default last-modified time is `now`.
-        // Be lenient in assertion, in case unlucky that clock ticked over to next hour/minute as test was running.
-        // TODO Code could be greatly improved, but low priority!
-        // Output format:
-        //   -rw-r--r--  1   aled  wheel  18  Apr 24  15:03 /tmp/ssh-test-remote-CvFN9zQA
-        //   [0]         [1] [2]   [3]    [4] [5] [6] [7]   [8]
-        
-        String lsout = execCommands("ls -l "+remotePath);
-        
-        String[] lsparts = lsout.split("\\s+");
-        int day = Integer.parseInt(lsparts[6]);
-        int hour = Integer.parseInt(lsparts[7].split(":")[0]);
-        int minute = Integer.parseInt(lsparts[7].split(":")[1]);
-        
-        Calendar expected = Calendar.getInstance();
-        int expectedDay = expected.get(Calendar.DAY_OF_MONTH);
-        int expectedHour = expected.get(Calendar.HOUR_OF_DAY);
-        int expectedMinute = expected.get(Calendar.MINUTE);
-        
-        assertEquals(day, expectedDay, "ls="+lsout+"; lsparts="+Arrays.toString(lsparts)+"; expected="+expected+"; expectedDay="+expectedDay+"; day="+day+"; zone="+expected.getTimeZone());
-        assertTrue(Math.abs(hour - expectedHour) <= 1, "ls="+lsout+"; lsparts="+Arrays.toString(lsparts)+"; expected="+expected+"; expectedHour="+expectedHour+"; hour="+hour+"; zone="+expected.getTimeZone());
-        assertTrue(Math.abs(minute - expectedMinute) <= 1, "ls="+lsout+"; lsparts="+Arrays.toString(lsparts)+"; expected="+expected+"; expectedMinute="+expectedMinute+"; minute="+minute+"; zone="+expected.getTimeZone());
-    }
-
-    @Test(groups = {"Integration"})
-    public void testCopyToServerFromBytes() throws Exception {
-        String contents = "echo hello world!\n";
-        byte[] contentBytes = contents.getBytes();
-        tool().copyToServer(MutableMap.<String,Object>of(), contentBytes, remoteFilePath);
-
-        assertRemoteFileContents(remoteFilePath, contents);
-        assertRemoteFilePermissions(remoteFilePath, "-rw-r--r--");
-        
-        // TODO would like to also assert lastModified time, but on jenkins the jvm locale
-        // and the OS locale are different (i.e. different timezones) so the file time-stamp 
-        // is several hours out.
-        //assertRemoteFileLastModifiedIsNow(remoteFilePath);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testCopyToServerFromInputStream() throws Exception {
-        String contents = "echo hello world!\n";
-        ByteArrayInputStream contentsStream = new ByteArrayInputStream(contents.getBytes());
-        tool().copyToServer(MutableMap.<String,Object>of(), contentsStream, remoteFilePath);
-
-        assertRemoteFileContents(remoteFilePath, contents);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testCopyToServerWithPermissions() throws Exception {
-        tool().copyToServer(ImmutableMap.of("permissions","0754"), "echo hello world!\n".getBytes(), remoteFilePath);
-
-        assertRemoteFilePermissions(remoteFilePath, "-rwxr-xr--");
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testCopyToServerWithLastModifiedDate() throws Exception {
-        long lastModificationTime = 1234567;
-        tool().copyToServer(ImmutableMap.of("lastModificationDate", lastModificationTime), "echo hello world!\n".getBytes(), remoteFilePath);
-
-        String lsout = execCommands("ls -l "+remoteFilePath);//+" | awk '{print \$6 \" \" \$7 \" \" \$8}'"])
-        //execCommands([ "ls -l "+remoteFilePath+" | awk '{print \$6 \" \" \$7 \" \" \$8}'"])
-        //varies depending on timezone
-        assertTrue(lsout.contains("Jan 15  1970") || lsout.contains("Jan 14  1970") || lsout.contains("Jan 16  1970"), lsout);
-        //assertLastModified(lsout, lastModifiedDate)
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testCopyFileToServerWithPermissions() throws Exception {
-        String contents = "echo hello world!\n";
-        Files.write(contents, new File(localFilePath), Charsets.UTF_8);
-        tool().copyToServer(ImmutableMap.of("permissions", "0754"), new File(localFilePath), remoteFilePath);
-
-        assertRemoteFileContents(remoteFilePath, contents);
-
-        String lsout = execCommands("ls -l "+remoteFilePath);
-        assertTrue(lsout.contains("-rwxr-xr--"), lsout);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testCopyFromServer() throws Exception {
-        String contentsWithoutLineBreak = "echo hello world!";
-        String contents = contentsWithoutLineBreak+"\n";
-        tool().copyToServer(MutableMap.<String,Object>of(), contents.getBytes(), remoteFilePath);
-        
-        tool().copyFromServer(MutableMap.<String,Object>of(), remoteFilePath, new File(localFilePath));
-
-        List<String> actual = Files.readLines(new File(localFilePath), Charsets.UTF_8);
-        assertEquals(actual, ImmutableList.of(contentsWithoutLineBreak));
-    }
-    
-    // TODO No config options in sshj or scp for auto-creating the parent directories
-    @Test(enabled=false, groups = {"Integration"})
-    public void testCopyFileToNonExistantDir() throws Exception {
-        String contents = "echo hello world!\n";
-        String remoteFileDirPath = "/tmp/ssh-test-remote-dir-"+Identifiers.makeRandomId(8);
-        String remoteFileInDirPath = remoteFileDirPath + File.separator + "ssh-test-remote-"+Identifiers.makeRandomId(8);
-        filesCreated.add(remoteFileInDirPath);
-        filesCreated.add(remoteFileDirPath);
-        
-        tool().copyToServer(MutableMap.<String,Object>of(), contents.getBytes(), remoteFileInDirPath);
-
-        assertRemoteFileContents(remoteFileInDirPath, contents);
-    }
-    
-
-    @Test(groups = {"Integration"})
-    public void testAllocatePty() {
-        final ShellTool localtool = newTool(MutableMap.of("host", "localhost", SshTool.PROP_ALLOCATE_PTY.getName(), true));
-        Map<String,Object> props = new LinkedHashMap<String, Object>();
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        ByteArrayOutputStream err = new ByteArrayOutputStream();
-        props.put("out", out);
-        props.put("err", err);
-        int exitcode = localtool.execScript(props, Arrays.asList("echo hello err > /dev/stderr"), null);
-        assertTrue(out.toString().contains("hello err"), "no hello in output: "+out+" (err is '"+err+"')");
-        assertFalse(err.toString().contains("hello err"), "hello found in stderr: "+err);
-        assertEquals(0, exitcode);
-    }
-
-    // Requires setting up an extra ssh key, with a passphrase, and adding it to ~/.ssh/authorized_keys
-    @Test(groups = {"Integration"})
-    public void testSshKeyWithPassphrase() throws Exception {
-        final SshTool localtool = newTool(ImmutableMap.<String,Object>builder()
-                .put(SshTool.PROP_HOST.getName(), "localhost")
-                .put(SshTool.PROP_PRIVATE_KEY_FILE.getName(), SSH_KEY_WITH_PASSPHRASE)
-                .put(SshTool.PROP_PRIVATE_KEY_PASSPHRASE.getName(), SSH_PASSPHRASE)
-                .build());
-        localtool.connect();
-        
-        assertEquals(tool.execScript(MutableMap.<String,Object>of(), ImmutableList.of("date")), 0);
-
-        // Also needs the negative test to prove that we're really using an ssh-key with a passphrase
-        try {
-            final SshTool localtool2 = newTool(ImmutableMap.<String,Object>builder()
-                    .put(SshTool.PROP_HOST.getName(), "localhost")
-                    .put(SshTool.PROP_PRIVATE_KEY_FILE.getName(), SSH_KEY_WITH_PASSPHRASE)
-                    .build());
-            localtool2.connect();
-            fail();
-        } catch (Exception e) {
-            SshException se = Exceptions.getFirstThrowableOfType(e, SshException.class);
-            if (se == null) throw e;
-        }
-    }
-
-    @Test(groups = {"Integration"})
-    public void testConnectWithInvalidUserThrowsException() throws Exception {
-        final ShellTool localtool = newTool(ImmutableMap.of("user", "wronguser", "host", "localhost", "privateKeyFile", "~/.ssh/id_rsa"));
-        tools.add(localtool);
-        try {
-            connect(localtool);
-            fail();
-        } catch (SshException e) {
-            if (!e.toString().contains("failed to connect")) throw e;
-        }
-    }
-
-    @Test(groups = {"Integration"})
-    public void testOutputAsExpected() throws Exception {
-        final String CONTENTS = "hello world\n"
-            + "bye bye\n";
-        execCommands("cat > "+Os.mergePaths(Os.tmp(), "test1")+" << X\n"
-            + CONTENTS
-            + "X\n");
-        String read = execCommands("echo START_FOO", "cat "+Os.mergePaths(Os.tmp(), "test1"), "echo END_FOO");
-        log.debug("read back data written, as:\n"+read);
-        String contents = Strings.getFragmentBetween(read, "START_FOO", "END_FOO");
-        Assert.assertEquals(CONTENTS.trim(), contents.trim());
-    }
-
-    @Test(groups = {"Integration"})
-    public void testScriptDirPropertiesIsRespected() {
-        // For explanation of (some of) the magic behind this command, see http://stackoverflow.com/a/229606/68898
-        final String command = "if [[ \"$0\" == \"/var/tmp/\"* ]]; then true; else false; fi";
-
-        SshTool sshTool = newTool(ImmutableMap.<String, Object>builder()
-                .put(SshTool.PROP_HOST.getName(), "localhost")
-                .build());
-        int rc = sshTool.execScript(ImmutableMap.<String, Object>builder()
-                .put(SshTool.PROP_SCRIPT_DIR.getName(), "/var/tmp")
-                .build(), ImmutableList.of(command));
-        assertEquals(rc, 0);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/ssh/SshToolAbstractPerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/SshToolAbstractPerformanceTest.java b/core/src/test/java/brooklyn/util/internal/ssh/SshToolAbstractPerformanceTest.java
deleted file mode 100644
index be1441a..0000000
--- a/core/src/test/java/brooklyn/util/internal/ssh/SshToolAbstractPerformanceTest.java
+++ /dev/null
@@ -1,137 +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.internal.ssh;
-
-import java.io.ByteArrayOutputStream;
-import java.lang.management.ManagementFactory;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import javax.management.MBeanServer;
-import javax.management.ObjectName;
-
-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.util.collections.MutableMap;
-import brooklyn.util.text.Identifiers;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableList;
-
-/**
- * Test the performance of different variants of invoking the sshj tool.
- * 
- * Intended for human-invocation and inspection, to see which parts are most expensive.
- */
-public abstract class SshToolAbstractPerformanceTest {
-
-    private static final Logger LOG = LoggerFactory.getLogger(SshToolAbstractPerformanceTest.class);
-    
-    private SshTool tool;
-    
-    protected abstract SshTool newSshTool(Map<String,?> flags);
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (tool != null) tool.disconnect();
-    }
-
-    @Test(groups = {"Integration"})
-    public void testConsecutiveConnectAndDisconnect() throws Exception {
-        Runnable task = new Runnable() {
-            public void run() {
-                tool = newSshTool(MutableMap.of("host", "localhost"));
-                tool.connect();
-                tool.disconnect();
-            }
-        };
-        runMany(task, "connect-disconnect", 10);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testConsecutiveSmallCommands() throws Exception {
-        runExecManyCommands(ImmutableList.of("true"), false, "small-cmd", 10);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testConsecutiveSmallCommandsWithStdouterr() throws Exception {
-        runExecManyCommands(ImmutableList.of("true"), true, "small-cmd-with-stdout", 10);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testConsecutiveBigStdoutCommands() throws Exception {
-        runExecManyCommands(ImmutableList.of("head -c 100000 /dev/urandom"), true, "big-stdout", 10);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testConsecutiveBigStdinCommands() throws Exception {
-        String bigstr = Identifiers.makeRandomId(100000);
-        runExecManyCommands(ImmutableList.of("echo "+bigstr+" | wc -c"), true, "big-stdin", 10);
-    }
-
-    private void runExecManyCommands(final List<String> cmds, final boolean captureOutAndErr, String context, int iterations) throws Exception {
-        Runnable task = new Runnable() {
-                @Override public void run() {
-                    execScript(cmds, captureOutAndErr);
-                }};
-        runMany(task, context, iterations);
-    }
-
-    private void runMany(Runnable task, String context, int iterations) throws Exception {
-        MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
-        ObjectName osMBeanName = ObjectName.getInstance(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME);
-        long preCpuTime = (Long) mbeanServer.getAttribute(osMBeanName, "ProcessCpuTime");
-        Stopwatch stopwatch = Stopwatch.createStarted();
-        
-        for (int i = 0; i < iterations; i++) {
-            task.run();
-            
-            long postCpuTime = (Long) mbeanServer.getAttribute(osMBeanName, "ProcessCpuTime");
-            long elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS);
-            double fractionCpu = (elapsedTime > 0) ? ((double)postCpuTime-preCpuTime) / TimeUnit.MILLISECONDS.toNanos(elapsedTime) : -1;
-            LOG.info("Executing {}; completed {}; took {}; fraction cpu {}", new Object[] {context, (i+1), Time.makeTimeStringRounded(elapsedTime), fractionCpu});
-        }
-    }
-
-    private int execScript(List<String> cmds, boolean captureOutandErr) {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        ByteArrayOutputStream err = new ByteArrayOutputStream();
-        MutableMap<String,?> flags = (captureOutandErr) ? MutableMap.of("out", out, "err", err) : MutableMap.<String,Object>of();
-        
-        tool = newSshTool(MutableMap.of("host", "localhost"));
-        tool.connect();
-        int result = tool.execScript(flags, cmds);
-        tool.disconnect();
-        
-        int outlen = out.toByteArray().length;
-        int errlen = out.toByteArray().length;
-        if (LOG.isTraceEnabled()) LOG.trace("Executed: result={}; stdout={}; stderr={}", new Object[] {result, outlen, errlen});
-        return result;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/ssh/cli/SshCliToolIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/cli/SshCliToolIntegrationTest.java b/core/src/test/java/brooklyn/util/internal/ssh/cli/SshCliToolIntegrationTest.java
deleted file mode 100644
index c8640e7..0000000
--- a/core/src/test/java/brooklyn/util/internal/ssh/cli/SshCliToolIntegrationTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.internal.ssh.cli;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-
-import java.io.ByteArrayOutputStream;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.internal.ssh.SshException;
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.internal.ssh.SshToolAbstractIntegrationTest;
-import brooklyn.util.internal.ssh.cli.SshCliTool;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-/**
- * Test the operation of the {@link SshJschTool} utility class.
- */
-public class SshCliToolIntegrationTest extends SshToolAbstractIntegrationTest {
-
-    private static final Logger log = LoggerFactory.getLogger(SshCliToolIntegrationTest.class);
-    
-    protected SshTool newUnregisteredTool(Map<String,?> flags) {
-        return new SshCliTool(flags);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testFlags() throws Exception {
-        final SshTool localtool = newTool(ImmutableMap.of("sshFlags", "-vvv -tt", "host", "localhost"));
-        tools.add(localtool);
-        try {
-            localtool.connect();
-            Map<String,Object> props = new LinkedHashMap<String, Object>();
-            ByteArrayOutputStream out = new ByteArrayOutputStream();
-            ByteArrayOutputStream err = new ByteArrayOutputStream();
-            props.put("out", out);
-            props.put("err", err);
-            int exitcode = localtool.execScript(props, Arrays.asList("echo hello err > /dev/stderr"), null);
-            Assert.assertEquals(0, exitcode, "exitCode="+exitcode+", but expected 0");
-            log.debug("OUT from ssh -vvv command is: "+out);
-            log.debug("ERR from ssh -vvv command is: "+err);
-            assertFalse(err.toString().contains("hello err"), "hello found where it shouldn't have been, in stderr (should have been tty merged to stdout): "+err);
-            assertTrue(out.toString().contains("hello err"), "no hello in stdout: "+err);
-            // look for word 'ssh' to confirm we got verbose output
-            assertTrue(err.toString().toLowerCase().contains("ssh"), "no mention of ssh in stderr: "+err);
-        } catch (SshException e) {
-            if (!e.toString().contains("failed to connect")) throw e;
-        }
-    }
-
-    // Need to have at least one test method here (rather than just inherited) for eclipse to recognize it
-    @Test(enabled = false)
-    public void testDummy() throws Exception {
-    }
-    
-    // TODO When running mvn on the command line (for Aled), this test hangs when prompting for a password (but works in the IDE!)
-    // Doing .connect() isn't enough; need to cause ssh or scp to be invoked
-    @Test(enabled=false, groups = {"Integration"})
-    public void testConnectWithInvalidUserThrowsException() throws Exception {
-        final SshTool localtool = newTool(ImmutableMap.of("user", "wronguser", "host", "localhost", "privateKeyFile", "~/.ssh/id_rsa"));
-        tools.add(localtool);
-        try {
-            localtool.connect();
-            int result = localtool.execScript(ImmutableMap.<String,Object>of(), ImmutableList.of("date"));
-            fail("exitCode="+result+", but expected exception");
-        } catch (SshException e) {
-            if (!e.toString().contains("failed to connect")) throw e;
-        }
-    }
-    
-    // TODO ssh-cli doesn't support pass-phrases yet
-    @Test(enabled=false, groups = {"Integration"})
-    public void testSshKeyWithPassphrase() throws Exception {
-        super.testSshKeyWithPassphrase();
-    }
-
-    // Setting last modified date not yet supported for cli-based ssh
-    @Override
-    @Test(enabled=false, groups = {"Integration"})
-    public void testCopyToServerWithLastModifiedDate() throws Exception {
-        super.testCopyToServerWithLastModifiedDate();
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testExecReturningNonZeroExitCode() throws Exception {
-        int exitcode = tool.execCommands(MutableMap.<String,Object>of(), ImmutableList.of("exit 123"));
-        assertEquals(exitcode, 123);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/ssh/cli/SshCliToolPerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/cli/SshCliToolPerformanceTest.java b/core/src/test/java/brooklyn/util/internal/ssh/cli/SshCliToolPerformanceTest.java
deleted file mode 100644
index 4d2ba20..0000000
--- a/core/src/test/java/brooklyn/util/internal/ssh/cli/SshCliToolPerformanceTest.java
+++ /dev/null
@@ -1,44 +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.internal.ssh.cli;
-
-import java.util.Map;
-
-import org.testng.annotations.Test;
-
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.internal.ssh.SshToolAbstractPerformanceTest;
-
-/**
- * Test the performance of different variants of invoking the sshj tool.
- * 
- * Intended for human-invocation and inspection, to see which parts are most expensive.
- */
-public class SshCliToolPerformanceTest extends SshToolAbstractPerformanceTest {
-
-    @Override
-    protected SshTool newSshTool(Map<String,?> flags) {
-        return new SshCliTool(flags);
-    }
-    
-    // Need to have at least one test method here (rather than just inherited) for eclipse to recognize it
-    @Test(enabled = false)
-    public void testDummy() throws Exception {
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/ssh/process/ProcessToolIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/process/ProcessToolIntegrationTest.java b/core/src/test/java/brooklyn/util/internal/ssh/process/ProcessToolIntegrationTest.java
deleted file mode 100644
index 64054ba..0000000
--- a/core/src/test/java/brooklyn/util/internal/ssh/process/ProcessToolIntegrationTest.java
+++ /dev/null
@@ -1,69 +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.internal.ssh.process;
-
-import static org.testng.Assert.assertTrue;
-
-import java.util.Arrays;
-import java.util.Map;
-
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.internal.ssh.ShellToolAbstractTest;
-
-/**
- * Test the operation of the {@link ProcessTool} utility class.
- */
-public class ProcessToolIntegrationTest extends ShellToolAbstractTest {
-
-    @Override
-    protected ProcessTool newUnregisteredTool(Map<String,?> flags) {
-        return new ProcessTool(flags);
-    }
-
-    // ones here included as *non*-integration tests. must run on windows and linux.
-    // (also includes integration tests from parent)
-
-    @Test(groups="UNIX")
-    public void testPortableCommand() throws Exception {
-        String out = execScript("echo hello world");
-        assertTrue(out.contains("hello world"), "out="+out);
-    }
-
-    @Test(groups="Integration")
-    public void testLoginShell() {
-        // this detection scheme only works for commands; can't test whether it works for scripts without 
-        // requiring stuff in bash_profile / profile / etc, which gets hard to make portable;
-        // it is nearly the same code path on the impl so this is probably enough 
-        
-        final String LOGIN_SHELL_CHECK = "shopt -q login_shell && echo 'yes, login shell' || echo 'no, not login shell'";
-        ConfigBag config = ConfigBag.newInstance().configure(ProcessTool.PROP_NO_EXTRA_OUTPUT, true);
-        String out;
-        
-        out = execCommands(config, Arrays.asList(LOGIN_SHELL_CHECK), null);
-        Assert.assertEquals(out.trim(), "no, not login shell", "out = "+out);
-        
-        config.configure(ProcessTool.PROP_LOGIN_SHELL, true);
-        out = execCommands(config, Arrays.asList(LOGIN_SHELL_CHECK), null);
-        Assert.assertEquals(out.trim(), "yes, login shell", "out = "+out);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/ssh/process/ProcessToolStaticsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/process/ProcessToolStaticsTest.java b/core/src/test/java/brooklyn/util/internal/ssh/process/ProcessToolStaticsTest.java
deleted file mode 100644
index 787c776..0000000
--- a/core/src/test/java/brooklyn/util/internal/ssh/process/ProcessToolStaticsTest.java
+++ /dev/null
@@ -1,79 +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.internal.ssh.process;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.util.Arrays;
-import java.util.List;
-
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.os.Os;
-
-public class ProcessToolStaticsTest {
-
-    ByteArrayOutputStream out;
-    ByteArrayOutputStream err;
-    
-    @BeforeMethod(alwaysRun=true)
-    public void clear() {
-        out = new ByteArrayOutputStream();
-        err = new ByteArrayOutputStream();
-    }
-    
-    private List<String> getTestCommand() {
-        if(Os.isMicrosoftWindows()) {
-            return Arrays.asList("cmd", "/c", "echo", "hello", "world");
-        } else {
-            return Arrays.asList("echo", "hello", "world");
-        }
-    }
-
-    @Test
-    public void testRunsWithStdout() throws Exception {
-        int code = ProcessTool.execSingleProcess(getTestCommand(), null, (File)null, out, err, this);
-        Assert.assertEquals(err.toString().trim(), "");
-        Assert.assertEquals(out.toString().trim(), "hello world");
-        Assert.assertEquals(code, 0);
-    }
-
-    @Test(groups="Integration") // *nix only
-    public void testRunsWithBashEnvVarAndStderr() throws Exception {
-        int code = ProcessTool.execSingleProcess(Arrays.asList("/bin/bash", "-c", "echo hello $NAME | tee /dev/stderr"), 
-                MutableMap.of("NAME", "BOB"), (File)null, out, err, this);
-        Assert.assertEquals(err.toString().trim(), "hello BOB", "err is: "+err);
-        Assert.assertEquals(out.toString().trim(), "hello BOB", "out is: "+out);
-        Assert.assertEquals(code, 0);
-    }
-
-    @Test(groups="Integration") // *nix only
-    public void testRunsManyCommandsWithBashEnvVarAndStderr() throws Exception {
-        int code = ProcessTool.execProcesses(Arrays.asList("echo hello $NAME", "export NAME=JOHN", "echo goodbye $NAME | tee /dev/stderr"), 
-                MutableMap.of("NAME", "BOB"), (File)null, out, err, " ; ", false, this);
-        Assert.assertEquals(err.toString().trim(), "goodbye JOHN", "err is: "+err);
-        Assert.assertEquals(out.toString().trim(), "hello BOB\ngoodbye JOHN", "out is: "+out);
-        Assert.assertEquals(code, 0);
-    }
-
-
-}


[21/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ValueResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ValueResolver.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ValueResolver.java
new file mode 100644
index 0000000..7fc112b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ValueResolver.java
@@ -0,0 +1,426 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.javalang.JavaClassNames;
+import brooklyn.util.repeat.Repeater;
+import brooklyn.util.time.CountdownTimer;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Durations;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.reflect.TypeToken;
+
+/** 
+ * Resolves a given object, as follows:
+ * <li> If it is a {@link Tasks} or a {@link DeferredSupplier} then get its contents
+ * <li> If it's a map and {@link #deep(boolean)} is requested, it applies resolution to contents
+ * <li> It applies coercion
+ * <p>
+ * Fluent-style API exposes a number of other options.
+ */
+public class ValueResolver<T> implements DeferredSupplier<T> {
+
+    /** 
+     * Period to wait if we're expected to return real quick 
+     * but we want fast things to have time to finish.
+     * <p>
+     * Timings are always somewhat arbitrary but this at least
+     * allows some intention to be captured in code rather than arbitrary values. */
+    public static Duration REAL_QUICK_WAIT = Duration.millis(50);
+    /** 
+     * Period to wait if we're expected to return quickly 
+     * but we want to be a bit more generous for things to finish,
+     * without letting a caller get annoyed. 
+     * <p>
+     * See {@link #REAL_QUICK_WAIT}. */
+    public static Duration PRETTY_QUICK_WAIT = Duration.millis(200);
+    
+    /** Period to wait when we have to poll but want to give the illusion of no wait.
+     * See {@link Repeater#DEFAULT_REAL_QUICK_PERIOD} */ 
+    public static Duration REAL_QUICK_PERIOD = Repeater.DEFAULT_REAL_QUICK_PERIOD;
+    
+    private static final Logger log = LoggerFactory.getLogger(ValueResolver.class);
+    
+    final Object value;
+    final Class<T> type;
+    ExecutionContext exec;
+    String description;
+    boolean forceDeep;
+    /** null means do it if you can; true means always, false means never */
+    Boolean embedResolutionInTask;
+    /** timeout on execution, if possible, or if embedResolutionInTask is true */
+    Duration timeout;
+    boolean isTransientTask = true;
+    
+    T defaultValue = null;
+    boolean returnDefaultOnGet = false;
+    boolean swallowExceptions = false;
+    
+    // internal fields
+    final Object parentOriginalValue;
+    final CountdownTimer parentTimer;
+    AtomicBoolean started = new AtomicBoolean(false);
+    boolean expired;
+    
+    ValueResolver(Object v, Class<T> type) {
+        this.value = v;
+        this.type = type;
+        checkTypeNotNull();
+        parentOriginalValue = null;
+        parentTimer = null;
+    }
+    
+    ValueResolver(Object v, Class<T> type, ValueResolver<?> parent) {
+        this.value = v;
+        this.type = type;
+        checkTypeNotNull();
+        
+        exec = parent.exec;
+        description = parent.description;
+        forceDeep = parent.forceDeep;
+        embedResolutionInTask = parent.embedResolutionInTask;
+
+        parentOriginalValue = parent.getOriginalValue();
+
+        timeout = parent.timeout;
+        parentTimer = parent.parentTimer;
+        if (parentTimer!=null && parentTimer.isExpired())
+            expired = true;
+        
+        // default value and swallow exceptions do not need to be nested
+    }
+
+    public static class ResolverBuilderPretype {
+        final Object v;
+        public ResolverBuilderPretype(Object v) {
+            this.v = v;
+        }
+        public <T> ValueResolver<T> as(Class<T> type) {
+            return new ValueResolver<T>(v, type);
+        }
+    }
+
+    /** returns a copy of this resolver which can be queried, even if the original (single-use instance) has already been copied */
+    public ValueResolver<T> clone() {
+        ValueResolver<T> result = new ValueResolver<T>(value, type)
+            .context(exec).description(description)
+            .embedResolutionInTask(embedResolutionInTask)
+            .deep(forceDeep)
+            .timeout(timeout);
+        if (returnDefaultOnGet) result.defaultValue(defaultValue);
+        if (swallowExceptions) result.swallowExceptions();
+        return result;
+    }
+    
+    /** execution context to use when resolving; required if resolving unsubmitted tasks or running with a time limit */
+    public ValueResolver<T> context(ExecutionContext exec) {
+        this.exec = exec;
+        return this;
+    }
+    /** as {@link #context(ExecutionContext)} for use from an entity */
+    public ValueResolver<T> context(Entity entity) {
+        return context(entity!=null ? ((EntityInternal)entity).getExecutionContext() : null);
+    }
+    
+    /** sets a message which will be displayed in status reports while it waits (e.g. the name of the config key being looked up) */
+    public ValueResolver<T> description(String description) {
+        this.description = description;
+        return this;
+    }
+    
+    /** sets a default value which will be returned on a call to {@link #get()} if the task does not complete
+     * or completes with an error
+     * <p>
+     * note that {@link #getMaybe()} returns an absent object even in the presence of
+     * a default, so that any error can still be accessed */
+    public ValueResolver<T> defaultValue(T defaultValue) {
+        this.defaultValue = defaultValue;
+        this.returnDefaultOnGet = true;
+        return this;
+    }
+
+    /** indicates that no default value should be returned on a call to {@link #get()}, and instead it should throw
+     * (this is the default; this method is provided to undo a call to {@link #defaultValue(Object)}) */
+    public ValueResolver<T> noDefaultValue() {
+        this.returnDefaultOnGet = false;
+        this.defaultValue = null;
+        return this;
+    }
+    
+    /** indicates that exceptions in resolution should not be thrown on a call to {@link #getMaybe()}, 
+     * but rather used as part of the {@link Maybe#get()} if it's absent, 
+     * and swallowed altogether on a call to {@link #get()} in the presence of a {@link #defaultValue(Object)} */
+    public ValueResolver<T> swallowExceptions() {
+        this.swallowExceptions = true;
+        return this;
+    }
+    
+    /** whether the task should be marked as transient; defaults true */
+    public ValueResolver<T> transientTask(boolean isTransientTask) {
+        this.isTransientTask = isTransientTask;
+        return this;
+    }
+    
+    public Maybe<T> getDefault() {
+        if (returnDefaultOnGet) return Maybe.of(defaultValue);
+        else return Maybe.absent("No default value set");
+    }
+    
+    /** causes nested structures (maps, lists) to be descended and nested unresolved values resolved */
+    public ValueResolver<T> deep(boolean forceDeep) {
+        this.forceDeep = forceDeep;
+        return this;
+    }
+
+    /** if true, forces execution of a deferred supplier to be run in a task;
+     * if false, it prevents it (meaning time limits may not be applied);
+     * if null, the default, it runs in a task if a time limit is applied.
+     * <p>
+     * running inside a task is required for some {@link DeferredSupplier}
+     * instances which look up a task {@link ExecutionContext}. */
+    public ValueResolver<T> embedResolutionInTask(Boolean embedResolutionInTask) {
+        this.embedResolutionInTask = embedResolutionInTask;
+        return this;
+    }
+    
+    /** sets a time limit on executions
+     * <p>
+     * used for {@link Task} and {@link DeferredSupplier} instances.
+     * may require an execution context at runtime. */
+    public ValueResolver<T> timeout(Duration timeout) {
+        this.timeout = timeout;
+        return this;
+    }
+    
+    protected void checkTypeNotNull() {
+        if (type==null) 
+            throw new NullPointerException("type must be set to resolve, for '"+value+"'"+(description!=null ? ", "+description : ""));
+    }
+
+    public T get() {
+        Maybe<T> m = getMaybe();
+        if (m.isPresent()) return m.get();
+        if (returnDefaultOnGet) return defaultValue;
+        return m.get();
+    }
+    
+    public Maybe<T> getMaybe() {
+        Maybe<T> result = getMaybeInternal();
+        if (log.isTraceEnabled()) {
+            log.trace(this+" evaluated as "+result);
+        }
+        return result;
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    protected Maybe<T> getMaybeInternal() {
+        if (started.getAndSet(true))
+            throw new IllegalStateException("ValueResolver can only be used once");
+        
+        if (expired) return Maybe.absent("Nested resolution of "+getOriginalValue()+" did not complete within "+timeout);
+        
+        ExecutionContext exec = this.exec;
+        if (exec==null) {
+            // if execution context not specified, take it from the current task if present
+            exec = BasicExecutionContext.getCurrentExecutionContext();
+        }
+        
+        CountdownTimer timerU = parentTimer;
+        if (timerU==null && timeout!=null)
+            timerU = timeout.countdownTimer();
+        final CountdownTimer timer = timerU;
+        if (timer!=null && !timer.isRunning())
+            timer.start();
+        
+        checkTypeNotNull();
+        Object v = this.value;
+        
+        //if the expected type is a closure or map and that's what we have, we're done (or if it's null);
+        //but not allowed to return a future or DeferredSupplier as the resolved value
+        if (v==null || (!forceDeep && type.isInstance(v) && !Future.class.isInstance(v) && !DeferredSupplier.class.isInstance(v)))
+            return Maybe.of((T) v);
+        
+        try {
+            //if it's a task or a future, we wait for the task to complete
+            if (v instanceof TaskAdaptable<?>) {
+                //if it's a task, we make sure it is submitted
+                if (!((TaskAdaptable<?>) v).asTask().isSubmitted() ) {
+                    if (exec==null)
+                        return Maybe.absent("Value for unsubmitted task '"+getDescription()+"' requested but no execution context available");
+                    exec.submit(((TaskAdaptable<?>) v).asTask());
+                }
+            }
+
+            if (v instanceof Future) {
+                final Future<?> vfuture = (Future<?>) v;
+
+                //including tasks, above
+                if (!vfuture.isDone()) {
+                    Callable<Maybe> callable = new Callable<Maybe>() {
+                        public Maybe call() throws Exception {
+                            return Durations.get(vfuture, timer);
+                        } };
+
+                    String description = getDescription();
+                    Maybe vm = Tasks.withBlockingDetails("Waiting for "+description, callable);
+                    if (vm.isAbsent()) return vm;
+                    v = vm.get();
+
+                } else {
+                    v = vfuture.get();
+                    
+                }
+
+            } else if (v instanceof DeferredSupplier<?>) {
+                final Object vf = v;
+
+                if ((!Boolean.FALSE.equals(embedResolutionInTask) && (exec!=null || timeout!=null)) || Boolean.TRUE.equals(embedResolutionInTask)) {
+                    if (exec==null)
+                        return Maybe.absent("Embedding in task needed for '"+getDescription()+"' but no execution context available");
+                        
+                    Callable<Object> callable = new Callable<Object>() {
+                        public Object call() throws Exception {
+                            try {
+                                Tasks.setBlockingDetails("Retrieving "+vf);
+                                return ((DeferredSupplier<?>) vf).get();
+                            } finally {
+                                Tasks.resetBlockingDetails();
+                            }
+                        } };
+                    String description = getDescription();
+                    TaskBuilder<Object> vb = Tasks.<Object>builder().body(callable).name("Resolving dependent value").description(description);
+                    if (isTransientTask) vb.tag(BrooklynTaskTags.TRANSIENT_TASK_TAG);
+                    Task<Object> vt = exec.submit(vb.build());
+                    // TODO to handle immediate resolution, it would be nice to be able to submit 
+                    // so it executes in the current thread,
+                    // or put a marker in the target thread or task while it is running that the task 
+                    // should never wait on anything other than another value being resolved 
+                    // (though either could recurse infinitely) 
+                    Maybe<Object> vm = Durations.get(vt, timer);
+                    vt.cancel(true);
+                    if (vm.isAbsent()) return (Maybe<T>)vm;
+                    v = vm.get();
+                    
+                } else {
+                    try {
+                        Tasks.setBlockingDetails("Retrieving (non-task) "+vf);
+                        v = ((DeferredSupplier<?>) vf).get();
+                    } finally {
+                        Tasks.resetBlockingDetails();
+                    }
+                }
+
+            } else if (v instanceof Map) {
+                //and if a map or list we look inside
+                Map result = Maps.newLinkedHashMap();
+                for (Map.Entry<?,?> entry : ((Map<?,?>)v).entrySet()) {
+                    Maybe<?> kk = new ValueResolver(entry.getKey(), type, this)
+                        .description( (description!=null ? description+", " : "") + "map key "+entry.getKey() )
+                        .getMaybe();
+                    if (kk.isAbsent()) return (Maybe<T>)kk;
+                    Maybe<?> vv = new ValueResolver(entry.getValue(), type, this)
+                        .description( (description!=null ? description+", " : "") + "map value for key "+kk.get() )
+                        .getMaybe();
+                    if (vv.isAbsent()) return (Maybe<T>)vv;
+                    result.put(kk.get(), vv.get());
+                }
+                return Maybe.of((T) result);
+
+            } else if (v instanceof Set) {
+                Set result = Sets.newLinkedHashSet();
+                int count = 0;
+                for (Object it : (Set)v) {
+                    Maybe<?> vv = new ValueResolver(it, type, this)
+                        .description( (description!=null ? description+", " : "") + "entry "+count )
+                        .getMaybe();
+                    if (vv.isAbsent()) return (Maybe<T>)vv;
+                    result.add(vv.get());
+                    count++;
+                }
+                return Maybe.of((T) result);
+
+            } else if (v instanceof Iterable) {
+                List result = Lists.newArrayList();
+                int count = 0;
+                for (Object it : (Iterable)v) {
+                    Maybe<?> vv = new ValueResolver(it, type, this)
+                        .description( (description!=null ? description+", " : "") + "entry "+count )
+                        .getMaybe();
+                    if (vv.isAbsent()) return (Maybe<T>)vv;
+                    result.add(vv.get());
+                    count++;
+                }
+                return Maybe.of((T) result);
+
+            } else {
+                return TypeCoercions.tryCoerce(v, TypeToken.of(type));
+            }
+
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            
+            IllegalArgumentException problem = new IllegalArgumentException("Error resolving "+(description!=null ? description+", " : "")+v+", in "+exec+": "+e, e);
+            if (swallowExceptions) {
+                if (log.isDebugEnabled())
+                    log.debug("Resolution of "+this+" failed, swallowing and returning: "+e);
+                return Maybe.absent(problem);
+            }
+            if (log.isDebugEnabled())
+                log.debug("Resolution of "+this+" failed, throwing: "+e);
+            throw problem;
+        }
+        
+        return new ValueResolver(v, type, this).getMaybe();
+    }
+
+    protected String getDescription() {
+        return description!=null ? description : ""+value;
+    }
+    protected Object getOriginalValue() {
+        if (parentOriginalValue!=null) return parentOriginalValue;
+        return value;
+    }
+    
+    @Override
+    public String toString() {
+        return JavaClassNames.cleanSimpleClassName(this)+"["+JavaClassNames.cleanSimpleClassName(type)+" "+value+"]";
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshFetchTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshFetchTaskFactory.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshFetchTaskFactory.java
new file mode 100644
index 0000000..a38e305
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshFetchTaskFactory.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.ssh;
+
+import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+// cannot be (cleanly) instantiated due to nested generic self-referential type; however trivial subclasses do allow it 
+public class SshFetchTaskFactory implements TaskFactory<SshFetchTaskWrapper> {
+    
+    private static final Logger log = LoggerFactory.getLogger(SshFetchTaskFactory.class);
+    
+    private boolean dirty = false;
+    
+    protected SshMachineLocation machine;
+    protected String remoteFile;
+    protected final ConfigBag config = ConfigBag.newInstance();
+
+    /** constructor where machine will be added later */
+    public SshFetchTaskFactory(String remoteFile) {
+        remoteFile(remoteFile);
+    }
+
+    /** convenience constructor to supply machine immediately */
+    public SshFetchTaskFactory(SshMachineLocation machine, String remoteFile) {
+        machine(machine);
+        remoteFile(remoteFile);
+    }
+
+    protected SshFetchTaskFactory self() { return this; }
+
+    protected void markDirty() {
+        dirty = true;
+    }
+    
+    public SshFetchTaskFactory machine(SshMachineLocation machine) {
+        markDirty();
+        this.machine = machine;
+        return self();
+    }
+        
+    public SshMachineLocation getMachine() {
+        return machine;
+    }
+    
+    public SshFetchTaskFactory remoteFile(String remoteFile) {
+        this.remoteFile = remoteFile;
+        return self();
+    }
+
+    public ConfigBag getConfig() {
+        return config;
+    }
+    
+    @Override
+    public SshFetchTaskWrapper newTask() {
+        dirty = false;
+        return new SshFetchTaskWrapper(this);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        // help let people know of API usage error
+        if (dirty)
+            log.warn("Task "+this+" was modified but modification was never used");
+        super.finalize();
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshFetchTaskWrapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshFetchTaskWrapper.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshFetchTaskWrapper.java
new file mode 100644
index 0000000..b6c2931
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshFetchTaskWrapper.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.ssh;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskWrapper;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.os.Os;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+/**
+ * As {@link ProcessTaskWrapper}, but putting a file on the remote machine
+ * 
+ * @since 0.6.0
+ */
+@Beta
+public class SshFetchTaskWrapper implements TaskWrapper<String> {
+
+    private final Task<String> task;
+
+    private final String remoteFile;
+    private final SshMachineLocation machine;
+    private File backingFile;
+    private final ConfigBag config;
+    
+    
+    // package private as only AbstractSshTaskFactory should invoke
+    SshFetchTaskWrapper(SshFetchTaskFactory factory) {
+        this.remoteFile = Preconditions.checkNotNull(factory.remoteFile, "remoteFile");
+        this.machine = Preconditions.checkNotNull(factory.machine, "machine");
+        TaskBuilder<String> tb = TaskBuilder.<String>builder().dynamic(false).name("ssh fetch "+factory.remoteFile);
+        task = tb.body(new SshFetchJob()).build();
+        config = factory.getConfig();
+    }
+    
+    @Override
+    public Task<String> asTask() {
+        return getTask();
+    }
+    
+    @Override
+    public Task<String> getTask() {
+        return task;
+    }
+    
+    public String getRemoteFile() {
+        return remoteFile;
+    }
+    
+    public SshMachineLocation getMachine() {
+        return machine;
+    }
+        
+    private class SshFetchJob implements Callable<String> {
+        @Override
+        public String call() throws Exception {
+            int result = -1;
+            try {
+                Preconditions.checkNotNull(getMachine(), "machine");
+                backingFile = Os.newTempFile("brooklyn-ssh-fetch-", FilenameUtils.getName(remoteFile));
+                backingFile.deleteOnExit();
+                
+                result = getMachine().copyFrom(config.getAllConfig(), remoteFile, backingFile.getPath());
+            } catch (Exception e) {
+                throw new IllegalStateException("SSH fetch "+getRemoteFile()+" from "+getMachine()+" returned threw exception, in "+Tasks.current()+": "+e, e);
+            }
+            if (result!=0) {
+                throw new IllegalStateException("SSH fetch "+getRemoteFile()+" from "+getMachine()+" returned non-zero exit code  "+result+", in "+Tasks.current());
+            }
+            return FileUtils.readFileToString(backingFile);
+        }
+    }
+    
+    @Override
+    public String toString() {
+        return super.toString()+"["+task+"]";
+    }
+
+    /** blocks, returns the fetched file as a string, throwing if there was an exception */
+    public String get() {
+        return getTask().getUnchecked();
+    }
+    
+    /** blocks, returns the fetched file as bytes, throwing if there was an exception */
+    public byte[] getBytes() {
+        block();
+        try {
+            return FileUtils.readFileToByteArray(backingFile);
+        } catch (IOException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+    /** blocks until the task completes; does not throw */
+    public SshFetchTaskWrapper block() {
+        getTask().blockUntilEnded();
+        return this;
+    }
+ 
+    /** true iff the ssh job has completed (with or without failure) */
+    public boolean isDone() {
+        return getTask().isDone();
+    }   
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskFactory.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskFactory.java
new file mode 100644
index 0000000..05cc42e
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskFactory.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.ssh;
+
+import java.io.InputStream;
+import java.io.Reader;
+
+import org.apache.brooklyn.api.management.TaskFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+import brooklyn.util.stream.KnownSizeInputStream;
+import brooklyn.util.stream.ReaderInputStream;
+
+import com.google.common.base.Suppliers;
+
+// cannot be (cleanly) instantiated due to nested generic self-referential type; however trivial subclasses do allow it 
+public class SshPutTaskFactory extends SshPutTaskStub implements TaskFactory<SshPutTaskWrapper> {
+    
+    private static final Logger log = LoggerFactory.getLogger(SshPutTaskFactory.class);
+    
+    private boolean dirty = false;
+
+    /** constructor where machine will be added later */
+    public SshPutTaskFactory(String remoteFile) {
+        remoteFile(remoteFile);
+    }
+
+    /** convenience constructor to supply machine immediately */
+    public SshPutTaskFactory(SshMachineLocation machine, String remoteFile) {
+        machine(machine);
+        remoteFile(remoteFile);
+    }
+
+    protected SshPutTaskFactory self() { return this; }
+
+    protected void markDirty() {
+        dirty = true;
+    }
+    
+    public SshPutTaskFactory machine(SshMachineLocation machine) {
+        markDirty();
+        this.machine = machine;
+        return self();
+    }
+        
+    public SshPutTaskFactory remoteFile(String remoteFile) {
+        this.remoteFile = remoteFile;
+        return self();
+    }
+
+    public SshPutTaskFactory summary(String summary) {
+        markDirty();
+        this.summary = summary;
+        return self();
+    }
+
+    public SshPutTaskFactory contents(String contents) {
+        markDirty();
+        this.contents = Suppliers.ofInstance(KnownSizeInputStream.of(contents));  
+        return self();
+    }
+
+    public SshPutTaskFactory contents(byte[] contents) {
+        markDirty();
+        this.contents = Suppliers.ofInstance(KnownSizeInputStream.of(contents));  
+        return self();
+    }
+
+    public SshPutTaskFactory contents(InputStream stream) {
+        markDirty();
+        this.contents = Suppliers.ofInstance(stream);  
+        return self();
+    }
+
+    public SshPutTaskFactory contents(Reader reader) {
+        markDirty();
+        this.contents = Suppliers.ofInstance(new ReaderInputStream(reader));  
+        return self();
+    }
+
+    public SshPutTaskFactory allowFailure() {
+        markDirty();
+        allowFailure = true;
+        return self();
+    }
+    
+    public SshPutTaskFactory createDirectory() {
+        markDirty();
+        createDirectory = true;
+        return self();
+    }
+    
+    public SshPutTaskWrapper newTask() {
+        dirty = false;
+        return new SshPutTaskWrapper(this);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        // help let people know of API usage error
+        if (dirty)
+            log.warn("Task "+this+" was modified but modification was never used");
+        super.finalize();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskStub.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskStub.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskStub.java
new file mode 100644
index 0000000..4e3a024
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskStub.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.ssh;
+
+import java.io.InputStream;
+
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import com.google.common.base.Supplier;
+
+public class SshPutTaskStub {
+
+    protected String remoteFile;
+    protected SshMachineLocation machine;
+    protected Supplier<? extends InputStream> contents;
+    protected String summary;
+    protected String permissions;
+    protected boolean allowFailure = false;
+    protected boolean createDirectory = false;
+    protected final ConfigBag config = ConfigBag.newInstance();
+
+    protected SshPutTaskStub() {
+    }
+    
+    protected SshPutTaskStub(SshPutTaskStub constructor) {
+        this.remoteFile = constructor.remoteFile;
+        this.machine = constructor.machine;
+        this.contents = constructor.contents;
+        this.summary = constructor.summary;
+        this.allowFailure = constructor.allowFailure;
+        this.createDirectory = constructor.createDirectory;
+        this.permissions = constructor.permissions;
+        this.config.copy(constructor.config);
+    }
+
+    public String getRemoteFile() {
+        return remoteFile;
+    }
+    
+    public String getSummary() {
+        if (summary!=null) return summary;
+        return "scp put: "+remoteFile;
+    }
+
+    public SshMachineLocation getMachine() {
+        return machine;
+    }
+    
+    protected ConfigBag getConfig() {
+        return config;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskWrapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskWrapper.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskWrapper.java
new file mode 100644
index 0000000..13449d0
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshPutTaskWrapper.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.ssh;
+
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskWrapper;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+/** As {@link ProcessTaskWrapper}, but putting a file on the remote machine */
+@Beta
+public class SshPutTaskWrapper extends SshPutTaskStub implements TaskWrapper<Void> {
+
+    private static final Logger log = LoggerFactory.getLogger(SshPutTaskWrapper.class);
+    
+    private final Task<Void> task;
+
+    protected Integer exitCodeOfCopy = null;
+    protected Exception exception = null;
+    protected boolean successful = false;
+    
+    // package private as only AbstractSshTaskFactory should invoke
+    SshPutTaskWrapper(SshPutTaskFactory constructor) {
+        super(constructor);
+        TaskBuilder<Void> tb = TaskBuilder.<Void>builder().dynamic(false).name(getSummary());
+        task = tb.body(new SshPutJob()).build();
+    }
+    
+    @Override
+    public Task<Void> asTask() {
+        return getTask();
+    }
+    
+    @Override
+    public Task<Void> getTask() {
+        return task;
+    }
+        
+    // TODO:
+    //   verify
+    //   copyAsRoot
+    //   owner
+    //   lastModificationDate - see {@link #PROP_LAST_MODIFICATION_DATE}; not supported by all SshTool implementations
+    //   lastAccessDate - see {@link #PROP_LAST_ACCESS_DATE}; not supported by all SshTool implementations
+
+    private class SshPutJob implements Callable<Void> {
+        @Override
+        public Void call() throws Exception {
+            try {
+                Preconditions.checkNotNull(getMachine(), "machine");
+                
+                String remoteFile = getRemoteFile();
+
+                if (createDirectory) {
+                    String remoteDir = remoteFile;
+                    int exitCodeOfCreate = -1;
+                    try {
+                        int li = remoteDir.lastIndexOf("/");
+                        if (li>=0) {
+                            remoteDir = remoteDir.substring(0, li+1);
+                            exitCodeOfCreate = getMachine().execCommands("creating directory for "+getSummary(), 
+                                    Arrays.asList("mkdir -p "+remoteDir));
+                        } else {
+                            // nothing to create
+                            exitCodeOfCreate = 0;
+                        }
+                    } catch (Exception e) {
+                        if (log.isDebugEnabled())
+                            log.debug("SSH put "+getRemoteFile()+" (create dir, in task "+getSummary()+") to "+getMachine()+" threw exception: "+e);
+                        exception = e;
+                    }
+                    if (exception!=null || !((Integer)0).equals(exitCodeOfCreate)) {
+                        if (!allowFailure) {
+                            if (exception != null) {
+                                throw new IllegalStateException(getSummary()+" (creating dir "+remoteDir+" for SSH put task) ended with exception, in "+Tasks.current()+": "+exception, exception);
+                            }
+                            if (exitCodeOfCreate!=0) {
+                                exception = new IllegalStateException(getSummary()+" (creating dir "+remoteDir+" SSH put task) ended with exit code "+exitCodeOfCreate+", in "+Tasks.current());
+                                throw exception;
+                            }
+                        }
+                        // not successful, but allowed
+                        return null;
+                    }
+                }
+                
+                ConfigBag config = ConfigBag.newInstanceCopying(getConfig());
+                if (permissions!=null) config.put(SshTool.PROP_PERMISSIONS, permissions);
+                
+                exitCodeOfCopy = getMachine().copyTo(config.getAllConfig(), contents.get(), remoteFile);
+
+                if (log.isDebugEnabled())
+                    log.debug("SSH put "+getRemoteFile()+" (task "+getSummary()+") to "+getMachine()+" completed with exit code "+exitCodeOfCopy);
+            } catch (Exception e) {
+                if (log.isDebugEnabled())
+                    log.debug("SSH put "+getRemoteFile()+" (task "+getSummary()+") to "+getMachine()+" threw exception: "+e);
+                exception = e;
+            }
+            
+            if (exception!=null || !((Integer)0).equals(exitCodeOfCopy)) {
+                if (!allowFailure) {
+                    if (exception != null) {
+                        throw new IllegalStateException(getSummary()+" (SSH put task) ended with exception, in "+Tasks.current()+": "+exception, exception);
+                    }
+                    if (exitCodeOfCopy!=0) {
+                        exception = new IllegalStateException(getSummary()+" (SSH put task) ended with exit code "+exitCodeOfCopy+", in "+Tasks.current());
+                        throw exception;
+                    }
+                }
+                // not successful, but allowed
+                return null;
+            }
+            
+            // TODO verify
+
+            successful = (exception==null && ((Integer)0).equals(exitCodeOfCopy));
+            return null;
+        }
+    }
+    
+    @Override
+    public String toString() {
+        return super.toString()+"["+task+"]";
+    }
+
+    /** blocks, throwing if there was an exception */
+    public Void get() {
+        return getTask().getUnchecked();
+    }
+    
+    /** returns the exit code from the copy, 0 on success; 
+     * null if it has not completed or threw exception
+     * (not sure if this is ever a non-zero integer or if it is meaningful)
+     * <p>
+     * most callers will want the simpler {@link #isSuccessful()} */
+    public Integer getExitCode() {
+        return exitCodeOfCopy;
+    }
+    
+    /** returns any exception encountered in the operation */
+    public Exception getException() {
+        return exception;
+    }
+    
+    /** blocks until the task completes; does not throw */
+    public SshPutTaskWrapper block() {
+        getTask().blockUntilEnded();
+        return this;
+    }
+ 
+    /** true iff the ssh job has completed (with or without failure) */
+    public boolean isDone() {
+        return getTask().isDone();
+    }
+
+    /** true iff the scp has completed successfully; guaranteed to be set before {@link #isDone()} or {@link #block()} are satisfied */
+    public boolean isSuccessful() {
+        return successful;
+    }
+    
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshTasks.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshTasks.java
new file mode 100644
index 0000000..5f8d735
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/SshTasks.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.ssh;
+
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.api.management.TaskQueueingContext;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ssh.internal.PlainSshExecTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.config.ConfigUtils;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.ConfigKeys;
+
+import org.apache.brooklyn.location.basic.AbstractLocation;
+import org.apache.brooklyn.location.basic.LocationInternal;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.net.Urls;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Identifiers;
+import brooklyn.util.text.Strings;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+/**
+ * Conveniences for generating {@link Task} instances to perform SSH activities on an {@link SshMachineLocation}.
+ * <p>
+ * To infer the {@link SshMachineLocation} and take properties from entities and global management context the
+ * {@link SshEffectorTasks} should be preferred over this class.
+ *  
+ * @see SshEffectorTasks
+ * @since 0.6.0
+ */
+@Beta
+public class SshTasks {
+
+    private static final Logger log = LoggerFactory.getLogger(SshTasks.class);
+        
+    public static ProcessTaskFactory<Integer> newSshExecTaskFactory(SshMachineLocation machine, String ...commands) {
+        return newSshExecTaskFactory(machine, true, commands);
+    }
+    
+    public static ProcessTaskFactory<Integer> newSshExecTaskFactory(SshMachineLocation machine, final boolean useMachineConfig, String ...commands) {
+        return new PlainSshExecTaskFactory<Integer>(machine, commands) {
+            {
+                if (useMachineConfig)
+                    config.putIfAbsent(getSshFlags(machine));
+            }
+        };
+    }
+
+    public static SshPutTaskFactory newSshPutTaskFactory(SshMachineLocation machine, String remoteFile) {
+        return newSshPutTaskFactory(machine, true, remoteFile);
+    }
+    
+    public static SshPutTaskFactory newSshPutTaskFactory(SshMachineLocation machine, final boolean useMachineConfig, String remoteFile) {
+        return new SshPutTaskFactory(machine, remoteFile) {
+            {
+                if (useMachineConfig)
+                    config.putIfAbsent(getSshFlags(machine));
+            }
+        };
+    }
+
+    public static SshFetchTaskFactory newSshFetchTaskFactory(SshMachineLocation machine, String remoteFile) {
+        return newSshFetchTaskFactory(machine, true, remoteFile);
+    }
+    
+    public static SshFetchTaskFactory newSshFetchTaskFactory(SshMachineLocation machine, final boolean useMachineConfig, String remoteFile) {
+        return new SshFetchTaskFactory(machine, remoteFile) {
+            {
+                if (useMachineConfig)
+                    config.putIfAbsent(getSshFlags(machine));
+            }
+        };
+    }
+
+    private static Map<String, Object> getSshFlags(Location location) {
+        ConfigBag allConfig = ConfigBag.newInstance();
+        
+        if (location instanceof AbstractLocation) {
+            ManagementContext mgmt = ((AbstractLocation)location).getManagementContext();
+            if (mgmt!=null)
+                allConfig.putAll(mgmt.getConfig().getAllConfig());
+        }
+        
+        allConfig.putAll(((LocationInternal)location).config().getBag());
+        
+        Map<String, Object> result = Maps.newLinkedHashMap();
+        for (String keyS : allConfig.getAllConfig().keySet()) {
+            ConfigKey<?> key = ConfigKeys.newConfigKey(Object.class, keyS);
+            if (key.getName().startsWith(SshTool.BROOKLYN_CONFIG_KEY_PREFIX)) {
+                result.put(ConfigUtils.unprefixedKey(SshTool.BROOKLYN_CONFIG_KEY_PREFIX, key).getName(), allConfig.get(key));
+            }
+        }
+        return result;
+    }
+
+    @Beta
+    public static enum OnFailingTask { 
+        FAIL,
+        /** issues a warning, sometimes implemented as marking the task inessential and failing it if it appears
+         * we are in a dynamic {@link TaskQueueingContext};
+         * useful because this way the warning appears to the user;
+         * but note that the check is done against the calling thread so use with some care
+         * (and thus this enum is currently here rather then elsewhere) */
+        WARN_OR_IF_DYNAMIC_FAIL_MARKING_INESSENTIAL,
+        /** issues a warning in the log if the task fails, otherwise swallows it */
+        WARN_IN_LOG_ONLY, 
+        /** not even a warning if the task fails (the caller is expected to handle it as appropriate) */
+        IGNORE }
+    
+    public static ProcessTaskFactory<Boolean> dontRequireTtyForSudo(SshMachineLocation machine, final boolean failIfCantSudo) {
+        return dontRequireTtyForSudo(machine, failIfCantSudo ? OnFailingTask.FAIL : OnFailingTask.WARN_IN_LOG_ONLY);
+    }
+    /** creates a task which returns modifies sudoers to ensure non-tty access is permitted;
+     * also gives nice warnings if sudo is not permitted */
+    public static ProcessTaskFactory<Boolean> dontRequireTtyForSudo(SshMachineLocation machine, OnFailingTask onFailingTaskRequested) {
+        final OnFailingTask onFailingTask;
+        if (onFailingTaskRequested==OnFailingTask.WARN_OR_IF_DYNAMIC_FAIL_MARKING_INESSENTIAL) {
+            if (DynamicTasks.getTaskQueuingContext()!=null)
+                onFailingTask = onFailingTaskRequested;
+            else
+                onFailingTask = OnFailingTask.WARN_IN_LOG_ONLY;
+        } else {
+            onFailingTask = onFailingTaskRequested;
+        }
+        
+        final String id = Identifiers.makeRandomId(6);
+        return newSshExecTaskFactory(machine, 
+                BashCommands.dontRequireTtyForSudo(),
+                // strange quotes are to ensure we don't match against echoed stdin
+                BashCommands.sudo("echo \"sudo\"-is-working-"+id))
+            .summary("setting up sudo")
+            .configure(SshTool.PROP_ALLOCATE_PTY, true)
+            .allowingNonZeroExitCode()
+            .returning(new Function<ProcessTaskWrapper<?>,Boolean>() { public Boolean apply(ProcessTaskWrapper<?> task) {
+                if (task.getExitCode()==0 && task.getStdout().contains("sudo-is-working-"+id)) return true;
+                Entity entity = BrooklynTaskTags.getTargetOrContextEntity(Tasks.current());
+                
+                
+                if (onFailingTask!=OnFailingTask.IGNORE) {
+                    // TODO if in a queueing context can we mark this task inessential and throw?
+                    // that way user sees the message...
+                    String message = "Error setting up sudo for "+task.getMachine().getUser()+"@"+task.getMachine().getAddress().getHostName()+" "+
+                        " (exit code "+task.getExitCode()+(entity!=null ? ", entity "+entity : "")+")";
+                    DynamicTasks.queueIfPossible(Tasks.warning(message, null));
+                }
+                Streams.logStreamTail(log, "STDERR of sudo setup problem", Streams.byteArrayOfString(task.getStderr()), 1024);
+                
+                if (onFailingTask==OnFailingTask.WARN_OR_IF_DYNAMIC_FAIL_MARKING_INESSENTIAL) {
+                    Tasks.markInessential();
+                }
+                if (onFailingTask==OnFailingTask.FAIL || onFailingTask==OnFailingTask.WARN_OR_IF_DYNAMIC_FAIL_MARKING_INESSENTIAL) {
+                    throw new IllegalStateException("Passwordless sudo is required for "+task.getMachine().getUser()+"@"+task.getMachine().getAddress().getHostName()+
+                            (entity!=null ? " ("+entity+")" : ""));
+                }
+                return false; 
+            } });
+    }
+
+    /** Function for use in {@link ProcessTaskFactory#returning(Function)} which logs all information, optionally requires zero exit code, 
+     * and then returns stdout */
+    public static Function<ProcessTaskWrapper<?>, String> returningStdoutLoggingInfo(final Logger logger, final boolean requireZero) {
+        return new Function<ProcessTaskWrapper<?>, String>() {
+          public String apply(@Nullable ProcessTaskWrapper<?> input) {
+            if (logger!=null) logger.info(input+" COMMANDS:\n"+Strings.join(input.getCommands(),"\n"));
+            if (logger!=null) logger.info(input+" STDOUT:\n"+input.getStdout());
+            if (logger!=null) logger.info(input+" STDERR:\n"+input.getStderr());
+            if (requireZero && input.getExitCode()!=0) 
+                throw new IllegalStateException("non-zero exit code in "+input.getSummary()+": see log for more details!");
+            return input.getStdout();
+          }
+        };
+    }
+
+    /** task to install a file given a url, where the url is resolved remotely first then locally */
+    public static TaskFactory<?> installFromUrl(final SshMachineLocation location, final String url, final String destPath) {
+        return installFromUrl(ResourceUtils.create(SshTasks.class), ImmutableMap.<String,Object>of(), location, url, destPath);
+    }
+    /** task to install a file given a url, where the url is resolved remotely first then locally */
+    public static TaskFactory<?> installFromUrl(final ResourceUtils utils, final Map<String, ?> props, final SshMachineLocation location, final String url, final String destPath) {
+        return new TaskFactory<TaskAdaptable<?>>() {
+            @Override
+            public TaskAdaptable<?> newTask() {
+                return Tasks.<Void>builder().name("installing "+Urls.getBasename(url)).description("installing "+url+" to "+destPath).body(new Runnable() {
+                    @Override
+                    public void run() {
+                        int result = location.installTo(utils, props, url, destPath);
+                        if (result!=0) 
+                            throw new IllegalStateException("Failed to install '"+url+"' to '"+destPath+"' at "+location+": exit code "+result);
+                    }
+                }).build();
+            }
+        };
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/internal/AbstractSshExecTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/internal/AbstractSshExecTaskFactory.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/internal/AbstractSshExecTaskFactory.java
new file mode 100644
index 0000000..45600d5
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/internal/AbstractSshExecTaskFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.ssh.internal;
+
+import com.google.common.base.Preconditions;
+
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.core.util.task.system.internal.AbstractProcessTaskFactory;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+// cannot be (cleanly) instantiated due to nested generic self-referential type; however trivial subclasses do allow it 
+public abstract class AbstractSshExecTaskFactory<T extends AbstractProcessTaskFactory<T,RET>,RET> extends AbstractProcessTaskFactory<T,RET> implements ProcessTaskFactory<RET> {
+    
+    /** constructor where machine will be added later */
+    public AbstractSshExecTaskFactory(String ...commands) {
+        super(commands);
+    }
+
+    /** convenience constructor to supply machine immediately */
+    public AbstractSshExecTaskFactory(SshMachineLocation machine, String ...commands) {
+        this(commands);
+        machine(machine);
+    }
+    
+    @Override
+    public ProcessTaskWrapper<RET> newTask() {
+        dirty = false;
+        return new ProcessTaskWrapper<RET>(this) {
+            protected void run(ConfigBag config) {
+                Preconditions.checkNotNull(getMachine(), "machine");
+                if (Boolean.FALSE.equals(this.runAsScript)) {
+                    this.exitCode = getMachine().execCommands(config.getAllConfig(), getSummary(), commands, shellEnvironment);
+                } else { // runScript = null or TRUE
+                    this.exitCode = getMachine().execScript(config.getAllConfig(), getSummary(), commands, shellEnvironment);
+                }
+            }
+            protected String taskTypeShortName() { return "SSH"; }
+        };
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/internal/PlainSshExecTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/internal/PlainSshExecTaskFactory.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/internal/PlainSshExecTaskFactory.java
new file mode 100644
index 0000000..4d5dfce
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ssh/internal/PlainSshExecTaskFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.ssh.internal;
+
+import java.util.List;
+
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import com.google.common.base.Function;
+
+/** the "Plain" class exists purely so we can massage return types for callers' convenience */
+public class PlainSshExecTaskFactory<RET> extends AbstractSshExecTaskFactory<PlainSshExecTaskFactory<RET>,RET> {
+    /** constructor where machine will be added later */
+    public PlainSshExecTaskFactory(String ...commands) {
+        super(commands);
+    }
+
+    /** convenience constructor to supply machine immediately */
+    public PlainSshExecTaskFactory(SshMachineLocation machine, String ...commands) {
+        this(commands);
+        machine(machine);
+    }
+
+    /** Constructor where machine will be added later */
+    public PlainSshExecTaskFactory(List<String> commands) {
+        this(commands.toArray(new String[commands.size()]));
+    }
+
+    /** Convenience constructor to supply machine immediately */
+    public PlainSshExecTaskFactory(SshMachineLocation machine, List<String> commands) {
+        this(machine, commands.toArray(new String[commands.size()]));
+    }
+
+    @Override
+    public <T2> PlainSshExecTaskFactory<T2> returning(ScriptReturnType type) {
+        return (PlainSshExecTaskFactory<T2>) super.<T2>returning(type);
+    }
+
+    @Override
+    public <RET2> PlainSshExecTaskFactory<RET2> returning(Function<ProcessTaskWrapper<?>, RET2> resultTransformation) {
+        return (PlainSshExecTaskFactory<RET2>) super.returning(resultTransformation);
+    }
+    
+    @Override
+    public PlainSshExecTaskFactory<Boolean> returningIsExitCodeZero() {
+        return (PlainSshExecTaskFactory<Boolean>) super.returningIsExitCodeZero();
+    }
+    
+    @Override
+    public PlainSshExecTaskFactory<String> requiringZeroAndReturningStdout() {
+        return (PlainSshExecTaskFactory<String>) super.requiringZeroAndReturningStdout();
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskFactory.java b/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskFactory.java
new file mode 100644
index 0000000..f66e1ea
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.system;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskStub.ScriptReturnType;
+
+import brooklyn.config.ConfigKey;
+
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+
+public interface ProcessTaskFactory<T> extends TaskFactory<ProcessTaskWrapper<T>> {
+    public ProcessTaskFactory<T> machine(SshMachineLocation machine);
+    public ProcessTaskFactory<T> add(String ...commandsToAdd);
+    public ProcessTaskFactory<T> add(Iterable<String> commandsToAdd);
+    public ProcessTaskFactory<T> requiringExitCodeZero();
+    public ProcessTaskFactory<T> requiringExitCodeZero(String extraErrorMessage);
+    public ProcessTaskFactory<T> allowingNonZeroExitCode();
+    public ProcessTaskFactory<String> requiringZeroAndReturningStdout();
+    public ProcessTaskFactory<Boolean> returningIsExitCodeZero();
+    public <RET2> ProcessTaskFactory<RET2> returning(ScriptReturnType type);
+    public <RET2> ProcessTaskFactory<RET2> returning(Function<ProcessTaskWrapper<?>, RET2> resultTransformation);
+    public ProcessTaskFactory<T> runAsCommand();
+    public ProcessTaskFactory<T> runAsScript();
+    public ProcessTaskFactory<T> runAsRoot();
+    public ProcessTaskFactory<T> environmentVariable(String key, String val);
+    public ProcessTaskFactory<T> environmentVariables(Map<String,String> vars);
+    public ProcessTaskFactory<T> summary(String summary);
+    
+    /** allows setting config-key based properties for specific underlying tools */
+    @Beta
+    public <V> ProcessTaskFactory<T> configure(ConfigKey<V> key, V value);
+
+    /** allows setting config-key/flag based properties for specific underlying tools;
+     * but note that if any are prefixed with {@link SshTool#BROOKLYN_CONFIG_KEY_PREFIX}
+     * these should normally be filtered out */
+    @Beta
+    public ProcessTaskFactory<T> configure(Map<?,?> flags);
+
+    /** adds a listener which will be notified of (otherwise) successful completion,
+     * typically used to invalidate the result (ie throw exception, to promote a string in the output to an exception);
+     * invoked even if return code is zero, so a better error can be thrown */
+    public ProcessTaskFactory<T> addCompletionListener(Function<ProcessTaskWrapper<?>, Void> function);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskStub.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskStub.java b/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskStub.java
new file mode 100644
index 0000000..1937d15
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskStub.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.system;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class ProcessTaskStub {
+    
+    protected final List<String> commands = new ArrayList<String>();
+    /** null for localhost */
+    protected SshMachineLocation machine;
+    
+    // config data
+    protected String summary;
+    protected final ConfigBag config = ConfigBag.newInstance();
+    
+    public static enum ScriptReturnType { CUSTOM, EXIT_CODE, STDOUT_STRING, STDOUT_BYTES, STDERR_STRING, STDERR_BYTES }
+    protected Function<ProcessTaskWrapper<?>, ?> returnResultTransformation = null;
+    protected ScriptReturnType returnType = ScriptReturnType.EXIT_CODE;
+    
+    protected Boolean runAsScript = null;
+    protected boolean runAsRoot = false;
+    protected Boolean requireExitCodeZero = null;
+    protected String extraErrorMessage = null;
+    protected Map<String,String> shellEnvironment = new MutableMap<String, String>();
+    protected final List<Function<ProcessTaskWrapper<?>, Void>> completionListeners = new ArrayList<Function<ProcessTaskWrapper<?>,Void>>();
+
+    public ProcessTaskStub() {}
+    
+    protected ProcessTaskStub(ProcessTaskStub source) {
+        commands.addAll(source.getCommands());
+        machine = source.getMachine();
+        summary = source.getSummary();
+        config.copy(source.getConfig());
+        returnResultTransformation = source.returnResultTransformation;
+        returnType = source.returnType;
+        runAsScript = source.runAsScript;
+        runAsRoot = source.runAsRoot;
+        requireExitCodeZero = source.requireExitCodeZero;
+        extraErrorMessage = source.extraErrorMessage;
+        shellEnvironment.putAll(source.getShellEnvironment());
+        completionListeners.addAll(source.getCompletionListeners());
+    }
+
+    public String getSummary() {
+        if (summary!=null) return summary;
+        return Strings.maxlen(Strings.join(commands, " ; "), 160);
+    }
+    
+    /** null for localhost */
+    public SshMachineLocation getMachine() {
+        return machine;
+    }
+    
+    public Map<String, String> getShellEnvironment() {
+        return ImmutableMap.copyOf(shellEnvironment);
+    }
+ 
+    @Override
+    public String toString() {
+        return super.toString()+"["+getSummary()+"]";
+    }
+
+    public List<String> getCommands() {
+        return ImmutableList.copyOf(commands);
+    }
+ 
+    public List<Function<ProcessTaskWrapper<?>, Void>> getCompletionListeners() {
+        return completionListeners;
+    }
+
+    protected ConfigBag getConfig() { return config; }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskWrapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskWrapper.java b/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskWrapper.java
new file mode 100644
index 0000000..045b3c9
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/system/ProcessTaskWrapper.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.system;
+
+import java.io.ByteArrayOutputStream;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskWrapper;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.system.internal.AbstractProcessTaskFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Function;
+
+/** Wraps a fully constructed process task, and allows callers to inspect status. 
+ * Note that methods in here such as {@link #getStdout()} will return partially completed streams while the task is ongoing
+ * (and exit code will be null). You can {@link #block()} or {@link #get()} as conveniences on the underlying {@link #getTask()}. */ 
+public abstract class ProcessTaskWrapper<RET> extends ProcessTaskStub implements TaskWrapper<RET> {
+
+    private static final Logger log = LoggerFactory.getLogger(ProcessTaskWrapper.class);
+    
+    private final Task<RET> task;
+
+    // execution details
+    protected ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+    protected ByteArrayOutputStream stderr = new ByteArrayOutputStream();
+    protected Integer exitCode = null;
+    
+    @SuppressWarnings("unchecked")
+    protected ProcessTaskWrapper(AbstractProcessTaskFactory<?,RET> constructor) {
+        super(constructor);
+        TaskBuilder<Object> tb = constructor.constructCustomizedTaskBuilder();
+        if (stdout!=null) tb.tag(BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDOUT, stdout));
+        if (stderr!=null) tb.tag(BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDERR, stderr));
+        task = (Task<RET>) tb.body(new ProcessTaskInternalJob()).build();
+    }
+    
+    @Override
+    public Task<RET> asTask() {
+        return getTask();
+    }
+    
+    @Override
+    public Task<RET> getTask() {
+        return task;
+    }
+    
+    public Integer getExitCode() {
+        return exitCode;
+    }
+    
+    public byte[] getStdoutBytes() {
+        if (stdout==null) return null;
+        return stdout.toByteArray();
+    }
+    
+    public byte[] getStderrBytes() {
+        if (stderr==null) return null;
+        return stderr.toByteArray();
+    }
+    
+    public String getStdout() {
+        if (stdout==null) return null;
+        return stdout.toString();
+    }
+    
+    public String getStderr() {
+        if (stderr==null) return null;
+        return stderr.toString();
+    }
+
+    protected class ProcessTaskInternalJob implements Callable<Object> {
+        @Override
+        public Object call() throws Exception {
+            run( getConfigForRunning() );
+            
+            for (Function<ProcessTaskWrapper<?>, Void> listener: completionListeners) {
+                try {
+                    listener.apply(ProcessTaskWrapper.this);
+                } catch (Exception e) {
+                    logWithDetailsAndThrow("Error in "+taskTypeShortName()+" task "+getSummary()+": "+e, e);                    
+                }
+            }
+            
+            if (exitCode!=0 && !Boolean.FALSE.equals(requireExitCodeZero)) {
+                if (Boolean.TRUE.equals(requireExitCodeZero)) {
+                    logWithDetailsAndThrow(taskTypeShortName()+" task ended with exit code "+exitCode+" when 0 was required, in "+Tasks.current()+": "+getSummary(), null);
+                } else {
+                    // warn, but allow, on non-zero not explicitly allowed
+                    log.warn(taskTypeShortName()+" task ended with exit code "+exitCode+" when non-zero was not explicitly allowed (error may be thrown in future), in "
+                            +Tasks.current()+": "+getSummary());
+                }
+            }
+            switch (returnType) {
+            case CUSTOM: return returnResultTransformation.apply(ProcessTaskWrapper.this);
+            case STDOUT_STRING: return stdout.toString();
+            case STDOUT_BYTES: return stdout.toByteArray();
+            case STDERR_STRING: return stderr.toString();
+            case STDERR_BYTES: return stderr.toByteArray();
+            case EXIT_CODE: return exitCode;
+            }
+
+            throw new IllegalStateException("Unknown return type for "+taskTypeShortName()+" job "+getSummary()+": "+returnType);
+        }
+
+        protected void logWithDetailsAndThrow(String message, Throwable optionalCause) {
+            message = (extraErrorMessage!=null ? extraErrorMessage+": " : "") + message;
+            log.warn(message+" (throwing)");
+            logProblemDetails("STDERR", stderr, 1024);
+            logProblemDetails("STDOUT", stdout, 1024);
+            logProblemDetails("STDIN", Streams.byteArrayOfString(Strings.join(commands,"\n")), 4096);
+            if (optionalCause!=null) throw new IllegalStateException(message, optionalCause);
+            throw new IllegalStateException(message);
+        }
+        
+        protected void logProblemDetails(String streamName, ByteArrayOutputStream stream, int max) {
+            Streams.logStreamTail(log, streamName+" for problem in "+Tasks.current(), stream, max);
+        }
+
+    }
+    
+    @Override
+    public String toString() {
+        return super.toString()+"["+task+"]";
+    }
+
+    /** blocks and gets the result, throwing if there was an exception */
+    public RET get() {
+        return getTask().getUnchecked();
+    }
+    
+    /** blocks until the task completes; does not throw */
+    public ProcessTaskWrapper<RET> block() {
+        getTask().blockUntilEnded();
+        return this;
+    }
+ 
+    /** true iff the process has completed (with or without failure) */
+    public boolean isDone() {
+        return getTask().isDone();
+    }
+
+    /** for overriding */
+    protected ConfigBag getConfigForRunning() {
+        ConfigBag config = ConfigBag.newInstanceCopying(ProcessTaskWrapper.this.config);
+        if (stdout!=null) config.put(ShellTool.PROP_OUT_STREAM, stdout);
+        if (stderr!=null) config.put(ShellTool.PROP_ERR_STREAM, stderr);
+        
+        if (!config.containsKey(ShellTool.PROP_NO_EXTRA_OUTPUT))
+            // by default no extra output (so things like cat, etc work as expected)
+            config.put(ShellTool.PROP_NO_EXTRA_OUTPUT, true);
+
+        if (runAsRoot)
+            config.put(ShellTool.PROP_RUN_AS_ROOT, true);
+        return config;
+    }
+
+    protected abstract void run(ConfigBag config);
+    
+    protected abstract String taskTypeShortName();
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/system/SystemTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/system/SystemTasks.java b/core/src/main/java/org/apache/brooklyn/core/util/task/system/SystemTasks.java
new file mode 100644
index 0000000..d05b09c
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/system/SystemTasks.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.system;
+
+import org.apache.brooklyn.core.util.task.system.internal.SystemProcessTaskFactory.ConcreteSystemProcessTaskFactory;
+
+public class SystemTasks {
+
+    public static ProcessTaskFactory<Integer> exec(String ...commands) {
+        return new ConcreteSystemProcessTaskFactory<Integer>(commands);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/AbstractProcessTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/AbstractProcessTaskFactory.java b/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/AbstractProcessTaskFactory.java
new file mode 100644
index 0000000..8b90263
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/AbstractProcessTaskFactory.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.system.internal;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.BrooklynTaskTags;
+
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskStub;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+
+public abstract class AbstractProcessTaskFactory<T extends AbstractProcessTaskFactory<T,RET>,RET> extends ProcessTaskStub implements ProcessTaskFactory<RET> {
+    
+    private static final Logger log = LoggerFactory.getLogger(AbstractProcessTaskFactory.class);
+    
+    protected boolean dirty = false;
+    
+    public AbstractProcessTaskFactory(String ...commands) {
+        this.commands.addAll(Arrays.asList(commands));
+    }
+
+    @SuppressWarnings("unchecked")
+    protected T self() { return (T)this; }
+    
+    protected void markDirty() {
+        dirty = true;
+    }
+    
+    @Override
+    public T add(String ...commandsToAdd) {
+        markDirty();
+        for (String commandToAdd: commandsToAdd) this.commands.add(commandToAdd);
+        return self();
+    }
+
+    @Override
+    public T add(Iterable<String> commandsToAdd) {
+        Iterables.addAll(this.commands, commandsToAdd);
+        return self();
+    }
+    
+    @Override
+    public T machine(SshMachineLocation machine) {
+        markDirty();
+        this.machine = machine;
+        return self();
+    }
+
+    @Override
+    public T requiringExitCodeZero() {
+        markDirty();
+        requireExitCodeZero = true;
+        return self();
+    }
+    
+    @Override
+    public T requiringExitCodeZero(String extraErrorMessage) {
+        markDirty();
+        requireExitCodeZero = true;
+        this.extraErrorMessage = extraErrorMessage;
+        return self();
+    }
+    
+    @Override
+    public T allowingNonZeroExitCode() {
+        markDirty();
+        requireExitCodeZero = false;
+        return self();
+    }
+
+    @Override
+    public ProcessTaskFactory<Boolean> returningIsExitCodeZero() {
+        if (requireExitCodeZero==null) allowingNonZeroExitCode();
+        return returning(new Function<ProcessTaskWrapper<?>,Boolean>() {
+            public Boolean apply(ProcessTaskWrapper<?> input) {
+                return input.getExitCode()==0;
+            }
+        });
+    }
+
+    @Override
+    public ProcessTaskFactory<String> requiringZeroAndReturningStdout() {
+        requiringExitCodeZero();
+        return this.<String>returning(ScriptReturnType.STDOUT_STRING);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <RET2> ProcessTaskFactory<RET2> returning(ScriptReturnType type) {
+        markDirty();
+        returnType = Preconditions.checkNotNull(type);
+        return (ProcessTaskFactory<RET2>) self();
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <RET2> ProcessTaskFactory<RET2> returning(Function<ProcessTaskWrapper<?>, RET2> resultTransformation) {
+        markDirty();
+        returnType = ScriptReturnType.CUSTOM;
+        this.returnResultTransformation = resultTransformation;
+        return (ProcessTaskFactory<RET2>) self();
+    }
+    
+    @Override
+    public T runAsCommand() {
+        markDirty();
+        runAsScript = false;
+        return self();
+    }
+
+    @Override
+    public T runAsScript() {
+        markDirty();
+        runAsScript = true;
+        return self();
+    }
+
+    @Override
+    public T runAsRoot() {
+        markDirty();
+        runAsRoot = true;
+        return self();
+    }
+    
+    @Override
+    public T environmentVariable(String key, String val) {
+        markDirty();
+        shellEnvironment.put(key, val);
+        return self();
+    }
+
+    @Override
+    public T environmentVariables(Map<String,String> vars) {
+        if (vars!=null) {
+            markDirty();
+            shellEnvironment.putAll(vars);
+        }
+        return self();
+    }
+
+    /** creates the TaskBuilder which can be further customized; typically invoked by the initial {@link #newTask()} */
+    public TaskBuilder<Object> constructCustomizedTaskBuilder() {
+        TaskBuilder<Object> tb = TaskBuilder.builder().dynamic(false).name("ssh: "+getSummary());
+        
+        tb.tag(BrooklynTaskTags.tagForStream(BrooklynTaskTags.STREAM_STDIN, 
+                Streams.byteArrayOfString(Strings.join(commands, "\n"))));
+        tb.tag(BrooklynTaskTags.tagForEnvStream(BrooklynTaskTags.STREAM_ENV, shellEnvironment));
+        
+        return tb;
+    }
+    
+    @Override
+    public T summary(String summary) {
+        markDirty();
+        this.summary = summary;
+        return self();
+    }
+
+    @Override
+    public <V> T configure(ConfigKey<V> key, V value) {
+        config.configure(key, value);
+        return self();
+    }
+    
+    @Override
+    public T configure(Map<?, ?> flags) {
+        if (flags!=null)
+            config.putAll(flags);
+        return self();
+    }
+ 
+    @Override
+    public T addCompletionListener(Function<ProcessTaskWrapper<?>, Void> listener) {
+        completionListeners.add(listener);
+        return self();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        // help let people know of API usage error
+        if (dirty)
+            log.warn("Task "+this+" was modified but modification was never used");
+        super.finalize();
+    }
+}



[34/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/BasicTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/BasicTask.java b/core/src/main/java/brooklyn/util/task/BasicTask.java
deleted file mode 100644
index 57b2bb2..0000000
--- a/core/src/main/java/brooklyn/util/task/BasicTask.java
+++ /dev/null
@@ -1,892 +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.util.JavaGroovyEquivalents.asString;
-import static brooklyn.util.JavaGroovyEquivalents.elvisString;
-import groovy.lang.Closure;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.management.LockInfo;
-import java.lang.management.ManagementFactory;
-import java.lang.management.ThreadInfo;
-import java.util.Collections;
-import java.util.ConcurrentModificationException;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.brooklyn.api.management.HasTaskChildren;
-import org.apache.brooklyn.api.management.Task;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.GroovyJavaMethods;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.text.Identifiers;
-import brooklyn.util.text.Strings;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Throwables;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.Callables;
-import com.google.common.util.concurrent.ExecutionList;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * The basic concrete implementation of a {@link Task} to be executed.
- *
- * A {@link Task} is a wrapper for an executable unit, such as a {@link Closure} or a {@link Runnable} or
- * {@link Callable} and will run in its own {@link Thread}.
- * <p>
- * The task can be given an optional displayName and description in its constructor (as named
- * arguments in the first {@link Map} parameter). It is guaranteed to have {@link Object#notify()} called
- * once whenever the task starts running and once again when the task is about to complete. Due to
- * the way executors work it is ugly to guarantee notification <em>after</em> completion, so instead we
- * notify just before then expect the user to call {@link #get()} - which will throw errors if the underlying job
- * did so - or {@link #blockUntilEnded()} which will not throw errors.
- */
-public class BasicTask<T> implements TaskInternal<T> {
-    private static final Logger log = LoggerFactory.getLogger(BasicTask.class);
-
-    private String id = Identifiers.makeRandomId(8);
-    protected Callable<T> job;
-    public final String displayName;
-    public final String description;
-
-    protected final Set<Object> tags = Sets.newConcurrentHashSet();
-    // for debugging, to record where tasks were created
-//    { tags.add(new Throwable("Creation stack trace")); }
-    
-    protected Task<?> proxyTargetTask = null;
-
-    protected String blockingDetails = null;
-    protected Task<?> blockingTask = null;
-    Object extraStatusText = null;
-
-    /** listeners attached at task level; these are stored here, but run on the underlying ListenableFuture */
-    protected final ExecutionList listeners = new ExecutionList();
-    
-    /**
-     * Constructor needed to prevent confusion in groovy stubs when looking for default constructor,
-     *
-     * The generics on {@link Closure} break it if that is first constructor.
-     */
-    protected BasicTask() { this(Collections.emptyMap()); }
-    protected BasicTask(Map<?,?> flags) { this(flags, (Callable<T>) null); }
-
-    public BasicTask(Callable<T> job) { this(Collections.emptyMap(), job); }
-    
-    public BasicTask(Map<?,?> flags, Callable<T> job) {
-        this.job = job;
-
-        if (flags.containsKey("tag")) tags.add(flags.remove("tag"));
-        Object ftags = flags.remove("tags");
-        if (ftags!=null) {
-            if (ftags instanceof Iterable) Iterables.addAll(tags, (Iterable<?>)ftags);
-            else {
-                log.info("deprecated use of non-collection argument for 'tags' ("+ftags+") in "+this, new Throwable("trace of discouraged use of non-colleciton tags argument"));
-                tags.add(ftags);
-            }
-        }
-
-        description = elvisString(flags.remove("description"), "");
-        String d = asString(flags.remove("displayName"));
-        displayName = (d==null ? "" : d);
-    }
-
-    public BasicTask(Runnable job) { this(GroovyJavaMethods.<T>callableFromRunnable(job)); }
-    public BasicTask(Map<?,?> flags, Runnable job) { this(flags, GroovyJavaMethods.<T>callableFromRunnable(job)); }
-    public BasicTask(Closure<T> job) { this(GroovyJavaMethods.callableFromClosure(job)); }
-    public BasicTask(Map<?,?> flags, Closure<T> job) { this(flags, GroovyJavaMethods.callableFromClosure(job)); }
-
-    @Override
-    public String getId() {
-        return id;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(id);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj instanceof Task)
-            return ((Task<?>)obj).getId().equals(getId());
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        // give display name plus id, or job and tags plus id; some jobs have been extended to include nice tostrings 
-        return "Task["+
-            (Strings.isNonEmpty(displayName) ? 
-                displayName : 
-                (job + (tags!=null && !tags.isEmpty() ? ";"+tags : "")) ) +
-            ":"+getId()+"]";
-    }
-
-    @Override
-    public Task<T> asTask() {
-        return this;
-    }
-    
-    // housekeeping --------------------
-
-    /*
-     * These flags are set by BasicExecutionManager.submit.
-     *
-     * Order is guaranteed to be as shown below, in order of #. Within each # line it is currently in the order specified by commas but this is not guaranteed.
-     * (The spaces between the # section indicate longer delays / logical separation ... it should be clear!)
-     *
-     * # submitter, submit time set, tags and other submit-time fields set
-     *
-     * # thread set, ThreadLocal getCurrentTask set
-     * # start time set, isBegun is true
-     * # task end callback run, if supplied
-     *
-     * # task runs
-     *
-     * # task end callback run, if supplied
-     * # end time set
-     * # thread cleared, ThreadLocal getCurrentTask set
-     * # Task.notifyAll()
-     * # Task.get() (result.get()) available, Task.isDone is true
-     *
-     * Few _consumers_ should care, but internally we rely on this so that, for example, status is displayed correctly.
-     * Tests should catch most things, but be careful if you change any of the above semantics.
-     */
-
-    protected long queuedTimeUtc = -1;
-    protected long submitTimeUtc = -1;
-    protected long startTimeUtc = -1;
-    protected long endTimeUtc = -1;
-    protected Maybe<Task<?>> submittedByTask;
-
-    protected volatile Thread thread = null;
-    private volatile boolean cancelled = false;
-    /** normally a {@link ListenableFuture}, except for scheduled tasks when it may be a {@link ScheduledFuture} */
-    protected volatile Future<T> internalFuture = null;
-    
-    @Override
-    public synchronized void initInternalFuture(ListenableFuture<T> result) {
-        if (this.internalFuture != null) 
-            throw new IllegalStateException("task "+this+" is being given a result twice");
-        this.internalFuture = result;
-        notifyAll();
-    }
-
-    // metadata accessors ------------
-
-    @Override
-    public Set<Object> getTags() { return Collections.unmodifiableSet(new LinkedHashSet<Object>(tags)); }
-    
-    /** if the job is queued for submission (e.g. by another task) it can indicate that fact (and time) here;
-     * note tasks can (and often are) submitted without any queueing, in which case this value may be -1 */
-    @Override
-    public long getQueuedTimeUtc() { return queuedTimeUtc; }
-    
-    @Override
-    public long getSubmitTimeUtc() { return submitTimeUtc; }
-    
-    @Override
-    public long getStartTimeUtc() { return startTimeUtc; }
-    
-    @Override
-    public long getEndTimeUtc() { return endTimeUtc; }
-
-    @Override
-    public Future<T> getInternalFuture() { return internalFuture; }
-    
-    @Override
-    public Task<?> getSubmittedByTask() { 
-        if (submittedByTask==null) return null;
-        return submittedByTask.orNull(); 
-    }
-
-    /** the thread where the task is running, if it is running */
-    @Override
-    public Thread getThread() { return thread; }
-
-    // basic fields --------------------
-
-    @Override
-    public boolean isQueued() {
-        return (queuedTimeUtc >= 0);
-    }
-
-    @Override
-    public boolean isQueuedOrSubmitted() {
-        return isQueued() || isSubmitted();
-    }
-
-    @Override
-    public boolean isQueuedAndNotSubmitted() {
-        return isQueued() && (!isSubmitted());
-    }
-
-    @Override
-    public boolean isSubmitted() {
-        return submitTimeUtc >= 0;
-    }
-
-    @Override
-    public boolean isBegun() {
-        return startTimeUtc >= 0;
-    }
-
-    /** marks the task as queued for execution */
-    @Override
-    public void markQueued() {
-        if (queuedTimeUtc<0)
-            queuedTimeUtc = System.currentTimeMillis();
-    }
-
-    @Override
-    public final synchronized boolean cancel() { return cancel(true); }
-
-    /** doesn't resume it, just means if something was cancelled but not submitted it could now be submitted;
-     * probably going to be removed and perhaps some mechanism for running again made available
-     * @since 0.7.0  */
-    @Beta
-    public synchronized boolean uncancel() {
-        boolean wasCancelled = cancelled;
-        cancelled = false; 
-        return wasCancelled;
-    }
-    
-    @Override
-    public synchronized boolean cancel(boolean mayInterruptIfRunning) {
-        if (isDone()) return false;
-        boolean cancel = true;
-        cancelled = true;
-        if (internalFuture!=null) { 
-            cancel = internalFuture.cancel(mayInterruptIfRunning);
-        }
-        notifyAll();
-        return cancel;
-    }
-
-    @Override
-    public boolean isCancelled() {
-        return cancelled || (internalFuture!=null && internalFuture.isCancelled());
-    }
-
-    @Override
-    public boolean isDone() {
-        // if endTime is set, result might not be completed yet, but it will be set very soon 
-        // (the two values are set close in time, result right after the endTime;
-        // but callback hooks might not see the result yet)
-        return cancelled || (internalFuture!=null && internalFuture.isDone()) || endTimeUtc>0;
-    }
-
-    /**
-     * Returns true if the task has had an error.
-     *
-     * Only true if calling {@link #get()} will throw an exception when it completes (including cancel).
-     * Implementations may set this true before completion if they have that insight, or
-     * (the default) they may compute it lazily after completion (returning false before completion).
-     */
-    @Override
-    public boolean isError() {
-        if (!isDone()) return false;
-        if (isCancelled()) return true;
-        try {
-            get();
-            return false;
-        } catch (Throwable t) {
-            return true;
-        }
-    }
-
-    // future value --------------------
-
-    @Override
-    public T get() throws InterruptedException, ExecutionException {
-        try {
-            if (!isDone())
-                Tasks.setBlockingTask(this);
-            blockUntilStarted();
-            return internalFuture.get();
-        } finally {
-            Tasks.resetBlockingTask();
-        }
-    }
-
-    @Override
-    public T getUnchecked() {
-        try {
-            return get();
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-    
-    @Override
-    public synchronized void blockUntilStarted() {
-        blockUntilStarted(null);
-    }
-    
-    @Override
-    public synchronized boolean blockUntilStarted(Duration timeout) {
-        Long endTime = timeout==null ? null : System.currentTimeMillis() + timeout.toMillisecondsRoundingUp();
-        while (true) {
-            if (cancelled) throw new CancellationException();
-            if (internalFuture==null)
-                try {
-                    if (timeout==null) {
-                        wait();
-                    } else {
-                        long remaining = endTime - System.currentTimeMillis();
-                        if (remaining>0)
-                            wait(remaining);
-                        else
-                            return false;
-                    }
-                } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt();
-                    Throwables.propagate(e);
-                }
-            if (internalFuture!=null) return true;
-        }
-    }
-
-    @Override
-    public void blockUntilEnded() {
-        blockUntilEnded(null);
-    }
-    
-    @Override
-    public boolean blockUntilEnded(Duration timeout) {
-        Long endTime = timeout==null ? null : System.currentTimeMillis() + timeout.toMillisecondsRoundingUp();
-        try { 
-            boolean started = blockUntilStarted(timeout);
-            if (!started) return false;
-            if (timeout==null) {
-                internalFuture.get();
-            } else {
-                long remaining = endTime - System.currentTimeMillis();
-                if (remaining>0)
-                    internalFuture.get(remaining, TimeUnit.MILLISECONDS);
-            }
-            return isDone();
-        } catch (Throwable t) {
-            Exceptions.propagateIfFatal(t);
-            if (!(t instanceof TimeoutException) && log.isDebugEnabled())
-                log.debug("call from "+Thread.currentThread()+", blocking until '"+this+"' finishes, ended with error: "+t);
-            return isDone(); 
-        }
-    }
-
-    @Override
-    public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
-        return get(new Duration(timeout, unit));
-    }
-    
-    @Override
-    public T get(Duration duration) throws InterruptedException, ExecutionException, TimeoutException {
-        long start = System.currentTimeMillis();
-        Long end  = duration==null ? null : start + duration.toMillisecondsRoundingUp();
-        while (end==null || end > System.currentTimeMillis()) {
-            if (cancelled) throw new CancellationException();
-            if (internalFuture == null) {
-                synchronized (this) {
-                    long remaining = end - System.currentTimeMillis();
-                    if (internalFuture==null && remaining>0)
-                        wait(remaining);
-                }
-            }
-            if (internalFuture != null) break;
-        }
-        Long remaining = end==null ? null : end -  System.currentTimeMillis();
-        if (isDone()) {
-            return internalFuture.get(1, TimeUnit.MILLISECONDS);
-        } else if (remaining == null) {
-            return internalFuture.get();
-        } else if (remaining > 0) {
-            return internalFuture.get(remaining, TimeUnit.MILLISECONDS);
-        } else {
-            throw new TimeoutException();
-        }
-    }
-
-    @Override
-    public T getUnchecked(Duration duration) {
-        try {
-            return get(duration);
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-    
-    // ------------------ status ---------------------------
-    
-    /**
-     * Returns a brief status string
-     *
-     * Plain-text format. Reported status if there is one, otherwise state which will be one of:
-     * <ul>
-     * <li>Not submitted
-     * <li>Submitted for execution
-     * <li>Ended by error
-     * <li>Ended by cancellation
-     * <li>Ended normally
-     * <li>Running
-     * <li>Waiting
-     * </ul>
-     */
-    @Override
-    public String getStatusSummary() {
-        return getStatusString(0);
-    }
-
-    /**
-     * Returns detailed status, suitable for a hover
-     *
-     * Plain-text format, with new-lines (and sometimes extra info) if multiline enabled.
-     */
-    @Override
-    public String getStatusDetail(boolean multiline) {
-        return getStatusString(multiline?2:1);
-    }
-
-    /**
-     * This method is useful for callers to see the status of a task.
-     *
-     * Also for developers to see best practices for examining status fields etc
-     *
-     * @param verbosity 0 = brief, 1 = one-line with some detail, 2 = lots of detail
-     */
-    protected String getStatusString(int verbosity) {
-//        Thread t = getThread();
-        String rv;
-        if (submitTimeUtc <= 0) rv = "Not submitted";
-        else if (!isCancelled() && startTimeUtc <= 0) {
-            rv = "Submitted for execution";
-            if (verbosity>0) {
-                long elapsed = System.currentTimeMillis() - submitTimeUtc;
-                rv += " "+Time.makeTimeStringRoundedSince(elapsed)+" ago";
-            }
-            if (verbosity >= 2 && getExtraStatusText()!=null) {
-                rv += "\n\n"+getExtraStatusText();
-            }
-        } else if (isDone()) {
-            long elapsed = endTimeUtc - submitTimeUtc;
-            String duration = Time.makeTimeStringRounded(elapsed);
-            if (isCancelled()) {
-                rv = "Cancelled";
-                if (verbosity >= 1) rv+=" after "+duration;
-                
-                if (verbosity >= 2 && getExtraStatusText()!=null) {
-                    rv += "\n\n"+getExtraStatusText();
-                }
-            } else if (isError()) {
-                rv = "Failed";
-                if (verbosity >= 1) {
-                    rv += " after "+duration;
-                    Throwable error = Tasks.getError(this);
-
-                    if (verbosity >= 2 && getExtraStatusText()!=null) {
-                        rv += "\n\n"+getExtraStatusText();
-                    }
-                    
-                    //remove outer ExecException which is reported by the get(), we want the exception the task threw
-                    while (error instanceof ExecutionException) error = error.getCause();
-                    String errorMessage = Exceptions.collapseText(error);
-
-                    if (verbosity == 1) rv += ": "+abbreviate(errorMessage);
-                    if (verbosity >= 2) {
-                        rv += ": "+errorMessage;
-                        StringWriter sw = new StringWriter();
-                        ((Throwable)error).printStackTrace(new PrintWriter(sw));
-                        rv += "\n\n"+sw.getBuffer();
-                    }
-                }
-            } else {
-                rv = "Completed";
-                if (verbosity>=1) {
-                    if (verbosity==1) {
-                        try {
-                            Object v = get();
-                            rv += ", " +(v==null ? "no return value (null)" : "result: "+abbreviate(v.toString()));
-                        } catch (Exception e) {
-                            rv += ", but error accessing result ["+e+"]"; //shouldn't happen
-                        }
-                    } else {
-                        rv += " after "+duration;
-                        try {
-                            Object v = get();
-                            rv += "\n\n" + (v==null ? "No return value (null)" : "Result: "+v);
-                        } catch (Exception e) {
-                            rv += " at first\n" +
-                                    "Error accessing result ["+e+"]"; //shouldn't happen
-                        }
-                        if (verbosity >= 2 && getExtraStatusText()!=null) {
-                            rv += "\n\n"+getExtraStatusText();
-                        }
-                    }
-                }
-            }
-        } else {
-            rv = getActiveTaskStatusString(verbosity);
-        }
-        return rv;
-    }
-    
-    private static String abbreviate(String s) {
-        s = Strings.getFirstLine(s);
-        if (s.length()>255) s = s.substring(0, 252)+ "...";
-        return s;
-    }
-
-    protected String getActiveTaskStatusString(int verbosity) {
-        String rv = "";
-        Thread t = getThread();
-    
-        // Normally, it's not possible for thread==null as we were started and not ended
-        
-        // However, there is a race where the task starts sand completes between the calls to getThread()
-        // at the start of the method and this call to getThread(), so both return null even though
-        // the intermediate checks returned started==true isDone()==false.
-        if (t == null) {
-            if (isDone()) {
-                return getStatusString(verbosity);
-            } else {
-                //should only happen for repeating task which is not active
-                return "Sleeping";
-            }
-        }
-
-        ThreadInfo ti = ManagementFactory.getThreadMXBean().getThreadInfo(t.getId(), (verbosity<=0 ? 0 : verbosity==1 ? 1 : Integer.MAX_VALUE));
-        if (getThread()==null)
-            //thread might have moved on to a new task; if so, recompute (it should now say "done")
-            return getStatusString(verbosity);
-        
-        if (verbosity >= 1 && Strings.isNonBlank(blockingDetails)) {
-            if (verbosity==1)
-                // short status string will just show blocking details
-                return blockingDetails;
-            //otherwise show the blocking details, then a new line, then additional information
-            rv = blockingDetails + "\n\n";
-        }
-        
-        if (verbosity >= 1 && blockingTask!=null) {
-            if (verbosity==1)
-                // short status string will just show blocking details
-                return "Waiting on "+blockingTask;
-            //otherwise show the blocking details, then a new line, then additional information
-            rv = "Waiting on "+blockingTask + "\n\n";
-        }
-
-        if (verbosity>=2) {
-            if (getExtraStatusText()!=null) {
-                rv += getExtraStatusText()+"\n\n";
-            }
-            
-            rv += ""+toString()+"\n";
-            if (submittedByTask!=null) {
-                rv += "Submitted by "+submittedByTask+"\n";
-            }
-
-            if (this instanceof HasTaskChildren) {
-                // list children tasks for compound tasks
-                try {
-                    Iterable<Task<?>> childrenTasks = ((HasTaskChildren)this).getChildren();
-                    if (childrenTasks.iterator().hasNext()) {
-                        rv += "Children:\n";
-                        for (Task<?> child: childrenTasks) {
-                            rv += "  "+child+": "+child.getStatusDetail(false)+"\n";
-                        }
-                    }
-                } catch (ConcurrentModificationException exc) {
-                    rv += "  (children not available - currently being modified)\n";
-                }
-            }
-            rv += "\n";
-        }
-        
-        LockInfo lock = ti.getLockInfo();
-        rv += "In progress";
-        if (verbosity>=1) {
-            if (lock==null && ti.getThreadState()==Thread.State.RUNNABLE) {
-                //not blocked
-                if (ti.isSuspended()) {
-                    // when does this happen?
-                    rv += ", thread suspended";
-                } else {
-                    if (verbosity >= 2) rv += " ("+ti.getThreadState()+")";
-                }
-            } else {
-                rv +=", thread waiting ";
-                if (ti.getThreadState() == Thread.State.BLOCKED) {
-                    rv += "(mutex) on "+lookup(lock);
-                    //TODO could say who holds it
-                } else if (ti.getThreadState() == Thread.State.WAITING) {
-                    rv += "(notify) on "+lookup(lock);
-                } else if (ti.getThreadState() == Thread.State.TIMED_WAITING) {
-                    rv += "(timed) on "+lookup(lock);
-                } else {
-                    rv = "("+ti.getThreadState()+") on "+lookup(lock);
-                }
-            }
-        }
-        if (verbosity>=2) {
-            StackTraceElement[] st = ti.getStackTrace();
-            st = brooklyn.util.javalang.StackTraceSimplifier.cleanStackTrace(st);
-            if (st!=null && st.length>0)
-                rv += "\n" +"At: "+st[0];
-            for (int ii=1; ii<st.length; ii++) {
-                rv += "\n" +"    "+st[ii];
-            }
-        }
-        return rv;
-    }
-    
-    protected String lookup(LockInfo info) {
-        return info!=null ? ""+info : "unknown (sleep)";
-    }
-
-    @Override
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    @Override
-    public String getDescription() {
-        return description;
-    }
-
-    
-    /** allows a task user to specify why a task is blocked; for use immediately before a blocking/wait,
-     * and typically cleared immediately afterwards; referenced by management api to inspect a task
-     * which is blocking
-     */
-    @Override
-    public String setBlockingDetails(String blockingDetails) {
-        String old = this.blockingDetails;
-        this.blockingDetails = blockingDetails;
-        return old;
-    }
-    
-    @Override
-    public Task<?> setBlockingTask(Task<?> blockingTask) {
-        Task<?> old = this.blockingTask;
-        this.blockingTask = blockingTask;
-        return old;
-    }
-    
-    @Override
-    public void resetBlockingDetails() {
-        this.blockingDetails = null;
-    }
-    
-    @Override
-    public void resetBlockingTask() {
-        this.blockingTask = null;
-    }
-
-    /** returns a textual message giving details while the task is blocked */
-    @Override
-    public String getBlockingDetails() {
-        return blockingDetails;
-    }
-    
-    /** returns a task that this task is blocked on */
-    @Override
-    public Task<?> getBlockingTask() {
-        return blockingTask;
-    }
-    
-    @Override
-    public void setExtraStatusText(Object extraStatus) {
-        this.extraStatusText = extraStatus;
-    }
-    
-    @Override
-    public Object getExtraStatusText() {
-        return extraStatusText;
-    }
-
-    // ---- add a way to warn if task is not run
-    
-    public interface TaskFinalizer {
-        public void onTaskFinalization(Task<?> t);
-    }
-
-    public static final TaskFinalizer WARN_IF_NOT_RUN = new TaskFinalizer() {
-        @Override
-        public void onTaskFinalization(Task<?> t) {
-            if (!Tasks.isAncestorCancelled(t) && !t.isSubmitted()) {
-                log.warn(t+" was never submitted; did the code create it and forget to run it? ('cancel' the task to suppress this message)");
-                log.debug("Detail of unsubmitted task "+t+":\n"+t.getStatusDetail(true));
-                return;
-            }
-            if (!t.isDone()) {
-                // shouldn't happen
-                // TODO But does happen if management context was terminated (e.g. running test suite).
-                //      Should check if Execution Manager is running, and only log if it was not terminated?
-                log.warn("Task "+t+" is being finalized before completion");
-                return;
-            }
-        }
-    };
-
-    public static final TaskFinalizer NO_OP = new TaskFinalizer() {
-        @Override
-        public void onTaskFinalization(Task<?> t) {
-        }
-    };
-    
-    public void ignoreIfNotRun() {
-        setFinalizer(NO_OP);
-    }
-    
-    public void setFinalizer(TaskFinalizer f) {
-        TaskFinalizer finalizer = Tasks.tag(this, TaskFinalizer.class, false);
-        if (finalizer!=null && finalizer!=f)
-            throw new IllegalStateException("Cannot apply multiple finalizers");
-        if (isDone())
-            throw new IllegalStateException("Finalizer cannot be set on task "+this+" after it is finished");
-        tags.add(f);
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        TaskFinalizer finalizer = Tasks.tag(this, TaskFinalizer.class, false);
-        if (finalizer==null) finalizer = WARN_IF_NOT_RUN;
-        finalizer.onTaskFinalization(this);
-    }
-    
-    public static class SubmissionErrorCatchingExecutor implements Executor {
-        final Executor target;
-        public SubmissionErrorCatchingExecutor(Executor target) {
-            this.target = target;
-        }
-        @Override
-        public void execute(Runnable command) {
-            if (isShutdown()) {
-                log.debug("Skipping execution of task callback hook "+command+" because executor is shutdown.");
-                return;
-            }
-            try {
-                target.execute(command);
-            } catch (Exception e) {
-                if (isShutdown()) {
-                    log.debug("Ignoring failed execution of task callback hook "+command+" because executor is shutdown.");
-                } else {
-                    log.warn("Execution of task callback hook "+command+" failed: "+e, e);
-                }
-            }
-        }
-        protected boolean isShutdown() {
-            return target instanceof ExecutorService && ((ExecutorService)target).isShutdown();
-        }
-    }
-    
-    @Override
-    public void addListener(Runnable listener, Executor executor) {
-        listeners.add(listener, new SubmissionErrorCatchingExecutor(executor));
-    }
-    
-    @Override
-    public void runListeners() {
-        listeners.execute();
-    }
-    
-    @Override
-    public void setEndTimeUtc(long val) {
-        endTimeUtc = val;
-    }
-    
-    @Override
-    public void setThread(Thread thread) {
-        this.thread = thread;
-    }
-    
-    @Override
-    public Callable<T> getJob() {
-        return job;
-    }
-    
-    @Override
-    public void setJob(Callable<T> job) {
-        this.job = job;
-    }
-    
-    @Override
-    public ExecutionList getListeners() {
-        return listeners;
-    }
-    
-    @Override
-    public void setSubmitTimeUtc(long val) {
-        submitTimeUtc = val;
-    }
-    
-    private static <T> Task<T> newGoneTaskFor(Task<?> task) {
-        Task<T> t = Tasks.<T>builder().dynamic(false).name(task.getDisplayName())
-            .description("Details of the original task "+task+" have been forgotten.")
-            .body(Callables.returning((T)null)).build();
-        ((BasicTask<T>)t).ignoreIfNotRun();
-        return t;
-    }
-    
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    @Override
-    public void setSubmittedByTask(Task<?> task) {
-        submittedByTask = (Maybe)Maybe.softThen((Task)task, (Maybe)Maybe.of(BasicTask.newGoneTaskFor(task)));
-    }
-    
-    @Override
-    public Set<Object> getMutableTags() {
-        return tags;
-    }
-    
-    @Override
-    public void setStartTimeUtc(long val) {
-        startTimeUtc = val;
-    }
-
-    @Override
-    public void applyTagModifier(Function<Set<Object>,Void> modifier) {
-        modifier.apply(tags);
-    }
-
-    @Override
-    public Task<?> getProxyTarget() {
-        return proxyTargetTask;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/CanSetName.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/CanSetName.java b/core/src/main/java/brooklyn/util/task/CanSetName.java
deleted file mode 100644
index 760c99e..0000000
--- a/core/src/main/java/brooklyn/util/task/CanSetName.java
+++ /dev/null
@@ -1,25 +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;
-
-public interface CanSetName {
-
-    void setName(String name);
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/CompoundTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/CompoundTask.java b/core/src/main/java/brooklyn/util/task/CompoundTask.java
deleted file mode 100644
index e33120c..0000000
--- a/core/src/main/java/brooklyn/util/task/CompoundTask.java
+++ /dev/null
@@ -1,131 +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 groovy.lang.Closure;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-
-import org.apache.brooklyn.api.management.HasTaskChildren;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskAdaptable;
-import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.util.collections.MutableMap;
-
-
-/**
- * A {@link Task} that is comprised of other units of work: possibly a heterogeneous mix of {@link Task},
- * {@link Runnable}, {@link Callable} and {@link Closure} instances.
- * 
- * This class holds the collection of child tasks, but subclasses have the responsibility of executing them in a
- * sensible manner by implementing the abstract {@link #runJobs} method.
- */
-public abstract class CompoundTask<T> extends BasicTask<List<T>> implements HasTaskChildren {
-
-    @SuppressWarnings("unused")
-    private static final Logger log = LoggerFactory.getLogger(CompoundTask.class);
-                
-    protected final List<Task<? extends T>> children;
-    protected final List<Object> result;
-    
-    /**
-     * Constructs a new compound task containing the specified units of work.
-     * 
-     * @param jobs  A potentially heterogeneous mixture of {@link Runnable}, {@link Callable}, {@link Closure} and {@link Task} can be provided. 
-     * @throws IllegalArgumentException if any of the passed child jobs is not one of the above types 
-     */
-    public CompoundTask(Object... jobs) {
-        this( Arrays.asList(jobs) );
-    }
-    
-    /**
-     * Constructs a new compound task containing the specified units of work.
-     * 
-     * @param jobs  A potentially heterogeneous mixture of {@link Runnable}, {@link Callable}, {@link Closure} and {@link Task} can be provided. 
-     * @throws IllegalArgumentException if any of the passed child jobs is not one of the above types 
-     */
-    public CompoundTask(Collection<?> jobs) {
-        this(MutableMap.of("tag", "compound"), jobs);
-    }
-    
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public CompoundTask(Map<String,?> flags, Collection<?> jobs) {
-        super(flags);
-        super.job = new Callable<List<T>>() {
-            @Override public List<T> call() throws Exception {
-                return runJobs();
-            }
-        };
-        
-        this.result = new ArrayList<Object>(jobs.size());
-        this.children = new ArrayList<Task<? extends T>>(jobs.size());
-        for (Object job : jobs) {
-            Task subtask;
-            if (job instanceof TaskAdaptable) { subtask = ((TaskAdaptable)job).asTask(); }
-            else if (job instanceof Closure)  { subtask = new BasicTask<T>((Closure) job); }
-            else if (job instanceof Callable) { subtask = new BasicTask<T>((Callable) job); }
-            else if (job instanceof Runnable) { subtask = new BasicTask<T>((Runnable) job); }
-            
-            else throw new IllegalArgumentException("Invalid child "+(job == null ? null : job.getClass() + " ("+job+")")+
-                " passed to compound task; must be Runnable, Callable, Closure or Task");
-            
-            BrooklynTaskTags.addTagDynamically(subtask, ManagementContextInternal.SUB_TASK_TAG);
-            children.add(subtask);
-        }
-        
-        for (Task<?> t: getChildren()) {
-            ((TaskInternal<?>)t).markQueued();
-        }
-    }
-
-    /** return value needs to be specified by subclass; subclass should also setBlockingDetails 
-     * @throws ExecutionException 
-     * @throws InterruptedException */    
-    protected abstract List<T> runJobs() throws InterruptedException, ExecutionException;
-    
-    protected void submitIfNecessary(TaskAdaptable<?> task) {
-        if (!task.asTask().isSubmitted()) {
-            if (BasicExecutionContext.getCurrentExecutionContext() == null) {
-                throw new IllegalStateException("Compound task ("+task+") launched from "+this+" missing required execution context");
-            } else {
-                BasicExecutionContext.getCurrentExecutionContext().submit(task);
-            }
-        }
-    }
-    
-    public List<Task<? extends T>> getChildrenTyped() {
-        return children;
-    }
-    
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public List<Task<?>> getChildren() {
-        return (List) getChildrenTyped();
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/DeferredSupplier.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/DeferredSupplier.java b/core/src/main/java/brooklyn/util/task/DeferredSupplier.java
deleted file mode 100644
index d82b3fb..0000000
--- a/core/src/main/java/brooklyn/util/task/DeferredSupplier.java
+++ /dev/null
@@ -1,38 +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 com.google.common.base.Supplier;
-
-/**
- * A class that supplies objects of a single type. When used as a ConfigKey value,
- * the evaluation is deferred until getConfig() is called. The returned value will then
- * be coerced to the correct type. 
- * 
- * Subsequent calls to getConfig will result in further calls to deferredProvider.get(), 
- * rather than reusing the result. If you want to reuse the result, consider instead 
- * using a Future.
- * 
- * Note that this functionality replaces the ues of Closure in brooklyn 0.4.0, which 
- * served the same purpose.
- */
-public interface DeferredSupplier<T> extends Supplier<T> {
-    @Override
-    T get();
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/DynamicSequentialTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/DynamicSequentialTask.java b/core/src/main/java/brooklyn/util/task/DynamicSequentialTask.java
deleted file mode 100644
index 455a889..0000000
--- a/core/src/main/java/brooklyn/util/task/DynamicSequentialTask.java
+++ /dev/null
@@ -1,480 +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 groovy.lang.Closure;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-import org.apache.brooklyn.api.management.HasTaskChildren;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskQueueingContext;
-import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.time.CountdownTimer;
-import brooklyn.util.time.Duration;
-
-import com.google.common.annotations.Beta;
-import com.google.common.collect.ImmutableList;
-
-/** Represents a task whose run() method can create other tasks
- * which are run sequentially, but that sequence runs in parallel to this task
- * <p>
- * There is an optional primary job run with this task, along with multiple secondary children.
- * If any secondary task fails (assuming it isn't {@link Tasks#markInessential()} then by default
- * subsequent tasks are not submitted and the primary task fails (but no tasks are cancelled or interrupted).
- * You can change the behavior of this task with fields in {@link FailureHandlingConfig},
- * or the convenience {@link TaskQueueingContext#swallowChildrenFailures()}
- * (and {@link DynamicTasks#swallowChildrenFailures()} if you are inside the task).
- * <p>
- * This synchronizes on secondary tasks when submitting them, in case they may be manually submitted
- * and the submitter wishes to ensure it is only submitted once.
- * <p>
- * Improvements which would be nice to have:
- * <li> unqueued tasks not visible in api; would like that
- * <li> uses an extra thread (submitted as background task) to monitor the secondary jobs; would be nice to remove this,
- *      and rely on {@link BasicExecutionManager} to run the jobs sequentially (combined with fix to item above)
- * <li> would be nice to have cancel, resume, and possibly skipQueue available as operations (ideally in the REST API and GUI)   
- **/
-public class DynamicSequentialTask<T> extends BasicTask<T> implements HasTaskChildren, TaskQueueingContext {
-
-    private static final Logger log = LoggerFactory.getLogger(CompoundTask.class);
-                
-    protected final Queue<Task<?>> secondaryJobsAll = new ConcurrentLinkedQueue<Task<?>>();
-    protected final Queue<Task<?>> secondaryJobsRemaining = new ConcurrentLinkedQueue<Task<?>>();
-    protected final Object jobTransitionLock = new Object();
-    protected volatile boolean primaryStarted = false;
-    protected volatile boolean primaryFinished = false;
-    protected volatile boolean secondaryQueueAborted = false;
-    protected Thread primaryThread;
-    protected DstJob dstJob;
-    protected FailureHandlingConfig failureHandlingConfig = FailureHandlingConfig.DEFAULT;
-
-    // default values for how to handle the various failures
-    @Beta
-    public static class FailureHandlingConfig {
-        /** secondary queue runs independently of primary task (submitting and blocking on each secondary task in order), 
-         * but can set it up not to submit any more tasks if the primary fails */
-        public final boolean abortSecondaryQueueOnPrimaryFailure;
-        /** as {@link #abortSecondaryQueueOnPrimaryFailure} but controls cancelling of secondary queue*/
-        public final boolean cancelSecondariesOnPrimaryFailure;
-        /** secondary queue can continue submitting+blocking tasks even if a secondary task fails (unusual;
-         * typically handled by {@link TaskTags#markInessential(Task)} on the secondary tasks, in which case
-         * the secondary queue is never aborted */
-        public final boolean abortSecondaryQueueOnSecondaryFailure;
-        /** unsubmitted secondary tasks (ie those further in the queue) can be cancelled if a secondary task fails */
-        public final boolean cancelSecondariesOnSecondaryFailure;
-        /** whether to issue cancel against primary task if a secondary task fails */
-        public final boolean cancelPrimaryOnSecondaryFailure;
-        /** whether to fail this task if a secondary task fails */
-        public final boolean failParentOnSecondaryFailure;
-        
-        @Beta
-        public FailureHandlingConfig(
-                boolean abortSecondaryQueueOnPrimaryFailure, boolean cancelSecondariesOnPrimaryFailure,
-                boolean abortSecondaryQueueOnSecondaryFailure, boolean cancelSecondariesOnSecondaryFailure,
-                boolean cancelPrimaryOnSecondaryFailure, boolean failParentOnSecondaryFailure) {
-            this.abortSecondaryQueueOnPrimaryFailure = abortSecondaryQueueOnPrimaryFailure;
-            this.cancelSecondariesOnPrimaryFailure = cancelSecondariesOnPrimaryFailure;
-            this.abortSecondaryQueueOnSecondaryFailure = abortSecondaryQueueOnSecondaryFailure;
-            this.cancelSecondariesOnSecondaryFailure = cancelSecondariesOnSecondaryFailure;
-            this.cancelPrimaryOnSecondaryFailure = cancelPrimaryOnSecondaryFailure;
-            this.failParentOnSecondaryFailure = failParentOnSecondaryFailure;
-        }
-        
-        public static final FailureHandlingConfig DEFAULT = new FailureHandlingConfig(false, false, true, false, false, true);
-        public static final FailureHandlingConfig SWALLOWING_CHILDREN_FAILURES = new FailureHandlingConfig(false, false, false, false, false, false);
-    }
-    
-    public static class QueueAbortedException extends IllegalStateException {
-        private static final long serialVersionUID = -7569362887826818524L;
-        
-        public QueueAbortedException(String msg) {
-            super(msg);
-        }
-        public QueueAbortedException(String msg, Throwable cause) {
-            super(msg, cause);
-        }
-    }
-
-    /**
-     * Constructs a new compound task containing the specified units of work.
-     * 
-     * @param jobs  A potentially heterogeneous mixture of {@link Runnable}, {@link Callable}, {@link Closure} and {@link Task} can be provided. 
-     * @throws IllegalArgumentException if any of the passed child jobs is not one of the above types 
-     */
-    public DynamicSequentialTask() {
-        this(null);
-    }
-    
-    public DynamicSequentialTask(Callable<T> mainJob) {
-        this(MutableMap.of("tag", "compound"), mainJob);
-    }
-    
-    public DynamicSequentialTask(Map<?,?> flags, Callable<T> mainJob) {
-        super(flags);
-        this.job = dstJob = new DstJob(mainJob);
-    }
-    
-    @Override
-    public void queue(Task<?> t) {
-        synchronized (jobTransitionLock) {
-            if (primaryFinished)
-                throw new IllegalStateException("Cannot add a task to "+this+" which is already finished (trying to add "+t+")");
-            if (secondaryQueueAborted)
-                throw new QueueAbortedException("Cannot add a task to "+this+" whose queue has been aborted (trying to add "+t+")");
-            secondaryJobsAll.add(t);
-            secondaryJobsRemaining.add(t);
-            BrooklynTaskTags.addTagsDynamically(t, ManagementContextInternal.SUB_TASK_TAG);
-            ((TaskInternal<?>)t).markQueued();
-            jobTransitionLock.notifyAll();
-        }
-    }
-
-    @Override
-    public boolean cancel(boolean mayInterruptIfRunning) {
-        return cancel(mayInterruptIfRunning, mayInterruptIfRunning, true);
-    }
-    public boolean cancel(boolean mayInterruptTask, boolean interruptPrimaryThread, boolean alsoCancelChildren) {
-        if (isDone()) return false;
-        if (log.isTraceEnabled()) log.trace("cancelling {}", this);
-        boolean cancel = super.cancel(mayInterruptTask);
-        if (alsoCancelChildren) {
-            for (Task<?> t: secondaryJobsAll)
-                cancel |= t.cancel(mayInterruptTask);
-        }
-        synchronized (jobTransitionLock) {
-            if (primaryThread!=null) {
-                if (interruptPrimaryThread) {
-                    if (log.isTraceEnabled()) log.trace("cancelling {} - interrupting", this);
-                    primaryThread.interrupt();
-                }
-                cancel = true;
-            }
-        }
-        return cancel;
-    }
-    
-    @Override
-    public synchronized boolean uncancel() {
-        secondaryQueueAborted = false;
-        return super.uncancel();
-    }
-
-    @Override
-    public Iterable<Task<?>> getChildren() {
-        return Collections.unmodifiableCollection(secondaryJobsAll);
-    }
-    
-    /** submits the indicated task for execution in the current execution context, and returns immediately */
-    protected void submitBackgroundInheritingContext(Task<?> task) {
-        BasicExecutionContext ec = BasicExecutionContext.getCurrentExecutionContext();
-        if (log.isTraceEnabled()) {
-            log.trace("task {} - submitting background task {} ({})", new Object[] { Tasks.current(), task, ec });
-        }
-        if (ec==null) {
-            String message = Tasks.current()!=null ?
-                    // user forgot ExecContext:
-                        "Task "+this+" submitting background task requires an ExecutionContext (an ExecutionManager is not enough): submitting "+task+" in "+Tasks.current()
-                    : // should not happen:
-                        "Cannot submit tasks inside DST when not in a task : submitting "+task+" in "+this;
-            log.warn(message+" (rethrowing)");
-            throw new IllegalStateException(message);
-        }
-        synchronized (task) {
-            if (task.isSubmitted()) {
-                if (log.isTraceEnabled()) {
-                    log.trace("DST "+this+" skipping submission of child "+task+" because it is already submitted");
-                }
-            } else {
-                try {
-                    ec.submit(task);
-                } catch (Exception e) {
-                    Exceptions.propagateIfFatal(e);
-                    // Give some context when the submit fails (happens when the target is already unmanaged)
-                    throw new IllegalStateException("Failure submitting task "+task+" in "+this+": "+e.getMessage(), e);
-                }
-            }
-        }
-    }
-
-    public void setFailureHandlingConfig(FailureHandlingConfig failureHandlingConfig) {
-        this.failureHandlingConfig = failureHandlingConfig;
-    }
-    @Override
-    public void swallowChildrenFailures() {
-        setFailureHandlingConfig(FailureHandlingConfig.SWALLOWING_CHILDREN_FAILURES);
-    }
-    
-    protected class DstJob implements Callable<T> {
-        protected Callable<T> primaryJob;
-        /** currently executing (or just completed) secondary task, or null if none;
-         * with jobTransitionLock notified on change and completion */
-        protected volatile Task<?> currentSecondary = null;
-        protected volatile boolean finishedSecondaries = false;
-        
-        public DstJob(Callable<T> mainJob) {
-            this.primaryJob = mainJob;
-        }
-
-        @SuppressWarnings("unchecked")
-        @Override
-        public T call() throws Exception {
-
-            synchronized (jobTransitionLock) {
-                primaryStarted = true;
-                primaryThread = Thread.currentThread();
-                for (Task<?> t: secondaryJobsAll)
-                    ((TaskInternal<?>)t).markQueued();
-            }
-            // TODO overkill having a thread/task for this, but it works
-            // optimisation would either use newTaskEndCallback property on task to submit
-            // or use some kind of single threaded executor for the queued tasks
-            Task<List<Object>> secondaryJobMaster = Tasks.<List<Object>>builder().dynamic(false)
-                    .name("DST manager (internal)")
-                    // TODO marking it transient helps it be GC'd sooner, 
-                    // but ideally we wouldn't have this,
-                    // or else it would be a child
-                    .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
-                    .body(new Callable<List<Object>>() {
-
-                @Override
-                public List<Object> call() throws Exception {
-                    List<Object> result = new ArrayList<Object>();
-                    try { 
-                        while (!secondaryQueueAborted && (!primaryFinished || !secondaryJobsRemaining.isEmpty())) {
-                            synchronized (jobTransitionLock) {
-                                if (!primaryFinished && secondaryJobsRemaining.isEmpty()) {
-                                    currentSecondary = null;
-                                    jobTransitionLock.wait(1000);
-                                }
-                            }
-                            @SuppressWarnings("rawtypes")
-                            Task secondaryJob = secondaryJobsRemaining.poll();
-                            if (secondaryJob != null) {
-                                synchronized (jobTransitionLock) {
-                                    currentSecondary = secondaryJob;
-                                    submitBackgroundInheritingContext(secondaryJob);
-                                    jobTransitionLock.notifyAll();
-                                }
-                                try {
-                                    result.add(secondaryJob.get());
-                                } catch (Exception e) {
-                                    if (TaskTags.isInessential(secondaryJob)) {
-                                        result.add(Tasks.getError(secondaryJob));
-                                        if (log.isDebugEnabled())
-                                            log.debug("Secondary job queue for "+DynamicSequentialTask.this+" ignoring error in inessential task "+secondaryJob+": "+e);
-                                    } else {
-                                        if (failureHandlingConfig.cancelSecondariesOnSecondaryFailure) {
-                                            if (log.isDebugEnabled())
-                                                log.debug("Secondary job queue for "+DynamicSequentialTask.this+" cancelling "+secondaryJobsRemaining.size()+" remaining, due to error in task "+secondaryJob+": "+e);
-                                            synchronized (jobTransitionLock) {
-                                                for (Task<?> t: secondaryJobsRemaining)
-                                                    t.cancel(true);
-                                                jobTransitionLock.notifyAll();
-                                            }
-                                        }
-                                        
-                                        if (failureHandlingConfig.abortSecondaryQueueOnSecondaryFailure) {
-                                            if (log.isDebugEnabled())
-                                                log.debug("Aborting secondary job queue for "+DynamicSequentialTask.this+" due to error in child task "+secondaryJob+" ("+e+", being rethrown)");
-                                            secondaryQueueAborted = true;
-                                            throw e;
-                                        }
-
-                                        if (!primaryFinished && failureHandlingConfig.cancelPrimaryOnSecondaryFailure) {
-                                            cancel(true, false, false);
-                                        }
-                                        
-                                        result.add(Tasks.getError(secondaryJob));
-                                        if (log.isDebugEnabled())
-                                            log.debug("Secondary job queue for "+DynamicSequentialTask.this+" continuing in presence of error in child task "+secondaryJob+" ("+e+", being remembered)");
-                                    }
-                                }
-                            }
-                        }
-                    } finally {
-                        synchronized (jobTransitionLock) {
-                            currentSecondary = null;
-                            finishedSecondaries = true;
-                            jobTransitionLock.notifyAll();
-                        }
-                    }
-                    return result;
-                }
-            }).build();
-            ((BasicTask<?>)secondaryJobMaster).proxyTargetTask = DynamicSequentialTask.this;
-            
-            submitBackgroundInheritingContext(secondaryJobMaster);
-            
-            T result = null;
-            Throwable error = null;
-            Throwable uninterestingSelfError = null;
-            boolean errorIsFromChild = false;
-            try {
-                if (log.isTraceEnabled()) log.trace("calling primary job for {}", this);
-                if (primaryJob!=null) result = primaryJob.call();
-            } catch (Throwable selfException) {
-                Exceptions.propagateIfFatal(selfException);
-                if (Exceptions.getFirstThrowableOfType(selfException, QueueAbortedException.class) != null) {
-                    // Error was caused by the task already having failed, and this thread calling queue() to try
-                    // to queue more work. The underlying cause will be much more interesting.
-                    // Without this special catch, we record error = "Cannot add a task to ... whose queue has been aborted",
-                    // which gets propagated instead of the more interesting child exception.
-                    uninterestingSelfError = selfException;
-                } else {
-                    error = selfException;
-                    errorIsFromChild = false;
-                }
-                if (failureHandlingConfig.abortSecondaryQueueOnPrimaryFailure) {
-                    if (log.isDebugEnabled())
-                        log.debug("Secondary job queue for "+DynamicSequentialTask.this+" aborting with "+secondaryJobsRemaining.size()+" remaining, due to error in primary task: "+selfException);
-                    secondaryQueueAborted = true;
-                }
-                if (failureHandlingConfig.cancelSecondariesOnPrimaryFailure) {
-                    if (log.isDebugEnabled())
-                        log.debug(DynamicSequentialTask.this+" cancelling "+secondaryJobsRemaining.size()+" remaining, due to error in primary task: "+selfException);
-                    synchronized (jobTransitionLock) {
-                        for (Task<?> t: secondaryJobsRemaining)
-                            t.cancel(true);
-                        // do this early to prevent additions; and note we notify very soon below, so not notify is help off until below
-                        primaryThread = null;
-                        primaryFinished = true;
-                    }
-                }
-            } finally {
-                try {
-                    if (log.isTraceEnabled()) log.trace("cleaning up for {}", this);
-                    synchronized (jobTransitionLock) {
-                        // semaphore might be nicer here (aled notes as it is this is a little hard to read)
-                        primaryThread = null;
-                        primaryFinished = true;
-                        jobTransitionLock.notifyAll();
-                    }
-                    if (!isCancelled() && !Thread.currentThread().isInterrupted()) {
-                        if (log.isTraceEnabled()) log.trace("waiting for secondaries for {}", this);
-                        // wait on tasks sequentially so that blocking information is more interesting
-                        DynamicTasks.waitForLast();
-                        List<Object> result2 = secondaryJobMaster.get();
-                        try {
-                            if (primaryJob==null) result = (T)result2;
-                        } catch (ClassCastException e) { /* ignore class cast exception; leave the result as null */ }
-                    }
-                } catch (Throwable childException) {
-                    Exceptions.propagateIfFatal(childException);
-                    if (error==null) {
-                        error = childException;
-                        errorIsFromChild = true;
-                    } else {
-                        if (log.isDebugEnabled()) log.debug("Parent task "+this+" ignoring child error ("+childException+") in presence of our own error ("+error+")");
-                    }
-                }
-            }
-            if (error!=null) {
-                handleException(error, errorIsFromChild);
-            }
-            if (uninterestingSelfError != null) {
-                handleException(uninterestingSelfError, false);
-            }
-            return result;
-        }
-        
-        @Override
-        public String toString() {
-            return "DstJob:"+DynamicSequentialTask.this.getId();
-        }
-
-        /** waits for this job to complete, or the given time to elapse */
-        public void join(boolean includePrimary, Duration optionalTimeout) throws InterruptedException {
-            CountdownTimer timeLeft = optionalTimeout!=null ? CountdownTimer.newInstanceStarted(optionalTimeout) : null;
-            while (true) {
-                Task<?> cs;
-                Duration remaining;
-                synchronized (jobTransitionLock) {
-                    cs = currentSecondary;
-                    if (finishedSecondaries) return;
-                    remaining = timeLeft==null ? Duration.ONE_SECOND : timeLeft.getDurationRemaining();
-                    if (!remaining.isPositive()) return;
-                    if (cs==null) {
-                        if (!includePrimary && secondaryJobsRemaining.isEmpty()) return;
-                        // parent still running, no children though
-                        Tasks.setBlockingTask(DynamicSequentialTask.this);
-                        jobTransitionLock.wait(remaining.toMilliseconds());
-                        Tasks.resetBlockingDetails();
-                    }
-                }
-                if (cs!=null) {
-                    Tasks.setBlockingTask(cs);
-                    cs.blockUntilEnded(remaining);
-                    Tasks.resetBlockingDetails();
-                }
-            }
-        }
-    }
-
-    @Override
-    public List<Task<?>> getQueue() {
-        return ImmutableList.copyOf(secondaryJobsAll);
-    }
-
-    public void handleException(Throwable throwable, boolean fromChild) throws Exception {
-        Exceptions.propagateIfFatal(throwable);
-        if (fromChild && !failureHandlingConfig.failParentOnSecondaryFailure) {
-            log.debug("Parent task "+this+" swallowing child error: "+throwable);
-            return;
-        }
-        handleException(throwable);
-    }
-    public void handleException(Throwable throwable) throws Exception { 
-        Exceptions.propagateIfFatal(throwable);
-        if (throwable instanceof Exception) {
-            // allow checked exceptions to be passed through
-            throw (Exception)throwable;
-        }
-        throw Exceptions.propagate(throwable);
-    }
-
-    @Override
-    public void drain(Duration optionalTimeout, boolean includePrimary, boolean throwFirstError) {
-        try {
-            dstJob.join(includePrimary, optionalTimeout);
-        } catch (InterruptedException e) {
-            throw Exceptions.propagate(e);
-        }
-        if (throwFirstError) {
-            if (isError()) 
-                getUnchecked();
-            for (Task<?> t: getQueue())
-                if (t.isError() && !TaskTags.isInessential(t))
-                    t.getUnchecked();
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/DynamicTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/DynamicTasks.java b/core/src/main/java/brooklyn/util/task/DynamicTasks.java
deleted file mode 100644
index 9d552c6..0000000
--- a/core/src/main/java/brooklyn/util/task/DynamicTasks.java
+++ /dev/null
@@ -1,337 +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 java.util.List;
-import java.util.concurrent.Callable;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskAdaptable;
-import org.apache.brooklyn.api.management.TaskFactory;
-import org.apache.brooklyn.api.management.TaskQueueingContext;
-import org.apache.brooklyn.api.management.TaskWrapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.time.Duration;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
-
-/** 
- * Contains static methods which detect and use the current {@link TaskQueueingContext} to execute tasks.
- * 
- * @since 0.6.0
- */
-@Beta
-public class DynamicTasks {
-
-    private static final Logger log = LoggerFactory.getLogger(DynamicTasks.class);
-    private static final ThreadLocal<TaskQueueingContext> taskQueueingContext = new ThreadLocal<TaskQueueingContext>();
-    
-    public static void setTaskQueueingContext(TaskQueueingContext newTaskQC) {
-        taskQueueingContext.set(newTaskQC);
-    }
-    
-    public static TaskQueueingContext getThreadTaskQueuingContext() {
-        return taskQueueingContext.get();
-    }
-    
-    public static TaskQueueingContext getTaskQueuingContext() {
-        TaskQueueingContext adder = getThreadTaskQueuingContext();
-        if (adder!=null) return adder;
-        Task<?> t = Tasks.current();
-        if (t instanceof TaskQueueingContext) return (TaskQueueingContext) t;
-        return null;
-    }
-
-    
-    public static void removeTaskQueueingContext() {
-        taskQueueingContext.remove();
-    }
-
-    public static class TaskQueueingResult<T> implements TaskWrapper<T> {
-        private final Task<T> task;
-        private final boolean wasQueued;
-        private ExecutionContext execContext = null;
-        
-        private TaskQueueingResult(TaskAdaptable<T> task, boolean wasQueued) {
-            this.task = task.asTask();
-            this.wasQueued = wasQueued;
-        }
-        @Override
-        public Task<T> asTask() {
-            return task;
-        }
-        @Override
-        public Task<T> getTask() {
-            return task;
-        }
-        /** returns true if the task was queued */
-        public boolean wasQueued() {
-            return wasQueued;
-        }
-        /** returns true if the task either is currently queued or has been submitted */
-        public boolean isQueuedOrSubmitted() {
-            return wasQueued || Tasks.isQueuedOrSubmitted(task);
-        }
-        /** specifies an execContext to use if the task has to be explicitly submitted;
-         * if omitted it will attempt to find one based on the current thread's context */
-        public TaskQueueingResult<T> executionContext(ExecutionContext execContext) {
-            this.execContext = execContext;
-            return this;
-        }
-        /** as {@link #executionContext(ExecutionContext)} but inferring from the entity */
-        public TaskQueueingResult<T> executionContext(Entity entity) {
-            this.execContext = ((EntityInternal)entity).getManagementSupport().getExecutionContext();
-            return this;
-        }
-        private boolean orSubmitInternal() {
-            if (!wasQueued()) {
-                if (isQueuedOrSubmitted()) {
-                    log.warn("Redundant call to execute "+getTask()+"; skipping");
-                    return false;
-                } else {
-                    ExecutionContext ec = execContext;
-                    if (ec==null)
-                        ec = BasicExecutionContext.getCurrentExecutionContext();
-                    if (ec==null)
-                        throw new IllegalStateException("Cannot execute "+getTask()+" without an execution context; ensure caller is in an ExecutionContext");
-                    ec.submit(getTask());
-                    return true;
-                }
-            } else {
-                return false;
-            }
-        }
-        /** causes the task to be submitted (asynchronously) if it hasn't already been,
-         * requiring an entity execution context (will try to find a default if not set) */
-        public TaskQueueingResult<T> orSubmitAsync() {
-            orSubmitInternal();
-            return this;
-        }
-        /** convenience for setting {@link #executionContext(ExecutionContext)} then submitting async */
-        public TaskQueueingResult<T> orSubmitAsync(Entity entity) {
-            executionContext(entity);
-            return orSubmitAsync();
-        }
-        /** causes the task to be submitted *synchronously* if it hasn't already been submitted;
-         * useful in contexts such as libraries where callers may be either on a legacy call path 
-         * (which assumes all commands complete immediately);
-         * requiring an entity execution context (will try to find a default if not set) */
-        public TaskQueueingResult<T> orSubmitAndBlock() {
-            if (orSubmitInternal()) task.getUnchecked();
-            return this;
-        }
-        /** convenience for setting {@link #executionContext(ExecutionContext)} then submitting blocking */
-        public TaskQueueingResult<T> orSubmitAndBlock(Entity entity) {
-            executionContext(entity);
-            return orSubmitAndBlock();
-        }
-        /** blocks for the task to be completed
-         * <p>
-         * needed in any context where subsequent commands assume the task has completed.
-         * not needed in a context where the task is simply being built up and queued.
-         * <p>
-         * throws if there are any errors
-         */
-        public T andWaitForSuccess() {
-            return task.getUnchecked();
-        }
-        public void orCancel() {
-            if (!wasQueued()) {
-                task.cancel(false);
-            }
-        }
-    }
-    
-    /**
-     * Tries to add the task to the current addition context if there is one, otherwise does nothing.
-     * <p/>
-     * Call {@link TaskQueueingResult#orSubmitAsync() orSubmitAsync()} on the returned
-     * {@link TaskQueueingResult TaskQueueingResult} to handle execution of tasks in a
-     * {@link BasicExecutionContext}.
-     */
-    public static <T> TaskQueueingResult<T> queueIfPossible(TaskAdaptable<T> task) {
-        TaskQueueingContext adder = getTaskQueuingContext();
-        boolean result = false;
-        if (adder!=null)
-            result = Tasks.tryQueueing(adder, task);
-        return new TaskQueueingResult<T>(task, result);
-    }
-
-    /** @see #queueIfPossible(TaskAdaptable) */
-    public static <T> TaskQueueingResult<T> queueIfPossible(TaskFactory<? extends TaskAdaptable<T>> task) {
-        return queueIfPossible(task.newTask());
-    }
-
-    /** adds the given task to the nearest task addition context,
-     * either set as a thread-local, or in the current task, or the submitter of the task, etc
-     * <p>
-     * throws if it cannot add */
-    public static <T> Task<T> queueInTaskHierarchy(Task<T> task) {
-        Preconditions.checkNotNull(task, "Task to queue cannot be null");
-        Preconditions.checkState(!Tasks.isQueuedOrSubmitted(task), "Task to queue must not yet be submitted: {}", task);
-        
-        TaskQueueingContext adder = getTaskQueuingContext();
-        if (adder!=null) { 
-            if (Tasks.tryQueueing(adder, task)) {
-                log.debug("Queued task {} at context {} (no hierarchy)", task, adder);
-                return task;
-            }
-        }
-        
-        Task<?> t = Tasks.current();
-        Preconditions.checkState(t!=null || adder!=null, "No task addition context available for queueing task "+task);
-        
-        while (t!=null) {
-            if (t instanceof TaskQueueingContext) {
-                if (Tasks.tryQueueing((TaskQueueingContext)t, task)) {
-                    log.debug("Queued task {} at hierarchical context {}", task, t);
-                    return task;
-                }
-            }
-            t = t.getSubmittedByTask();
-        }
-        
-        throw new IllegalStateException("No task addition context available in current task hierarchy for adding task "+task);
-    }
-
-    /**
-     * Queues the given task.
-     * <p/>
-     * This method is only valid within a dynamic task. Use {@link #queueIfPossible(TaskAdaptable)}
-     * and {@link TaskQueueingResult#orSubmitAsync()} if the calling context is a basic task.
-     *
-     * @param task The task to queue
-     * @throws IllegalStateException if no task queueing context is available
-     * @return The queued task
-     */
-    public static <V extends TaskAdaptable<?>> V queue(V task) {
-        try {
-            Preconditions.checkNotNull(task, "Task to queue cannot be null");
-            Preconditions.checkState(!Tasks.isQueued(task), "Task to queue must not yet be queued: %s", task);
-            TaskQueueingContext adder = getTaskQueuingContext();
-            if (adder==null) {
-                throw new IllegalStateException("Task "+task+" cannot be queued here; no queueing context available");
-            }
-            adder.queue(task.asTask());
-            return task;
-        } catch (Throwable e) {
-            log.warn("Error queueing "+task+" (rethrowing): "+e);
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    /** @see #queue(org.apache.brooklyn.api.management.TaskAdaptable)  */
-    public static void queue(TaskAdaptable<?> task1, TaskAdaptable<?> task2, TaskAdaptable<?> ...tasks) {
-        queue(task1);
-        queue(task2);
-        for (TaskAdaptable<?> task: tasks) queue(task);
-    }
-
-    /** @see #queue(org.apache.brooklyn.api.management.TaskAdaptable)  */
-    public static <T extends TaskAdaptable<?>> T queue(TaskFactory<T> taskFactory) {
-        return queue(taskFactory.newTask());
-    }
-
-    /** @see #queue(org.apache.brooklyn.api.management.TaskAdaptable)  */
-    public static void queue(TaskFactory<?> task1, TaskFactory<?> task2, TaskFactory<?> ...tasks) {
-        queue(task1.newTask());
-        queue(task2.newTask());
-        for (TaskFactory<?> task: tasks) queue(task.newTask());
-    }
-
-    /** @see #queue(org.apache.brooklyn.api.management.TaskAdaptable)  */
-    public static <T> Task<T> queue(String name, Callable<T> job) {
-        return DynamicTasks.queue(Tasks.<T>builder().name(name).body(job).build());
-    }
-
-    /** @see #queue(org.apache.brooklyn.api.management.TaskAdaptable)  */
-    public static <T> Task<T> queue(String name, Runnable job) {
-        return DynamicTasks.queue(Tasks.<T>builder().name(name).body(job).build());
-    }
-
-    /** queues the task if needed, i.e. if it is not yet submitted (so it will run), 
-     * or if it is submitted but not queued and we are in a queueing context (so it is available for informational purposes) */
-    public static <T extends TaskAdaptable<?>> T queueIfNeeded(T task) {
-        if (!Tasks.isQueued(task)) {
-            if (Tasks.isSubmitted(task) && getTaskQueuingContext()==null) {
-                // already submitted and not in a queueing context, don't try to queue
-            } else {
-                // needs submitting, put it in the queue
-                // (will throw an error if we are not a queueing context)
-                queue(task);
-            }
-        }
-        return task;
-    }
-    
-    /** submits/queues the given task if needed, and gets the result (unchecked) 
-     * only permitted in a queueing context (ie a DST main job) if the task is not yet submitted */
-    // things get really confusing if you try to queueInTaskHierarchy -- easy to cause deadlocks!
-    public static <T> T get(TaskAdaptable<T> t) {
-        return queueIfNeeded(t).asTask().getUnchecked();
-    }
-
-    /** As {@link #drain(Duration, boolean)} waiting forever and throwing the first error 
-     * (excluding errors in inessential tasks),
-     * then returning the last task in the queue (which is guaranteed to have finished without error,
-     * if this method returns without throwing) */
-    public static Task<?> waitForLast() {
-        drain(null, true);
-        // this call to last is safe, as the above guarantees everything will have run
-        // (on errors the above will throw so we won't come here)
-        List<Task<?>> q = DynamicTasks.getTaskQueuingContext().getQueue();
-        return q.isEmpty() ? null : Iterables.getLast(q);
-    }
-    
-    /** Calls {@link TaskQueueingContext#drain(Duration, boolean, boolean)} on the current task context */
-    public static TaskQueueingContext drain(Duration optionalTimeout, boolean throwFirstError) {
-        TaskQueueingContext qc = DynamicTasks.getTaskQueuingContext();
-        Preconditions.checkNotNull(qc, "Cannot drain when there is no queueing context");
-        qc.drain(optionalTimeout, false, throwFirstError);
-        return qc;
-    }
-
-    /** as {@link Tasks#swallowChildrenFailures()} but requiring a {@link TaskQueueingContext}. */
-    @Beta
-    public static void swallowChildrenFailures() {
-        Preconditions.checkNotNull(DynamicTasks.getTaskQueuingContext(), "Task queueing context required here");
-        Tasks.swallowChildrenFailures();
-    }
-
-    /** same as {@link Tasks#markInessential()}
-     * (but included here for convenience as it is often used in conjunction with {@link DynamicTasks}) */
-    public static void markInessential() {
-        Tasks.markInessential();
-    }
-
-    /** queues the task if possible, otherwise submits it asynchronously; returns the task for callers to 
-     * {@link Task#getUnchecked()} or {@link Task#blockUntilEnded()} */
-    public static <T> Task<T> submit(TaskAdaptable<T> task, Entity entity) {
-        return queueIfPossible(task).orSubmitAsync(entity).asTask();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ExecutionListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ExecutionListener.java b/core/src/main/java/brooklyn/util/task/ExecutionListener.java
deleted file mode 100644
index 7753588..0000000
--- a/core/src/main/java/brooklyn/util/task/ExecutionListener.java
+++ /dev/null
@@ -1,31 +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 org.apache.brooklyn.api.management.Task;
-
-public interface ExecutionListener {
-
-    /** invoked when a task completes: 
-     * {@link Task#getEndTimeUtc()} and {@link Task#isDone()} are guaranteed to be set,
-     * and {@link Task#get()} should return immediately for most Task implementations
-     * (care has been taken to avoid potential deadlocks here, waiting for a result!)  */
-    public void onTaskDone(Task<?> task);
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ExecutionUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ExecutionUtils.java b/core/src/main/java/brooklyn/util/task/ExecutionUtils.java
deleted file mode 100644
index 37c19d2..0000000
--- a/core/src/main/java/brooklyn/util/task/ExecutionUtils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task;
-
-import groovy.lang.Closure;
-
-import java.util.concurrent.Callable;
-
-import com.google.common.base.Function;
-import com.google.common.base.Throwables;
-
-public class ExecutionUtils {
-    /**
-     * Attempts to run/call the given object, with the given arguments if possible, preserving the return value if there is one (null otherwise);
-     * throws exception if the callable is a non-null object which cannot be invoked (not a callable or runnable)
-     * @deprecated since 0.7.0 ; this super-loose typing should be avoided; if it is needed, let's move it to one of the Groovy compatibility classes
-     */
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static Object invoke(Object callable, Object ...args) {
-        if (callable instanceof Closure) return ((Closure<?>)callable).call(args);
-        if (callable instanceof Callable) {
-            try {
-                return ((Callable<?>)callable).call();
-            } catch (Throwable t) {
-                throw Throwables.propagate(t);
-            }
-        }
-        if (callable instanceof Runnable) { ((Runnable)callable).run(); return null; }
-        if (callable instanceof Function && args.length == 1) { return ((Function)callable).apply(args[0]); }
-        if (callable==null) return null;
-        throw new IllegalArgumentException("Cannot invoke unexpected object "+callable+" of type "+callable.getClass()+", with "+args.length+" args");
-    }
-}


[19/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/XmlUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/XmlUtil.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/XmlUtil.java
new file mode 100644
index 0000000..637f078
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/XmlUtil.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import brooklyn.util.exceptions.Exceptions;
+
+public class XmlUtil {
+
+    public static Object xpath(String xml, String xpath) {
+        // TODO Could share factory/doc in thread-local storage; see http://stackoverflow.com/questions/9828254/is-documentbuilderfactory-thread-safe-in-java-5
+        try {
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            Document doc = builder.parse(new ByteArrayInputStream(xml.getBytes()));
+            XPathFactory xPathfactory = XPathFactory.newInstance();
+            XPathExpression expr = xPathfactory.newXPath().compile(xpath);
+            
+            return expr.evaluate(doc);
+            
+        } catch (ParserConfigurationException e) {
+            throw Exceptions.propagate(e);
+        } catch (SAXException e) {
+            throw Exceptions.propagate(e);
+        } catch (IOException e) {
+            throw Exceptions.propagate(e);
+        } catch (XPathExpressionException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/access/BrooklynAccessUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/access/BrooklynAccessUtils.java b/core/src/main/java/org/apache/brooklyn/location/access/BrooklynAccessUtils.java
index 9daf1c5..c3b6c61 100644
--- a/core/src/main/java/org/apache/brooklyn/location/access/BrooklynAccessUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/location/access/BrooklynAccessUtils.java
@@ -23,6 +23,10 @@ import java.util.Collection;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,10 +41,6 @@ import org.apache.brooklyn.location.basic.SupportsPortForwarding;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.net.Cidr;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ssh.SshTasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.Strings;
 
 import com.google.common.base.Supplier;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/access/PortForwardManagerLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/access/PortForwardManagerLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/access/PortForwardManagerLocationResolver.java
index db35a34..3e23f45 100644
--- a/core/src/main/java/org/apache/brooklyn/location/access/PortForwardManagerLocationResolver.java
+++ b/core/src/main/java/org/apache/brooklyn/location/access/PortForwardManagerLocationResolver.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.basic.LocationConfigUtils;
 import org.apache.brooklyn.location.basic.LocationInternal;
 import org.apache.brooklyn.location.basic.LocationPredicates;
@@ -30,8 +31,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.location.basic.AbstractLocationResolver;
 
-import brooklyn.util.config.ConfigBag;
-
 import com.google.common.base.Optional;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/AbstractLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/AbstractLocation.java b/core/src/main/java/org/apache/brooklyn/location/basic/AbstractLocation.java
index 554a781..0a96956 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/AbstractLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/AbstractLocation.java
@@ -38,6 +38,9 @@ import org.apache.brooklyn.api.mementos.LocationMemento;
 import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.internal.LocalLocationManager;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,9 +59,6 @@ import org.apache.brooklyn.location.geo.HasHostGeoInfo;
 import org.apache.brooklyn.location.geo.HostGeoInfo;
 
 import brooklyn.util.collections.SetFromLiveMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.stream.Streams;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/AbstractLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/AbstractLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/AbstractLocationResolver.java
index 26f895c..aea65ae 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/AbstractLocationResolver.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/AbstractLocationResolver.java
@@ -31,12 +31,12 @@ import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.location.LocationResolver;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.location.basic.AbstractLocationResolver.SpecParser.ParsedSpec;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.text.KeyValueParser;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/AggregatingMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/AggregatingMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/basic/AggregatingMachineProvisioningLocation.java
index c814209..ae1a534 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/AggregatingMachineProvisioningLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/AggregatingMachineProvisioningLocation.java
@@ -29,8 +29,8 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.stream.Streams;
 
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/BasicLocationRegistry.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/BasicLocationRegistry.java b/core/src/main/java/org/apache/brooklyn/location/basic/BasicLocationRegistry.java
index 3cb9f14..4f8637e 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/BasicLocationRegistry.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/BasicLocationRegistry.java
@@ -42,12 +42,12 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.catalog.CatalogPredicates;
 import org.apache.brooklyn.core.management.internal.LocalLocationManager;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.config.ConfigMap;
 import brooklyn.config.ConfigPredicates;
 import brooklyn.config.ConfigUtils;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.guava.Maybe.Absent;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/BasicMachineDetails.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/BasicMachineDetails.java b/core/src/main/java/org/apache/brooklyn/location/basic/BasicMachineDetails.java
index d6b51c6..9404af2 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/BasicMachineDetails.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/BasicMachineDetails.java
@@ -32,15 +32,15 @@ import org.apache.brooklyn.api.location.HardwareDetails;
 import org.apache.brooklyn.api.location.MachineDetails;
 import org.apache.brooklyn.api.location.OsDetails;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskTags;
+import org.apache.brooklyn.core.util.task.ssh.internal.PlainSshExecTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskTags;
-import brooklyn.util.task.ssh.internal.PlainSshExecTaskFactory;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/ByonLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/ByonLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/ByonLocationResolver.java
index 70735e5..c5680ba 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/ByonLocationResolver.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/ByonLocationResolver.java
@@ -29,6 +29,8 @@ import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.core.management.internal.LocalLocationManager;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,8 +44,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Sanitizer;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.net.UserAndHostAndPort;
 import brooklyn.util.text.WildcardGlobs;
 import brooklyn.util.text.WildcardGlobs.PhraseTreatment;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocation.java
index 7b9d73e..170867d 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocation.java
@@ -35,6 +35,8 @@ import org.apache.brooklyn.api.location.MachineLocationCustomizer;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.management.LocationManager;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,8 +58,6 @@ import org.apache.brooklyn.location.cloud.CloudLocationConfig;
 import brooklyn.util.collections.CollectionFunctionals;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.stream.Streams;
 import brooklyn.util.text.WildcardGlobs;
 import brooklyn.util.text.WildcardGlobs.PhraseTreatment;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/HostLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/HostLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/HostLocationResolver.java
index 8eb983c..d1eb838 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/HostLocationResolver.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/HostLocationResolver.java
@@ -23,9 +23,9 @@ import java.util.Map;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.basic.AbstractLocationResolver.SpecParser.ParsedSpec;
 
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.guava.Maybe.Absent;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostLocationResolver.java
index 859fa99..9e06f58 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostLocationResolver.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostLocationResolver.java
@@ -23,8 +23,7 @@ import java.util.Map;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.location.LocationResolver;
-
-import brooklyn.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 /**
  * Examples of valid specs:

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocation.java
index cfaf241..b87b85e 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocation.java
@@ -32,6 +32,11 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.OsDetails;
 import org.apache.brooklyn.api.location.PortRange;
+import org.apache.brooklyn.core.util.BrooklynNetworkUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.internal.ssh.process.ProcessTool;
+import org.apache.brooklyn.core.util.mutex.MutexSupport;
+import org.apache.brooklyn.core.util.mutex.WithMutexes;
 import org.apache.brooklyn.location.geo.HostGeoInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,12 +48,7 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.rebind.persister.FileBasedObjectStore;
 import brooklyn.entity.rebind.persister.LocationWithObjectStore;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-import brooklyn.util.BrooklynNetworkUtils;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.internal.ssh.process.ProcessTool;
-import brooklyn.util.mutex.MutexSupport;
-import brooklyn.util.mutex.WithMutexes;
 import brooklyn.util.net.Networking;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostPropertiesFromBrooklynProperties.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostPropertiesFromBrooklynProperties.java b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostPropertiesFromBrooklynProperties.java
index 1830ddd..7a6c8a9 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostPropertiesFromBrooklynProperties.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostPropertiesFromBrooklynProperties.java
@@ -20,11 +20,10 @@ package org.apache.brooklyn.location.basic;
 
 import java.util.Map;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.util.config.ConfigBag;
-
 import com.google.common.base.Strings;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/LocationConfigUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/LocationConfigUtils.java b/core/src/main/java/org/apache/brooklyn/location/basic/LocationConfigUtils.java
index d34a199..47784b1 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/LocationConfigUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/LocationConfigUtils.java
@@ -32,6 +32,10 @@ import java.util.Set;
 
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
+import org.apache.brooklyn.core.util.crypto.SecureKeys.PassphraseProblem;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,13 +44,9 @@ import brooklyn.entity.basic.ConfigKeys;
 
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.crypto.AuthorizedKeysParser;
-import brooklyn.util.crypto.SecureKeys;
-import brooklyn.util.crypto.SecureKeys.PassphraseProblem;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.StringFunctions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/LocationInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/LocationInternal.java b/core/src/main/java/org/apache/brooklyn/location/basic/LocationInternal.java
index de3e9a0..4a444c7 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/LocationInternal.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/LocationInternal.java
@@ -24,12 +24,12 @@ import org.apache.brooklyn.api.entity.rebind.RebindSupport;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.mementos.LocationMemento;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.basic.BrooklynObjectInternal;
 import brooklyn.config.ConfigInheritance;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.annotations.Beta;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/LocationPropertiesFromBrooklynProperties.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/LocationPropertiesFromBrooklynProperties.java b/core/src/main/java/org/apache/brooklyn/location/basic/LocationPropertiesFromBrooklynProperties.java
index e1feb18..2655fc9 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/LocationPropertiesFromBrooklynProperties.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/LocationPropertiesFromBrooklynProperties.java
@@ -23,6 +23,8 @@ import static com.google.common.base.Preconditions.checkArgument;
 import java.io.File;
 import java.util.Map;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -30,8 +32,6 @@ import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.ConfigUtils;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.os.Os;
 
 import com.google.common.base.Predicates;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/MultiLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/MultiLocation.java b/core/src/main/java/org/apache/brooklyn/location/basic/MultiLocation.java
index b702a92..5740a8f 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/MultiLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/MultiLocation.java
@@ -32,6 +32,7 @@ import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.location.cloud.AbstractAvailabilityZoneExtension;
 import org.apache.brooklyn.location.cloud.AvailabilityZoneExtension;
 
@@ -41,7 +42,6 @@ import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.CompoundRuntimeException;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.text.Strings;
 
 import com.google.common.base.Predicate;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/NamedLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/NamedLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/NamedLocationResolver.java
index a9f6499..d57b681 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/NamedLocationResolver.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/NamedLocationResolver.java
@@ -28,10 +28,10 @@ import org.apache.brooklyn.api.location.LocationDefinition;
 import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.location.LocationResolver;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.text.Strings;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/PortRanges.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/PortRanges.java b/core/src/main/java/org/apache/brooklyn/location/basic/PortRanges.java
index 2e1bdcc..92c3010 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/PortRanges.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/PortRanges.java
@@ -30,8 +30,7 @@ import java.util.NoSuchElementException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.brooklyn.api.location.PortRange;
-
-import brooklyn.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import com.google.common.base.Function;
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/SingleMachineLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/SingleMachineLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/SingleMachineLocationResolver.java
index fb5f676..3db980e 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/SingleMachineLocationResolver.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/SingleMachineLocationResolver.java
@@ -23,8 +23,8 @@ import java.util.Map;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.guava.Maybe.Absent;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/SingleMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/SingleMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/basic/SingleMachineProvisioningLocation.java
index 5f249d2..bac439f 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/SingleMachineProvisioningLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/SingleMachineProvisioningLocation.java
@@ -23,11 +23,10 @@ import java.util.Map;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.util.flags.SetFromFlag;
-
 import com.google.common.collect.ImmutableMap;
 
 public class SingleMachineProvisioningLocation<T extends MachineLocation> extends FixedListMachineProvisioningLocation<T> {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/basic/SshMachineLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/SshMachineLocation.java b/core/src/main/java/org/apache/brooklyn/location/basic/SshMachineLocation.java
index 0a8dd44..7c58194 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/SshMachineLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/SshMachineLocation.java
@@ -51,6 +51,22 @@ import org.apache.brooklyn.api.location.OsDetails;
 import org.apache.brooklyn.api.location.PortRange;
 import org.apache.brooklyn.api.location.PortSupplier;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
+import org.apache.brooklyn.core.util.file.ArchiveUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshException;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.internal.ssh.sshj.SshjTool;
+import org.apache.brooklyn.core.util.mutex.MutexSupport;
+import org.apache.brooklyn.core.util.mutex.WithMutexes;
+import org.apache.brooklyn.core.util.task.ScheduledTask;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.system.internal.ExecWithLoggingHelpers;
+import org.apache.brooklyn.core.util.task.system.internal.ExecWithLoggingHelpers.ExecRunner;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -87,22 +103,10 @@ import brooklyn.event.basic.MapConfigKey;
 
 import org.apache.brooklyn.location.access.PortForwardManager;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.crypto.SecureKeys;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.RuntimeInterruptedException;
-import brooklyn.util.file.ArchiveUtils;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.KeyTransformingLoadingCache.KeyTransformingSameTypeLoadingCache;
-import brooklyn.util.internal.ssh.ShellTool;
-import brooklyn.util.internal.ssh.SshException;
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.internal.ssh.sshj.SshjTool;
-import brooklyn.util.mutex.MutexSupport;
-import brooklyn.util.mutex.WithMutexes;
 import brooklyn.util.net.Urls;
 import brooklyn.util.pool.BasicPool;
 import brooklyn.util.pool.Pool;
@@ -110,10 +114,6 @@ import brooklyn.util.ssh.BashCommands;
 import brooklyn.util.stream.KnownSizeInputStream;
 import brooklyn.util.stream.ReaderInputStream;
 import brooklyn.util.stream.StreamGobbler;
-import brooklyn.util.task.ScheduledTask;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.system.internal.ExecWithLoggingHelpers;
-import brooklyn.util.task.system.internal.ExecWithLoggingHelpers.ExecRunner;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import groovy.lang.Closure;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/cloud/AbstractCloudMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/cloud/AbstractCloudMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/cloud/AbstractCloudMachineProvisioningLocation.java
index 437d49b..4b297f8 100644
--- a/core/src/main/java/org/apache/brooklyn/location/cloud/AbstractCloudMachineProvisioningLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/location/cloud/AbstractCloudMachineProvisioningLocation.java
@@ -24,11 +24,11 @@ import java.util.Map;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
 import org.apache.brooklyn.location.basic.AbstractLocation;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.internal.ssh.SshTool;
 
 public abstract class AbstractCloudMachineProvisioningLocation extends AbstractLocation
 implements MachineProvisioningLocation<MachineLocation>, CloudLocationConfig

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/cloud/CloudLocationConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/cloud/CloudLocationConfig.java b/core/src/main/java/org/apache/brooklyn/location/cloud/CloudLocationConfig.java
index a44ca49..ad6a11c 100644
--- a/core/src/main/java/org/apache/brooklyn/location/cloud/CloudLocationConfig.java
+++ b/core/src/main/java/org/apache/brooklyn/location/cloud/CloudLocationConfig.java
@@ -28,10 +28,9 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicConfigKey;
 
 import org.apache.brooklyn.api.location.MachineLocationCustomizer;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.location.basic.LocationConfigKeys;
 
-import brooklyn.util.flags.SetFromFlag;
-
 public interface CloudLocationConfig {
 
     public static final ConfigKey<String> CLOUD_ENDPOINT = LocationConfigKeys.CLOUD_ENDPOINT;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java b/core/src/main/java/org/apache/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java
index b06b88a..2df845e 100644
--- a/core/src/main/java/org/apache/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java
+++ b/core/src/main/java/org/apache/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java
@@ -20,9 +20,9 @@ package org.apache.brooklyn.location.cloud.names;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.trait.HasShortName;
-
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
-import brooklyn.util.config.ConfigBag;
+
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/cloud/names/BasicCloudMachineNamer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/cloud/names/BasicCloudMachineNamer.java b/core/src/main/java/org/apache/brooklyn/location/cloud/names/BasicCloudMachineNamer.java
index 818053d..26c41ee 100644
--- a/core/src/main/java/org/apache/brooklyn/location/cloud/names/BasicCloudMachineNamer.java
+++ b/core/src/main/java/org/apache/brooklyn/location/cloud/names/BasicCloudMachineNamer.java
@@ -20,9 +20,9 @@ package org.apache.brooklyn.location.cloud.names;
 
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
-
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
-import brooklyn.util.config.ConfigBag;
+
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.StringShortener;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/cloud/names/CloudMachineNamer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/cloud/names/CloudMachineNamer.java b/core/src/main/java/org/apache/brooklyn/location/cloud/names/CloudMachineNamer.java
index df7fd8b..b475436 100644
--- a/core/src/main/java/org/apache/brooklyn/location/cloud/names/CloudMachineNamer.java
+++ b/core/src/main/java/org/apache/brooklyn/location/cloud/names/CloudMachineNamer.java
@@ -20,10 +20,9 @@ package org.apache.brooklyn.location.cloud.names;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
 
-import brooklyn.util.config.ConfigBag;
-
 /**
  * Interface used to construct names for individual cloud machines and for groups of machines.
  * <p>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/cloud/names/CustomMachineNamer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/cloud/names/CustomMachineNamer.java b/core/src/main/java/org/apache/brooklyn/location/cloud/names/CustomMachineNamer.java
index 472adde..0bac6c8 100644
--- a/core/src/main/java/org/apache/brooklyn/location/cloud/names/CustomMachineNamer.java
+++ b/core/src/main/java/org/apache/brooklyn/location/cloud/names/CustomMachineNamer.java
@@ -21,14 +21,16 @@ package org.apache.brooklyn.location.cloud.names;
 import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
+
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
-import brooklyn.util.config.ConfigBag;
+
 import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/dynamic/DynamicLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/dynamic/DynamicLocation.java b/core/src/main/java/org/apache/brooklyn/location/dynamic/DynamicLocation.java
index 4c38b7e..5b6fd05 100644
--- a/core/src/main/java/org/apache/brooklyn/location/dynamic/DynamicLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/location/dynamic/DynamicLocation.java
@@ -20,12 +20,12 @@ package org.apache.brooklyn.location.dynamic;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import com.google.common.annotations.Beta;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * A location that is created and owned by an entity at runtime.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/dynamic/LocationOwner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/dynamic/LocationOwner.java b/core/src/main/java/org/apache/brooklyn/location/dynamic/LocationOwner.java
index b061b69..84348cb 100644
--- a/core/src/main/java/org/apache/brooklyn/location/dynamic/LocationOwner.java
+++ b/core/src/main/java/org/apache/brooklyn/location/dynamic/LocationOwner.java
@@ -24,12 +24,12 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.annotations.Beta;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/geo/HostGeoInfo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/geo/HostGeoInfo.java b/core/src/main/java/org/apache/brooklyn/location/geo/HostGeoInfo.java
index ad05bbd..c85060c 100644
--- a/core/src/main/java/org/apache/brooklyn/location/geo/HostGeoInfo.java
+++ b/core/src/main/java/org/apache/brooklyn/location/geo/HostGeoInfo.java
@@ -26,13 +26,13 @@ import javax.annotation.Nullable;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.AddressableLocation;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.apache.brooklyn.location.basic.AbstractLocation;
 import org.apache.brooklyn.location.basic.LocationConfigKeys;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.internal.BrooklynSystemProperties;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/location/geo/LocalhostExternalIpLoader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/geo/LocalhostExternalIpLoader.java b/core/src/main/java/org/apache/brooklyn/location/geo/LocalhostExternalIpLoader.java
index cd46bdd..8738817 100644
--- a/core/src/main/java/org/apache/brooklyn/location/geo/LocalhostExternalIpLoader.java
+++ b/core/src/main/java/org/apache/brooklyn/location/geo/LocalhostExternalIpLoader.java
@@ -26,10 +26,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.text.StringPredicates;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
index d5590fc..d6a9ed0 100644
--- a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
+++ b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
@@ -55,15 +55,15 @@ import org.apache.brooklyn.core.catalog.internal.CatalogDto;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.effector.AddChildrenEffector;
 import brooklyn.entity.effector.Effectors;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.stream.Streams;
 
 import com.google.common.base.Joiner;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/camp/lite/TestAppAssemblyInstantiator.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/camp/lite/TestAppAssemblyInstantiator.java b/core/src/test/java/brooklyn/camp/lite/TestAppAssemblyInstantiator.java
index 22d3ab8..2fa3415 100644
--- a/core/src/test/java/brooklyn/camp/lite/TestAppAssemblyInstantiator.java
+++ b/core/src/test/java/brooklyn/camp/lite/TestAppAssemblyInstantiator.java
@@ -32,13 +32,13 @@ import java.util.Set;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
 
 import brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator;
 import brooklyn.camp.brooklyn.api.HasBrooklynManagementContext;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 
 /** simple illustrative instantiator which always makes a {@link TestApplication}, populated with {@link TestEntity} children,
  * all setting {@link TestEntity#CONF_NAME} for the name in the plan and in the service specs

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/enricher/basic/BasicEnricherTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/enricher/basic/BasicEnricherTest.java b/core/src/test/java/brooklyn/enricher/basic/BasicEnricherTest.java
index 396c76a..7ee1177 100644
--- a/core/src/test/java/brooklyn/enricher/basic/BasicEnricherTest.java
+++ b/core/src/test/java/brooklyn/enricher/basic/BasicEnricherTest.java
@@ -25,6 +25,7 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.EnricherSpec;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestApplicationNoEnrichersImpl;
 import org.testng.Assert;
@@ -38,7 +39,6 @@ import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.BrooklynConfigKeys;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * Test that enricher can be created and accessed, by construction and by spec

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/EffectorSayHiTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/EffectorSayHiTest.java b/core/src/test/java/brooklyn/entity/EffectorSayHiTest.java
index 62579e3..80cfe2a 100644
--- a/core/src/test/java/brooklyn/entity/EffectorSayHiTest.java
+++ b/core/src/test/java/brooklyn/entity/EffectorSayHiTest.java
@@ -34,6 +34,7 @@ import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.management.ExecutionContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.task.BasicTask;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.BeforeMethod;
@@ -45,7 +46,6 @@ import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.MethodEffector;
 import brooklyn.entity.trait.Startable;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.task.BasicTask;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/SetFromFlagTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/SetFromFlagTest.java b/core/src/test/java/brooklyn/entity/SetFromFlagTest.java
index c5cfc0b..08d0428 100644
--- a/core/src/test/java/brooklyn/entity/SetFromFlagTest.java
+++ b/core/src/test/java/brooklyn/entity/SetFromFlagTest.java
@@ -24,6 +24,7 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.PortRange;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.AbstractEntity;
@@ -31,7 +32,6 @@ import brooklyn.entity.basic.AbstractEntity;
 import org.apache.brooklyn.location.basic.PortRanges;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
 
 public class SetFromFlagTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/basic/ConfigMapTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/ConfigMapTest.java b/core/src/test/java/brooklyn/entity/basic/ConfigMapTest.java
index a532d15..f67a193 100644
--- a/core/src/test/java/brooklyn/entity/basic/ConfigMapTest.java
+++ b/core/src/test/java/brooklyn/entity/basic/ConfigMapTest.java
@@ -35,6 +35,8 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.ExecutionManager;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicTask;
+import org.apache.brooklyn.core.util.task.DeferredSupplier;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -46,8 +48,6 @@ import brooklyn.config.ConfigPredicates;
 import brooklyn.entity.BrooklynAppUnitTestSupport;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.IntegerAttributeSensorAndConfigKey;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.task.BasicTask;
-import brooklyn.util.task.DeferredSupplier;
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/basic/DependentConfigurationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/DependentConfigurationTest.java b/core/src/test/java/brooklyn/entity/basic/DependentConfigurationTest.java
index 2919055..dd107aa 100644
--- a/core/src/test/java/brooklyn/entity/basic/DependentConfigurationTest.java
+++ b/core/src/test/java/brooklyn/entity/basic/DependentConfigurationTest.java
@@ -31,6 +31,7 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicTask;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
@@ -46,7 +47,6 @@ import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.javalang.JavaClassNames;
-import brooklyn.util.task.BasicTask;
 import brooklyn.util.text.StringPredicates;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/basic/EntityConfigTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/EntityConfigTest.java b/core/src/test/java/brooklyn/entity/basic/EntityConfigTest.java
index 4878e33..e41e3a3 100644
--- a/core/src/test/java/brooklyn/entity/basic/EntityConfigTest.java
+++ b/core/src/test/java/brooklyn/entity/basic/EntityConfigTest.java
@@ -22,13 +22,13 @@ import static org.testng.Assert.assertEquals;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.config.ConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java b/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
index 0bb8c2f..80ebfb7 100644
--- a/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
+++ b/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
@@ -27,6 +27,7 @@ import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityImpl;
 import org.apache.brooklyn.test.entity.TestEntityNoEnrichersImpl;
@@ -42,7 +43,6 @@ import org.apache.brooklyn.location.basic.SimulatedLocation;
 
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.test.Asserts;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy b/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy
index a52e7d5..86bdc18 100644
--- a/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy
+++ b/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy
@@ -39,7 +39,7 @@ import org.apache.brooklyn.test.entity.TestApplication
 import org.apache.brooklyn.test.entity.TestEntity
 import brooklyn.util.collections.MutableMap
 import brooklyn.util.exceptions.Exceptions
-import brooklyn.util.task.DeferredSupplier
+import org.apache.brooklyn.core.util.task.DeferredSupplier
 
 import com.google.common.collect.ImmutableList
 import com.google.common.collect.ImmutableSet

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/basic/SanitizerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/SanitizerTest.java b/core/src/test/java/brooklyn/entity/basic/SanitizerTest.java
index c9d40a5..ed07c75 100644
--- a/core/src/test/java/brooklyn/entity/basic/SanitizerTest.java
+++ b/core/src/test/java/brooklyn/entity/basic/SanitizerTest.java
@@ -22,10 +22,9 @@ import static org.testng.Assert.assertEquals;
 
 import java.util.Map;
 
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.testng.annotations.Test;
 
-import brooklyn.util.config.ConfigBag;
-
 import com.google.common.collect.ImmutableMap;
 
 public class SanitizerTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/effector/EffectorBasicTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/effector/EffectorBasicTest.java b/core/src/test/java/brooklyn/entity/effector/EffectorBasicTest.java
index fa963c3..f8f4eb1 100644
--- a/core/src/test/java/brooklyn/entity/effector/EffectorBasicTest.java
+++ b/core/src/test/java/brooklyn/entity/effector/EffectorBasicTest.java
@@ -26,6 +26,7 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.HasTaskChildren;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.test.TestUtils;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
@@ -43,7 +44,6 @@ import org.apache.brooklyn.location.basic.SimulatedLocation;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.collect.ImmutableList;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/effector/EffectorConcatenateTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/effector/EffectorConcatenateTest.java b/core/src/test/java/brooklyn/entity/effector/EffectorConcatenateTest.java
index 56382d8..63b10da 100644
--- a/core/src/test/java/brooklyn/entity/effector/EffectorConcatenateTest.java
+++ b/core/src/test/java/brooklyn/entity/effector/EffectorConcatenateTest.java
@@ -30,6 +30,8 @@ import java.util.concurrent.atomic.AtomicReference;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.ExecutionManager;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestApplicationImpl;
 import org.slf4j.Logger;
@@ -45,8 +47,6 @@ import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.MethodEffector;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.task.BasicExecutionContext;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java b/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
index 38dd2b8..0f6f041 100644
--- a/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
+++ b/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
@@ -26,6 +26,11 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.HasTaskChildren;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicSequentialTask;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -38,12 +43,7 @@ import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.effector.EffectorTasks.EffectorTaskFactory;
 import brooklyn.entity.trait.Startable;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.DynamicSequentialTask;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/Dumpers.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/Dumpers.java b/core/src/test/java/brooklyn/entity/rebind/Dumpers.java
index 12e47ad..934df5c 100644
--- a/core/src/test/java/brooklyn/entity/rebind/Dumpers.java
+++ b/core/src/test/java/brooklyn/entity/rebind/Dumpers.java
@@ -31,11 +31,11 @@ import java.util.Random;
 
 import javax.annotation.Nullable;
 
+import org.apache.brooklyn.core.util.flags.FlagUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.FlagUtils;
 import brooklyn.util.javalang.Serializers;
 import brooklyn.util.javalang.Serializers.ObjectReplacer;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
index 3debe30..dc440bc 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
@@ -45,10 +45,9 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
 import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.javalang.UrlClassLoader;
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 
-import brooklyn.util.javalang.UrlClassLoader;
-
 import com.google.common.base.Function;
 
 public class RebindCatalogEntityTest extends RebindTestFixture<StartableApplication> {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/RebindEnricherTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindEnricherTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindEnricherTest.java
index 3102dd2..ea21787 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindEnricherTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindEnricherTest.java
@@ -32,6 +32,7 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.EnricherSpec;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -55,7 +56,6 @@ import org.apache.brooklyn.location.basic.SimulatedLocation;
 
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.StringFunctions;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/RebindEntityDynamicTypeInfoTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindEntityDynamicTypeInfoTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindEntityDynamicTypeInfoTest.java
index d7ad132..883b1aa 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindEntityDynamicTypeInfoTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindEntityDynamicTypeInfoTest.java
@@ -27,6 +27,7 @@ import java.util.concurrent.ExecutionException;
 
 import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
@@ -38,7 +39,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.effector.EffectorBody;
 import brooklyn.entity.effector.Effectors;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.stream.Streams;
 
 public class RebindEntityDynamicTypeInfoTest extends RebindTestFixtureWithApp {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/RebindEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindEntityTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindEntityTest.java
index 992ca8e..3ece27b 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindEntityTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindEntityTest.java
@@ -53,6 +53,7 @@ import org.apache.brooklyn.api.management.ha.ManagementNodeState;
 import org.apache.brooklyn.api.mementos.BrooklynMementoManifest;
 import org.apache.brooklyn.api.mementos.EntityMemento;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityImpl;
@@ -79,7 +80,6 @@ import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.RuntimeInterruptedException;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.time.Durations;
 
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
index ba168cf..674d77e 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
@@ -40,6 +40,7 @@ import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -54,7 +55,6 @@ import brooklyn.entity.rebind.RebindEntityTest.MyEntity;
 import brooklyn.entity.rebind.RebindEntityTest.MyEntityImpl;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.os.Os;
 
 import com.google.common.base.Charsets;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/RebindFeedTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindFeedTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindFeedTest.java
index c368a7a..d0d03d6 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindFeedTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindFeedTest.java
@@ -29,6 +29,8 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.core.management.internal.BrooklynGarbageCollector;
+import org.apache.brooklyn.core.util.http.BetterMockWebServer;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityImpl.TestEntityWithoutEnrichers;
@@ -57,8 +59,6 @@ import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableList;
-import brooklyn.util.http.BetterMockWebServer;
-import brooklyn.util.task.BasicExecutionManager;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/RebindFeedWithHaTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindFeedWithHaTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindFeedWithHaTest.java
index 868665f..c42090b 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindFeedWithHaTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindFeedWithHaTest.java
@@ -31,6 +31,8 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.http.BetterMockWebServer;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -41,9 +43,7 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import brooklyn.util.http.BetterMockWebServer;
 import brooklyn.util.repeat.Repeater;
-import brooklyn.util.task.BasicExecutionManager;
 import brooklyn.util.time.Duration;
 
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/RebindLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindLocationTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindLocationTest.java
index 8c93fc7..7e54ac4 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindLocationTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindLocationTest.java
@@ -33,6 +33,7 @@ import org.apache.brooklyn.api.entity.rebind.RebindSupport;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.mementos.LocationMemento;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
@@ -46,7 +47,6 @@ import org.apache.brooklyn.location.basic.AbstractLocation;
 
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/RebindManagerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindManagerTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindManagerTest.java
index 676d040..c1312e5 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindManagerTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindManagerTest.java
@@ -25,13 +25,12 @@ import java.util.concurrent.Callable;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicTask;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityImpl;
 import org.testng.annotations.Test;
 
-import brooklyn.util.task.BasicTask;
-import brooklyn.util.task.DynamicTasks;
-
 import com.google.common.base.Predicates;
 import com.google.common.collect.Iterables;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/RebindPolicyTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindPolicyTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindPolicyTest.java
index f77e3d4..d74a6fc 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindPolicyTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindPolicyTest.java
@@ -33,6 +33,7 @@ import org.apache.brooklyn.api.mementos.BrooklynMementoManifest;
 import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.testng.annotations.Test;
@@ -50,7 +51,6 @@ import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableSet;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
index 5c39216..966ecea 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
@@ -43,6 +43,7 @@ import org.apache.brooklyn.api.mementos.BrooklynMementoManifest;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
 
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityFunctions;
@@ -53,7 +54,6 @@ import brooklyn.entity.rebind.persister.PersistMode;
 import brooklyn.entity.trait.Startable;
 import brooklyn.util.os.Os;
 import brooklyn.util.repeat.Repeater;
-import brooklyn.util.task.BasicExecutionManager;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/rebind/transformer/impl/XsltTransformerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/transformer/impl/XsltTransformerTest.java b/core/src/test/java/brooklyn/entity/rebind/transformer/impl/XsltTransformerTest.java
index 5791dd4..cdd89a5 100644
--- a/core/src/test/java/brooklyn/entity/rebind/transformer/impl/XsltTransformerTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/transformer/impl/XsltTransformerTest.java
@@ -20,11 +20,11 @@ package brooklyn.entity.rebind.transformer.impl;
 
 import static org.testng.Assert.assertEquals;
 
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.testng.annotations.Test;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.os.Os;
-import brooklyn.util.text.TemplateProcessor;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/trait/FailingEntity.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/trait/FailingEntity.java b/core/src/test/java/brooklyn/entity/trait/FailingEntity.java
index 4d06fc4..4b8c85e 100644
--- a/core/src/test/java/brooklyn/entity/trait/FailingEntity.java
+++ b/core/src/test/java/brooklyn/entity/trait/FailingEntity.java
@@ -22,11 +22,11 @@ import java.util.List;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.test.entity.TestEntity;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.base.Function;
 import com.google.common.base.Functions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/trait/FailingEntityImpl.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/trait/FailingEntityImpl.java b/core/src/test/java/brooklyn/entity/trait/FailingEntityImpl.java
index 4d1d9f9..e4a5d77 100644
--- a/core/src/test/java/brooklyn/entity/trait/FailingEntityImpl.java
+++ b/core/src/test/java/brooklyn/entity/trait/FailingEntityImpl.java
@@ -22,12 +22,12 @@ import java.util.Collection;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.test.entity.TestEntityImpl;
 import org.testng.Assert;
 
 import brooklyn.entity.basic.Entities;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.Tasks;
 
 public class FailingEntityImpl extends TestEntityImpl implements FailingEntity {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/entity/trait/StartableMethodsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/trait/StartableMethodsTest.java b/core/src/test/java/brooklyn/entity/trait/StartableMethodsTest.java
index 1a3537d..f457b0d 100644
--- a/core/src/test/java/brooklyn/entity/trait/StartableMethodsTest.java
+++ b/core/src/test/java/brooklyn/entity/trait/StartableMethodsTest.java
@@ -23,6 +23,7 @@ import static org.testng.Assert.fail;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -30,8 +31,8 @@ import org.testng.annotations.Test;
 import brooklyn.entity.BrooklynAppUnitTestSupport;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.trait.FailingEntity.RecordingEventListener;
+
 import org.apache.brooklyn.location.basic.SimulatedLocation;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.collect.ImmutableList;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/event/feed/PollerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/event/feed/PollerTest.java b/core/src/test/java/brooklyn/event/feed/PollerTest.java
index fc799e0..1427c93 100644
--- a/core/src/test/java/brooklyn/event/feed/PollerTest.java
+++ b/core/src/test/java/brooklyn/event/feed/PollerTest.java
@@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,7 +36,6 @@ import org.testng.annotations.Test;
 import brooklyn.entity.BrooklynAppUnitTestSupport;
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.task.DynamicTasks;
 import brooklyn.util.time.Duration;
 
 public class PollerTest extends BrooklynAppUnitTestSupport {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/event/feed/http/HttpFeedTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/event/feed/http/HttpFeedTest.java b/core/src/test/java/brooklyn/event/feed/http/HttpFeedTest.java
index aa16b8c..e367bdf 100644
--- a/core/src/test/java/brooklyn/event/feed/http/HttpFeedTest.java
+++ b/core/src/test/java/brooklyn/event/feed/http/HttpFeedTest.java
@@ -29,6 +29,8 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.http.BetterMockWebServer;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,8 +51,6 @@ import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.guava.Functionals;
-import brooklyn.util.http.BetterMockWebServer;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.net.Networking;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/event/feed/http/HttpValueFunctionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/event/feed/http/HttpValueFunctionsTest.java b/core/src/test/java/brooklyn/event/feed/http/HttpValueFunctionsTest.java
index 7769427..2f83cc5 100644
--- a/core/src/test/java/brooklyn/event/feed/http/HttpValueFunctionsTest.java
+++ b/core/src/test/java/brooklyn/event/feed/http/HttpValueFunctionsTest.java
@@ -25,11 +25,10 @@ import static org.testng.Assert.assertTrue;
 
 import java.util.NoSuchElementException;
 
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import brooklyn.util.http.HttpToolResponse;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.gson.JsonElement;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/policy/basic/BasicPolicyTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/policy/basic/BasicPolicyTest.java b/core/src/test/java/brooklyn/policy/basic/BasicPolicyTest.java
index df779e1..5ac58c9 100644
--- a/core/src/test/java/brooklyn/policy/basic/BasicPolicyTest.java
+++ b/core/src/test/java/brooklyn/policy/basic/BasicPolicyTest.java
@@ -23,13 +23,13 @@ import static org.testng.Assert.assertEquals;
 import java.util.Map;
 
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.testng.annotations.Test;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.BrooklynAppUnitTestSupport;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * Test that policy can be created and accessed, by construction and by spec

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java b/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java
index 85eaf3d..790bfe1 100644
--- a/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java
+++ b/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java
@@ -29,6 +29,8 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.core.management.internal.AbstractManagementContext;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.TaskScheduler;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -46,8 +48,6 @@ import brooklyn.internal.storage.impl.BrooklynStorageImpl;
 
 import org.apache.brooklyn.location.basic.SimulatedLocation;
 
-import brooklyn.util.task.BasicExecutionManager;
-import brooklyn.util.task.TaskScheduler;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/qa/performance/FilePersistencePerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/qa/performance/FilePersistencePerformanceTest.java b/core/src/test/java/brooklyn/qa/performance/FilePersistencePerformanceTest.java
index 098648d..0b35670 100644
--- a/core/src/test/java/brooklyn/qa/performance/FilePersistencePerformanceTest.java
+++ b/core/src/test/java/brooklyn/qa/performance/FilePersistencePerformanceTest.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.brooklyn.core.util.internal.ssh.process.ProcessTool;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -31,7 +32,6 @@ import brooklyn.entity.rebind.persister.FileBasedStoreObjectAccessor;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.internal.ssh.process.ProcessTool;
 
 import com.google.common.base.Charsets;
 import com.google.common.collect.ImmutableList;



[23/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/BasicTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/BasicTask.java b/core/src/main/java/org/apache/brooklyn/core/util/task/BasicTask.java
new file mode 100644
index 0000000..c776e4d
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/BasicTask.java
@@ -0,0 +1,892 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import static brooklyn.util.JavaGroovyEquivalents.asString;
+import static brooklyn.util.JavaGroovyEquivalents.elvisString;
+import groovy.lang.Closure;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.management.LockInfo;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.brooklyn.api.management.HasTaskChildren;
+import org.apache.brooklyn.api.management.Task;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.GroovyJavaMethods;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.text.Identifiers;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Callables;
+import com.google.common.util.concurrent.ExecutionList;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * The basic concrete implementation of a {@link Task} to be executed.
+ *
+ * A {@link Task} is a wrapper for an executable unit, such as a {@link Closure} or a {@link Runnable} or
+ * {@link Callable} and will run in its own {@link Thread}.
+ * <p>
+ * The task can be given an optional displayName and description in its constructor (as named
+ * arguments in the first {@link Map} parameter). It is guaranteed to have {@link Object#notify()} called
+ * once whenever the task starts running and once again when the task is about to complete. Due to
+ * the way executors work it is ugly to guarantee notification <em>after</em> completion, so instead we
+ * notify just before then expect the user to call {@link #get()} - which will throw errors if the underlying job
+ * did so - or {@link #blockUntilEnded()} which will not throw errors.
+ */
+public class BasicTask<T> implements TaskInternal<T> {
+    private static final Logger log = LoggerFactory.getLogger(BasicTask.class);
+
+    private String id = Identifiers.makeRandomId(8);
+    protected Callable<T> job;
+    public final String displayName;
+    public final String description;
+
+    protected final Set<Object> tags = Sets.newConcurrentHashSet();
+    // for debugging, to record where tasks were created
+//    { tags.add(new Throwable("Creation stack trace")); }
+    
+    protected Task<?> proxyTargetTask = null;
+
+    protected String blockingDetails = null;
+    protected Task<?> blockingTask = null;
+    Object extraStatusText = null;
+
+    /** listeners attached at task level; these are stored here, but run on the underlying ListenableFuture */
+    protected final ExecutionList listeners = new ExecutionList();
+    
+    /**
+     * Constructor needed to prevent confusion in groovy stubs when looking for default constructor,
+     *
+     * The generics on {@link Closure} break it if that is first constructor.
+     */
+    protected BasicTask() { this(Collections.emptyMap()); }
+    protected BasicTask(Map<?,?> flags) { this(flags, (Callable<T>) null); }
+
+    public BasicTask(Callable<T> job) { this(Collections.emptyMap(), job); }
+    
+    public BasicTask(Map<?,?> flags, Callable<T> job) {
+        this.job = job;
+
+        if (flags.containsKey("tag")) tags.add(flags.remove("tag"));
+        Object ftags = flags.remove("tags");
+        if (ftags!=null) {
+            if (ftags instanceof Iterable) Iterables.addAll(tags, (Iterable<?>)ftags);
+            else {
+                log.info("deprecated use of non-collection argument for 'tags' ("+ftags+") in "+this, new Throwable("trace of discouraged use of non-colleciton tags argument"));
+                tags.add(ftags);
+            }
+        }
+
+        description = elvisString(flags.remove("description"), "");
+        String d = asString(flags.remove("displayName"));
+        displayName = (d==null ? "" : d);
+    }
+
+    public BasicTask(Runnable job) { this(GroovyJavaMethods.<T>callableFromRunnable(job)); }
+    public BasicTask(Map<?,?> flags, Runnable job) { this(flags, GroovyJavaMethods.<T>callableFromRunnable(job)); }
+    public BasicTask(Closure<T> job) { this(GroovyJavaMethods.callableFromClosure(job)); }
+    public BasicTask(Map<?,?> flags, Closure<T> job) { this(flags, GroovyJavaMethods.callableFromClosure(job)); }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(id);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Task)
+            return ((Task<?>)obj).getId().equals(getId());
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        // give display name plus id, or job and tags plus id; some jobs have been extended to include nice tostrings 
+        return "Task["+
+            (Strings.isNonEmpty(displayName) ? 
+                displayName : 
+                (job + (tags!=null && !tags.isEmpty() ? ";"+tags : "")) ) +
+            ":"+getId()+"]";
+    }
+
+    @Override
+    public Task<T> asTask() {
+        return this;
+    }
+    
+    // housekeeping --------------------
+
+    /*
+     * These flags are set by BasicExecutionManager.submit.
+     *
+     * Order is guaranteed to be as shown below, in order of #. Within each # line it is currently in the order specified by commas but this is not guaranteed.
+     * (The spaces between the # section indicate longer delays / logical separation ... it should be clear!)
+     *
+     * # submitter, submit time set, tags and other submit-time fields set
+     *
+     * # thread set, ThreadLocal getCurrentTask set
+     * # start time set, isBegun is true
+     * # task end callback run, if supplied
+     *
+     * # task runs
+     *
+     * # task end callback run, if supplied
+     * # end time set
+     * # thread cleared, ThreadLocal getCurrentTask set
+     * # Task.notifyAll()
+     * # Task.get() (result.get()) available, Task.isDone is true
+     *
+     * Few _consumers_ should care, but internally we rely on this so that, for example, status is displayed correctly.
+     * Tests should catch most things, but be careful if you change any of the above semantics.
+     */
+
+    protected long queuedTimeUtc = -1;
+    protected long submitTimeUtc = -1;
+    protected long startTimeUtc = -1;
+    protected long endTimeUtc = -1;
+    protected Maybe<Task<?>> submittedByTask;
+
+    protected volatile Thread thread = null;
+    private volatile boolean cancelled = false;
+    /** normally a {@link ListenableFuture}, except for scheduled tasks when it may be a {@link ScheduledFuture} */
+    protected volatile Future<T> internalFuture = null;
+    
+    @Override
+    public synchronized void initInternalFuture(ListenableFuture<T> result) {
+        if (this.internalFuture != null) 
+            throw new IllegalStateException("task "+this+" is being given a result twice");
+        this.internalFuture = result;
+        notifyAll();
+    }
+
+    // metadata accessors ------------
+
+    @Override
+    public Set<Object> getTags() { return Collections.unmodifiableSet(new LinkedHashSet<Object>(tags)); }
+    
+    /** if the job is queued for submission (e.g. by another task) it can indicate that fact (and time) here;
+     * note tasks can (and often are) submitted without any queueing, in which case this value may be -1 */
+    @Override
+    public long getQueuedTimeUtc() { return queuedTimeUtc; }
+    
+    @Override
+    public long getSubmitTimeUtc() { return submitTimeUtc; }
+    
+    @Override
+    public long getStartTimeUtc() { return startTimeUtc; }
+    
+    @Override
+    public long getEndTimeUtc() { return endTimeUtc; }
+
+    @Override
+    public Future<T> getInternalFuture() { return internalFuture; }
+    
+    @Override
+    public Task<?> getSubmittedByTask() { 
+        if (submittedByTask==null) return null;
+        return submittedByTask.orNull(); 
+    }
+
+    /** the thread where the task is running, if it is running */
+    @Override
+    public Thread getThread() { return thread; }
+
+    // basic fields --------------------
+
+    @Override
+    public boolean isQueued() {
+        return (queuedTimeUtc >= 0);
+    }
+
+    @Override
+    public boolean isQueuedOrSubmitted() {
+        return isQueued() || isSubmitted();
+    }
+
+    @Override
+    public boolean isQueuedAndNotSubmitted() {
+        return isQueued() && (!isSubmitted());
+    }
+
+    @Override
+    public boolean isSubmitted() {
+        return submitTimeUtc >= 0;
+    }
+
+    @Override
+    public boolean isBegun() {
+        return startTimeUtc >= 0;
+    }
+
+    /** marks the task as queued for execution */
+    @Override
+    public void markQueued() {
+        if (queuedTimeUtc<0)
+            queuedTimeUtc = System.currentTimeMillis();
+    }
+
+    @Override
+    public final synchronized boolean cancel() { return cancel(true); }
+
+    /** doesn't resume it, just means if something was cancelled but not submitted it could now be submitted;
+     * probably going to be removed and perhaps some mechanism for running again made available
+     * @since 0.7.0  */
+    @Beta
+    public synchronized boolean uncancel() {
+        boolean wasCancelled = cancelled;
+        cancelled = false; 
+        return wasCancelled;
+    }
+    
+    @Override
+    public synchronized boolean cancel(boolean mayInterruptIfRunning) {
+        if (isDone()) return false;
+        boolean cancel = true;
+        cancelled = true;
+        if (internalFuture!=null) { 
+            cancel = internalFuture.cancel(mayInterruptIfRunning);
+        }
+        notifyAll();
+        return cancel;
+    }
+
+    @Override
+    public boolean isCancelled() {
+        return cancelled || (internalFuture!=null && internalFuture.isCancelled());
+    }
+
+    @Override
+    public boolean isDone() {
+        // if endTime is set, result might not be completed yet, but it will be set very soon 
+        // (the two values are set close in time, result right after the endTime;
+        // but callback hooks might not see the result yet)
+        return cancelled || (internalFuture!=null && internalFuture.isDone()) || endTimeUtc>0;
+    }
+
+    /**
+     * Returns true if the task has had an error.
+     *
+     * Only true if calling {@link #get()} will throw an exception when it completes (including cancel).
+     * Implementations may set this true before completion if they have that insight, or
+     * (the default) they may compute it lazily after completion (returning false before completion).
+     */
+    @Override
+    public boolean isError() {
+        if (!isDone()) return false;
+        if (isCancelled()) return true;
+        try {
+            get();
+            return false;
+        } catch (Throwable t) {
+            return true;
+        }
+    }
+
+    // future value --------------------
+
+    @Override
+    public T get() throws InterruptedException, ExecutionException {
+        try {
+            if (!isDone())
+                Tasks.setBlockingTask(this);
+            blockUntilStarted();
+            return internalFuture.get();
+        } finally {
+            Tasks.resetBlockingTask();
+        }
+    }
+
+    @Override
+    public T getUnchecked() {
+        try {
+            return get();
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+    @Override
+    public synchronized void blockUntilStarted() {
+        blockUntilStarted(null);
+    }
+    
+    @Override
+    public synchronized boolean blockUntilStarted(Duration timeout) {
+        Long endTime = timeout==null ? null : System.currentTimeMillis() + timeout.toMillisecondsRoundingUp();
+        while (true) {
+            if (cancelled) throw new CancellationException();
+            if (internalFuture==null)
+                try {
+                    if (timeout==null) {
+                        wait();
+                    } else {
+                        long remaining = endTime - System.currentTimeMillis();
+                        if (remaining>0)
+                            wait(remaining);
+                        else
+                            return false;
+                    }
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    Throwables.propagate(e);
+                }
+            if (internalFuture!=null) return true;
+        }
+    }
+
+    @Override
+    public void blockUntilEnded() {
+        blockUntilEnded(null);
+    }
+    
+    @Override
+    public boolean blockUntilEnded(Duration timeout) {
+        Long endTime = timeout==null ? null : System.currentTimeMillis() + timeout.toMillisecondsRoundingUp();
+        try { 
+            boolean started = blockUntilStarted(timeout);
+            if (!started) return false;
+            if (timeout==null) {
+                internalFuture.get();
+            } else {
+                long remaining = endTime - System.currentTimeMillis();
+                if (remaining>0)
+                    internalFuture.get(remaining, TimeUnit.MILLISECONDS);
+            }
+            return isDone();
+        } catch (Throwable t) {
+            Exceptions.propagateIfFatal(t);
+            if (!(t instanceof TimeoutException) && log.isDebugEnabled())
+                log.debug("call from "+Thread.currentThread()+", blocking until '"+this+"' finishes, ended with error: "+t);
+            return isDone(); 
+        }
+    }
+
+    @Override
+    public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+        return get(new Duration(timeout, unit));
+    }
+    
+    @Override
+    public T get(Duration duration) throws InterruptedException, ExecutionException, TimeoutException {
+        long start = System.currentTimeMillis();
+        Long end  = duration==null ? null : start + duration.toMillisecondsRoundingUp();
+        while (end==null || end > System.currentTimeMillis()) {
+            if (cancelled) throw new CancellationException();
+            if (internalFuture == null) {
+                synchronized (this) {
+                    long remaining = end - System.currentTimeMillis();
+                    if (internalFuture==null && remaining>0)
+                        wait(remaining);
+                }
+            }
+            if (internalFuture != null) break;
+        }
+        Long remaining = end==null ? null : end -  System.currentTimeMillis();
+        if (isDone()) {
+            return internalFuture.get(1, TimeUnit.MILLISECONDS);
+        } else if (remaining == null) {
+            return internalFuture.get();
+        } else if (remaining > 0) {
+            return internalFuture.get(remaining, TimeUnit.MILLISECONDS);
+        } else {
+            throw new TimeoutException();
+        }
+    }
+
+    @Override
+    public T getUnchecked(Duration duration) {
+        try {
+            return get(duration);
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+    // ------------------ status ---------------------------
+    
+    /**
+     * Returns a brief status string
+     *
+     * Plain-text format. Reported status if there is one, otherwise state which will be one of:
+     * <ul>
+     * <li>Not submitted
+     * <li>Submitted for execution
+     * <li>Ended by error
+     * <li>Ended by cancellation
+     * <li>Ended normally
+     * <li>Running
+     * <li>Waiting
+     * </ul>
+     */
+    @Override
+    public String getStatusSummary() {
+        return getStatusString(0);
+    }
+
+    /**
+     * Returns detailed status, suitable for a hover
+     *
+     * Plain-text format, with new-lines (and sometimes extra info) if multiline enabled.
+     */
+    @Override
+    public String getStatusDetail(boolean multiline) {
+        return getStatusString(multiline?2:1);
+    }
+
+    /**
+     * This method is useful for callers to see the status of a task.
+     *
+     * Also for developers to see best practices for examining status fields etc
+     *
+     * @param verbosity 0 = brief, 1 = one-line with some detail, 2 = lots of detail
+     */
+    protected String getStatusString(int verbosity) {
+//        Thread t = getThread();
+        String rv;
+        if (submitTimeUtc <= 0) rv = "Not submitted";
+        else if (!isCancelled() && startTimeUtc <= 0) {
+            rv = "Submitted for execution";
+            if (verbosity>0) {
+                long elapsed = System.currentTimeMillis() - submitTimeUtc;
+                rv += " "+Time.makeTimeStringRoundedSince(elapsed)+" ago";
+            }
+            if (verbosity >= 2 && getExtraStatusText()!=null) {
+                rv += "\n\n"+getExtraStatusText();
+            }
+        } else if (isDone()) {
+            long elapsed = endTimeUtc - submitTimeUtc;
+            String duration = Time.makeTimeStringRounded(elapsed);
+            if (isCancelled()) {
+                rv = "Cancelled";
+                if (verbosity >= 1) rv+=" after "+duration;
+                
+                if (verbosity >= 2 && getExtraStatusText()!=null) {
+                    rv += "\n\n"+getExtraStatusText();
+                }
+            } else if (isError()) {
+                rv = "Failed";
+                if (verbosity >= 1) {
+                    rv += " after "+duration;
+                    Throwable error = Tasks.getError(this);
+
+                    if (verbosity >= 2 && getExtraStatusText()!=null) {
+                        rv += "\n\n"+getExtraStatusText();
+                    }
+                    
+                    //remove outer ExecException which is reported by the get(), we want the exception the task threw
+                    while (error instanceof ExecutionException) error = error.getCause();
+                    String errorMessage = Exceptions.collapseText(error);
+
+                    if (verbosity == 1) rv += ": "+abbreviate(errorMessage);
+                    if (verbosity >= 2) {
+                        rv += ": "+errorMessage;
+                        StringWriter sw = new StringWriter();
+                        ((Throwable)error).printStackTrace(new PrintWriter(sw));
+                        rv += "\n\n"+sw.getBuffer();
+                    }
+                }
+            } else {
+                rv = "Completed";
+                if (verbosity>=1) {
+                    if (verbosity==1) {
+                        try {
+                            Object v = get();
+                            rv += ", " +(v==null ? "no return value (null)" : "result: "+abbreviate(v.toString()));
+                        } catch (Exception e) {
+                            rv += ", but error accessing result ["+e+"]"; //shouldn't happen
+                        }
+                    } else {
+                        rv += " after "+duration;
+                        try {
+                            Object v = get();
+                            rv += "\n\n" + (v==null ? "No return value (null)" : "Result: "+v);
+                        } catch (Exception e) {
+                            rv += " at first\n" +
+                                    "Error accessing result ["+e+"]"; //shouldn't happen
+                        }
+                        if (verbosity >= 2 && getExtraStatusText()!=null) {
+                            rv += "\n\n"+getExtraStatusText();
+                        }
+                    }
+                }
+            }
+        } else {
+            rv = getActiveTaskStatusString(verbosity);
+        }
+        return rv;
+    }
+    
+    private static String abbreviate(String s) {
+        s = Strings.getFirstLine(s);
+        if (s.length()>255) s = s.substring(0, 252)+ "...";
+        return s;
+    }
+
+    protected String getActiveTaskStatusString(int verbosity) {
+        String rv = "";
+        Thread t = getThread();
+    
+        // Normally, it's not possible for thread==null as we were started and not ended
+        
+        // However, there is a race where the task starts sand completes between the calls to getThread()
+        // at the start of the method and this call to getThread(), so both return null even though
+        // the intermediate checks returned started==true isDone()==false.
+        if (t == null) {
+            if (isDone()) {
+                return getStatusString(verbosity);
+            } else {
+                //should only happen for repeating task which is not active
+                return "Sleeping";
+            }
+        }
+
+        ThreadInfo ti = ManagementFactory.getThreadMXBean().getThreadInfo(t.getId(), (verbosity<=0 ? 0 : verbosity==1 ? 1 : Integer.MAX_VALUE));
+        if (getThread()==null)
+            //thread might have moved on to a new task; if so, recompute (it should now say "done")
+            return getStatusString(verbosity);
+        
+        if (verbosity >= 1 && Strings.isNonBlank(blockingDetails)) {
+            if (verbosity==1)
+                // short status string will just show blocking details
+                return blockingDetails;
+            //otherwise show the blocking details, then a new line, then additional information
+            rv = blockingDetails + "\n\n";
+        }
+        
+        if (verbosity >= 1 && blockingTask!=null) {
+            if (verbosity==1)
+                // short status string will just show blocking details
+                return "Waiting on "+blockingTask;
+            //otherwise show the blocking details, then a new line, then additional information
+            rv = "Waiting on "+blockingTask + "\n\n";
+        }
+
+        if (verbosity>=2) {
+            if (getExtraStatusText()!=null) {
+                rv += getExtraStatusText()+"\n\n";
+            }
+            
+            rv += ""+toString()+"\n";
+            if (submittedByTask!=null) {
+                rv += "Submitted by "+submittedByTask+"\n";
+            }
+
+            if (this instanceof HasTaskChildren) {
+                // list children tasks for compound tasks
+                try {
+                    Iterable<Task<?>> childrenTasks = ((HasTaskChildren)this).getChildren();
+                    if (childrenTasks.iterator().hasNext()) {
+                        rv += "Children:\n";
+                        for (Task<?> child: childrenTasks) {
+                            rv += "  "+child+": "+child.getStatusDetail(false)+"\n";
+                        }
+                    }
+                } catch (ConcurrentModificationException exc) {
+                    rv += "  (children not available - currently being modified)\n";
+                }
+            }
+            rv += "\n";
+        }
+        
+        LockInfo lock = ti.getLockInfo();
+        rv += "In progress";
+        if (verbosity>=1) {
+            if (lock==null && ti.getThreadState()==Thread.State.RUNNABLE) {
+                //not blocked
+                if (ti.isSuspended()) {
+                    // when does this happen?
+                    rv += ", thread suspended";
+                } else {
+                    if (verbosity >= 2) rv += " ("+ti.getThreadState()+")";
+                }
+            } else {
+                rv +=", thread waiting ";
+                if (ti.getThreadState() == Thread.State.BLOCKED) {
+                    rv += "(mutex) on "+lookup(lock);
+                    //TODO could say who holds it
+                } else if (ti.getThreadState() == Thread.State.WAITING) {
+                    rv += "(notify) on "+lookup(lock);
+                } else if (ti.getThreadState() == Thread.State.TIMED_WAITING) {
+                    rv += "(timed) on "+lookup(lock);
+                } else {
+                    rv = "("+ti.getThreadState()+") on "+lookup(lock);
+                }
+            }
+        }
+        if (verbosity>=2) {
+            StackTraceElement[] st = ti.getStackTrace();
+            st = brooklyn.util.javalang.StackTraceSimplifier.cleanStackTrace(st);
+            if (st!=null && st.length>0)
+                rv += "\n" +"At: "+st[0];
+            for (int ii=1; ii<st.length; ii++) {
+                rv += "\n" +"    "+st[ii];
+            }
+        }
+        return rv;
+    }
+    
+    protected String lookup(LockInfo info) {
+        return info!=null ? ""+info : "unknown (sleep)";
+    }
+
+    @Override
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    
+    /** allows a task user to specify why a task is blocked; for use immediately before a blocking/wait,
+     * and typically cleared immediately afterwards; referenced by management api to inspect a task
+     * which is blocking
+     */
+    @Override
+    public String setBlockingDetails(String blockingDetails) {
+        String old = this.blockingDetails;
+        this.blockingDetails = blockingDetails;
+        return old;
+    }
+    
+    @Override
+    public Task<?> setBlockingTask(Task<?> blockingTask) {
+        Task<?> old = this.blockingTask;
+        this.blockingTask = blockingTask;
+        return old;
+    }
+    
+    @Override
+    public void resetBlockingDetails() {
+        this.blockingDetails = null;
+    }
+    
+    @Override
+    public void resetBlockingTask() {
+        this.blockingTask = null;
+    }
+
+    /** returns a textual message giving details while the task is blocked */
+    @Override
+    public String getBlockingDetails() {
+        return blockingDetails;
+    }
+    
+    /** returns a task that this task is blocked on */
+    @Override
+    public Task<?> getBlockingTask() {
+        return blockingTask;
+    }
+    
+    @Override
+    public void setExtraStatusText(Object extraStatus) {
+        this.extraStatusText = extraStatus;
+    }
+    
+    @Override
+    public Object getExtraStatusText() {
+        return extraStatusText;
+    }
+
+    // ---- add a way to warn if task is not run
+    
+    public interface TaskFinalizer {
+        public void onTaskFinalization(Task<?> t);
+    }
+
+    public static final TaskFinalizer WARN_IF_NOT_RUN = new TaskFinalizer() {
+        @Override
+        public void onTaskFinalization(Task<?> t) {
+            if (!Tasks.isAncestorCancelled(t) && !t.isSubmitted()) {
+                log.warn(t+" was never submitted; did the code create it and forget to run it? ('cancel' the task to suppress this message)");
+                log.debug("Detail of unsubmitted task "+t+":\n"+t.getStatusDetail(true));
+                return;
+            }
+            if (!t.isDone()) {
+                // shouldn't happen
+                // TODO But does happen if management context was terminated (e.g. running test suite).
+                //      Should check if Execution Manager is running, and only log if it was not terminated?
+                log.warn("Task "+t+" is being finalized before completion");
+                return;
+            }
+        }
+    };
+
+    public static final TaskFinalizer NO_OP = new TaskFinalizer() {
+        @Override
+        public void onTaskFinalization(Task<?> t) {
+        }
+    };
+    
+    public void ignoreIfNotRun() {
+        setFinalizer(NO_OP);
+    }
+    
+    public void setFinalizer(TaskFinalizer f) {
+        TaskFinalizer finalizer = Tasks.tag(this, TaskFinalizer.class, false);
+        if (finalizer!=null && finalizer!=f)
+            throw new IllegalStateException("Cannot apply multiple finalizers");
+        if (isDone())
+            throw new IllegalStateException("Finalizer cannot be set on task "+this+" after it is finished");
+        tags.add(f);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        TaskFinalizer finalizer = Tasks.tag(this, TaskFinalizer.class, false);
+        if (finalizer==null) finalizer = WARN_IF_NOT_RUN;
+        finalizer.onTaskFinalization(this);
+    }
+    
+    public static class SubmissionErrorCatchingExecutor implements Executor {
+        final Executor target;
+        public SubmissionErrorCatchingExecutor(Executor target) {
+            this.target = target;
+        }
+        @Override
+        public void execute(Runnable command) {
+            if (isShutdown()) {
+                log.debug("Skipping execution of task callback hook "+command+" because executor is shutdown.");
+                return;
+            }
+            try {
+                target.execute(command);
+            } catch (Exception e) {
+                if (isShutdown()) {
+                    log.debug("Ignoring failed execution of task callback hook "+command+" because executor is shutdown.");
+                } else {
+                    log.warn("Execution of task callback hook "+command+" failed: "+e, e);
+                }
+            }
+        }
+        protected boolean isShutdown() {
+            return target instanceof ExecutorService && ((ExecutorService)target).isShutdown();
+        }
+    }
+    
+    @Override
+    public void addListener(Runnable listener, Executor executor) {
+        listeners.add(listener, new SubmissionErrorCatchingExecutor(executor));
+    }
+    
+    @Override
+    public void runListeners() {
+        listeners.execute();
+    }
+    
+    @Override
+    public void setEndTimeUtc(long val) {
+        endTimeUtc = val;
+    }
+    
+    @Override
+    public void setThread(Thread thread) {
+        this.thread = thread;
+    }
+    
+    @Override
+    public Callable<T> getJob() {
+        return job;
+    }
+    
+    @Override
+    public void setJob(Callable<T> job) {
+        this.job = job;
+    }
+    
+    @Override
+    public ExecutionList getListeners() {
+        return listeners;
+    }
+    
+    @Override
+    public void setSubmitTimeUtc(long val) {
+        submitTimeUtc = val;
+    }
+    
+    private static <T> Task<T> newGoneTaskFor(Task<?> task) {
+        Task<T> t = Tasks.<T>builder().dynamic(false).name(task.getDisplayName())
+            .description("Details of the original task "+task+" have been forgotten.")
+            .body(Callables.returning((T)null)).build();
+        ((BasicTask<T>)t).ignoreIfNotRun();
+        return t;
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public void setSubmittedByTask(Task<?> task) {
+        submittedByTask = (Maybe)Maybe.softThen((Task)task, (Maybe)Maybe.of(BasicTask.newGoneTaskFor(task)));
+    }
+    
+    @Override
+    public Set<Object> getMutableTags() {
+        return tags;
+    }
+    
+    @Override
+    public void setStartTimeUtc(long val) {
+        startTimeUtc = val;
+    }
+
+    @Override
+    public void applyTagModifier(Function<Set<Object>,Void> modifier) {
+        modifier.apply(tags);
+    }
+
+    @Override
+    public Task<?> getProxyTarget() {
+        return proxyTargetTask;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/CanSetName.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/CanSetName.java b/core/src/main/java/org/apache/brooklyn/core/util/task/CanSetName.java
new file mode 100644
index 0000000..407a93a
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/CanSetName.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+public interface CanSetName {
+
+    void setName(String name);
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/CompoundTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/CompoundTask.java b/core/src/main/java/org/apache/brooklyn/core/util/task/CompoundTask.java
new file mode 100644
index 0000000..8fdb146
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/CompoundTask.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import groovy.lang.Closure;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+import org.apache.brooklyn.api.management.HasTaskChildren;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.util.collections.MutableMap;
+
+
+/**
+ * A {@link Task} that is comprised of other units of work: possibly a heterogeneous mix of {@link Task},
+ * {@link Runnable}, {@link Callable} and {@link Closure} instances.
+ * 
+ * This class holds the collection of child tasks, but subclasses have the responsibility of executing them in a
+ * sensible manner by implementing the abstract {@link #runJobs} method.
+ */
+public abstract class CompoundTask<T> extends BasicTask<List<T>> implements HasTaskChildren {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(CompoundTask.class);
+                
+    protected final List<Task<? extends T>> children;
+    protected final List<Object> result;
+    
+    /**
+     * Constructs a new compound task containing the specified units of work.
+     * 
+     * @param jobs  A potentially heterogeneous mixture of {@link Runnable}, {@link Callable}, {@link Closure} and {@link Task} can be provided. 
+     * @throws IllegalArgumentException if any of the passed child jobs is not one of the above types 
+     */
+    public CompoundTask(Object... jobs) {
+        this( Arrays.asList(jobs) );
+    }
+    
+    /**
+     * Constructs a new compound task containing the specified units of work.
+     * 
+     * @param jobs  A potentially heterogeneous mixture of {@link Runnable}, {@link Callable}, {@link Closure} and {@link Task} can be provided. 
+     * @throws IllegalArgumentException if any of the passed child jobs is not one of the above types 
+     */
+    public CompoundTask(Collection<?> jobs) {
+        this(MutableMap.of("tag", "compound"), jobs);
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public CompoundTask(Map<String,?> flags, Collection<?> jobs) {
+        super(flags);
+        super.job = new Callable<List<T>>() {
+            @Override public List<T> call() throws Exception {
+                return runJobs();
+            }
+        };
+        
+        this.result = new ArrayList<Object>(jobs.size());
+        this.children = new ArrayList<Task<? extends T>>(jobs.size());
+        for (Object job : jobs) {
+            Task subtask;
+            if (job instanceof TaskAdaptable) { subtask = ((TaskAdaptable)job).asTask(); }
+            else if (job instanceof Closure)  { subtask = new BasicTask<T>((Closure) job); }
+            else if (job instanceof Callable) { subtask = new BasicTask<T>((Callable) job); }
+            else if (job instanceof Runnable) { subtask = new BasicTask<T>((Runnable) job); }
+            
+            else throw new IllegalArgumentException("Invalid child "+(job == null ? null : job.getClass() + " ("+job+")")+
+                " passed to compound task; must be Runnable, Callable, Closure or Task");
+            
+            BrooklynTaskTags.addTagDynamically(subtask, ManagementContextInternal.SUB_TASK_TAG);
+            children.add(subtask);
+        }
+        
+        for (Task<?> t: getChildren()) {
+            ((TaskInternal<?>)t).markQueued();
+        }
+    }
+
+    /** return value needs to be specified by subclass; subclass should also setBlockingDetails 
+     * @throws ExecutionException 
+     * @throws InterruptedException */    
+    protected abstract List<T> runJobs() throws InterruptedException, ExecutionException;
+    
+    protected void submitIfNecessary(TaskAdaptable<?> task) {
+        if (!task.asTask().isSubmitted()) {
+            if (BasicExecutionContext.getCurrentExecutionContext() == null) {
+                throw new IllegalStateException("Compound task ("+task+") launched from "+this+" missing required execution context");
+            } else {
+                BasicExecutionContext.getCurrentExecutionContext().submit(task);
+            }
+        }
+    }
+    
+    public List<Task<? extends T>> getChildrenTyped() {
+        return children;
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public List<Task<?>> getChildren() {
+        return (List) getChildrenTyped();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/DeferredSupplier.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/DeferredSupplier.java b/core/src/main/java/org/apache/brooklyn/core/util/task/DeferredSupplier.java
new file mode 100644
index 0000000..ad9416b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/DeferredSupplier.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import com.google.common.base.Supplier;
+
+/**
+ * A class that supplies objects of a single type. When used as a ConfigKey value,
+ * the evaluation is deferred until getConfig() is called. The returned value will then
+ * be coerced to the correct type. 
+ * 
+ * Subsequent calls to getConfig will result in further calls to deferredProvider.get(), 
+ * rather than reusing the result. If you want to reuse the result, consider instead 
+ * using a Future.
+ * 
+ * Note that this functionality replaces the ues of Closure in brooklyn 0.4.0, which 
+ * served the same purpose.
+ */
+public interface DeferredSupplier<T> extends Supplier<T> {
+    @Override
+    T get();
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/DynamicSequentialTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/DynamicSequentialTask.java b/core/src/main/java/org/apache/brooklyn/core/util/task/DynamicSequentialTask.java
new file mode 100644
index 0000000..e197705
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/DynamicSequentialTask.java
@@ -0,0 +1,480 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import groovy.lang.Closure;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.apache.brooklyn.api.management.HasTaskChildren;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskQueueingContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.time.CountdownTimer;
+import brooklyn.util.time.Duration;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+
+/** Represents a task whose run() method can create other tasks
+ * which are run sequentially, but that sequence runs in parallel to this task
+ * <p>
+ * There is an optional primary job run with this task, along with multiple secondary children.
+ * If any secondary task fails (assuming it isn't {@link Tasks#markInessential()} then by default
+ * subsequent tasks are not submitted and the primary task fails (but no tasks are cancelled or interrupted).
+ * You can change the behavior of this task with fields in {@link FailureHandlingConfig},
+ * or the convenience {@link TaskQueueingContext#swallowChildrenFailures()}
+ * (and {@link DynamicTasks#swallowChildrenFailures()} if you are inside the task).
+ * <p>
+ * This synchronizes on secondary tasks when submitting them, in case they may be manually submitted
+ * and the submitter wishes to ensure it is only submitted once.
+ * <p>
+ * Improvements which would be nice to have:
+ * <li> unqueued tasks not visible in api; would like that
+ * <li> uses an extra thread (submitted as background task) to monitor the secondary jobs; would be nice to remove this,
+ *      and rely on {@link BasicExecutionManager} to run the jobs sequentially (combined with fix to item above)
+ * <li> would be nice to have cancel, resume, and possibly skipQueue available as operations (ideally in the REST API and GUI)   
+ **/
+public class DynamicSequentialTask<T> extends BasicTask<T> implements HasTaskChildren, TaskQueueingContext {
+
+    private static final Logger log = LoggerFactory.getLogger(CompoundTask.class);
+                
+    protected final Queue<Task<?>> secondaryJobsAll = new ConcurrentLinkedQueue<Task<?>>();
+    protected final Queue<Task<?>> secondaryJobsRemaining = new ConcurrentLinkedQueue<Task<?>>();
+    protected final Object jobTransitionLock = new Object();
+    protected volatile boolean primaryStarted = false;
+    protected volatile boolean primaryFinished = false;
+    protected volatile boolean secondaryQueueAborted = false;
+    protected Thread primaryThread;
+    protected DstJob dstJob;
+    protected FailureHandlingConfig failureHandlingConfig = FailureHandlingConfig.DEFAULT;
+
+    // default values for how to handle the various failures
+    @Beta
+    public static class FailureHandlingConfig {
+        /** secondary queue runs independently of primary task (submitting and blocking on each secondary task in order), 
+         * but can set it up not to submit any more tasks if the primary fails */
+        public final boolean abortSecondaryQueueOnPrimaryFailure;
+        /** as {@link #abortSecondaryQueueOnPrimaryFailure} but controls cancelling of secondary queue*/
+        public final boolean cancelSecondariesOnPrimaryFailure;
+        /** secondary queue can continue submitting+blocking tasks even if a secondary task fails (unusual;
+         * typically handled by {@link TaskTags#markInessential(Task)} on the secondary tasks, in which case
+         * the secondary queue is never aborted */
+        public final boolean abortSecondaryQueueOnSecondaryFailure;
+        /** unsubmitted secondary tasks (ie those further in the queue) can be cancelled if a secondary task fails */
+        public final boolean cancelSecondariesOnSecondaryFailure;
+        /** whether to issue cancel against primary task if a secondary task fails */
+        public final boolean cancelPrimaryOnSecondaryFailure;
+        /** whether to fail this task if a secondary task fails */
+        public final boolean failParentOnSecondaryFailure;
+        
+        @Beta
+        public FailureHandlingConfig(
+                boolean abortSecondaryQueueOnPrimaryFailure, boolean cancelSecondariesOnPrimaryFailure,
+                boolean abortSecondaryQueueOnSecondaryFailure, boolean cancelSecondariesOnSecondaryFailure,
+                boolean cancelPrimaryOnSecondaryFailure, boolean failParentOnSecondaryFailure) {
+            this.abortSecondaryQueueOnPrimaryFailure = abortSecondaryQueueOnPrimaryFailure;
+            this.cancelSecondariesOnPrimaryFailure = cancelSecondariesOnPrimaryFailure;
+            this.abortSecondaryQueueOnSecondaryFailure = abortSecondaryQueueOnSecondaryFailure;
+            this.cancelSecondariesOnSecondaryFailure = cancelSecondariesOnSecondaryFailure;
+            this.cancelPrimaryOnSecondaryFailure = cancelPrimaryOnSecondaryFailure;
+            this.failParentOnSecondaryFailure = failParentOnSecondaryFailure;
+        }
+        
+        public static final FailureHandlingConfig DEFAULT = new FailureHandlingConfig(false, false, true, false, false, true);
+        public static final FailureHandlingConfig SWALLOWING_CHILDREN_FAILURES = new FailureHandlingConfig(false, false, false, false, false, false);
+    }
+    
+    public static class QueueAbortedException extends IllegalStateException {
+        private static final long serialVersionUID = -7569362887826818524L;
+        
+        public QueueAbortedException(String msg) {
+            super(msg);
+        }
+        public QueueAbortedException(String msg, Throwable cause) {
+            super(msg, cause);
+        }
+    }
+
+    /**
+     * Constructs a new compound task containing the specified units of work.
+     * 
+     * @param jobs  A potentially heterogeneous mixture of {@link Runnable}, {@link Callable}, {@link Closure} and {@link Task} can be provided. 
+     * @throws IllegalArgumentException if any of the passed child jobs is not one of the above types 
+     */
+    public DynamicSequentialTask() {
+        this(null);
+    }
+    
+    public DynamicSequentialTask(Callable<T> mainJob) {
+        this(MutableMap.of("tag", "compound"), mainJob);
+    }
+    
+    public DynamicSequentialTask(Map<?,?> flags, Callable<T> mainJob) {
+        super(flags);
+        this.job = dstJob = new DstJob(mainJob);
+    }
+    
+    @Override
+    public void queue(Task<?> t) {
+        synchronized (jobTransitionLock) {
+            if (primaryFinished)
+                throw new IllegalStateException("Cannot add a task to "+this+" which is already finished (trying to add "+t+")");
+            if (secondaryQueueAborted)
+                throw new QueueAbortedException("Cannot add a task to "+this+" whose queue has been aborted (trying to add "+t+")");
+            secondaryJobsAll.add(t);
+            secondaryJobsRemaining.add(t);
+            BrooklynTaskTags.addTagsDynamically(t, ManagementContextInternal.SUB_TASK_TAG);
+            ((TaskInternal<?>)t).markQueued();
+            jobTransitionLock.notifyAll();
+        }
+    }
+
+    @Override
+    public boolean cancel(boolean mayInterruptIfRunning) {
+        return cancel(mayInterruptIfRunning, mayInterruptIfRunning, true);
+    }
+    public boolean cancel(boolean mayInterruptTask, boolean interruptPrimaryThread, boolean alsoCancelChildren) {
+        if (isDone()) return false;
+        if (log.isTraceEnabled()) log.trace("cancelling {}", this);
+        boolean cancel = super.cancel(mayInterruptTask);
+        if (alsoCancelChildren) {
+            for (Task<?> t: secondaryJobsAll)
+                cancel |= t.cancel(mayInterruptTask);
+        }
+        synchronized (jobTransitionLock) {
+            if (primaryThread!=null) {
+                if (interruptPrimaryThread) {
+                    if (log.isTraceEnabled()) log.trace("cancelling {} - interrupting", this);
+                    primaryThread.interrupt();
+                }
+                cancel = true;
+            }
+        }
+        return cancel;
+    }
+    
+    @Override
+    public synchronized boolean uncancel() {
+        secondaryQueueAborted = false;
+        return super.uncancel();
+    }
+
+    @Override
+    public Iterable<Task<?>> getChildren() {
+        return Collections.unmodifiableCollection(secondaryJobsAll);
+    }
+    
+    /** submits the indicated task for execution in the current execution context, and returns immediately */
+    protected void submitBackgroundInheritingContext(Task<?> task) {
+        BasicExecutionContext ec = BasicExecutionContext.getCurrentExecutionContext();
+        if (log.isTraceEnabled()) {
+            log.trace("task {} - submitting background task {} ({})", new Object[] { Tasks.current(), task, ec });
+        }
+        if (ec==null) {
+            String message = Tasks.current()!=null ?
+                    // user forgot ExecContext:
+                        "Task "+this+" submitting background task requires an ExecutionContext (an ExecutionManager is not enough): submitting "+task+" in "+Tasks.current()
+                    : // should not happen:
+                        "Cannot submit tasks inside DST when not in a task : submitting "+task+" in "+this;
+            log.warn(message+" (rethrowing)");
+            throw new IllegalStateException(message);
+        }
+        synchronized (task) {
+            if (task.isSubmitted()) {
+                if (log.isTraceEnabled()) {
+                    log.trace("DST "+this+" skipping submission of child "+task+" because it is already submitted");
+                }
+            } else {
+                try {
+                    ec.submit(task);
+                } catch (Exception e) {
+                    Exceptions.propagateIfFatal(e);
+                    // Give some context when the submit fails (happens when the target is already unmanaged)
+                    throw new IllegalStateException("Failure submitting task "+task+" in "+this+": "+e.getMessage(), e);
+                }
+            }
+        }
+    }
+
+    public void setFailureHandlingConfig(FailureHandlingConfig failureHandlingConfig) {
+        this.failureHandlingConfig = failureHandlingConfig;
+    }
+    @Override
+    public void swallowChildrenFailures() {
+        setFailureHandlingConfig(FailureHandlingConfig.SWALLOWING_CHILDREN_FAILURES);
+    }
+    
+    protected class DstJob implements Callable<T> {
+        protected Callable<T> primaryJob;
+        /** currently executing (or just completed) secondary task, or null if none;
+         * with jobTransitionLock notified on change and completion */
+        protected volatile Task<?> currentSecondary = null;
+        protected volatile boolean finishedSecondaries = false;
+        
+        public DstJob(Callable<T> mainJob) {
+            this.primaryJob = mainJob;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public T call() throws Exception {
+
+            synchronized (jobTransitionLock) {
+                primaryStarted = true;
+                primaryThread = Thread.currentThread();
+                for (Task<?> t: secondaryJobsAll)
+                    ((TaskInternal<?>)t).markQueued();
+            }
+            // TODO overkill having a thread/task for this, but it works
+            // optimisation would either use newTaskEndCallback property on task to submit
+            // or use some kind of single threaded executor for the queued tasks
+            Task<List<Object>> secondaryJobMaster = Tasks.<List<Object>>builder().dynamic(false)
+                    .name("DST manager (internal)")
+                    // TODO marking it transient helps it be GC'd sooner, 
+                    // but ideally we wouldn't have this,
+                    // or else it would be a child
+                    .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
+                    .body(new Callable<List<Object>>() {
+
+                @Override
+                public List<Object> call() throws Exception {
+                    List<Object> result = new ArrayList<Object>();
+                    try { 
+                        while (!secondaryQueueAborted && (!primaryFinished || !secondaryJobsRemaining.isEmpty())) {
+                            synchronized (jobTransitionLock) {
+                                if (!primaryFinished && secondaryJobsRemaining.isEmpty()) {
+                                    currentSecondary = null;
+                                    jobTransitionLock.wait(1000);
+                                }
+                            }
+                            @SuppressWarnings("rawtypes")
+                            Task secondaryJob = secondaryJobsRemaining.poll();
+                            if (secondaryJob != null) {
+                                synchronized (jobTransitionLock) {
+                                    currentSecondary = secondaryJob;
+                                    submitBackgroundInheritingContext(secondaryJob);
+                                    jobTransitionLock.notifyAll();
+                                }
+                                try {
+                                    result.add(secondaryJob.get());
+                                } catch (Exception e) {
+                                    if (TaskTags.isInessential(secondaryJob)) {
+                                        result.add(Tasks.getError(secondaryJob));
+                                        if (log.isDebugEnabled())
+                                            log.debug("Secondary job queue for "+DynamicSequentialTask.this+" ignoring error in inessential task "+secondaryJob+": "+e);
+                                    } else {
+                                        if (failureHandlingConfig.cancelSecondariesOnSecondaryFailure) {
+                                            if (log.isDebugEnabled())
+                                                log.debug("Secondary job queue for "+DynamicSequentialTask.this+" cancelling "+secondaryJobsRemaining.size()+" remaining, due to error in task "+secondaryJob+": "+e);
+                                            synchronized (jobTransitionLock) {
+                                                for (Task<?> t: secondaryJobsRemaining)
+                                                    t.cancel(true);
+                                                jobTransitionLock.notifyAll();
+                                            }
+                                        }
+                                        
+                                        if (failureHandlingConfig.abortSecondaryQueueOnSecondaryFailure) {
+                                            if (log.isDebugEnabled())
+                                                log.debug("Aborting secondary job queue for "+DynamicSequentialTask.this+" due to error in child task "+secondaryJob+" ("+e+", being rethrown)");
+                                            secondaryQueueAborted = true;
+                                            throw e;
+                                        }
+
+                                        if (!primaryFinished && failureHandlingConfig.cancelPrimaryOnSecondaryFailure) {
+                                            cancel(true, false, false);
+                                        }
+                                        
+                                        result.add(Tasks.getError(secondaryJob));
+                                        if (log.isDebugEnabled())
+                                            log.debug("Secondary job queue for "+DynamicSequentialTask.this+" continuing in presence of error in child task "+secondaryJob+" ("+e+", being remembered)");
+                                    }
+                                }
+                            }
+                        }
+                    } finally {
+                        synchronized (jobTransitionLock) {
+                            currentSecondary = null;
+                            finishedSecondaries = true;
+                            jobTransitionLock.notifyAll();
+                        }
+                    }
+                    return result;
+                }
+            }).build();
+            ((BasicTask<?>)secondaryJobMaster).proxyTargetTask = DynamicSequentialTask.this;
+            
+            submitBackgroundInheritingContext(secondaryJobMaster);
+            
+            T result = null;
+            Throwable error = null;
+            Throwable uninterestingSelfError = null;
+            boolean errorIsFromChild = false;
+            try {
+                if (log.isTraceEnabled()) log.trace("calling primary job for {}", this);
+                if (primaryJob!=null) result = primaryJob.call();
+            } catch (Throwable selfException) {
+                Exceptions.propagateIfFatal(selfException);
+                if (Exceptions.getFirstThrowableOfType(selfException, QueueAbortedException.class) != null) {
+                    // Error was caused by the task already having failed, and this thread calling queue() to try
+                    // to queue more work. The underlying cause will be much more interesting.
+                    // Without this special catch, we record error = "Cannot add a task to ... whose queue has been aborted",
+                    // which gets propagated instead of the more interesting child exception.
+                    uninterestingSelfError = selfException;
+                } else {
+                    error = selfException;
+                    errorIsFromChild = false;
+                }
+                if (failureHandlingConfig.abortSecondaryQueueOnPrimaryFailure) {
+                    if (log.isDebugEnabled())
+                        log.debug("Secondary job queue for "+DynamicSequentialTask.this+" aborting with "+secondaryJobsRemaining.size()+" remaining, due to error in primary task: "+selfException);
+                    secondaryQueueAborted = true;
+                }
+                if (failureHandlingConfig.cancelSecondariesOnPrimaryFailure) {
+                    if (log.isDebugEnabled())
+                        log.debug(DynamicSequentialTask.this+" cancelling "+secondaryJobsRemaining.size()+" remaining, due to error in primary task: "+selfException);
+                    synchronized (jobTransitionLock) {
+                        for (Task<?> t: secondaryJobsRemaining)
+                            t.cancel(true);
+                        // do this early to prevent additions; and note we notify very soon below, so not notify is help off until below
+                        primaryThread = null;
+                        primaryFinished = true;
+                    }
+                }
+            } finally {
+                try {
+                    if (log.isTraceEnabled()) log.trace("cleaning up for {}", this);
+                    synchronized (jobTransitionLock) {
+                        // semaphore might be nicer here (aled notes as it is this is a little hard to read)
+                        primaryThread = null;
+                        primaryFinished = true;
+                        jobTransitionLock.notifyAll();
+                    }
+                    if (!isCancelled() && !Thread.currentThread().isInterrupted()) {
+                        if (log.isTraceEnabled()) log.trace("waiting for secondaries for {}", this);
+                        // wait on tasks sequentially so that blocking information is more interesting
+                        DynamicTasks.waitForLast();
+                        List<Object> result2 = secondaryJobMaster.get();
+                        try {
+                            if (primaryJob==null) result = (T)result2;
+                        } catch (ClassCastException e) { /* ignore class cast exception; leave the result as null */ }
+                    }
+                } catch (Throwable childException) {
+                    Exceptions.propagateIfFatal(childException);
+                    if (error==null) {
+                        error = childException;
+                        errorIsFromChild = true;
+                    } else {
+                        if (log.isDebugEnabled()) log.debug("Parent task "+this+" ignoring child error ("+childException+") in presence of our own error ("+error+")");
+                    }
+                }
+            }
+            if (error!=null) {
+                handleException(error, errorIsFromChild);
+            }
+            if (uninterestingSelfError != null) {
+                handleException(uninterestingSelfError, false);
+            }
+            return result;
+        }
+        
+        @Override
+        public String toString() {
+            return "DstJob:"+DynamicSequentialTask.this.getId();
+        }
+
+        /** waits for this job to complete, or the given time to elapse */
+        public void join(boolean includePrimary, Duration optionalTimeout) throws InterruptedException {
+            CountdownTimer timeLeft = optionalTimeout!=null ? CountdownTimer.newInstanceStarted(optionalTimeout) : null;
+            while (true) {
+                Task<?> cs;
+                Duration remaining;
+                synchronized (jobTransitionLock) {
+                    cs = currentSecondary;
+                    if (finishedSecondaries) return;
+                    remaining = timeLeft==null ? Duration.ONE_SECOND : timeLeft.getDurationRemaining();
+                    if (!remaining.isPositive()) return;
+                    if (cs==null) {
+                        if (!includePrimary && secondaryJobsRemaining.isEmpty()) return;
+                        // parent still running, no children though
+                        Tasks.setBlockingTask(DynamicSequentialTask.this);
+                        jobTransitionLock.wait(remaining.toMilliseconds());
+                        Tasks.resetBlockingDetails();
+                    }
+                }
+                if (cs!=null) {
+                    Tasks.setBlockingTask(cs);
+                    cs.blockUntilEnded(remaining);
+                    Tasks.resetBlockingDetails();
+                }
+            }
+        }
+    }
+
+    @Override
+    public List<Task<?>> getQueue() {
+        return ImmutableList.copyOf(secondaryJobsAll);
+    }
+
+    public void handleException(Throwable throwable, boolean fromChild) throws Exception {
+        Exceptions.propagateIfFatal(throwable);
+        if (fromChild && !failureHandlingConfig.failParentOnSecondaryFailure) {
+            log.debug("Parent task "+this+" swallowing child error: "+throwable);
+            return;
+        }
+        handleException(throwable);
+    }
+    public void handleException(Throwable throwable) throws Exception { 
+        Exceptions.propagateIfFatal(throwable);
+        if (throwable instanceof Exception) {
+            // allow checked exceptions to be passed through
+            throw (Exception)throwable;
+        }
+        throw Exceptions.propagate(throwable);
+    }
+
+    @Override
+    public void drain(Duration optionalTimeout, boolean includePrimary, boolean throwFirstError) {
+        try {
+            dstJob.join(includePrimary, optionalTimeout);
+        } catch (InterruptedException e) {
+            throw Exceptions.propagate(e);
+        }
+        if (throwFirstError) {
+            if (isError()) 
+                getUnchecked();
+            for (Task<?> t: getQueue())
+                if (t.isError() && !TaskTags.isInessential(t))
+                    t.getUnchecked();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/DynamicTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/DynamicTasks.java b/core/src/main/java/org/apache/brooklyn/core/util/task/DynamicTasks.java
new file mode 100644
index 0000000..ed46558
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/DynamicTasks.java
@@ -0,0 +1,337 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.api.management.TaskQueueingContext;
+import org.apache.brooklyn.api.management.TaskWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.time.Duration;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+
+/** 
+ * Contains static methods which detect and use the current {@link TaskQueueingContext} to execute tasks.
+ * 
+ * @since 0.6.0
+ */
+@Beta
+public class DynamicTasks {
+
+    private static final Logger log = LoggerFactory.getLogger(DynamicTasks.class);
+    private static final ThreadLocal<TaskQueueingContext> taskQueueingContext = new ThreadLocal<TaskQueueingContext>();
+    
+    public static void setTaskQueueingContext(TaskQueueingContext newTaskQC) {
+        taskQueueingContext.set(newTaskQC);
+    }
+    
+    public static TaskQueueingContext getThreadTaskQueuingContext() {
+        return taskQueueingContext.get();
+    }
+    
+    public static TaskQueueingContext getTaskQueuingContext() {
+        TaskQueueingContext adder = getThreadTaskQueuingContext();
+        if (adder!=null) return adder;
+        Task<?> t = Tasks.current();
+        if (t instanceof TaskQueueingContext) return (TaskQueueingContext) t;
+        return null;
+    }
+
+    
+    public static void removeTaskQueueingContext() {
+        taskQueueingContext.remove();
+    }
+
+    public static class TaskQueueingResult<T> implements TaskWrapper<T> {
+        private final Task<T> task;
+        private final boolean wasQueued;
+        private ExecutionContext execContext = null;
+        
+        private TaskQueueingResult(TaskAdaptable<T> task, boolean wasQueued) {
+            this.task = task.asTask();
+            this.wasQueued = wasQueued;
+        }
+        @Override
+        public Task<T> asTask() {
+            return task;
+        }
+        @Override
+        public Task<T> getTask() {
+            return task;
+        }
+        /** returns true if the task was queued */
+        public boolean wasQueued() {
+            return wasQueued;
+        }
+        /** returns true if the task either is currently queued or has been submitted */
+        public boolean isQueuedOrSubmitted() {
+            return wasQueued || Tasks.isQueuedOrSubmitted(task);
+        }
+        /** specifies an execContext to use if the task has to be explicitly submitted;
+         * if omitted it will attempt to find one based on the current thread's context */
+        public TaskQueueingResult<T> executionContext(ExecutionContext execContext) {
+            this.execContext = execContext;
+            return this;
+        }
+        /** as {@link #executionContext(ExecutionContext)} but inferring from the entity */
+        public TaskQueueingResult<T> executionContext(Entity entity) {
+            this.execContext = ((EntityInternal)entity).getManagementSupport().getExecutionContext();
+            return this;
+        }
+        private boolean orSubmitInternal() {
+            if (!wasQueued()) {
+                if (isQueuedOrSubmitted()) {
+                    log.warn("Redundant call to execute "+getTask()+"; skipping");
+                    return false;
+                } else {
+                    ExecutionContext ec = execContext;
+                    if (ec==null)
+                        ec = BasicExecutionContext.getCurrentExecutionContext();
+                    if (ec==null)
+                        throw new IllegalStateException("Cannot execute "+getTask()+" without an execution context; ensure caller is in an ExecutionContext");
+                    ec.submit(getTask());
+                    return true;
+                }
+            } else {
+                return false;
+            }
+        }
+        /** causes the task to be submitted (asynchronously) if it hasn't already been,
+         * requiring an entity execution context (will try to find a default if not set) */
+        public TaskQueueingResult<T> orSubmitAsync() {
+            orSubmitInternal();
+            return this;
+        }
+        /** convenience for setting {@link #executionContext(ExecutionContext)} then submitting async */
+        public TaskQueueingResult<T> orSubmitAsync(Entity entity) {
+            executionContext(entity);
+            return orSubmitAsync();
+        }
+        /** causes the task to be submitted *synchronously* if it hasn't already been submitted;
+         * useful in contexts such as libraries where callers may be either on a legacy call path 
+         * (which assumes all commands complete immediately);
+         * requiring an entity execution context (will try to find a default if not set) */
+        public TaskQueueingResult<T> orSubmitAndBlock() {
+            if (orSubmitInternal()) task.getUnchecked();
+            return this;
+        }
+        /** convenience for setting {@link #executionContext(ExecutionContext)} then submitting blocking */
+        public TaskQueueingResult<T> orSubmitAndBlock(Entity entity) {
+            executionContext(entity);
+            return orSubmitAndBlock();
+        }
+        /** blocks for the task to be completed
+         * <p>
+         * needed in any context where subsequent commands assume the task has completed.
+         * not needed in a context where the task is simply being built up and queued.
+         * <p>
+         * throws if there are any errors
+         */
+        public T andWaitForSuccess() {
+            return task.getUnchecked();
+        }
+        public void orCancel() {
+            if (!wasQueued()) {
+                task.cancel(false);
+            }
+        }
+    }
+    
+    /**
+     * Tries to add the task to the current addition context if there is one, otherwise does nothing.
+     * <p/>
+     * Call {@link TaskQueueingResult#orSubmitAsync() orSubmitAsync()} on the returned
+     * {@link TaskQueueingResult TaskQueueingResult} to handle execution of tasks in a
+     * {@link BasicExecutionContext}.
+     */
+    public static <T> TaskQueueingResult<T> queueIfPossible(TaskAdaptable<T> task) {
+        TaskQueueingContext adder = getTaskQueuingContext();
+        boolean result = false;
+        if (adder!=null)
+            result = Tasks.tryQueueing(adder, task);
+        return new TaskQueueingResult<T>(task, result);
+    }
+
+    /** @see #queueIfPossible(TaskAdaptable) */
+    public static <T> TaskQueueingResult<T> queueIfPossible(TaskFactory<? extends TaskAdaptable<T>> task) {
+        return queueIfPossible(task.newTask());
+    }
+
+    /** adds the given task to the nearest task addition context,
+     * either set as a thread-local, or in the current task, or the submitter of the task, etc
+     * <p>
+     * throws if it cannot add */
+    public static <T> Task<T> queueInTaskHierarchy(Task<T> task) {
+        Preconditions.checkNotNull(task, "Task to queue cannot be null");
+        Preconditions.checkState(!Tasks.isQueuedOrSubmitted(task), "Task to queue must not yet be submitted: {}", task);
+        
+        TaskQueueingContext adder = getTaskQueuingContext();
+        if (adder!=null) { 
+            if (Tasks.tryQueueing(adder, task)) {
+                log.debug("Queued task {} at context {} (no hierarchy)", task, adder);
+                return task;
+            }
+        }
+        
+        Task<?> t = Tasks.current();
+        Preconditions.checkState(t!=null || adder!=null, "No task addition context available for queueing task "+task);
+        
+        while (t!=null) {
+            if (t instanceof TaskQueueingContext) {
+                if (Tasks.tryQueueing((TaskQueueingContext)t, task)) {
+                    log.debug("Queued task {} at hierarchical context {}", task, t);
+                    return task;
+                }
+            }
+            t = t.getSubmittedByTask();
+        }
+        
+        throw new IllegalStateException("No task addition context available in current task hierarchy for adding task "+task);
+    }
+
+    /**
+     * Queues the given task.
+     * <p/>
+     * This method is only valid within a dynamic task. Use {@link #queueIfPossible(TaskAdaptable)}
+     * and {@link TaskQueueingResult#orSubmitAsync()} if the calling context is a basic task.
+     *
+     * @param task The task to queue
+     * @throws IllegalStateException if no task queueing context is available
+     * @return The queued task
+     */
+    public static <V extends TaskAdaptable<?>> V queue(V task) {
+        try {
+            Preconditions.checkNotNull(task, "Task to queue cannot be null");
+            Preconditions.checkState(!Tasks.isQueued(task), "Task to queue must not yet be queued: %s", task);
+            TaskQueueingContext adder = getTaskQueuingContext();
+            if (adder==null) {
+                throw new IllegalStateException("Task "+task+" cannot be queued here; no queueing context available");
+            }
+            adder.queue(task.asTask());
+            return task;
+        } catch (Throwable e) {
+            log.warn("Error queueing "+task+" (rethrowing): "+e);
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    /** @see #queue(org.apache.brooklyn.api.management.TaskAdaptable)  */
+    public static void queue(TaskAdaptable<?> task1, TaskAdaptable<?> task2, TaskAdaptable<?> ...tasks) {
+        queue(task1);
+        queue(task2);
+        for (TaskAdaptable<?> task: tasks) queue(task);
+    }
+
+    /** @see #queue(org.apache.brooklyn.api.management.TaskAdaptable)  */
+    public static <T extends TaskAdaptable<?>> T queue(TaskFactory<T> taskFactory) {
+        return queue(taskFactory.newTask());
+    }
+
+    /** @see #queue(org.apache.brooklyn.api.management.TaskAdaptable)  */
+    public static void queue(TaskFactory<?> task1, TaskFactory<?> task2, TaskFactory<?> ...tasks) {
+        queue(task1.newTask());
+        queue(task2.newTask());
+        for (TaskFactory<?> task: tasks) queue(task.newTask());
+    }
+
+    /** @see #queue(org.apache.brooklyn.api.management.TaskAdaptable)  */
+    public static <T> Task<T> queue(String name, Callable<T> job) {
+        return DynamicTasks.queue(Tasks.<T>builder().name(name).body(job).build());
+    }
+
+    /** @see #queue(org.apache.brooklyn.api.management.TaskAdaptable)  */
+    public static <T> Task<T> queue(String name, Runnable job) {
+        return DynamicTasks.queue(Tasks.<T>builder().name(name).body(job).build());
+    }
+
+    /** queues the task if needed, i.e. if it is not yet submitted (so it will run), 
+     * or if it is submitted but not queued and we are in a queueing context (so it is available for informational purposes) */
+    public static <T extends TaskAdaptable<?>> T queueIfNeeded(T task) {
+        if (!Tasks.isQueued(task)) {
+            if (Tasks.isSubmitted(task) && getTaskQueuingContext()==null) {
+                // already submitted and not in a queueing context, don't try to queue
+            } else {
+                // needs submitting, put it in the queue
+                // (will throw an error if we are not a queueing context)
+                queue(task);
+            }
+        }
+        return task;
+    }
+    
+    /** submits/queues the given task if needed, and gets the result (unchecked) 
+     * only permitted in a queueing context (ie a DST main job) if the task is not yet submitted */
+    // things get really confusing if you try to queueInTaskHierarchy -- easy to cause deadlocks!
+    public static <T> T get(TaskAdaptable<T> t) {
+        return queueIfNeeded(t).asTask().getUnchecked();
+    }
+
+    /** As {@link #drain(Duration, boolean)} waiting forever and throwing the first error 
+     * (excluding errors in inessential tasks),
+     * then returning the last task in the queue (which is guaranteed to have finished without error,
+     * if this method returns without throwing) */
+    public static Task<?> waitForLast() {
+        drain(null, true);
+        // this call to last is safe, as the above guarantees everything will have run
+        // (on errors the above will throw so we won't come here)
+        List<Task<?>> q = DynamicTasks.getTaskQueuingContext().getQueue();
+        return q.isEmpty() ? null : Iterables.getLast(q);
+    }
+    
+    /** Calls {@link TaskQueueingContext#drain(Duration, boolean, boolean)} on the current task context */
+    public static TaskQueueingContext drain(Duration optionalTimeout, boolean throwFirstError) {
+        TaskQueueingContext qc = DynamicTasks.getTaskQueuingContext();
+        Preconditions.checkNotNull(qc, "Cannot drain when there is no queueing context");
+        qc.drain(optionalTimeout, false, throwFirstError);
+        return qc;
+    }
+
+    /** as {@link Tasks#swallowChildrenFailures()} but requiring a {@link TaskQueueingContext}. */
+    @Beta
+    public static void swallowChildrenFailures() {
+        Preconditions.checkNotNull(DynamicTasks.getTaskQueuingContext(), "Task queueing context required here");
+        Tasks.swallowChildrenFailures();
+    }
+
+    /** same as {@link Tasks#markInessential()}
+     * (but included here for convenience as it is often used in conjunction with {@link DynamicTasks}) */
+    public static void markInessential() {
+        Tasks.markInessential();
+    }
+
+    /** queues the task if possible, otherwise submits it asynchronously; returns the task for callers to 
+     * {@link Task#getUnchecked()} or {@link Task#blockUntilEnded()} */
+    public static <T> Task<T> submit(TaskAdaptable<T> task, Entity entity) {
+        return queueIfPossible(task).orSubmitAsync(entity).asTask();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ExecutionListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ExecutionListener.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ExecutionListener.java
new file mode 100644
index 0000000..5341b21
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ExecutionListener.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 org.apache.brooklyn.core.util.task;
+
+import org.apache.brooklyn.api.management.Task;
+
+public interface ExecutionListener {
+
+    /** invoked when a task completes: 
+     * {@link Task#getEndTimeUtc()} and {@link Task#isDone()} are guaranteed to be set,
+     * and {@link Task#get()} should return immediately for most Task implementations
+     * (care has been taken to avoid potential deadlocks here, waiting for a result!)  */
+    public void onTaskDone(Task<?> task);
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ExecutionUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ExecutionUtils.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ExecutionUtils.java
new file mode 100644
index 0000000..be677e3
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ExecutionUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import groovy.lang.Closure;
+
+import java.util.concurrent.Callable;
+
+import com.google.common.base.Function;
+import com.google.common.base.Throwables;
+
+public class ExecutionUtils {
+    /**
+     * Attempts to run/call the given object, with the given arguments if possible, preserving the return value if there is one (null otherwise);
+     * throws exception if the callable is a non-null object which cannot be invoked (not a callable or runnable)
+     * @deprecated since 0.7.0 ; this super-loose typing should be avoided; if it is needed, let's move it to one of the Groovy compatibility classes
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static Object invoke(Object callable, Object ...args) {
+        if (callable instanceof Closure) return ((Closure<?>)callable).call(args);
+        if (callable instanceof Callable) {
+            try {
+                return ((Callable<?>)callable).call();
+            } catch (Throwable t) {
+                throw Throwables.propagate(t);
+            }
+        }
+        if (callable instanceof Runnable) { ((Runnable)callable).run(); return null; }
+        if (callable instanceof Function && args.length == 1) { return ((Function)callable).apply(args[0]); }
+        if (callable==null) return null;
+        throw new IllegalArgumentException("Cannot invoke unexpected object "+callable+" of type "+callable.getClass()+", with "+args.length+" args");
+    }
+}



[35/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/osgi/Osgis.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/osgi/Osgis.java b/core/src/main/java/brooklyn/util/osgi/Osgis.java
deleted file mode 100644
index 3bf972e..0000000
--- a/core/src/main/java/brooklyn/util/osgi/Osgis.java
+++ /dev/null
@@ -1,719 +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.osgi;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.jar.Attributes;
-import java.util.jar.JarInputStream;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-
-import javax.annotation.Nullable;
-
-import org.apache.felix.framework.FrameworkFactory;
-import org.apache.felix.framework.util.StringMap;
-import org.apache.felix.framework.util.manifestparser.ManifestParser;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-import org.osgi.framework.launch.Framework;
-import org.osgi.framework.namespace.PackageNamespace;
-import org.osgi.framework.wiring.BundleCapability;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
-
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.collections.MutableSet;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.exceptions.ReferenceWithError;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.net.Urls;
-import brooklyn.util.os.Os;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.text.Strings;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.base.Stopwatch;
-
-/** 
- * utilities for working with osgi.
- * osgi support is in early days (June 2014) so this class is beta, subject to change,
- * particularly in how framework is started and bundles installed.
- * 
- * @since 0.7.0  */
-@Beta
-public class Osgis {
-    private static final Logger LOG = LoggerFactory.getLogger(Osgis.class);
-
-    private static final String EXTENSION_PROTOCOL = "system";
-    private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
-    private static final Set<String> SYSTEM_BUNDLES = MutableSet.of();
-
-    public static class VersionedName {
-        private final String symbolicName;
-        private final Version version;
-        public VersionedName(Bundle b) {
-            this.symbolicName = b.getSymbolicName();
-            this.version = b.getVersion();
-        }
-        public VersionedName(String symbolicName, Version version) {
-            this.symbolicName = symbolicName;
-            this.version = version;
-        }
-        @Override public String toString() {
-            return symbolicName + ":" + Strings.toString(version);
-        }
-        public boolean equals(String sn, String v) {
-            return symbolicName.equals(sn) && (version == null && v == null || version != null && version.toString().equals(v));
-        }
-        public boolean equals(String sn, Version v) {
-            return symbolicName.equals(sn) && (version == null && v == null || version != null && version.equals(v));
-        }
-        public String getSymbolicName() {
-            return symbolicName;
-        }
-        public Version getVersion() {
-            return version;
-        }
-        @Override
-        public int hashCode() {
-            return Objects.hashCode(symbolicName, version);
-        }
-        @Override
-        public boolean equals(Object other) {
-            if (!(other instanceof VersionedName)) return false;
-            VersionedName o = (VersionedName) other;
-            return Objects.equal(symbolicName, o.symbolicName) && Objects.equal(version, o.version);
-        }
-    }
-    
-    public static class BundleFinder {
-        protected final Framework framework;
-        protected String symbolicName;
-        protected String version;
-        protected String url;
-        protected boolean urlMandatory = false;
-        protected final List<Predicate<? super Bundle>> predicates = MutableList.of();
-        
-        protected BundleFinder(Framework framework) {
-            this.framework = framework;
-        }
-
-        public BundleFinder symbolicName(String symbolicName) {
-            this.symbolicName = symbolicName;
-            return this;
-        }
-
-        public BundleFinder version(String version) {
-            this.version = version;
-            return this;
-        }
-        
-        public BundleFinder id(String symbolicNameOptionallyWithVersion) {
-            if (Strings.isBlank(symbolicNameOptionallyWithVersion))
-                return this;
-            
-            Maybe<VersionedName> nv = parseOsgiIdentifier(symbolicNameOptionallyWithVersion);
-            if (nv.isAbsent())
-                throw new IllegalArgumentException("Cannot parse symbolic-name:version string '"+symbolicNameOptionallyWithVersion+"'");
-
-            return id(nv.get());
-        }
-
-        private BundleFinder id(VersionedName nv) {
-            symbolicName(nv.getSymbolicName());
-            if (nv.getVersion() != null) {
-                version(nv.getVersion().toString());
-            }
-            return this;
-        }
-
-        public BundleFinder bundle(CatalogBundle bundle) {
-            if (bundle.isNamed()) {
-                symbolicName(bundle.getSymbolicName());
-                version(bundle.getVersion());
-            }
-            if (bundle.getUrl() != null) {
-                requiringFromUrl(bundle.getUrl());
-            }
-            return this;
-        }
-
-        /** Looks for a bundle matching the given URL;
-         * unlike {@link #requiringFromUrl(String)} however, if the URL does not match any bundles
-         * it will return other matching bundles <i>if</if> a {@link #symbolicName(String)} is specified.
-         */
-        public BundleFinder preferringFromUrl(String url) {
-            this.url = url;
-            urlMandatory = false;
-            return this;
-        }
-
-        /** Requires the bundle to have the given URL set as its location. */
-        public BundleFinder requiringFromUrl(String url) {
-            this.url = url;
-            urlMandatory = true;
-            return this;
-        }
-
-        /** Finds the best matching bundle. */
-        public Maybe<Bundle> find() {
-            return findOne(false);
-        }
-        
-        /** Finds the matching bundle, requiring it to be unique. */
-        public Maybe<Bundle> findUnique() {
-            return findOne(true);
-        }
-
-        protected Maybe<Bundle> findOne(boolean requireExactlyOne) {
-            if (symbolicName==null && url==null)
-                throw new IllegalStateException(this+" must be given either a symbolic name or a URL");
-            
-            List<Bundle> result = findAll();
-            if (result.isEmpty())
-                return Maybe.absent("No bundle matching "+getConstraintsDescription());
-            if (requireExactlyOne && result.size()>1)
-                return Maybe.absent("Multiple bundles ("+result.size()+") matching "+getConstraintsDescription());
-            
-            return Maybe.of(result.get(0));
-        }
-        
-        /** Finds all matching bundles, in decreasing version order. */
-        public List<Bundle> findAll() {
-            boolean urlMatched = false;
-            List<Bundle> result = MutableList.of();
-            for (Bundle b: framework.getBundleContext().getBundles()) {
-                if (symbolicName!=null && !symbolicName.equals(b.getSymbolicName())) continue;
-                if (version!=null && !Version.parseVersion(version).equals(b.getVersion())) continue;
-                for (Predicate<? super Bundle> predicate: predicates) {
-                    if (!predicate.apply(b)) continue;
-                }
-
-                // check url last, because if it isn't mandatory we should only clear if we find a url
-                // for which the other items also match
-                if (url!=null) {
-                    boolean matches = url.equals(b.getLocation());
-                    if (urlMandatory) {
-                        if (!matches) continue;
-                        else urlMatched = true;
-                    } else {
-                        if (matches) {
-                            if (!urlMatched) {
-                                result.clear();
-                                urlMatched = true;
-                            }
-                        } else {
-                            if (urlMatched) {
-                                // can't use this bundle as we have previously found a preferred bundle, with a matching url
-                                continue;
-                            }
-                        }
-                    }
-                }
-                                
-                result.add(b);
-            }
-            
-            if (symbolicName==null && url!=null && !urlMatched) {
-                // if we only "preferred" the url, and we did not match it, and we did not have a symbolic name,
-                // then clear the results list!
-                result.clear();
-            }
-
-            Collections.sort(result, new Comparator<Bundle>() {
-                @Override
-                public int compare(Bundle o1, Bundle o2) {
-                    return o2.getVersion().compareTo(o1.getVersion());
-                }
-            });
-            
-            return result;
-        }
-        
-        public String getConstraintsDescription() {
-            List<String> parts = MutableList.of();
-            if (symbolicName!=null) parts.add("symbolicName="+symbolicName);
-            if (version!=null) parts.add("version="+version);
-            if (url!=null)
-                parts.add("url["+(urlMandatory ? "required" : "preferred")+"]="+url);
-            if (!predicates.isEmpty())
-                parts.add("predicates="+predicates);
-            return Joiner.on(";").join(parts);
-        }
-        
-        public String toString() {
-            return getClass().getCanonicalName()+"["+getConstraintsDescription()+"]";
-        }
-
-        public BundleFinder version(final Predicate<Version> versionPredicate) {
-            return satisfying(new Predicate<Bundle>() {
-                @Override
-                public boolean apply(Bundle input) {
-                    return versionPredicate.apply(input.getVersion());
-                }
-            });
-        }
-        
-        public BundleFinder satisfying(Predicate<? super Bundle> predicate) {
-            predicates.add(predicate);
-            return this;
-        }
-    }
-    
-    public static BundleFinder bundleFinder(Framework framework) {
-        return new BundleFinder(framework);
-    }
-
-    /** @deprecated since 0.7.0 use {@link #bundleFinder(Framework)} */ @Deprecated
-    public static List<Bundle> getBundlesByName(Framework framework, String symbolicName, Predicate<Version> versionMatcher) {
-        return bundleFinder(framework).symbolicName(symbolicName).version(versionMatcher).findAll();
-    }
-
-    /** @deprecated since 0.7.0 use {@link #bundleFinder(Framework)} */ @Deprecated
-    public static List<Bundle> getBundlesByName(Framework framework, String symbolicName) {
-        return bundleFinder(framework).symbolicName(symbolicName).findAll();
-    }
-
-    /**
-     * Tries to find a bundle in the given framework with name matching either `name' or `name:version'.
-     * @deprecated since 0.7.0 use {@link #bundleFinder(Framework)} */ @Deprecated
-    public static Maybe<Bundle> getBundle(Framework framework, String symbolicNameOptionallyWithVersion) {
-        return bundleFinder(framework).id(symbolicNameOptionallyWithVersion).find();
-    }
-    
-    /** @deprecated since 0.7.0 use {@link #bundleFinder(Framework)} */ @Deprecated
-    public static Maybe<Bundle> getBundle(Framework framework, String symbolicName, String version) {
-        return bundleFinder(framework).symbolicName(symbolicName).version(version).find();
-    }
-
-    /** @deprecated since 0.7.0 use {@link #bundleFinder(Framework)} */ @Deprecated
-    public static Maybe<Bundle> getBundle(Framework framework, String symbolicName, Version version) {
-        return bundleFinder(framework).symbolicName(symbolicName).version(Predicates.equalTo(version)).findUnique();
-    }
-
-    // -------- creating
-    
-    /*
-     * loading framework factory and starting framework based on:
-     * http://felix.apache.org/documentation/subprojects/apache-felix-framework/apache-felix-framework-launching-and-embedding.html
-     */
-    
-    public static FrameworkFactory newFrameworkFactory() {
-        URL url = Osgis.class.getClassLoader().getResource(
-                "META-INF/services/org.osgi.framework.launch.FrameworkFactory");
-        if (url != null) {
-            try {
-                BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
-                try {
-                    for (String s = br.readLine(); s != null; s = br.readLine()) {
-                        s = s.trim();
-                        // load the first non-empty, non-commented line
-                        if ((s.length() > 0) && (s.charAt(0) != '#')) {
-                            return (FrameworkFactory) Class.forName(s).newInstance();
-                        }
-                    }
-                } finally {
-                    if (br != null) br.close();
-                }
-            } catch (Exception e) {
-                // class creation exceptions are not interesting to caller...
-                throw Exceptions.propagate(e);
-            }
-        }
-        throw new IllegalStateException("Could not find framework factory.");
-    }
-    
-    public static Framework newFrameworkStarted(String felixCacheDir, boolean clean, Map<?,?> extraStartupConfig) {
-        Map<Object,Object> cfg = MutableMap.copyOf(extraStartupConfig);
-        if (clean) cfg.put(Constants.FRAMEWORK_STORAGE_CLEAN, "onFirstInit");
-        if (felixCacheDir!=null) cfg.put(Constants.FRAMEWORK_STORAGE, felixCacheDir);
-        cfg.put(Constants.FRAMEWORK_BSNVERSION, Constants.FRAMEWORK_BSNVERSION_MULTIPLE);
-        FrameworkFactory factory = newFrameworkFactory();
-
-        Stopwatch timer = Stopwatch.createStarted();
-        Framework framework = factory.newFramework(cfg);
-        try {
-            framework.init();
-            installBootBundles(framework);
-            framework.start();
-        } catch (Exception e) {
-            // framework bundle start exceptions are not interesting to caller...
-            throw Exceptions.propagate(e);
-        }
-        LOG.debug("System bundles are: "+SYSTEM_BUNDLES);
-        LOG.debug("OSGi framework started in " + Duration.of(timer));
-        return framework;
-    }
-
-    private static void installBootBundles(Framework framework) {
-        Stopwatch timer = Stopwatch.createStarted();
-        LOG.debug("Installing OSGi boot bundles from "+Osgis.class.getClassLoader()+"...");
-        Enumeration<URL> resources;
-        try {
-            resources = Osgis.class.getClassLoader().getResources(MANIFEST_PATH);
-        } catch (IOException e) {
-            throw Exceptions.propagate(e);
-        }
-        BundleContext bundleContext = framework.getBundleContext();
-        Map<String, Bundle> installedBundles = getInstalledBundlesById(bundleContext);
-        while(resources.hasMoreElements()) {
-            URL url = resources.nextElement();
-            ReferenceWithError<?> installResult = installExtensionBundle(bundleContext, url, installedBundles, getVersionedId(framework));
-            if (installResult.hasError() && !installResult.masksErrorIfPresent()) {
-                // it's reported as a critical error, so warn here
-                LOG.warn("Unable to install manifest from "+url+": "+installResult.getError(), installResult.getError());
-            } else {
-                Object result = installResult.getWithoutError();
-                if (result instanceof Bundle) {
-                    String v = getVersionedId( (Bundle)result );
-                    SYSTEM_BUNDLES.add(v);
-                    if (installResult.hasError()) {
-                        LOG.debug(installResult.getError().getMessage()+(result!=null ? " ("+result+"/"+v+")" : ""));
-                    } else {
-                        LOG.debug("Installed "+v+" from "+url);
-                    }
-                } else if (installResult.hasError()) {
-                    LOG.debug(installResult.getError().getMessage());
-                }
-            }
-        }
-        LOG.debug("Installed OSGi boot bundles in "+Time.makeTimeStringRounded(timer)+": "+Arrays.asList(framework.getBundleContext().getBundles()));
-    }
-
-    private static Map<String, Bundle> getInstalledBundlesById(BundleContext bundleContext) {
-        Map<String, Bundle> installedBundles = new HashMap<String, Bundle>();
-        Bundle[] bundles = bundleContext.getBundles();
-        for (Bundle b : bundles) {
-            installedBundles.put(getVersionedId(b), b);
-        }
-        return installedBundles;
-    }
-
-    /** Wraps the bundle if successful or already installed, wraps TRUE if it's the system entry,
-     * wraps null if the bundle is already installed from somewhere else;
-     * in all these cases <i>masking</i> an explanatory error if already installed or it's the system entry.
-     * <p>
-     * Returns an instance wrapping null and <i>throwing</i> an error if the bundle could not be installed.
-     */
-    private static ReferenceWithError<?> installExtensionBundle(BundleContext bundleContext, URL manifestUrl, Map<String, Bundle> installedBundles, String frameworkVersionedId) {
-        //ignore http://felix.extensions:9/ system entry
-        if("felix.extensions".equals(manifestUrl.getHost())) 
-            return ReferenceWithError.newInstanceMaskingError(null, new IllegalArgumentException("Skipping install of internal extension bundle from "+manifestUrl));
-
-        try {
-            Manifest manifest = readManifest(manifestUrl);
-            if (!isValidBundle(manifest)) 
-                return ReferenceWithError.newInstanceMaskingError(null, new IllegalArgumentException("Resource at "+manifestUrl+" is not an OSGi bundle: no valid manifest"));
-            
-            String versionedId = getVersionedId(manifest);
-            URL bundleUrl = ResourceUtils.getContainerUrl(manifestUrl, MANIFEST_PATH);
-
-            Bundle existingBundle = installedBundles.get(versionedId);
-            if (existingBundle != null) {
-                if (!bundleUrl.equals(existingBundle.getLocation()) &&
-                        //the framework bundle is always pre-installed, don't display duplicate info
-                        !versionedId.equals(frameworkVersionedId)) {
-                    return ReferenceWithError.newInstanceMaskingError(null, new IllegalArgumentException("Bundle "+versionedId+" (from manifest " + manifestUrl + ") is already installed, from " + existingBundle.getLocation()));
-                }
-                return ReferenceWithError.newInstanceMaskingError(existingBundle, new IllegalArgumentException("Bundle "+versionedId+" from manifest " + manifestUrl + " is already installed"));
-            }
-            
-            byte[] jar = buildExtensionBundle(manifest);
-            LOG.debug("Installing boot bundle " + bundleUrl);
-            //mark the bundle as extension so we can detect it later using the "system:" protocol
-            //(since we cannot access BundleImpl.isExtension)
-            Bundle newBundle = bundleContext.installBundle(EXTENSION_PROTOCOL + ":" + bundleUrl.toString(), new ByteArrayInputStream(jar));
-            installedBundles.put(versionedId, newBundle);
-            return ReferenceWithError.newInstanceWithoutError(newBundle);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            return ReferenceWithError.newInstanceThrowingError(null, 
-                new IllegalStateException("Problem installing extension bundle " + manifestUrl + ": "+e, e));
-        }
-    }
-
-    private static Manifest readManifest(URL manifestUrl) throws IOException {
-        Manifest manifest;
-        InputStream in = null;
-        try {
-            in = manifestUrl.openStream();
-            manifest = new Manifest(in);
-        } finally {
-            if (in != null) {
-                try {in.close();} 
-                catch (Exception e) {};
-            }
-        }
-        return manifest;
-    }
-
-    private static byte[] buildExtensionBundle(Manifest manifest) throws IOException {
-        Attributes atts = manifest.getMainAttributes();
-
-        //the following properties are invalid in extension bundles
-        atts.remove(new Attributes.Name(Constants.IMPORT_PACKAGE));
-        atts.remove(new Attributes.Name(Constants.REQUIRE_BUNDLE));
-        atts.remove(new Attributes.Name(Constants.BUNDLE_NATIVECODE));
-        atts.remove(new Attributes.Name(Constants.DYNAMICIMPORT_PACKAGE));
-        atts.remove(new Attributes.Name(Constants.BUNDLE_ACTIVATOR));
-        
-        //mark as extension bundle
-        atts.putValue(Constants.FRAGMENT_HOST, "system.bundle; extension:=framework");
-
-        //create the jar containing the manifest
-        ByteArrayOutputStream jar = new ByteArrayOutputStream();
-        JarOutputStream out = new JarOutputStream(jar, manifest);
-        out.close();
-        return jar.toByteArray();
-    }
-
-    private static boolean isValidBundle(Manifest manifest) {
-        Attributes atts = manifest.getMainAttributes();
-        return atts.containsKey(new Attributes.Name(Constants.BUNDLE_MANIFESTVERSION));
-    }
-
-    private static String getVersionedId(Bundle b) {
-        return b.getSymbolicName() + ":" + b.getVersion();
-    }
-
-    private static String getVersionedId(Manifest manifest) {
-        Attributes atts = manifest.getMainAttributes();
-        return atts.getValue(Constants.BUNDLE_SYMBOLICNAME) + ":" +
-            atts.getValue(Constants.BUNDLE_VERSION);
-    }
-
-    /**
-     * Installs a bundle from the given URL, doing a check if already installed, and
-     * using the {@link ResourceUtils} loader for this project (brooklyn core)
-     */
-    public static Bundle install(Framework framework, String url) throws BundleException {
-        boolean isLocal = isLocalUrl(url);
-        String localUrl = url;
-        if (!isLocal) {
-            localUrl = cacheFile(url);
-        }
-
-        try {
-            Bundle bundle = getInstalledBundle(framework, localUrl);
-            if (bundle != null) {
-                return bundle;
-            }
-    
-            // use our URL resolution so we get classpath items
-            LOG.debug("Installing bundle into {} from url: {}", framework, url);
-            InputStream stream = getUrlStream(localUrl);
-            Bundle installedBundle = framework.getBundleContext().installBundle(url, stream);
-            
-            return installedBundle;
-        } finally {
-            if (!isLocal) {
-                try {
-                    new File(new URI(localUrl)).delete();
-                } catch (URISyntaxException e) {
-                    throw Exceptions.propagate(e);
-                }
-            }
-        }
-    }
-
-    private static String cacheFile(String url) {
-        InputStream in = getUrlStream(url);
-        File cache = Os.writeToTempFile(in, "bundle-cache", "jar");
-        return cache.toURI().toString();
-    }
-
-    private static boolean isLocalUrl(String url) {
-        String protocol = Urls.getProtocol(url);
-        return "file".equals(protocol) ||
-                "classpath".equals(protocol) ||
-                "jar".equals(protocol);
-    }
-
-    private static Bundle getInstalledBundle(Framework framework, String url) {
-        Bundle bundle = framework.getBundleContext().getBundle(url);
-        if (bundle != null) {
-            return bundle;
-        }
-
-        // We now support same version installed multiple times (avail since OSGi 4.3+).
-        // However we do not support overriding *system* bundles, ie anything already on the classpath.
-        // If we wanted to disable multiple versions, see comments below, and reference to FRAMEWORK_BSNVERSION_MULTIPLE above.
-        
-        // Felix already assumes the stream is pointing to a JAR
-        JarInputStream stream;
-        try {
-            stream = new JarInputStream(getUrlStream(url));
-        } catch (IOException e) {
-            throw Exceptions.propagate(e);
-        }
-        Manifest manifest = stream.getManifest();
-        Streams.closeQuietly(stream);
-        if (manifest == null) {
-            throw new IllegalStateException("Missing manifest file in bundle or not a jar file.");
-        }
-        String versionedId = getVersionedId(manifest);
-        for (Bundle installedBundle : framework.getBundleContext().getBundles()) {
-            if (versionedId.equals(getVersionedId(installedBundle))) {
-                if (SYSTEM_BUNDLES.contains(versionedId)) {
-                    LOG.debug("Already have system bundle "+versionedId+" from "+installedBundle+"/"+installedBundle.getLocation()+" when requested "+url+"; not installing");
-                    // "System bundles" (ie things on the classpath) cannot be overridden
-                    return installedBundle;
-                } else {
-                    LOG.debug("Already have bundle "+versionedId+" from "+installedBundle+"/"+installedBundle.getLocation()+" when requested "+url+"; but it is not a system bundle so proceeding");
-                    // Other bundles can be installed multiple times. To ignore multiples and continue to use the old one, 
-                    // just return the installedBundle as done just above for system bundles.
-                }
-            }
-        }
-        return null;
-    }
-
-    private static InputStream getUrlStream(String url) {
-        return ResourceUtils.create(Osgis.class).getResourceFromUrl(url);
-    }
-    
-    public static boolean isExtensionBundle(Bundle bundle) {
-        String location = bundle.getLocation();
-        return location != null && 
-                EXTENSION_PROTOCOL.equals(Urls.getProtocol(location));
-    }
-
-    /** Takes a string which might be of the form "symbolic-name" or "symbolic-name:version" (or something else entirely)
-     * and returns a VersionedName. The versionedName.getVersion() will be null if if there was no version in the input
-     * (or returning {@link Maybe#absent()} if not valid, with a suitable error message). */
-    public static Maybe<VersionedName> parseOsgiIdentifier(String symbolicNameOptionalWithVersion) {
-        if (Strings.isBlank(symbolicNameOptionalWithVersion))
-            return Maybe.absent("OSGi identifier is blank");
-        
-        String[] parts = symbolicNameOptionalWithVersion.split(":");
-        if (parts.length>2)
-            return Maybe.absent("OSGi identifier has too many parts; max one ':' symbol");
-        
-        Version v = null;
-        if (parts.length == 2) {
-            try {
-                v = Version.parseVersion(parts[1]);
-            } catch (IllegalArgumentException e) {
-                return Maybe.absent("OSGi identifier has invalid version string ("+e.getMessage()+")");
-            }
-        }
-        
-        return Maybe.of(new VersionedName(parts[0], v));
-    }
-
-    /**
-     * The class is not used, staying for future reference.
-     * Remove after OSGi transition is completed.
-     */
-    public static class ManifestHelper {
-        
-        private static ManifestParser parse;
-        private Manifest manifest;
-        private String source;
-
-        private static final String WIRING_PACKAGE = PackageNamespace.PACKAGE_NAMESPACE;
-        
-        public static ManifestHelper forManifestContents(String contents) throws IOException, BundleException {
-            ManifestHelper result = forManifest(Streams.newInputStreamWithContents(contents));
-            result.source = contents;
-            return result;
-        }
-        
-        public static ManifestHelper forManifest(URL url) throws IOException, BundleException {
-            InputStream in = null;
-            try {
-                in = url.openStream();
-                return forManifest(in);
-            } finally {
-                if (in != null) in.close();
-            }
-        }
-        
-        public static ManifestHelper forManifest(InputStream in) throws IOException, BundleException {
-            return forManifest(new Manifest(in));
-        }
-
-        public static ManifestHelper forManifest(Manifest manifest) throws BundleException {
-            ManifestHelper result = new ManifestHelper();
-            result.manifest = manifest;
-            parse = new ManifestParser(null, null, null, new StringMap(manifest.getMainAttributes()));
-            return result;
-        }
-        
-        public String getSymbolicName() {
-            return parse.getSymbolicName();
-        }
-
-        public Version getVersion() {
-            return parse.getBundleVersion();
-        }
-
-        public String getSymbolicNameVersion() {
-            return getSymbolicName()+":"+getVersion();
-        }
-
-        public List<String> getExportedPackages() {
-            MutableList<String> result = MutableList.of();
-            for (BundleCapability c: parse.getCapabilities()) {
-                if (WIRING_PACKAGE.equals(c.getNamespace())) {
-                    result.add((String)c.getAttributes().get(WIRING_PACKAGE));
-                }
-            }
-            return result;
-        }
-        
-        @Nullable public String getSource() {
-            return source;
-        }
-        
-        public Manifest getManifest() {
-            return manifest;
-        }
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/AbstractExecutionContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/AbstractExecutionContext.java b/core/src/main/java/brooklyn/util/task/AbstractExecutionContext.java
deleted file mode 100644
index f3511d7..0000000
--- a/core/src/main/java/brooklyn/util/task/AbstractExecutionContext.java
+++ /dev/null
@@ -1,75 +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 java.util.Map;
-import java.util.concurrent.Callable;
-
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.ExecutionManager;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskAdaptable;
-
-import com.google.common.collect.Maps;
-
-public abstract class AbstractExecutionContext implements ExecutionContext {
-
-    /**
-     * Submits the given runnable/callable/task for execution (in a separate thread);
-     * supported keys in the map include: tags (add'l tags to put on the resulting task), 
-     * description (string), and others as described in the reference below
-     *   
-     * @see ExecutionManager#submit(Map, Task) 
-     */
-    @Override
-    public Task<?> submit(Map<?,?> properties, Runnable runnable) { return submitInternal(properties, runnable); }
-    
-    /** @see #submit(Map, Runnable) */
-    @Override
-    public Task<?> submit(Runnable runnable) { return submitInternal(Maps.newLinkedHashMap(), runnable); }
- 
-    /** @see #submit(Map, Runnable) */
-    @Override
-    public <T> Task<T> submit(Callable<T> callable) { return submitInternal(Maps.newLinkedHashMap(), callable); }
-    
-    /** @see #submit(Map, Runnable) */
-    @Override
-    public <T> Task<T> submit(Map<?,?> properties, Callable<T> callable) { return submitInternal(properties, callable); }
- 
-    /** @see #submit(Map, Runnable) */
-    @Override
-    public <T> Task<T> submit(TaskAdaptable<T> task) { return submitInternal(Maps.newLinkedHashMap(), task.asTask()); }
-
-    /** @see #submit(Map, Runnable) */
-    @Override
-    public <T> Task<T> submit(Map<?,?> properties, TaskAdaptable<T> task) { return submitInternal(properties, task.asTask()); }
-
-    /**
-     * Provided for compatibility
-     * 
-     * Submit is preferred if a handle on the resulting Task is desired (although a task can be passed in so this is not always necessary) 
-     *
-     * @see #submit(Map, Runnable) 
-     */
-    public void execute(Runnable r) { submit(r); }
-
-    /** does the work internally of submitting the task; note that the return value may be a wrapper task even if a task is passed in,
-     * if the execution context where the target should run is different (e.g. submitting an effector task cross-context) */
-    protected abstract <T> Task<T> submitInternal(Map<?,?> properties, Object task);
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/BasicExecutionContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/BasicExecutionContext.java b/core/src/main/java/brooklyn/util/task/BasicExecutionContext.java
deleted file mode 100644
index 8942a18..0000000
--- a/core/src/main/java/brooklyn/util/task/BasicExecutionContext.java
+++ /dev/null
@@ -1,221 +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 java.lang.reflect.Proxy;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.ExecutionManager;
-import org.apache.brooklyn.api.management.HasTaskChildren;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskAdaptable;
-import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
-import org.apache.brooklyn.core.management.entitlement.Entitlements;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.basic.BrooklynTaskTags.WrappedEntity;
-import brooklyn.entity.basic.EntityInternal;
-
-import com.google.common.base.Function;
-import com.google.common.collect.Iterables;
-
-/**
- * A means of executing tasks against an ExecutionManager with a given bucket/set of tags pre-defined
- * (so that it can look like an {@link Executor} and also supply {@link ExecutorService#submit(Callable)}
- */
-public class BasicExecutionContext extends AbstractExecutionContext {
-    
-    private static final Logger log = LoggerFactory.getLogger(BasicExecutionContext.class);
-    
-    static final ThreadLocal<BasicExecutionContext> perThreadExecutionContext = new ThreadLocal<BasicExecutionContext>();
-    
-    public static BasicExecutionContext getCurrentExecutionContext() { return perThreadExecutionContext.get(); }
-
-    final ExecutionManager executionManager;
-    final Set<Object> tags = new LinkedHashSet<Object>();
-
-    public BasicExecutionContext(ExecutionManager executionManager) {
-        this(Collections.emptyMap(), executionManager);
-    }
-    
-    /**
-     * Supported flags are {@code tag} and {@code tags}
-     * 
-     * @see ExecutionManager#submit(Map, Task)
-     */
-    public BasicExecutionContext(Map<?, ?> flags, ExecutionManager executionManager) {
-        this.executionManager = executionManager;
-
-        if (flags.get("tag") != null) tags.add(flags.remove("tag"));
-        if (flags.containsKey("tags")) tags.addAll((Collection<?>)flags.remove("tags"));
-
-        // FIXME brooklyn-specific check, just for sanity
-        // the context tag should always be a non-proxy entity, because that is what is passed to effector tasks
-        // which may require access to internal methods
-        for (Object tag: tags) {
-            if (tag instanceof BrooklynTaskTags.WrappedEntity) {
-                if (Proxy.isProxyClass(((WrappedEntity)tag).entity.getClass())) {
-                    log.warn(""+this+" has entity proxy in "+tag);
-                }
-            }
-        }
-    }
-
-    public ExecutionManager getExecutionManager() {
-        return executionManager;
-    }
-    
-    /** returns tasks started by this context (or tasks which have all the tags on this object) */
-    public Set<Task<?>> getTasks() { return executionManager.getTasksWithAllTags((Set<?>)tags); }
-     
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    @Override
-    protected <T> Task<T> submitInternal(Map<?,?> propertiesQ, final Object task) {
-        if (task instanceof TaskAdaptable<?> && !(task instanceof Task<?>)) 
-            return submitInternal(propertiesQ, ((TaskAdaptable<?>)task).asTask());
-        
-        Map properties = propertiesQ;
-        if (properties.get("tags")==null) properties.put("tags", new ArrayList()); 
-        Collection taskTags = (Collection)properties.get("tags");
-        
-        // FIXME some of this is brooklyn-specific logic, should be moved to a BrooklynExecContext subclass;
-        // the issue is that we want to ensure that cross-entity calls switch execution contexts;
-        // previously it was all very messy how that was handled (and it didn't really handle it in many cases)
-        if (task instanceof Task<?>) taskTags.addAll( ((Task<?>)task).getTags() ); 
-        Entity target = BrooklynTaskTags.getWrappedEntityOfType(taskTags, BrooklynTaskTags.TARGET_ENTITY);
-        
-        if (target!=null && !tags.contains(BrooklynTaskTags.tagForContextEntity(target))) {
-            // task is switching execution context boundaries
-            /* 
-             * longer notes:
-             * you fall in to this block if the caller requests a target entity different to the current context 
-             * (e.g. where entity X is invoking an effector on Y, it will start in X's context, 
-             * but the effector should run in Y's context).
-             * 
-             * if X is invoking an effector on himself in his own context, or a sensor or other task, it will not come in to this block.
-             */
-            final ExecutionContext tc = ((EntityInternal)target).getExecutionContext();
-            if (log.isDebugEnabled())
-                log.debug("Switching task context on execution of "+task+": from "+this+" to "+target+" (in "+Tasks.current()+")");
-            
-            if (task instanceof Task<?>) {
-                final Task<T> t = (Task<T>)task;
-                if (!Tasks.isQueuedOrSubmitted(t) && (!(Tasks.current() instanceof HasTaskChildren) || 
-                        !Iterables.contains( ((HasTaskChildren)Tasks.current()).getChildren(), t ))) {
-                    // this task is switching execution context boundaries _and_ it is not a child and not yet queued,
-                    // so wrap it in a task running in this context to keep a reference to the child
-                    // (this matters when we are navigating in the GUI; without it we lose the reference to the child 
-                    // when browsing in the context of the parent)
-                    return submit(Tasks.<T>builder().name("Cross-context execution: "+t.getDescription()).dynamic(true).body(new Callable<T>() {
-                        public T call() { 
-                            return DynamicTasks.get(t); 
-                        }
-                    }).build());
-                } else {
-                    // if we are already tracked by parent, just submit it 
-                    return tc.submit(t);
-                }
-            } else {
-                // as above, but here we are definitely not a child (what we are submitting isn't even a task)
-                // (will only come here if properties defines tags including a target entity, which probably never happens) 
-                submit(Tasks.<T>builder().name("Cross-context execution").dynamic(true).body(new Callable<T>() {
-                    public T call() {
-                        if (task instanceof Callable) {
-                            return DynamicTasks.queue( Tasks.<T>builder().dynamic(false).body((Callable<T>)task).build() ).getUnchecked();
-                        } else if (task instanceof Runnable) {
-                            return DynamicTasks.queue( Tasks.<T>builder().dynamic(false).body((Runnable)task).build() ).getUnchecked();
-                        } else {
-                            throw new IllegalArgumentException("Unhandled task type: "+task+"; type="+(task!=null ? task.getClass() : "null"));
-                        }
-                    }
-                }).build());
-            }
-        }
-        
-        EntitlementContext entitlementContext = BrooklynTaskTags.getEntitlement(taskTags);
-        if (entitlementContext==null)
-        entitlementContext = Entitlements.getEntitlementContext();
-        if (entitlementContext!=null) {
-            taskTags.add(BrooklynTaskTags.tagForEntitlement(entitlementContext));
-        }
-
-        taskTags.addAll(tags);
-        
-        if (Tasks.current()!=null && BrooklynTaskTags.isTransient(Tasks.current()) 
-                && !taskTags.contains(BrooklynTaskTags.NON_TRANSIENT_TASK_TAG) && !taskTags.contains(BrooklynTaskTags.TRANSIENT_TASK_TAG)) {
-            // tag as transient if submitter is transient, unless explicitly tagged as non-transient
-            taskTags.add(BrooklynTaskTags.TRANSIENT_TASK_TAG);
-        }
-        
-        final Object startCallback = properties.get("newTaskStartCallback");
-        properties.put("newTaskStartCallback", new Function<Object,Void>() {
-            public Void apply(Object it) {
-                registerPerThreadExecutionContext();
-                if (startCallback!=null) ExecutionUtils.invoke(startCallback, it);
-                return null;
-            }});
-        
-        final Object endCallback = properties.get("newTaskEndCallback");
-        properties.put("newTaskEndCallback", new Function<Object,Void>() {
-            public Void apply(Object it) {
-                try {
-                    if (endCallback!=null) ExecutionUtils.invoke(endCallback, it);
-                } finally {
-                    clearPerThreadExecutionContext();
-                }
-                return null;
-            }});
-        
-        if (task instanceof Task) {
-            return executionManager.submit(properties, (Task)task);
-        } else if (task instanceof Callable) {
-            return executionManager.submit(properties, (Callable)task);
-        } else if (task instanceof Runnable) {
-            return (Task<T>) executionManager.submit(properties, (Runnable)task);
-        } else {
-            throw new IllegalArgumentException("Unhandled task type: task="+task+"; type="+(task!=null ? task.getClass() : "null"));
-        }
-    }
-    
-    private void registerPerThreadExecutionContext() { perThreadExecutionContext.set(this); }
-
-    private void clearPerThreadExecutionContext() { perThreadExecutionContext.remove(); }
-
-    @Override
-    public boolean isShutdown() {
-        return getExecutionManager().isShutdown();
-    }
-
-    @Override
-    public String toString() {
-        return super.toString()+"("+tags+")";
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/BasicExecutionManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/BasicExecutionManager.java b/core/src/main/java/brooklyn/util/task/BasicExecutionManager.java
deleted file mode 100644
index 13d035b..0000000
--- a/core/src/main/java/brooklyn/util/task/BasicExecutionManager.java
+++ /dev/null
@@ -1,755 +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 com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.brooklyn.api.management.ExecutionManager;
-import org.apache.brooklyn.api.management.HasTaskChildren;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskAdaptable;
-import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.text.Identifiers;
-
-import com.google.common.annotations.Beta;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.CaseFormat;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.ExecutionList;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-
-/**
- * Manages the execution of atomic tasks and scheduled (recurring) tasks,
- * including setting tags and invoking callbacks.
- */
-public class BasicExecutionManager implements ExecutionManager {
-    private static final Logger log = LoggerFactory.getLogger(BasicExecutionManager.class);
-
-    private static final boolean RENAME_THREADS = BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_RENAME_THREADS);
-    
-    private static class PerThreadCurrentTaskHolder {
-        public static final ThreadLocal<Task<?>> perThreadCurrentTask = new ThreadLocal<Task<?>>();
-    }
-
-    public static ThreadLocal<Task<?>> getPerThreadCurrentTask() {
-        return PerThreadCurrentTaskHolder.perThreadCurrentTask;
-    }
-
-    private final ThreadFactory threadFactory;
-    
-    private final ThreadFactory daemonThreadFactory;
-    
-    private final ExecutorService runner;
-        
-    private final ScheduledExecutorService delayedRunner;
-    
-    // TODO Could have a set of all knownTasks; but instead we're having a separate set per tag,
-    // so the same task could be listed multiple times if it has multiple tags...
-
-    //access to this field AND to members in this field is synchronized, 
-    //to allow us to preserve order while guaranteeing thread-safe
-    //(but more testing is needed before we are completely sure it is thread-safe!)
-    //synch blocks are as finely grained as possible for efficiency;
-    //NB CopyOnWriteArraySet is a perf bottleneck, and the simple map makes it easier to remove when a tag is empty
-    private Map<Object,Set<Task<?>>> tasksByTag = new HashMap<Object,Set<Task<?>>>();
-    
-    private ConcurrentMap<String,Task<?>> tasksById = new ConcurrentHashMap<String,Task<?>>();
-
-    private ConcurrentMap<Object, TaskScheduler> schedulerByTag = new ConcurrentHashMap<Object, TaskScheduler>();
-
-    /** count of all tasks submitted, including finished */
-    private final AtomicLong totalTaskCount = new AtomicLong();
-    
-    /** tasks submitted but not yet done (or in cases of interruption/cancelled not yet GC'd) */
-    private Map<String,String> incompleteTaskIds = new ConcurrentHashMap<String,String>();
-    
-    /** tasks started but not yet finished */
-    private final AtomicInteger activeTaskCount = new AtomicInteger();
-    
-    private final List<ExecutionListener> listeners = new CopyOnWriteArrayList<ExecutionListener>();
-    
-    private final static ThreadLocal<String> threadOriginalName = new ThreadLocal<String>() {
-        protected String initialValue() {
-            // should not happen, as only access is in _afterEnd with a check that _beforeStart was invoked 
-            log.warn("No original name recorded for thread "+Thread.currentThread().getName()+"; task "+Tasks.current());
-            return "brooklyn-thread-pool-"+Identifiers.makeRandomId(8);
-        }
-    };
-    
-    public BasicExecutionManager(String contextid) {
-        threadFactory = newThreadFactory(contextid);
-        daemonThreadFactory = new ThreadFactoryBuilder()
-                .setThreadFactory(threadFactory)
-                .setDaemon(true)
-                .build();
-                
-        // use Executors.newCachedThreadPool(daemonThreadFactory), but timeout of 1s rather than 60s for better shutdown!
-        runner = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 10L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), 
-                daemonThreadFactory);
-            
-        delayedRunner = new ScheduledThreadPoolExecutor(1, daemonThreadFactory);
-    }
-    
-    private final static class UncaughtExceptionHandlerImplementation implements Thread.UncaughtExceptionHandler {
-        @Override
-        public void uncaughtException(Thread t, Throwable e) {
-            log.error("Uncaught exception in thread "+t.getName(), e);
-        }
-    }
-    
-    /** 
-     * For use by overriders to use custom thread factory.
-     * But be extremely careful: called by constructor, so before sub-class' constructor will
-     * have been invoked!
-     */
-    protected ThreadFactory newThreadFactory(String contextid) {
-        return new ThreadFactoryBuilder()
-                .setNameFormat("brooklyn-execmanager-"+contextid+"-%d")
-                .setUncaughtExceptionHandler(new UncaughtExceptionHandlerImplementation())
-                .build();
-    }
-    
-    public void shutdownNow() {
-        runner.shutdownNow();
-        delayedRunner.shutdownNow();
-    }
-    
-    public void addListener(ExecutionListener listener) {
-        listeners.add(listener);
-    }
-    
-    public void removeListener(ExecutionListener listener) {
-        listeners.remove(listener);
-    }
-    
-    /**
-     * Deletes the given tag, including all tasks using this tag.
-     * 
-     * Useful, for example, if an entity is being expunged so that we don't keep holding
-     * a reference to it as a tag.
-     */
-    public void deleteTag(Object tag) {
-        Set<Task<?>> tasks;
-        synchronized (tasksByTag) {
-            tasks = tasksByTag.remove(tag);
-        }
-        if (tasks != null) {
-            for (Task<?> task : tasks) {
-                deleteTask(task);
-            }
-        }
-    }
-
-    public void deleteTask(Task<?> task) {
-        boolean removed = deleteTaskNonRecursive(task);
-        if (!removed) return;
-        
-        if (task instanceof HasTaskChildren) {
-            List<Task<?>> children = ImmutableList.copyOf(((HasTaskChildren)task).getChildren());
-            for (Task<?> child : children) {
-                deleteTask(child);
-            }
-        }
-    }
-
-    protected boolean deleteTaskNonRecursive(Task<?> task) {
-        Set<?> tags = checkNotNull(task, "task").getTags();
-        for (Object tag : tags) {
-            synchronized (tasksByTag) {
-                Set<Task<?>> tasks = tasksWithTagLiveOrNull(tag);
-                if (tasks != null) {
-                    tasks.remove(task);
-                    if (tasks.isEmpty()) {
-                        tasksByTag.remove(tag);
-                    }
-                }
-            }
-        }
-        Task<?> removed = tasksById.remove(task.getId());
-        incompleteTaskIds.remove(task.getId());
-        if (removed!=null && removed.isSubmitted() && !removed.isDone()) {
-            log.warn("Deleting submitted task before completion: "+removed+"; this task will continue to run in the background outwith "+this+", but perhaps it should have been cancelled?");
-        }
-        return removed != null;
-    }
-
-    public boolean isShutdown() {
-        return runner.isShutdown();
-    }
-    
-    /** count of all tasks submitted */
-    public long getTotalTasksSubmitted() {
-        return totalTaskCount.get();
-    }
-    
-    /** count of tasks submitted but not ended */
-    public long getNumIncompleteTasks() {
-        return incompleteTaskIds.size();
-    }
-    
-    /** count of tasks started but not ended */
-    public long getNumActiveTasks() {
-        return activeTaskCount.get();
-    }
-
-    /** count of tasks kept in memory, often including ended tasks */
-    public long getNumInMemoryTasks() {
-        return tasksById.size();
-    }
-
-    private Set<Task<?>> tasksWithTagCreating(Object tag) {
-        Preconditions.checkNotNull(tag);
-        synchronized (tasksByTag) {
-            Set<Task<?>> result = tasksWithTagLiveOrNull(tag);
-            if (result==null) {
-                result = Collections.synchronizedSet(new LinkedHashSet<Task<?>>());
-                tasksByTag.put(tag, result);
-            }
-            return result;
-        }
-    }
-
-    /** exposes live view, for internal use only */
-    @Beta
-    public Set<Task<?>> tasksWithTagLiveOrNull(Object tag) {
-        synchronized (tasksByTag) {
-            return tasksByTag.get(tag);
-        }
-    }
-
-    @Override
-    public Task<?> getTask(String id) {
-        return tasksById.get(id);
-    }
-    
-    /** not on interface because potentially expensive */
-    public List<Task<?>> getAllTasks() {
-        // not sure if synching makes any difference; have not observed CME's yet
-        // (and so far this is only called when a CME was caught on a previous operation)
-        synchronized (tasksById) {
-            return MutableList.copyOf(tasksById.values());
-        }
-    }
-    
-    @Override
-    public Set<Task<?>> getTasksWithTag(Object tag) {
-        Set<Task<?>> result = tasksWithTagLiveOrNull(tag);
-        if (result==null) return Collections.emptySet();
-        synchronized (result) {
-            return (Set<Task<?>>)Collections.unmodifiableSet(new LinkedHashSet<Task<?>>(result));
-        }
-    }
-    
-    @Override
-    public Set<Task<?>> getTasksWithAnyTag(Iterable<?> tags) {
-        Set<Task<?>> result = new LinkedHashSet<Task<?>>();
-        Iterator<?> ti = tags.iterator();
-        while (ti.hasNext()) {
-            Set<Task<?>> tasksForTag = tasksWithTagLiveOrNull(ti.next());
-            if (tasksForTag!=null) {
-                synchronized (tasksForTag) {
-                    result.addAll(tasksForTag);
-                }
-            }
-        }
-        return Collections.unmodifiableSet(result);
-    }
-
-    /** only works with at least one tag; returns empty if no tags */
-    @Override
-    public Set<Task<?>> getTasksWithAllTags(Iterable<?> tags) {
-        //NB: for this method retrieval for multiple tags could be made (much) more efficient (if/when it is used with multiple tags!)
-        //by first looking for the least-used tag, getting those tasks, and then for each of those tasks
-        //checking whether it contains the other tags (looking for second-least used, then third-least used, etc)
-        Set<Task<?>> result = new LinkedHashSet<Task<?>>();
-        boolean first = true;
-        Iterator<?> ti = tags.iterator();
-        while (ti.hasNext()) {
-            Object tag = ti.next();
-            if (first) { 
-                first = false;
-                result.addAll(getTasksWithTag(tag));
-            } else {
-                result.retainAll(getTasksWithTag(tag));
-            }
-        }
-        return Collections.unmodifiableSet(result);
-    }
-
-    /** live view of all tasks, for internal use only */
-    @Beta
-    public Collection<Task<?>> allTasksLive() { return tasksById.values(); }
-    
-    public Set<Object> getTaskTags() { 
-        synchronized (tasksByTag) {
-            return Collections.unmodifiableSet(Sets.newLinkedHashSet(tasksByTag.keySet())); 
-        }
-    }
-
-    public Task<?> submit(Runnable r) { return submit(new LinkedHashMap<Object,Object>(1), r); }
-    public Task<?> submit(Map<?,?> flags, Runnable r) { return submit(flags, new BasicTask<Void>(flags, r)); }
-
-    public <T> Task<T> submit(Callable<T> c) { return submit(new LinkedHashMap<Object,Object>(1), c); }
-    public <T> Task<T> submit(Map<?,?> flags, Callable<T> c) { return submit(flags, new BasicTask<T>(flags, c)); }
-
-    public <T> Task<T> submit(TaskAdaptable<T> t) { return submit(new LinkedHashMap<Object,Object>(1), t); }
-    public <T> Task<T> submit(Map<?,?> flags, TaskAdaptable<T> task) {
-        if (!(task instanceof Task))
-            task = task.asTask();
-        synchronized (task) {
-            if (((TaskInternal<?>)task).getInternalFuture()!=null) return (Task<T>)task;
-            return submitNewTask(flags, (Task<T>) task);
-        }
-    }
-
-    public <T> Task<T> scheduleWith(Task<T> task) { return scheduleWith(Collections.emptyMap(), task); }
-    public <T> Task<T> scheduleWith(Map<?,?> flags, Task<T> task) {
-        synchronized (task) {
-            if (((TaskInternal<?>)task).getInternalFuture()!=null) return task;
-            return submitNewTask(flags, task);
-        }
-    }
-
-    protected Task<?> submitNewScheduledTask(final Map<?,?> flags, final ScheduledTask task) {
-        tasksById.put(task.getId(), task);
-        totalTaskCount.incrementAndGet();
-        
-        beforeSubmitScheduledTaskAllIterations(flags, task);
-        
-        return submitSubsequentScheduledTask(flags, task);
-    }
-    
-    @SuppressWarnings("unchecked")
-    protected Task<?> submitSubsequentScheduledTask(final Map<?,?> flags, final ScheduledTask task) {
-        if (!task.isDone()) {
-            task.internalFuture = delayedRunner.schedule(new ScheduledTaskCallable(task, flags),
-                task.delay.toNanoseconds(), TimeUnit.NANOSECONDS);
-        } else {
-            afterEndScheduledTaskAllIterations(flags, task);
-        }
-        return task;
-    }
-
-    protected class ScheduledTaskCallable implements Callable<Object> {
-        public ScheduledTask task;
-        public Map<?,?> flags;
-
-        public ScheduledTaskCallable(ScheduledTask task, Map<?, ?> flags) {
-            this.task = task;
-            this.flags = flags;
-        }
-
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        public Object call() {
-            if (task.startTimeUtc==-1) task.startTimeUtc = System.currentTimeMillis();
-            TaskInternal<?> taskScheduled = null;
-            try {
-                beforeStartScheduledTaskSubmissionIteration(flags, task);
-                taskScheduled = (TaskInternal<?>) task.newTask();
-                taskScheduled.setSubmittedByTask(task);
-                final Callable<?> oldJob = taskScheduled.getJob();
-                final TaskInternal<?> taskScheduledF = taskScheduled;
-                taskScheduled.setJob(new Callable() { public Object call() {
-                    boolean resubmitted = false;
-                    task.recentRun = taskScheduledF;
-                    try {
-                        synchronized (task) {
-                            task.notifyAll();
-                        }
-                        Object result;
-                        try {
-                            result = oldJob.call();
-                        } catch (Exception e) {
-                            if (!Tasks.isInterrupted()) {
-                                log.warn("Error executing "+oldJob+" (scheduled job of "+task+" - "+task.getDescription()+"); cancelling scheduled execution", e);
-                            } else {
-                                log.debug("Interrupted executing "+oldJob+" (scheduled job of "+task+" - "+task.getDescription()+"); cancelling scheduled execution: "+e);
-                            }
-                            throw Exceptions.propagate(e);
-                        }
-                        task.runCount++;
-                        if (task.period!=null && !task.isCancelled()) {
-                            task.delay = task.period;
-                            submitSubsequentScheduledTask(flags, task);
-                            resubmitted = true;
-                        }
-                        return result;
-                    } finally {
-                        // do in finally block in case we were interrupted
-                        if (!resubmitted)
-                            afterEndScheduledTaskAllIterations(flags, task);
-                    }
-                }});
-                task.nextRun = taskScheduled;
-                BasicExecutionContext ec = BasicExecutionContext.getCurrentExecutionContext();
-                if (ec!=null) return ec.submit(taskScheduled);
-                else return submit(taskScheduled);
-            } finally {
-                afterEndScheduledTaskSubmissionIteration(flags, task, taskScheduled);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "ScheduledTaskCallable["+task+","+flags+"]";
-        }
-    }
-
-    private final class SubmissionCallable<T> implements Callable<T> {
-        private final Map<?, ?> flags;
-        private final Task<T> task;
-
-        private SubmissionCallable(Map<?, ?> flags, Task<T> task) {
-            this.flags = flags;
-            this.task = task;
-        }
-
-        public T call() {
-            try {
-                T result = null;
-                Throwable error = null;
-                String oldThreadName = Thread.currentThread().getName();
-                try {
-                    if (RENAME_THREADS) {
-                        String newThreadName = oldThreadName+"-"+task.getDisplayName()+
-                            "["+task.getId().substring(0, 8)+"]";
-                        Thread.currentThread().setName(newThreadName);
-                    }
-                    beforeStartAtomicTask(flags, task);
-                    if (!task.isCancelled()) {
-                        result = ((TaskInternal<T>)task).getJob().call();
-                    } else throw new CancellationException();
-                } catch(Throwable e) {
-                    error = e;
-                } finally {
-                    if (RENAME_THREADS) {
-                        Thread.currentThread().setName(oldThreadName);
-                    }
-                    afterEndAtomicTask(flags, task);
-                }
-                if (error!=null) {
-                    /* we throw, after logging debug.
-                     * the throw means the error is available for task submitters to monitor.
-                     * however it is possible no one is monitoring it, in which case we will have debug logging only for errors.
-                     * (the alternative, of warn-level logging in lots of places where we don't want it, seems worse!) 
-                     */
-                    if (log.isDebugEnabled()) {
-                        // debug only here, because most submitters will handle failures
-                        log.debug("Exception running task "+task+" (rethrowing): "+error.getMessage(), error);
-                        if (log.isTraceEnabled())
-                            log.trace("Trace for exception running task "+task+" (rethrowing): "+error.getMessage(), error);
-                    }
-                    throw Exceptions.propagate(error);
-                }
-                return result;
-            } finally {
-                ((TaskInternal<?>)task).runListeners();
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "BEM.call("+task+","+flags+")";
-        }
-    }
-
-    private final static class ListenableForwardingFutureForTask<T> extends ListenableForwardingFuture<T> {
-        private final Task<T> task;
-
-        private ListenableForwardingFutureForTask(Future<T> delegate, ExecutionList list, Task<T> task) {
-            super(delegate, list);
-            this.task = task;
-        }
-
-        @Override
-        public boolean cancel(boolean mayInterruptIfRunning) {
-            boolean result = false;
-            if (!task.isCancelled()) result |= task.cancel(mayInterruptIfRunning);
-            result |= super.cancel(mayInterruptIfRunning);
-            ((TaskInternal<?>)task).runListeners();
-            return result;
-        }
-    }
-
-    private final class SubmissionListenerToCallOtherListeners<T> implements Runnable {
-        private final Task<T> task;
-
-        private SubmissionListenerToCallOtherListeners(Task<T> task) {
-            this.task = task;
-        }
-
-        @Override
-        public void run() {
-            try {
-                ((TaskInternal<?>)task).runListeners();
-            } catch (Exception e) {
-                log.warn("Error running task listeners for task "+task+" done", e);
-            }
-            
-            for (ExecutionListener listener : listeners) {
-                try {
-                    listener.onTaskDone(task);
-                } catch (Exception e) {
-                    log.warn("Error running execution listener "+listener+" of task "+task+" done", e);
-                }
-            }
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    protected <T> Task<T> submitNewTask(final Map<?,?> flags, final Task<T> task) {
-        if (task instanceof ScheduledTask)
-            return (Task<T>) submitNewScheduledTask(flags, (ScheduledTask)task);
-        
-        tasksById.put(task.getId(), task);
-        totalTaskCount.incrementAndGet();
-        
-        beforeSubmitAtomicTask(flags, task);
-        
-        if (((TaskInternal<T>)task).getJob() == null) 
-            throw new NullPointerException("Task "+task+" submitted with with null job: job must be supplied.");
-        
-        Callable<T> job = new SubmissionCallable<T>(flags, task);
-        
-        // If there's a scheduler then use that; otherwise execute it directly
-        Set<TaskScheduler> schedulers = null;
-        for (Object tago: task.getTags()) {
-            TaskScheduler scheduler = getTaskSchedulerForTag(tago);
-            if (scheduler!=null) {
-                if (schedulers==null) schedulers = new LinkedHashSet<TaskScheduler>(2);
-                schedulers.add(scheduler);
-            }
-        }
-        Future<T> future;
-        if (schedulers!=null && !schedulers.isEmpty()) {
-            if (schedulers.size()>1) log.warn("multiple schedulers detected, using only the first, for "+task+": "+schedulers);
-            future = schedulers.iterator().next().submit(job);
-        } else {
-            future = runner.submit(job);
-        }
-        // on completion, listeners get triggered above; here, below we ensure they get triggered on cancel
-        // (and we make sure the same ExecutionList is used in the future as in the task)
-        ListenableFuture<T> listenableFuture = new ListenableForwardingFutureForTask<T>(future, ((TaskInternal<T>)task).getListeners(), task);
-        // doesn't matter whether the listener is added to the listenableFuture or the task,
-        // except that for the task we can more easily wrap it so that it only logs debug if the executor is shutdown
-        // (avoid a bunch of ugly warnings in tests which start and stop things a lot!)
-        // [probably even nicer to run this in the same thread, it doesn't do much; but that is messier to implement]
-        ((TaskInternal<T>)task).addListener(new SubmissionListenerToCallOtherListeners<T>(task), runner);
-        
-        ((TaskInternal<T>)task).initInternalFuture(listenableFuture);
-        
-        return task;
-    }
-    
-    protected void beforeSubmitScheduledTaskAllIterations(Map<?,?> flags, Task<?> task) {
-        internalBeforeSubmit(flags, task);
-    }
-    protected void beforeSubmitAtomicTask(Map<?,?> flags, Task<?> task) {
-        internalBeforeSubmit(flags, task);
-    }
-    /** invoked when a task is submitted */
-    protected void internalBeforeSubmit(Map<?,?> flags, Task<?> task) {
-        incompleteTaskIds.put(task.getId(), task.getId());
-        
-        Task<?> currentTask = Tasks.current();
-        if (currentTask!=null) ((TaskInternal<?>)task).setSubmittedByTask(currentTask);
-        ((TaskInternal<?>)task).setSubmitTimeUtc(System.currentTimeMillis());
-        
-        if (flags.get("tag")!=null) ((TaskInternal<?>)task).getMutableTags().add(flags.remove("tag"));
-        if (flags.get("tags")!=null) ((TaskInternal<?>)task).getMutableTags().addAll((Collection<?>)flags.remove("tags"));
-
-        for (Object tag: ((TaskInternal<?>)task).getTags()) {
-            tasksWithTagCreating(tag).add(task);
-        }
-    }
-
-    protected void beforeStartScheduledTaskSubmissionIteration(Map<?,?> flags, Task<?> task) {
-        internalBeforeStart(flags, task);
-    }
-    protected void beforeStartAtomicTask(Map<?,?> flags, Task<?> task) {
-        internalBeforeStart(flags, task);
-    }
-    
-    /** invoked in a task's thread when a task is starting to run (may be some time after submitted), 
-     * but before doing any of the task's work, so that we can update bookkeeping and notify callbacks */
-    protected void internalBeforeStart(Map<?,?> flags, Task<?> task) {
-        activeTaskCount.incrementAndGet();
-        
-        //set thread _before_ start time, so we won't get a null thread when there is a start-time
-        if (log.isTraceEnabled()) log.trace(""+this+" beforeStart, task: "+task);
-        if (!task.isCancelled()) {
-            Thread thread = Thread.currentThread();
-            ((TaskInternal<?>)task).setThread(thread);
-            if (RENAME_THREADS) {
-                threadOriginalName.set(thread.getName());
-                String newThreadName = "brooklyn-" + CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, task.getDisplayName().replace(" ", "")) + "-" + task.getId().substring(0, 8);
-                thread.setName(newThreadName);
-            }
-            PerThreadCurrentTaskHolder.perThreadCurrentTask.set(task);
-            ((TaskInternal<?>)task).setStartTimeUtc(System.currentTimeMillis());
-        }
-        ExecutionUtils.invoke(flags.get("newTaskStartCallback"), task);
-    }
-
-    /** normally (if not interrupted) called once for each call to {@link #beforeSubmitScheduledTaskAllIterations(Map, Task)} */
-    protected void afterEndScheduledTaskAllIterations(Map<?,?> flags, Task<?> task) {
-        internalAfterEnd(flags, task, false, true);
-    }
-    /** called once for each call to {@link #beforeStartScheduledTaskSubmissionIteration(Map, Task)},
-     * with a per-iteration task generated by the surrounding scheduled task */
-    protected void afterEndScheduledTaskSubmissionIteration(Map<?,?> flags, Task<?> scheduledTask, Task<?> taskIteration) {
-        internalAfterEnd(flags, scheduledTask, true, false);
-    }
-    /** called once for each task on which {@link #beforeStartAtomicTask(Map, Task)} is invoked,
-     * and normally (if not interrupted prior to start) 
-     * called once for each task on which {@link #beforeSubmitAtomicTask(Map, Task)} */
-    protected void afterEndAtomicTask(Map<?,?> flags, Task<?> task) {
-        internalAfterEnd(flags, task, true, true);
-    }
-    /** normally (if not interrupted) called once for each call to {@link #internalBeforeSubmit(Map, Task)},
-     * and, for atomic tasks and scheduled-task submission iterations where 
-     * always called once if {@link #internalBeforeStart(Map, Task)} is invoked and in the same thread as that method */
-    protected void internalAfterEnd(Map<?,?> flags, Task<?> task, boolean startedInThisThread, boolean isEndingAllIterations) {
-        if (log.isTraceEnabled()) log.trace(this+" afterEnd, task: "+task);
-        if (startedInThisThread) {
-            activeTaskCount.decrementAndGet();
-        }
-        if (isEndingAllIterations) {
-            incompleteTaskIds.remove(task.getId());
-            ExecutionUtils.invoke(flags.get("newTaskEndCallback"), task);
-            ((TaskInternal<?>)task).setEndTimeUtc(System.currentTimeMillis());
-        }
-
-        if (startedInThisThread) {
-            PerThreadCurrentTaskHolder.perThreadCurrentTask.remove();
-            //clear thread _after_ endTime set, so we won't get a null thread when there is no end-time
-            if (RENAME_THREADS && startedInThisThread) {
-                Thread thread = task.getThread();
-                if (thread==null) {
-                    log.warn("BasicTask.afterEnd invoked without corresponding beforeStart");
-                } else {
-                    thread.setName(threadOriginalName.get());
-                    threadOriginalName.remove();
-                }
-            }
-            ((TaskInternal<?>)task).setThread(null);
-        }
-        synchronized (task) { task.notifyAll(); }
-    }
-
-    public TaskScheduler getTaskSchedulerForTag(Object tag) {
-        return schedulerByTag.get(tag);
-    }
-    
-    public void setTaskSchedulerForTag(Object tag, Class<? extends TaskScheduler> scheduler) {
-        synchronized (schedulerByTag) {
-            TaskScheduler old = getTaskSchedulerForTag(tag);
-            if (old!=null) {
-                if (scheduler.isAssignableFrom(old.getClass())) {
-                    /* already have such an instance */
-                    return;
-                }
-                //might support multiple in future...
-                throw new IllegalStateException("Not allowed to set multiple TaskSchedulers on ExecutionManager tag (tag "+tag+", has "+old+", setting new "+scheduler+")");
-            }
-            try {
-                TaskScheduler schedulerI = scheduler.newInstance();
-                // allow scheduler to have a nice name, for logging etc
-                if (schedulerI instanceof CanSetName) ((CanSetName)schedulerI).setName(""+tag);
-                setTaskSchedulerForTag(tag, schedulerI);
-            } catch (InstantiationException e) {
-                throw Exceptions.propagate(e);
-            } catch (IllegalAccessException e) {
-                throw Exceptions.propagate(e);
-            }
-        }
-    }
-    
-    /**
-     * Defines a {@link TaskScheduler} to run on all subsequently submitted jobs with the given tag.
-     *
-     * Maximum of one allowed currently. Resubmissions of the same scheduler (or scheduler class)
-     * allowed. If changing, you must call {@link #clearTaskSchedulerForTag(Object)} between the two.
-     *
-     * @see #setTaskSchedulerForTag(Object, Class)
-     */
-    public void setTaskSchedulerForTag(Object tag, TaskScheduler scheduler) {
-        synchronized (schedulerByTag) {
-            scheduler.injectExecutor(runner);
-
-            Object old = schedulerByTag.put(tag, scheduler);
-            if (old!=null && old!=scheduler) {
-                //might support multiple in future...
-                throw new IllegalStateException("Not allowed to set multiple TaskSchedulers on ExecutionManager tag (tag "+tag+")");
-            }
-        }
-    }
-
-    /**
-     * Forgets that any scheduler was associated with a tag.
-     *
-     * @see #setTaskSchedulerForTag(Object, TaskScheduler)
-     * @see #setTaskSchedulerForTag(Object, Class)
-     */
-    public boolean clearTaskSchedulerForTag(Object tag) {
-        synchronized (schedulerByTag) {
-            Object old = schedulerByTag.remove(tag);
-            return (old!=null);
-        }
-    }
-    
-    @VisibleForTesting
-    public ConcurrentMap<Object, TaskScheduler> getSchedulerByTag() {
-        return schedulerByTag;
-    }
-
-}


[25/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjTool.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjTool.java
new file mode 100644
index 0000000..8262729
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjTool.java
@@ -0,0 +1,1091 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh.sshj;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Throwables.getCausalChain;
+import static com.google.common.collect.Iterables.any;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import net.schmizz.sshj.connection.ConnectionException;
+import net.schmizz.sshj.connection.channel.direct.PTYMode;
+import net.schmizz.sshj.connection.channel.direct.Session;
+import net.schmizz.sshj.connection.channel.direct.Session.Command;
+import net.schmizz.sshj.connection.channel.direct.Session.Shell;
+import net.schmizz.sshj.connection.channel.direct.SessionChannel;
+import net.schmizz.sshj.sftp.FileAttributes;
+import net.schmizz.sshj.sftp.SFTPClient;
+import net.schmizz.sshj.transport.TransportException;
+import net.schmizz.sshj.xfer.InMemorySourceFile;
+
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
+import org.apache.brooklyn.core.util.internal.ssh.BackoffLimitedRetryHandler;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshAbstractTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.commons.io.input.ProxyInputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.RuntimeTimeoutException;
+import brooklyn.util.io.FileUtil;
+import brooklyn.util.repeat.Repeater;
+import brooklyn.util.stream.KnownSizeInputStream;
+import brooklyn.util.stream.StreamGobbler;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.CountingOutputStream;
+import com.google.common.net.HostAndPort;
+import com.google.common.primitives.Ints;
+
+/**
+ * For ssh and scp-style commands, using the sshj library.
+ */
+public class SshjTool extends SshAbstractTool implements SshTool {
+
+    /*
+     * TODO synchronization of connect/disconnect needs revisited!
+     * Saw SshjToolIntegrationTest.testExecBigConcurrentCommand fail with:
+     *     Caused by: java.lang.AssertionError
+     *         at net.schmizz.sshj.SSHClient.auth(SSHClient.java:204)
+     * i.e. another thread had called disconnect just before the failing thread
+     * did SSHClient.auth.
+     * Having multiple threads call connect/disconnect is going to be brittle. With
+     * our retries we can get away with it usually, but it's not good!
+     *
+     * TODO need to upgrade sshj version from 0.8.1 to 0.9, but jclouds 1.7.2 still 
+     * relies on 0.8.1. In 0.9, it fixes the https://github.com/shikhar/sshj/issues/89
+     * so does not throw AssertionError.
+     */
+
+    private static final Logger LOG = LoggerFactory.getLogger(SshjTool.class);
+
+    protected final int sshTries;
+    protected final long sshTriesTimeout;
+    protected final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
+
+    /** Terminal type name for {@code allocatePTY} option. */
+    final static String TERM = "vt100"; // "dumb"
+    
+    private class CloseFtpChannelOnCloseInputStream extends ProxyInputStream {
+        private final SFTPClient sftp;
+
+        private CloseFtpChannelOnCloseInputStream(InputStream proxy, SFTPClient sftp) {
+            super(proxy);
+            this.sftp = sftp;
+        }
+
+        @Override
+        public void close() throws IOException {
+            super.close();
+            closeWhispering(sftp, this);
+        }
+    }
+
+    private final SshjClientConnection sshClientConnection;
+
+    public static SshjToolBuilder builder() {
+        return new SshjToolBuilder();
+    }
+    
+    public static class SshjToolBuilder extends Builder<SshjTool, SshjToolBuilder> {
+    }
+    
+    public static class Builder<T extends SshjTool, B extends Builder<T,B>> extends AbstractSshToolBuilder<T,B> {
+        protected long connectTimeout;
+        protected long sessionTimeout;
+        protected int sshTries = 4;  //allow 4 tries by default, much safer
+        protected long sshTriesTimeout = 2*60*1000;  //allow 2 minutes by default (so if too slow trying sshTries times, abort anyway)
+        protected long sshRetryDelay = 50L;
+        
+        @Override
+        public B from(Map<String,?> props) {
+            super.from(props);
+            sshTries = getOptionalVal(props, PROP_SSH_TRIES);
+            sshTriesTimeout = getOptionalVal(props, PROP_SSH_TRIES_TIMEOUT);
+            sshRetryDelay = getOptionalVal(props, PROP_SSH_RETRY_DELAY);
+            connectTimeout = getOptionalVal(props, PROP_CONNECT_TIMEOUT);
+            sessionTimeout = getOptionalVal(props, PROP_SESSION_TIMEOUT);
+            return self();
+        }
+        public B connectTimeout(int val) {
+            this.connectTimeout = val; return self();
+        }
+        public B sessionTimeout(int val) {
+            this.sessionTimeout = val; return self();
+        }
+        public B sshRetries(int val) {
+            this.sshTries = val; return self();
+        }
+        public B sshRetriesTimeout(int val) {
+            this.sshTriesTimeout = val; return self();
+        }
+        public B sshRetryDelay(long val) {
+            this.sshRetryDelay = val; return self();
+        }
+        @Override
+        @SuppressWarnings("unchecked")
+        public T build() {
+            return (T) new SshjTool(this);
+        }
+    }
+
+    public SshjTool(Map<String,?> map) {
+        this(builder().from(map));
+    }
+    
+    protected SshjTool(Builder<?,?> builder) {
+        super(builder);
+        
+        sshTries = builder.sshTries;
+        sshTriesTimeout = builder.sshTriesTimeout;
+        backoffLimitedRetryHandler = new BackoffLimitedRetryHandler(sshTries, builder.sshRetryDelay);
+
+        sshClientConnection = SshjClientConnection.builder()
+                .hostAndPort(HostAndPort.fromParts(host, port))
+                .username(user)
+                .password(password)
+                .privateKeyPassphrase(privateKeyPassphrase)
+                .privateKeyData(privateKeyData)
+                .privateKeyFile(privateKeyFile)
+                .strictHostKeyChecking(strictHostKeyChecking)
+                .connectTimeout(builder.connectTimeout)
+                .sessionTimeout(builder.sessionTimeout)
+                .build();
+        
+        if (LOG.isTraceEnabled()) LOG.trace("Created SshTool {} ({})", this, System.identityHashCode(this));
+    }
+    
+    @Override
+    public void connect() {
+        try {
+            if (LOG.isTraceEnabled()) LOG.trace("Connecting SshjTool {} ({})", this, System.identityHashCode(this));
+            acquire(sshClientConnection);
+        } catch (Exception e) {
+            if (LOG.isDebugEnabled()) LOG.debug(toString()+" failed to connect (rethrowing)", e);
+            throw propagate(e, "failed to connect");
+        }
+    }
+
+    @Override
+    @Deprecated // see super
+    public void connect(int maxAttempts) {
+        connect(); // FIXME Should callers instead configure sshTries? But that would apply to all ssh attempts
+    }
+
+    @Override
+    public void disconnect() {
+        if (LOG.isTraceEnabled()) LOG.trace("Disconnecting SshjTool {} ({})", this, System.identityHashCode(this));
+        try {
+            Stopwatch perfStopwatch = Stopwatch.createStarted();
+            sshClientConnection.clear();
+            if (LOG.isTraceEnabled()) LOG.trace("SSH Performance: {} disconnect took {}", sshClientConnection.getHostAndPort(), Time.makeTimeStringRounded(perfStopwatch));
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    @Override
+    public boolean isConnected() {
+        return sshClientConnection.isConnected() && sshClientConnection.isAuthenticated();
+    }
+    
+    @Override
+    public int copyToServer(java.util.Map<String,?> props, byte[] contents, String pathAndFileOnRemoteServer) {
+        return copyToServer(props, newInputStreamSupplier(contents), contents.length, pathAndFileOnRemoteServer);
+    }
+    
+    @Override
+    public int copyToServer(Map<String,?> props, InputStream contents, String pathAndFileOnRemoteServer) {
+        /* sshj needs to:
+         *   1) to know the length of the InputStream to copy the file to perform copy; and
+         *   2) re-read the input stream on retry if the first attempt fails.
+         * For now, write it to a file, unless caller supplies a KnownSizeInputStream
+         * 
+         * (We could have a switch where we hold it in memory if less than some max size,
+         * but most the routines should supply a string or byte array or similar,
+         * so we probably don't come here too often.)
+         */
+        if (contents instanceof KnownSizeInputStream) {
+            return copyToServer(props, Suppliers.ofInstance(contents), ((KnownSizeInputStream)contents).length(), pathAndFileOnRemoteServer);
+        } else {
+            File tempFile = writeTempFile(contents);
+            try {
+                return copyToServer(props, tempFile, pathAndFileOnRemoteServer);
+            } finally {
+                tempFile.delete();
+            }
+        }
+    }
+    
+    @Override
+    public int copyToServer(Map<String,?> props, File localFile, String pathAndFileOnRemoteServer) {
+        return copyToServer(props, newInputStreamSupplier(localFile), (int)localFile.length(), pathAndFileOnRemoteServer);
+    }
+    
+    private int copyToServer(Map<String,?> props, Supplier<InputStream> contentsSupplier, long length, String pathAndFileOnRemoteServer) {
+        acquire(new PutFileAction(props, pathAndFileOnRemoteServer, contentsSupplier, length));
+        return 0; // TODO Can we assume put will have thrown exception if failed? Rather than exit code != 0?
+    }
+
+
+    @Override
+    public int copyFromServer(Map<String,?> props, String pathAndFileOnRemoteServer, File localFile) {
+        InputStream contents = acquire(new GetFileAction(pathAndFileOnRemoteServer));
+        try {
+            FileUtil.copyTo(contents, localFile);
+            return 0; // TODO Can we assume put will have thrown exception if failed? Rather than exit code != 0?
+        } finally {
+            Streams.closeQuietly(contents);
+        }
+    }
+
+    /**
+     * This creates a script containing the user's commands, copies it to the remote server, and
+     * executes the script. The script is then deleted.
+     * <p>
+     * Executing commands directly is fraught with dangers! Here are other options, and their problems:
+     * <ul>
+     *   <li>Use execCommands, rather than shell.
+     *       The user's environment will not be setup normally (e.g. ~/.bash_profile will not have been sourced)
+     *       so things like wget may not be on the PATH.
+     *   <li>Send the stream of commands to the shell.
+     *       But characters being sent can be lost.
+     *       Try the following (e.g. in an OS X terminal):
+     *        - sleep 5
+     *        - <paste a command that is 1000s of characters long>
+     *       Only the first 1024 characters appear. The rest are lost.
+     *       If sending a stream of commands, you need to be careful not send the next (big) command while the
+     *       previous one is still executing.
+     *   <li>Send a stream to the shell, but spot when the previous command has completed.
+     *       e.g. by looking for the prompt (but what if the commands being executed change the prompt?)
+     *       e.g. by putting every second command as "echo <uid>", and waiting for the stdout.
+     *       This gets fiddly...
+     * </ul>
+     * 
+     * So on balance, the script-based approach seems most reliable, even if there is an overhead
+     * of separate message(s) for copying the file!
+     * 
+     * Another consideration is long-running scripts. On some clouds when executing a script that takes 
+     * several minutes, we have seen it fail with -1 (e.g. 1 in 20 times). This suggests the ssh connection
+     * is being dropped. To avoid this problem, we can execute the script asynchronously, writing to files
+     * the stdout/stderr/pid/exitStatus. We then periodically poll to retrieve the contents of these files.
+     * Use {@link #PROP_EXEC_ASYNC} to force this mode of execution.
+     */
+    @Override
+    public int execScript(final Map<String,?> props, final List<String> commands, final Map<String,?> env) {
+        Boolean execAsync = getOptionalVal(props, PROP_EXEC_ASYNC);
+        if (Boolean.TRUE.equals(execAsync) && BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC)) {
+            return execScriptAsyncAndPoll(props, commands, env);
+        } else {
+            if (Boolean.TRUE.equals(execAsync)) {
+                if (LOG.isDebugEnabled()) LOG.debug("Ignoring ssh exec-async configuration, because feature is disabled");
+            }
+            return new ToolAbstractExecScript(props) {
+                public int run() {
+                    String scriptContents = toScript(props, commands, env);
+                    if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} as script: {}", host, scriptContents);
+                    copyToServer(ImmutableMap.of("permissions", "0700"), scriptContents.getBytes(), scriptPath);
+                    return asInt(acquire(new ShellAction(buildRunScriptCommand(), out, err, execTimeout)), -1);
+                }
+            }.run();
+        }
+    }
+
+    /**
+     * Executes the script in the background (`nohup ... &`), and then executes other ssh commands to poll for the
+     * stdout, stderr and exit code of that original process (which will each have been written to separate files).
+     * 
+     * The polling is a "long poll". That is, it executes a long-running ssh command to retrieve the stdout, etc.
+     * If that long-poll command fails, then we just execute another one to pick up from where it left off.
+     * This means we do not need to execute many ssh commands (which are expensive), but can still return promptly
+     * when the command completes.
+     * 
+     * Much of this was motivated by https://issues.apache.org/jira/browse/BROOKLYN-106, which is no longer
+     * an issue. The retries (e.g. in the upload-script) are arguably overkill given that {@link #acquire(SshAction)}
+     * will already retry. However, leaving this in place as it could prove useful when working with flakey
+     * networks in the future.
+     * 
+     * TODO There are (probably) issues with this method when using {@link ShellTool#PROP_RUN_AS_ROOT}.
+     * I (Aled) saw the .pid file having an owner of root:root, and a failure message in stderr of:
+     *   -bash: line 3: /tmp/brooklyn-20150113-161203056-XMEo-move_install_dir_from_user_to_.pid: Permission denied
+     */
+    protected int execScriptAsyncAndPoll(final Map<String,?> props, final List<String> commands, final Map<String,?> env) {
+        return new ToolAbstractAsyncExecScript(props) {
+            private int maxConsecutiveSshFailures = 3;
+            private Duration maxDelayBetweenPolls = Duration.seconds(20);
+            private Duration pollTimeout = getOptionalVal(props, PROP_EXEC_ASYNC_POLLING_TIMEOUT, Duration.FIVE_MINUTES);
+            private int iteration = 0;
+            private int consecutiveSshFailures = 0;
+            private int stdoutCount = 0;
+            private int stderrCount = 0;
+            private Stopwatch timer;
+            
+            public int run() {
+                timer = Stopwatch.createStarted();
+                final String scriptContents = toScript(props, commands, env);
+                if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} as async script: {}", host, scriptContents);
+                
+                // Upload script; try repeatedly because have seen timeout intermittently on vcloud-director (BROOKLYN-106 related).
+                boolean uploadSuccess = Repeater.create("async script upload on "+SshjTool.this.toString()+" (for "+getSummary()+")")
+                        .backoffTo(maxDelayBetweenPolls)
+                        .limitIterationsTo(3)
+                        .rethrowException()
+                        .until(new Callable<Boolean>() {
+                            @Override
+                            public Boolean call() throws Exception {
+                                iteration++;
+                                if (LOG.isDebugEnabled()) {
+                                    String msg = "Uploading (iteration="+iteration+") for async script on "+SshjTool.this.toString()+" (for "+getSummary()+")";
+                                    if (iteration == 1) {
+                                        LOG.trace(msg);
+                                    } else {
+                                        LOG.debug(msg);
+                                    }
+                                }
+                                copyToServer(ImmutableMap.of("permissions", "0700"), scriptContents.getBytes(), scriptPath);
+                                return true;
+                            }})
+                        .run();
+                
+                if (!uploadSuccess) {
+                    // Unexpected! Should have either returned true or have rethrown the exception; should never get false.
+                    String msg = "Unexpected state: repeated failure for async script upload on "+SshjTool.this.toString()+" ("+getSummary()+")";
+                    LOG.warn(msg+"; rethrowing");
+                    throw new IllegalStateException(msg);
+                }
+                
+                // Execute script asynchronously
+                int execResult = asInt(acquire(new ShellAction(buildRunScriptCommand(), out, err, execTimeout)), -1);
+                if (execResult != 0) return execResult;
+
+                // Long polling to get the status
+                try {
+                    final AtomicReference<Integer> result = new AtomicReference<Integer>();
+                    boolean success = Repeater.create("async script long-poll on "+SshjTool.this.toString()+" (for "+getSummary()+")")
+                            .backoffTo(maxDelayBetweenPolls)
+                            .limitTimeTo(execTimeout)
+                            .until(new Callable<Boolean>() {
+                                @Override
+                                public Boolean call() throws Exception {
+                                    iteration++;
+                                    if (LOG.isDebugEnabled()) LOG.debug("Doing long-poll (iteration="+iteration+") for async script to complete on "+SshjTool.this.toString()+" (for "+getSummary()+")");
+                                    Integer exitstatus = longPoll();
+                                    result.set(exitstatus);
+                                    return exitstatus != null;
+                                }})
+                            .run();
+                    
+                    if (!success) {
+                        // Timed out
+                        String msg = "Timeout for async script to complete on "+SshjTool.this.toString()+" ("+getSummary()+")";
+                        LOG.warn(msg+"; rethrowing");
+                        throw new TimeoutException(msg);
+                    }
+                    
+                    return result.get();
+                    
+                } catch (Exception e) {
+                    LOG.debug("Problem polling for async script on "+SshjTool.this.toString()+" (for "+getSummary()+"); rethrowing after deleting temporary files", e);
+                    throw Exceptions.propagate(e);
+                } finally {
+                    // Delete the temporary files created (and the `tail -c` commands that might have been left behind by long-polls).
+                    // Using pollTimeout so doesn't wait forever, but waits for a reasonable (configurable) length of time.
+                    // TODO also execute this if the `buildRunScriptCommand` fails, as that might have left files behind?
+                    try {
+                        int execDeleteResult = asInt(acquire(new ShellAction(deleteTemporaryFilesCommand(), out, err, pollTimeout)), -1);
+                        if (execDeleteResult != 0) {
+                            LOG.debug("Problem deleting temporary files of async script on "+SshjTool.this.toString()+" (for "+getSummary()+"): exit status "+execDeleteResult);
+                        }
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        LOG.debug("Problem deleting temporary files of async script on "+SshjTool.this.toString()+" (for "+getSummary()+"); continuing", e);
+                    }
+                }
+            }
+            
+            Integer longPoll() throws IOException {
+                // Long-polling to get stdout, stderr + exit status of async task.
+                // If our long-poll disconnects, we will just re-execute.
+                // We wrap the stdout/stderr so that we can get the size count. 
+                // If we disconnect, we will pick up from that char of the stream.
+                // TODO Additional stdout/stderr written by buildLongPollCommand() could interfere, 
+                //      causing us to miss some characters.
+                Duration nextPollTimeout = Duration.min(pollTimeout, Duration.millis(execTimeout.toMilliseconds()-timer.elapsed(TimeUnit.MILLISECONDS)));
+                CountingOutputStream countingOut = (out == null) ? null : new CountingOutputStream(out);
+                CountingOutputStream countingErr = (err == null) ? null : new CountingOutputStream(err);
+                List<String> pollCommand = buildLongPollCommand(stdoutCount, stderrCount, nextPollTimeout);
+                Duration sshJoinTimeout = nextPollTimeout.add(Duration.TEN_SECONDS);
+                ShellAction action = new ShellAction(pollCommand, countingOut, countingErr, sshJoinTimeout);
+                
+                int longPollResult;
+                try {
+                    longPollResult = asInt(acquire(action, 3, nextPollTimeout), -1);
+                } catch (RuntimeTimeoutException e) {
+                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll timed out on "+SshjTool.this.toString()+" (for "+getSummary()+"): "+e);
+                    return null;
+                }
+                stdoutCount += (countingOut == null) ? 0 : countingOut.getCount();
+                stderrCount += (countingErr == null) ? 0 : countingErr.getCount();
+                
+                if (longPollResult == 0) {
+                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll succeeded (exit status 0) on "+SshjTool.this.toString()+" (for "+getSummary()+")");
+                    return longPollResult; // success
+                    
+                } else if (longPollResult == -1) {
+                    // probably a connection failure; try again
+                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll received exit status -1; will retry on "+SshjTool.this.toString()+" (for "+getSummary()+")");
+                    return null;
+
+                } else if (longPollResult == 125) {
+                    // 125 is the special code for timeout in long-poll (see buildLongPollCommand).
+                    // However, there is a tiny chance that the underlying command might have returned that exact exit code!
+                    // Don't treat a timeout as a "consecutiveSshFailure".
+                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll received exit status "+longPollResult+"; most likely timeout; retrieving actual status on "+SshjTool.this.toString()+" (for "+getSummary()+")");
+                    return retrieveStatusCommand();
+
+                } else {
+                    // want to double-check whether this is the exit-code from the async process, or
+                    // some unexpected failure in our long-poll command.
+                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll received exit status "+longPollResult+"; retrieving actual status on "+SshjTool.this.toString()+" (for "+getSummary()+")");
+                    Integer result = retrieveStatusCommand();
+                    if (result != null) {
+                        return result;
+                    }
+                }
+                    
+                consecutiveSshFailures++;
+                if (consecutiveSshFailures > maxConsecutiveSshFailures) {
+                    LOG.warn("Aborting on "+consecutiveSshFailures+" consecutive ssh connection errors (return -1) when polling for async script to complete on "+SshjTool.this.toString()+" ("+getSummary()+")");
+                    return -1;
+                } else {
+                    LOG.info("Retrying after ssh connection error when polling for async script to complete on "+SshjTool.this.toString()+" ("+getSummary()+")");
+                    return null;
+                }
+            }
+            
+            Integer retrieveStatusCommand() throws IOException {
+                // want to double-check whether this is the exit-code from the async process, or
+                // some unexpected failure in our long-poll command.
+                ByteArrayOutputStream statusOut = new ByteArrayOutputStream();
+                ByteArrayOutputStream statusErr = new ByteArrayOutputStream();
+                int statusResult = asInt(acquire(new ShellAction(buildRetrieveStatusCommand(), statusOut, statusErr, execTimeout)), -1);
+                
+                if (statusResult == 0) {
+                    // The status we retrieved really is valid; return it.
+                    // TODO How to ensure no additional output in stdout/stderr when parsing below?
+                    String statusOutStr = new String(statusOut.toByteArray()).trim();
+                    if (Strings.isEmpty(statusOutStr)) {
+                        // suggests not yet completed; will retry with long-poll
+                        if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieved status directly; command successful but no result available on "+SshjTool.this.toString()+" (for "+getSummary()+")");
+                        return null;
+                    } else {
+                        if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieved status directly; returning '"+statusOutStr+"' on "+SshjTool.this.toString()+" (for "+getSummary()+")");
+                        int result = Integer.parseInt(statusOutStr);
+                        return result;
+                    }
+
+                } else if (statusResult == -1) {
+                    // probably a connection failure; try again with long-poll
+                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieving status directly received exit status -1; will retry on "+SshjTool.this.toString()+" (for "+getSummary()+")");
+                    return null;
+                    
+                } else {
+                    if (out != null) {
+                        out.write(toUTF8ByteArray("retrieving status failed with exit code "+statusResult+" (stdout follow)"));
+                        out.write(statusOut.toByteArray());
+                    }
+                    if (err != null) {
+                        err.write(toUTF8ByteArray("retrieving status failed with exit code "+statusResult+" (stderr follow)"));
+                        err.write(statusErr.toByteArray());
+                    }
+                    
+                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieving status failed; returning "+statusResult+" on "+SshjTool.this.toString()+" (for "+getSummary()+")");
+                    return statusResult;
+                }
+            }
+        }.run();
+    }
+    
+    public int execShellDirect(Map<String,?> props, List<String> commands, Map<String,?> env) {
+        OutputStream out = getOptionalVal(props, PROP_OUT_STREAM);
+        OutputStream err = getOptionalVal(props, PROP_ERR_STREAM);
+        Duration execTimeout = getOptionalVal(props, PROP_EXEC_TIMEOUT);
+        
+        List<String> cmdSequence = toCommandSequence(commands, env);
+        List<String> allcmds = ImmutableList.<String>builder()
+                .add(getOptionalVal(props, PROP_DIRECT_HEADER))
+                .addAll(cmdSequence)
+                .add("exit $?")
+                .build();
+        
+        if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {}: {}", host, allcmds);
+        
+        Integer result = acquire(new ShellAction(allcmds, out, err, execTimeout));
+        if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} completed: return status {}", host, result);
+        return asInt(result, -1);
+    }
+
+    @Override
+    public int execCommands(Map<String,?> props, List<String> commands, Map<String,?> env) {
+        if (Boolean.FALSE.equals(props.get("blocks"))) {
+            throw new IllegalArgumentException("Cannot exec non-blocking: command="+commands);
+        }
+        
+        // If async is set, then do it as execScript
+        Boolean execAsync = getOptionalVal(props, PROP_EXEC_ASYNC);
+        if (Boolean.TRUE.equals(execAsync) && BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC)) {
+            return execScriptAsyncAndPoll(props, commands, env);
+        }
+
+        OutputStream out = getOptionalVal(props, PROP_OUT_STREAM);
+        OutputStream err = getOptionalVal(props, PROP_ERR_STREAM);
+        String separator = getOptionalVal(props, PROP_SEPARATOR);
+        Duration execTimeout = getOptionalVal(props, PROP_EXEC_TIMEOUT);
+
+        List<String> allcmds = toCommandSequence(commands, env);
+        String singlecmd = Joiner.on(separator).join(allcmds);
+
+        if (Boolean.TRUE.equals(getOptionalVal(props, PROP_RUN_AS_ROOT))) {
+            LOG.warn("Cannot run as root when executing as command; run as a script instead (will run as normal user): "+singlecmd);
+        }
+        
+        if (LOG.isTraceEnabled()) LOG.trace("Running command at {}: {}", host, singlecmd);
+        
+        Command result = acquire(new ExecAction(singlecmd, out, err, execTimeout));
+        if (LOG.isTraceEnabled()) LOG.trace("Running command at {} completed: exit code {}", host, result.getExitStatus());
+        // can be null if no exit status is received (observed on kill `ps aux | grep thing-to-grep-for | awk {print $2}`
+        if (result.getExitStatus()==null) LOG.warn("Null exit status running at {}: {}", host, singlecmd);
+        
+        return asInt(result.getExitStatus(), -1);
+    }
+
+    protected void checkConnected() {
+        if (!isConnected()) {
+            throw new IllegalStateException(String.format("(%s) ssh not connected!", toString()));
+        }
+    }
+
+    protected void backoffForAttempt(int retryAttempt, String message) {
+        backoffLimitedRetryHandler.imposeBackoffExponentialDelay(retryAttempt, message);
+    }
+
+    protected <T, C extends SshAction<T>> T acquire(C action) {
+        return acquire(action, sshTries, sshTriesTimeout == 0 ? Duration.PRACTICALLY_FOREVER : Duration.millis(sshTriesTimeout));
+    }
+    
+    protected <T, C extends SshAction<T>> T acquire(C action, int sshTries, Duration sshTriesTimeout) {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        
+        for (int i = 0; i < sshTries; i++) {
+            try {
+                action.clear();
+                if (LOG.isTraceEnabled()) LOG.trace(">> ({}) acquiring {}", toString(), action);
+                Stopwatch perfStopwatch = Stopwatch.createStarted();
+                
+                T returnVal;
+                try {
+                    returnVal = action.create();
+                } catch (AssertionError e) {
+                    /*
+                     * TODO In net.schmizz.sshj.SSHClient.auth(SSHClient.java:204) throws AssertionError
+                     * if not connected. This can happen if another thread has called disconnect
+                     * concurrently. This is changed in sshj v0.9.0 to instead throw an IllegalStateException.
+                     * 
+                     * For now, we'll retry. See "TODO" at top of class about synchronization.
+                     */
+                    throw new IllegalStateException("Problem in "+toString()+" for "+action, e);
+                }
+                
+                if (LOG.isTraceEnabled()) LOG.trace("<< ({}) acquired {}", toString(), returnVal);
+                if (LOG.isTraceEnabled()) LOG.trace("SSH Performance: {} {} took {}", new Object[] {
+                        sshClientConnection.getHostAndPort(), 
+                        action.getClass().getSimpleName() != null ? action.getClass().getSimpleName() : action, 
+                        Time.makeTimeStringRounded(perfStopwatch)});
+                return returnVal;
+            } catch (Exception e) {
+                // uninformative net.schmizz.sshj.connection.ConnectionException: 
+                //    Request failed (reason=UNKNOWN) may mean remote Subsytem is disabled (e.g. for FTP)
+                // if key is missing, get a UserAuth error
+                String errorMessage = String.format("(%s) error acquiring %s", toString(), action);
+                String fullMessage = String.format("%s (attempt %s/%s, in time %s/%s)", 
+                        errorMessage, (i+1), sshTries, Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)), 
+                        (sshTriesTimeout.equals(Duration.PRACTICALLY_FOREVER) ? "unlimited" : Time.makeTimeStringRounded(sshTriesTimeout)));
+                try {
+                    disconnect();
+                } catch (Exception e2) {
+                    LOG.debug("<< ("+toString()+") error closing connection: "+e+" / "+e2, e);
+                }
+                if (i + 1 == sshTries) {
+                    LOG.debug("<< {} (rethrowing, out of retries): {}", fullMessage, e.getMessage());
+                    throw propagate(e, fullMessage + "; out of retries");
+                } else if (sshTriesTimeout.isShorterThan(stopwatch)) {
+                    LOG.debug("<< {} (rethrowing, out of time - max {}): {}", new Object[] { fullMessage, Time.makeTimeStringRounded(sshTriesTimeout), e.getMessage() });
+                    throw new RuntimeTimeoutException(fullMessage + "; out of time", e);
+                } else {
+                    if (LOG.isDebugEnabled()) LOG.debug("<< {}: {}", fullMessage, e.getMessage());
+                    backoffForAttempt(i + 1, errorMessage + ": " + e.getMessage());
+                    if (action != sshClientConnection)
+                        connect();
+                    continue;
+                }
+            }
+        }
+        assert false : "should not reach here";
+        return null;
+    }
+
+    private final SshAction<SFTPClient> sftpConnection = new SshAction<SFTPClient>() {
+
+        private SFTPClient sftp;
+
+        @Override
+        public void clear() {
+            closeWhispering(sftp, this);
+            sftp = null;
+        }
+
+        @Override
+        public SFTPClient create() throws IOException {
+            checkConnected();
+            sftp = sshClientConnection.ssh.newSFTPClient();
+            return sftp;
+        }
+
+        @Override
+        public String toString() {
+            return "SFTPClient()";
+        }
+    };
+
+    private class GetFileAction implements SshAction<InputStream> {
+        private final String path;
+        private SFTPClient sftp;
+
+        GetFileAction(String path) {
+            this.path = checkNotNull(path, "path");
+        }
+
+        @Override
+        public void clear() throws IOException {
+            closeWhispering(sftp, this);
+            sftp = null;
+        }
+
+        @Override
+        public InputStream create() throws Exception {
+            sftp = acquire(sftpConnection);
+            return new CloseFtpChannelOnCloseInputStream(
+                    sftp.getSFTPEngine().open(path).getInputStream(), sftp);
+        }
+
+        @Override
+        public String toString() {
+            return "Payload(path=[" + path + "])";
+        }
+    }
+
+    private class PutFileAction implements SshAction<Void> {
+        // TODO support backup as a property?
+        
+        private SFTPClient sftp;
+        private final String path;
+        private final int permissionsMask;
+        private final long lastModificationDate;
+        private final long lastAccessDate;
+        private final int uid;
+        private final Supplier<InputStream> contentsSupplier;
+        private final Integer length;
+        
+        PutFileAction(Map<String,?> props, String path, Supplier<InputStream> contentsSupplier, long length) {
+            String permissions = getOptionalVal(props, PROP_PERMISSIONS);
+            long lastModificationDateVal = getOptionalVal(props, PROP_LAST_MODIFICATION_DATE);
+            long lastAccessDateVal = getOptionalVal(props, PROP_LAST_ACCESS_DATE);
+            if (lastAccessDateVal <= 0 ^ lastModificationDateVal <= 0) {
+                lastAccessDateVal = Math.max(lastAccessDateVal, lastModificationDateVal);
+                lastModificationDateVal = Math.max(lastAccessDateVal, lastModificationDateVal);
+            }
+            this.permissionsMask = Integer.parseInt(permissions, 8);
+            this.lastAccessDate = lastAccessDateVal;
+            this.lastModificationDate = lastModificationDateVal;
+            this.uid = getOptionalVal(props, PROP_OWNER_UID);
+            this.path = checkNotNull(path, "path");
+            this.contentsSupplier = checkNotNull(contentsSupplier, "contents");
+            this.length = Ints.checkedCast(checkNotNull((long)length, "size"));
+        }
+
+        @Override
+        public void clear() {
+            closeWhispering(sftp, this);
+            sftp = null;
+        }
+
+        @Override
+        public Void create() throws Exception {
+            final AtomicReference<InputStream> inputStreamRef = new AtomicReference<InputStream>();
+            sftp = acquire(sftpConnection);
+            try {
+                sftp.put(new InMemorySourceFile() {
+                    @Override public String getName() {
+                        return path;
+                    }
+                    @Override public long getLength() {
+                        return length;
+                    }
+                    @Override public InputStream getInputStream() throws IOException {
+                        InputStream contents = contentsSupplier.get();
+                        inputStreamRef.set(contents);
+                        return contents;
+                    }
+                }, path);
+                sftp.chmod(path, permissionsMask);
+                if (uid != -1) {
+                    sftp.chown(path, uid);
+                }
+                if (lastAccessDate > 0) {
+                    sftp.setattr(path, new FileAttributes.Builder()
+                            .withAtimeMtime(lastAccessDate, lastModificationDate)
+                            .build());
+                }
+            } finally {
+                closeWhispering(inputStreamRef.get(), this);
+            }
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return "Put(path=[" + path + " "+length+"])";
+        }
+    }
+
+    // TODO simpler not to use predicates
+    @VisibleForTesting
+    Predicate<String> causalChainHasMessageContaining(final Exception from) {
+        return new Predicate<String>() {
+            @Override
+            public boolean apply(final String input) {
+                return any(getCausalChain(from), new Predicate<Throwable>() {
+                    @Override
+                    public boolean apply(Throwable throwable) {
+                        return (throwable.toString().contains(input))
+                                || (throwable.getMessage() != null && throwable.getMessage().contains(input));
+                    }
+                });
+            }
+        };
+    }
+    
+    protected SshAction<Session> newSessionAction() {
+
+        return new SshAction<Session>() {
+
+            private Session session = null;
+
+            @Override
+            public void clear() throws TransportException, ConnectionException {
+                closeWhispering(session, this);
+                session = null;
+            }
+
+            @Override
+            public Session create() throws Exception {
+                checkConnected();
+                session = sshClientConnection.ssh.startSession();
+                if (allocatePTY) {
+                    session.allocatePTY(TERM, 80, 24, 0, 0, Collections.<PTYMode, Integer> emptyMap());
+                }
+                return session;
+            }
+
+            @Override
+            public String toString() {
+                return "Session()";
+            }
+        };
+
+    }
+
+    class ExecAction implements SshAction<Command> {
+        private final String command;
+        private final OutputStream out;
+        private final OutputStream err;
+        private final Duration timeout;
+        
+        private Session session;
+        private Shell shell;
+        private StreamGobbler outgobbler;
+        private StreamGobbler errgobbler;
+        
+        ExecAction(String command, OutputStream out, OutputStream err, Duration timeout) {
+            this.command = checkNotNull(command, "command");
+            this.out = out;
+            this.err = err;
+            Duration sessionTimeout = (sshClientConnection.getSessionTimeout() == 0) 
+                    ? Duration.PRACTICALLY_FOREVER 
+                    : Duration.millis(sshClientConnection.getSessionTimeout());
+            this.timeout = (timeout == null) ? sessionTimeout : Duration.min(timeout, sessionTimeout);
+        }
+
+        @Override
+        public void clear() throws TransportException, ConnectionException {
+            closeWhispering(session, this);
+            closeWhispering(shell, this);
+            closeWhispering(outgobbler, this);
+            closeWhispering(errgobbler, this);
+            session = null;
+            shell = null;
+        }
+
+        @Override
+        public Command create() throws Exception {
+            try {
+                session = acquire(newSessionAction());
+                
+                Command output = session.exec(checkNotNull(command, "command"));
+                
+                if (out != null) {
+                    outgobbler = new StreamGobbler(output.getInputStream(), out, (Logger)null);
+                    outgobbler.start();
+                }
+                if (err != null) {
+                    errgobbler = new StreamGobbler(output.getErrorStream(), err, (Logger)null);
+                    errgobbler.start();
+                }
+                try {
+                    output.join((int)Math.min(timeout.toMilliseconds(), Integer.MAX_VALUE), TimeUnit.MILLISECONDS);
+                    return output;
+                    
+                } finally {
+                    // wait for all stdout/stderr to have been re-directed
+                    try {
+                        // Don't use forever (i.e. 0) because BROOKLYN-106: ssh hangs
+                        long joinTimeout = 10*1000;
+                        if (outgobbler != null) outgobbler.join(joinTimeout);
+                        if (errgobbler != null) errgobbler.join(joinTimeout);
+                    } catch (InterruptedException e) {
+                        LOG.warn("Interrupted gobbling streams from ssh: "+command, e);
+                        Thread.currentThread().interrupt();
+                    }
+                }
+                
+            } finally {
+                clear();
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "Exec(command=[" + command + "])";
+        }
+    }
+
+    class ShellAction implements SshAction<Integer> {
+        @VisibleForTesting
+        final List<String> commands;
+        @VisibleForTesting
+        final OutputStream out;
+        @VisibleForTesting
+        final OutputStream err;
+        
+        private Session session;
+        private Shell shell;
+        private StreamGobbler outgobbler;
+        private StreamGobbler errgobbler;
+        private Duration timeout;
+
+        ShellAction(List<String> commands, OutputStream out, OutputStream err, Duration timeout) {
+            this.commands = checkNotNull(commands, "commands");
+            this.out = out;
+            this.err = err;
+            Duration sessionTimeout = (sshClientConnection.getSessionTimeout() == 0) 
+                    ? Duration.PRACTICALLY_FOREVER 
+                    : Duration.millis(sshClientConnection.getSessionTimeout());
+            this.timeout = (timeout == null) ? sessionTimeout : Duration.min(timeout, sessionTimeout);
+        }
+
+        @Override
+        public void clear() throws TransportException, ConnectionException {
+            closeWhispering(session, this);
+            closeWhispering(shell, this);
+            closeWhispering(outgobbler, this);
+            closeWhispering(errgobbler, this);
+            session = null;
+            shell = null;
+        }
+
+        @Override
+        public Integer create() throws Exception {
+            try {
+                session = acquire(newSessionAction());
+                
+                shell = session.startShell();
+                
+                if (out != null) {
+                    InputStream outstream = shell.getInputStream();
+                    outgobbler = new StreamGobbler(outstream, out, (Logger)null);
+                    outgobbler.start();
+                }
+                if (err != null) {
+                    InputStream errstream = shell.getErrorStream();
+                    errgobbler = new StreamGobbler(errstream, err, (Logger)null);
+                    errgobbler.start();
+                }
+                
+                OutputStream output = shell.getOutputStream();
+
+                for (CharSequence cmd : commands) {
+                    try {
+                        output.write(toUTF8ByteArray(cmd+"\n"));
+                        output.flush();
+                    } catch (ConnectionException e) {
+                        if (!shell.isOpen()) {
+                            // shell is closed; presumably the user command did `exit`
+                            if (LOG.isDebugEnabled()) LOG.debug("Shell closed to {} when executing {}", SshjTool.this.toString(), commands);
+                            break;
+                        } else {
+                            throw e;
+                        }
+                    }
+                }
+                // workaround attempt for SSHJ deadlock - https://github.com/shikhar/sshj/issues/105
+                synchronized (shell.getOutputStream()) {
+                    shell.sendEOF();
+                }
+                closeWhispering(output, this);
+                
+                boolean timedOut = false;
+                try {
+                    long timeoutMillis = Math.min(timeout.toMilliseconds(), Integer.MAX_VALUE);
+                    long timeoutEnd = System.currentTimeMillis() + timeoutMillis;
+                    Exception last = null;
+                    do {
+                        if (!shell.isOpen() && ((SessionChannel)session).getExitStatus()!=null)
+                            // shell closed, and exit status returned
+                            break;
+                        boolean endBecauseReturned =
+                            // if either condition is satisfied, then wait 1s in hopes the other does, then return
+                            (!shell.isOpen() || ((SessionChannel)session).getExitStatus()!=null);
+                        try {
+                            shell.join(1000, TimeUnit.MILLISECONDS);
+                        } catch (ConnectionException e) {
+                            last = e;
+                        }
+                        if (endBecauseReturned) {
+                            // shell is still open, ie some process is running
+                            // but we have a result code, so main shell is finished
+                            // we waited one second extra to allow any background process 
+                            // which is nohupped to really be in the background (#162)
+                            // now let's bail out
+                            break;
+                        }
+                    } while (System.currentTimeMillis() < timeoutEnd);
+                    if (shell.isOpen() && ((SessionChannel)session).getExitStatus()==null) {
+                        LOG.debug("Timeout ({}) in SSH shell to {}", timeout, this);
+                        // we timed out, or other problem -- reproduce the error.
+                        // The shell.join should always have thrown ConnectionExceptoin (looking at code of
+                        // AbstractChannel), but javadoc of Channel doesn't explicity say that so play it safe.
+                        timedOut = true;
+                        throw (last != null) ? last : new TimeoutException("Timeout after "+timeout+" executing "+this);
+                    }
+                    return ((SessionChannel)session).getExitStatus();
+                } finally {
+                    // wait for all stdout/stderr to have been re-directed
+                    closeWhispering(shell, this);
+                    shell = null;
+                    try {
+                        // Don't use forever (i.e. 0) because BROOKLYN-106: ssh hangs
+                        long joinTimeout = (timedOut) ? 1000 : 10*1000;
+                        if (outgobbler != null) {
+                            outgobbler.join(joinTimeout);
+                            outgobbler.close();
+                        }
+                        if (errgobbler != null) {
+                            errgobbler.join(joinTimeout);
+                            errgobbler.close();
+                        }
+                    } catch (InterruptedException e) {
+                        LOG.warn("Interrupted gobbling streams from ssh: "+commands, e);
+                        Thread.currentThread().interrupt();
+                    }
+                }
+                
+            } finally {
+                clear();
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "Shell(command=[" + commands + "])";
+        }
+    }
+
+    private byte[] toUTF8ByteArray(String string) {
+        return org.bouncycastle.util.Strings.toUTF8ByteArray(string);
+    }
+    
+    private Supplier<InputStream> newInputStreamSupplier(final byte[] contents) {
+        return new Supplier<InputStream>() {
+            @Override public InputStream get() {
+                return new ByteArrayInputStream(contents);
+            }
+        };
+    }
+
+    private Supplier<InputStream> newInputStreamSupplier(final File file) {
+        return new Supplier<InputStream>() {
+            @Override public InputStream get() {
+                try {
+                    return new FileInputStream(file);
+                } catch (FileNotFoundException e) {
+                    throw Exceptions.propagate(e);
+                }
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/javalang/ReflectionScanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/javalang/ReflectionScanner.java b/core/src/main/java/org/apache/brooklyn/core/util/javalang/ReflectionScanner.java
new file mode 100644
index 0000000..e0b824a
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/javalang/ReflectionScanner.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.javalang;
+
+import java.lang.annotation.Annotation;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.reflections.ReflectionUtils;
+import org.reflections.Reflections;
+import org.reflections.Store;
+import org.reflections.scanners.Scanner;
+import org.reflections.scanners.SubTypesScanner;
+import org.reflections.scanners.TypeAnnotationsScanner;
+import org.reflections.util.ClasspathHelper;
+import org.reflections.util.ConfigurationBuilder;
+import org.reflections.util.FilterBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+/** Facade on {@link Reflections} which logs warnings for unloadable classes but does not fail */
+public class ReflectionScanner {
+
+    private static final Logger log = LoggerFactory.getLogger(ReflectionScanner.class);
+    
+    protected final ClassLoader[] classLoaders;
+    protected final Reflections reflections;
+
+    /** scanner which will look in the given urls 
+     * (or if those are null attempt to infer from the first entry in the classloaders,
+     * although currently that seems to only pick up directories, not JAR's),
+     * optionally filtering for the given prefix;
+     * any or all arguments can be null to accept all (and use default classpath for classloading).
+     **/
+    public ReflectionScanner(
+            final Iterable<URL> urlsToScan, 
+            final String optionalPrefix,
+            final ClassLoader ...classLoaders) {
+        reflections = new Reflections(new ConfigurationBuilder() {
+            {
+                final Predicate<String> filter = 
+                        Strings.isNonEmpty(optionalPrefix) ? new FilterBuilder.Include(FilterBuilder.prefix(optionalPrefix)) : null;
+
+                if (urlsToScan!=null)
+                    setUrls(ImmutableSet.copyOf(urlsToScan));
+                else if (classLoaders.length>0 && classLoaders[0]!=null)
+                    setUrls(
+                            ClasspathHelper.forPackage(Strings.isNonEmpty(optionalPrefix) ? optionalPrefix : "",
+                                    asClassLoaderVarArgs(classLoaders[0])));
+                
+                if (filter!=null) filterInputsBy(filter);
+
+                Scanner typeScanner = new TypeAnnotationsScanner();
+                if (filter!=null) typeScanner = typeScanner.filterResultsBy(filter);
+                Scanner subTypeScanner = new SubTypesScanner();
+                if (filter!=null) subTypeScanner = subTypeScanner.filterResultsBy(filter);
+                setScanners(typeScanner, subTypeScanner);
+                
+                for (ClassLoader cl: classLoaders)
+                    if (cl!=null) addClassLoader(cl);
+            }
+        });
+        this.classLoaders = Iterables.toArray(Iterables.filter(Arrays.asList(classLoaders), Predicates.notNull()), ClassLoader.class);
+    }
+
+    private static ClassLoader[] asClassLoaderVarArgs(final ClassLoader classLoaderToSearch) {
+        return classLoaderToSearch==null ? new ClassLoader[0] : new ClassLoader[] { classLoaderToSearch };
+    }
+
+    public Store getStore() {
+        return reflections.getStore();
+    }
+    
+    /** overrides delegate so as to log rather than throw exception if a class cannot be loaded */
+    public <T> Set<Class<? extends T>> getSubTypesOf(final Class<T> type) {
+        Set<String> subTypes = getStore().getSubTypesOf(type.getName());
+        return ImmutableSet.copyOf(this.<T>forNames(subTypes, "sub-type of "+type));
+    }
+    
+    /** overrides delegate so as to log rather than throw exception if a class cannot be loaded */
+    public Set<Class<?>> getTypesAnnotatedWith(Class<? extends Annotation> annotation) {
+        Set<String> annotatedWith = getStore().getTypesAnnotatedWith(annotation.getName());
+        return ImmutableSet.copyOf(this.forNames(annotatedWith, "annotated "+annotation.getName()));
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T> List<Class<? extends T>> forNames(Set<String> classNames, final String context) {
+        List<Class<? extends T>> result = new ArrayList<Class<? extends T>>();
+        for (String className : classNames) {
+            //noinspection unchecked
+            try {
+                Class<? extends T> clazz = (Class<? extends T>) loadClass(className);
+                if (clazz != null) {
+                    result.add(clazz);
+                } else {
+                    log.warn("Unable to instantiate '"+className+"' ("+context+")");
+                }
+            } catch (Throwable e) {
+                log.warn("Unable to instantiate '"+className+"' ("+context+"): "+e);
+            }
+        }
+        return result;
+    }
+    
+    protected Class<?> loadClass(String className) {
+        return ReflectionUtils.forName(className, classLoaders);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/javalang/UrlClassLoader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/javalang/UrlClassLoader.java b/core/src/main/java/org/apache/brooklyn/core/util/javalang/UrlClassLoader.java
new file mode 100644
index 0000000..afd0c8e
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/javalang/UrlClassLoader.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.javalang;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+
+import org.apache.brooklyn.core.util.ResourceUtils;
+
+import brooklyn.util.exceptions.Exceptions;
+
+/** like URLClassLoader (and delegates to it) but:
+ * * has a nice toString
+ * * supports var args constructor
+ * * supports file://~/xxx semantics (remaps it to user.home); 
+ *      ideally we'd also support mvn, classpath, osgi, etc
+ */
+public class UrlClassLoader extends URLClassLoader {
+
+    private URL[] urls;
+
+    public UrlClassLoader(URL ...urls) {
+        super(tidy(urls));
+        this.urls = urls;
+    }
+
+    public UrlClassLoader(String ...urls) {
+        this(asUrls(urls));
+    }
+    
+    private static URL[] asUrls(String[] urls) {
+        URL[] urlo = new URL[urls.length];
+        try {
+        for (int i=0; i<urls.length; i++)
+            urlo[i] = new URL(urls[i]);
+        } catch (MalformedURLException e) {
+            throw Exceptions.propagate(e);
+        }
+        return urlo;
+    }
+
+    private static URL[] tidy(URL[] urls) {
+        for (int i=0; i<urls.length; i++)
+            urls[i] = ResourceUtils.tidy(urls[i]);
+        return urls;
+    }
+
+    @Override
+    public String toString() {
+        return "UrlClassLoader"+Arrays.asList(urls);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/mutex/MutexSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/mutex/MutexSupport.java b/core/src/main/java/org/apache/brooklyn/core/util/mutex/MutexSupport.java
new file mode 100644
index 0000000..2a90f95
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/mutex/MutexSupport.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.mutex;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableMap;
+
+public class MutexSupport implements WithMutexes {
+
+    private static final Logger log = LoggerFactory.getLogger(MutexSupport.class);
+    private final Map<String,SemaphoreWithOwners> semaphores = new LinkedHashMap<String,SemaphoreWithOwners>();
+
+    protected synchronized SemaphoreWithOwners getSemaphore(String mutexId) {
+        return getSemaphore(mutexId, false);
+    }
+    // NB: the map could be "lock-striped" (hashed on mutexId) to avoid the central lock 
+    protected synchronized SemaphoreWithOwners getSemaphore(String mutexId, boolean requestBeforeReturning) {
+        SemaphoreWithOwners s = semaphores.get(mutexId);
+        if (s==null) {
+            s = new SemaphoreWithOwners(mutexId);
+            semaphores.put(mutexId, s);
+        }
+        if (requestBeforeReturning) s.indicateCallingThreadWillRequest();
+        return s;
+    }
+    /** forces deletion of the given mutex if it is unused; 
+     * normally not required as is done automatically on close
+     * (but possibly needed where there are cancelations and risk of memory leaks) */
+    public synchronized void cleanupMutex(String mutexId) {
+        SemaphoreWithOwners s = semaphores.get(mutexId);
+        if (!s.isInUse()) semaphores.remove(mutexId);
+    }
+    public synchronized void cleanup() {
+        Iterator<SemaphoreWithOwners> si = semaphores.values().iterator();
+        while (si.hasNext()) {
+            SemaphoreWithOwners s = si.next();
+            if (!s.isInUse()) si.remove();
+        }
+    }
+
+    @Override
+    public synchronized boolean hasMutex(String mutexId) {
+        SemaphoreWithOwners s = semaphores.get(mutexId);
+        if (s!=null) return s.isCallingThreadAnOwner();
+        return false;
+    }
+    
+    @Override
+    public void acquireMutex(String mutexId, String description) throws InterruptedException {
+        SemaphoreWithOwners s = getSemaphore(mutexId, true);
+        if (description!=null) Tasks.setBlockingDetails(description+" - waiting for "+mutexId);
+        if (log.isDebugEnabled())
+            log.debug("Acquiring mutex: "+mutexId+"@"+this+" - "+description);
+        s.acquire(); 
+        if (description!=null) Tasks.setBlockingDetails(null);
+        s.setDescription(description);
+        if (log.isDebugEnabled())
+            log.debug("Acquired mutex: "+mutexId+"@"+this+" - "+description);
+    }
+
+    @Override
+    public boolean tryAcquireMutex(String mutexId, String description) {
+        SemaphoreWithOwners s = getSemaphore(mutexId, true);
+        if (s.tryAcquire()) {
+            if (log.isDebugEnabled())
+                log.debug("Acquired mutex (opportunistic): "+mutexId+"@"+this+" - "+description);
+            s.setDescription(description);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public synchronized void releaseMutex(String mutexId) {
+        SemaphoreWithOwners s;
+        if (log.isDebugEnabled())
+            log.debug("Releasing mutex: "+mutexId+"@"+this);
+        synchronized (this) { s = semaphores.get(mutexId); }
+        if (s==null) throw new IllegalStateException("No mutex known for '"+mutexId+"'");
+        s.release();
+        cleanupMutex(mutexId);
+    }
+    
+    @Override
+    public synchronized String toString() {
+        return super.toString()+"["+semaphores.size()+" semaphores: "+semaphores.values()+"]";
+    }
+    
+    /** Returns the semaphores in use at the time the method is called, for inspection purposes (and testing).
+     * The semaphores used by this class may change over time so callers are strongly discouraged
+     * from manipulating the semaphore objects themselves. 
+     */
+    public synchronized Map<String,SemaphoreWithOwners> getAllSemaphores() {
+        return ImmutableMap.<String,SemaphoreWithOwners>copyOf(semaphores);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/mutex/SemaphoreForTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/mutex/SemaphoreForTasks.java b/core/src/main/java/org/apache/brooklyn/core/util/mutex/SemaphoreForTasks.java
new file mode 100644
index 0000000..7e805b0
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/mutex/SemaphoreForTasks.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.mutex;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.Tasks;
+
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableSet;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+
+/** A subclass of {@link SemaphoreWithOwners} 
+ * which additionally sets Task blocking information. 
+ * <p>
+ * TODO As tasks are distributed this should support distribution across the management context. */
+public class SemaphoreForTasks extends SemaphoreWithOwners {
+    
+    private static final long serialVersionUID = 7898283056223005952L;
+    
+    /** unused at present, but wanted on the API for when this may be federated */
+    @SuppressWarnings("unused")
+    private final ManagementContext mgmt;
+    
+    final private MutableList<Task<?>> owningTasks = new MutableList<Task<?>>();
+    final private MutableSet<Task<?>> requestingTasks = new MutableSet<Task<?>>();
+
+    public SemaphoreForTasks(String name, ManagementContext mgmt) {
+        super(name);
+        this.mgmt = Preconditions.checkNotNull(mgmt);
+    }
+    
+    public SemaphoreForTasks(String name, int permits, boolean fair, ManagementContext mgmt) {
+        super(name, permits, fair);
+        this.mgmt = Preconditions.checkNotNull(mgmt);
+    }
+    
+    public synchronized Set<Task<?>> getRequestingTasks() {
+        return ImmutableSet.copyOf(requestingTasks);
+    }
+    
+    public synchronized List<Task<?>> getOwningTasks() {
+        return ImmutableList.copyOf(owningTasks);
+    }
+
+    @Override
+    protected synchronized void onRequesting() {
+        if (!owningTasks.isEmpty() || !requestingTasks.isEmpty()) {
+            Tasks.setBlockingTask( !requestingTasks.isEmpty() ? Iterables.getLast(requestingTasks) : Iterables.getFirst(owningTasks, null) );
+            Tasks.setBlockingDetails("Waiting on semaphore "+getName()+" ("+getDescription()+"); "
+                + "queued at "+Time.makeDateString()+" when "+getRequestingThreads().size()+" ahead in queue");
+        }
+        requestingTasks.addIfNotNull(Tasks.current());
+        super.onRequesting();
+    }
+    
+    @Override
+    protected synchronized void onRequestFinished() {
+        super.onRequestFinished();
+        requestingTasks.removeIfNotNull(Tasks.current());
+        
+        Tasks.resetBlockingDetails();
+        Tasks.resetBlockingTask();
+    }
+    
+    @Override
+    protected synchronized void onAcquired(int permits) {
+        super.onAcquired(permits);
+        for (int i=0; i<permits; i++)
+            owningTasks.appendIfNotNull(Tasks.current());
+    }
+    
+    @Override
+    protected synchronized void onReleased(int permits) {
+        super.onReleased(permits);
+        for (int i=0; i<permits; i++)
+            owningTasks.removeIfNotNull(Tasks.current());
+    }
+    
+    @Override
+    public synchronized String toString() {
+        return super.toString()+"["
+            + "owningTasks="+owningTasks
+            + "; requestingTasks="+requestingTasks+"]";
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/mutex/SemaphoreWithOwners.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/mutex/SemaphoreWithOwners.java b/core/src/main/java/org/apache/brooklyn/core/util/mutex/SemaphoreWithOwners.java
new file mode 100644
index 0000000..0144d4d
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/mutex/SemaphoreWithOwners.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.mutex;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import brooklyn.util.exceptions.Exceptions;
+
+import com.google.common.collect.ImmutableList;
+
+/** a subclass of {@link Semaphore} 
+ * which tracks who created and released the semaphores,
+ * and which requires the same thread to release as created it. */
+public class SemaphoreWithOwners extends Semaphore {
+    public SemaphoreWithOwners(String name) {
+        this(name, 1, true);
+    }
+    public SemaphoreWithOwners(String name, int permits, boolean fair) {
+        super(permits, fair);
+        this.name = name;
+    }
+    private static final long serialVersionUID = -5303474637353009454L;
+    final private List<Thread> owningThreads = new ArrayList<Thread>();
+    final private Set<Thread> requestingThreads = new LinkedHashSet<Thread>();
+    
+    @Override
+    public void acquire() throws InterruptedException {
+        try {
+            onRequesting();
+            super.acquire();
+            onAcquired(1);
+        } finally {
+            onRequestFinished();
+        }
+    }
+    @Override
+    public void acquire(int permits) throws InterruptedException {
+        try {
+            onRequesting();
+            super.acquire(permits);
+            onAcquired(permits);
+        } finally {
+            onRequestFinished();
+        }
+    }
+    @Override
+    public void acquireUninterruptibly() {
+        try {
+            onRequesting();
+            super.acquireUninterruptibly();
+            onAcquired(1);
+        } finally {
+            onRequestFinished();
+        }
+    }
+    @Override
+    public void acquireUninterruptibly(int permits) {
+        try {
+            onRequesting();
+            super.acquireUninterruptibly(permits);
+            onAcquired(permits);
+        } finally {
+            onRequestFinished();
+        }
+    }
+
+    public void acquireUnchecked() {
+        try {
+            acquire();
+        } catch (InterruptedException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    public void acquireUnchecked(int numPermits) {
+        try {
+            acquire(numPermits);
+        } catch (InterruptedException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+    @Override
+    public boolean tryAcquire() {
+        try {
+            onRequesting();
+            if (super.tryAcquire()) {
+                onAcquired(1);
+                return true;
+            }
+            return false;
+        } finally {
+            onRequestFinished();
+        }
+    }
+    @Override
+    public boolean tryAcquire(int permits) {
+        try {
+            onRequesting();
+            if (super.tryAcquire(permits)) {
+                onAcquired(permits);
+                return true;
+            }
+            return false;
+        } finally {
+            onRequestFinished();
+        }
+    }
+    @Override
+    public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException {
+        try {
+            onRequesting();
+            if (super.tryAcquire(permits, timeout, unit)) {
+                onAcquired(permits);
+                return true;
+            }
+            return false;
+        } finally {
+            onRequestFinished();
+        }
+    }
+    @Override
+    public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {
+        try {
+            onRequesting();
+            if (super.tryAcquire(timeout, unit)) {
+                onAcquired(1);
+                return true;
+            }
+            return false;
+        } finally {
+            onRequestFinished();
+        }
+    }
+
+    /** invoked when a caller successfully acquires a mutex, before {@link #onRequestFinished()} */
+    protected synchronized void onAcquired(int permits) {
+        for (int i=0; i<permits; i++) owningThreads.add(Thread.currentThread());
+    }
+    /** invoked when a caller is about to request a semaphore (before it might block);
+     * guaranteed to call {@link #onRequestFinished()} after the blocking,
+     * with a call to {@link #onAcquired(int)} beforehand if the acquisition was successful */
+    protected synchronized void onRequesting() {
+        requestingThreads.add(Thread.currentThread());
+    }
+    /** invoked when a caller has completed requesting a mutex, whether successful, aborted, or interrupted */
+    protected synchronized void onRequestFinished() {
+        requestingThreads.remove(Thread.currentThread());
+    }
+
+    @Override
+    public void release() {
+        super.release();
+        onReleased(1);
+    }
+    @Override
+    public void release(int permits) {
+        super.release(permits);
+        onReleased(permits);
+    }
+
+    /** invoked when a caller has released permits */
+    protected synchronized void onReleased(int permits) {
+        boolean result = true;
+        for (int i=0; i<permits; i++) result = owningThreads.remove(Thread.currentThread()) & result;
+        if (!result) throw new IllegalStateException("Thread "+Thread.currentThread()+" which released "+this+" did not own it.");  
+    }
+
+    /** true iff there are any owners or any requesters (callers blocked trying to acquire) */
+    public synchronized boolean isInUse() {
+        return !owningThreads.isEmpty() || !requestingThreads.isEmpty();
+    }
+
+    /** true iff the calling thread is one of the owners */ 
+    public synchronized boolean isCallingThreadAnOwner() {
+        return owningThreads.contains(Thread.currentThread());
+    }
+
+    private final String name;
+    /** constructor-time supplied name */
+    public String getName() { return name; }
+
+    private String description;
+    public void setDescription(String description) { this.description = description; }
+    /** caller supplied description */
+    public String getDescription() { return description; }
+
+    /** unmodifiable view of threads owning the permits; threads with multiple permits listed multiply */
+    public synchronized List<Thread> getOwningThreads() {
+        return ImmutableList.<Thread>copyOf(owningThreads);
+    }
+    /** unmodifiable view of threads requesting access (blocked or briefly trying to acquire);
+     * this is guaranteed to be cleared _after_ getOwners 
+     * (synchronizing on this class while reading both fields will give canonical access) */
+    public synchronized List<Thread> getRequestingThreads() {
+        return ImmutableList.<Thread>copyOf(requestingThreads);
+    }
+    
+    @Override
+    public synchronized String toString() {
+        return super.toString()+"["+name+"; description="+description+"; owning="+owningThreads+"; requesting="+requestingThreads+"]";
+    }
+    
+    /** Indicate that the calling thread is going to acquire or tryAcquire, 
+     * in order to set up the semaphore's isInUse() value appropriately for certain checks.
+     * It *must* do so after invoking this method. */ 
+    public void indicateCallingThreadWillRequest() {
+        requestingThreads.add(Thread.currentThread());
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/mutex/WithMutexes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/mutex/WithMutexes.java b/core/src/main/java/org/apache/brooklyn/core/util/mutex/WithMutexes.java
new file mode 100644
index 0000000..82eab02
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/mutex/WithMutexes.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.mutex;
+
+/** interface which allows multiple callers to co-operate using named mutexes, inspectably,
+ * and containing implementation as inner class
+ * <p>
+ * MutexSupport is a common implementation of this.
+ * mixin code frequently delegates to this, 
+ * as shown in the test case's WithMutexesTest.SampleWithMutexesDelegatingMixin class 
+ **/
+public interface WithMutexes {
+
+    /** returns true if the calling thread has the mutex with the given ID */
+    public boolean hasMutex(String mutexId);
+    
+    /** acquires a mutex, if available, otherwise blocks on its becoming available;
+     * caller must release after use */
+    public void acquireMutex(String mutexId, String description) throws InterruptedException;
+
+    /** acquires a mutex and returns true, if available; otherwise immediately returns false;
+     * caller must release after use if this returns true */
+    public boolean tryAcquireMutex(String mutexId, String description);
+
+    /** releases a mutex, triggering another thread to use it or cleaning it up if no one else is waiting;
+     * this should only be called by the mutex owner (thread) */
+    public void releaseMutex(String mutexId);
+    
+}


[18/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/qa/performance/TaskPerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/qa/performance/TaskPerformanceTest.java b/core/src/test/java/brooklyn/qa/performance/TaskPerformanceTest.java
index 6e4a0d0..c940a36 100644
--- a/core/src/test/java/brooklyn/qa/performance/TaskPerformanceTest.java
+++ b/core/src/test/java/brooklyn/qa/performance/TaskPerformanceTest.java
@@ -26,6 +26,8 @@ import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.SingleThreadedScheduler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.BeforeMethod;
@@ -33,8 +35,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.BasicExecutionManager;
-import brooklyn.util.task.SingleThreadedScheduler;
 import brooklyn.util.time.Time;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/test/HttpService.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/test/HttpService.java b/core/src/test/java/brooklyn/test/HttpService.java
index 2710211..80f63f0 100644
--- a/core/src/test/java/brooklyn/test/HttpService.java
+++ b/core/src/test/java/brooklyn/test/HttpService.java
@@ -40,10 +40,10 @@ import org.eclipse.jetty.webapp.WebAppContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.api.location.PortRange;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.crypto.SecureKeys;
 import brooklyn.util.javalang.Threads;
 import brooklyn.util.os.Os;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/test/policy/TestEnricher.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/test/policy/TestEnricher.java b/core/src/test/java/brooklyn/test/policy/TestEnricher.java
index c204c8b..88cc362 100644
--- a/core/src/test/java/brooklyn/test/policy/TestEnricher.java
+++ b/core/src/test/java/brooklyn/test/policy/TestEnricher.java
@@ -23,6 +23,7 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import com.google.common.reflect.TypeToken;
 
@@ -30,7 +31,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 public class TestEnricher extends AbstractEnricher {
     @SetFromFlag("confName")

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/test/policy/TestPolicy.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/test/policy/TestPolicy.java b/core/src/test/java/brooklyn/test/policy/TestPolicy.java
index b93450c..184eb4e 100644
--- a/core/src/test/java/brooklyn/test/policy/TestPolicy.java
+++ b/core/src/test/java/brooklyn/test/policy/TestPolicy.java
@@ -22,12 +22,12 @@ import java.util.Collections;
 import java.util.Map;
 
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.policy.basic.AbstractPolicy;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.reflect.TypeToken;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/BrooklynMavenArtifactsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/BrooklynMavenArtifactsTest.java b/core/src/test/java/brooklyn/util/BrooklynMavenArtifactsTest.java
deleted file mode 100644
index 0fcfdf7..0000000
--- a/core/src/test/java/brooklyn/util/BrooklynMavenArtifactsTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import brooklyn.test.Asserts;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.maven.MavenArtifact;
-import brooklyn.util.maven.MavenRetriever;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.text.Strings;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-@Test
-public class BrooklynMavenArtifactsTest {
-
-    private static final Logger log = LoggerFactory.getLogger(BrooklynMavenArtifactsTest.class);
-    
-    @Test(groups="Integration")
-    public void testUtilsCommon() {
-        ResourceUtils.create(this).checkUrlExists(BrooklynMavenArtifacts.localUrlForJar("brooklyn-utils-common"));
-    }
-
-    @Test(groups="Integration")
-    public void testExampleWar() {
-        String url = BrooklynMavenArtifacts.localUrl("example", "brooklyn-example-hello-world-sql-webapp", "war");
-        ResourceUtils.create(this).checkUrlExists(url);
-        log.info("found example war at: "+url);
-    }
-
-    @Test(groups="Integration")
-    // runs without internet but doesn't assert what it should, and can take a long time, so integration
-    public void testBadExampleWar() {
-        String url = BrooklynMavenArtifacts.localUrl("example", "brooklyn-example-GOODBYE-world-sql-webapp", "war");
-        Assert.assertFalse(ResourceUtils.create(this).doesUrlExist(url), "should not exist: "+url);
-    }
-
-    public void testHostedIsHttp() {
-        String common = BrooklynMavenArtifacts.hostedUrlForJar("brooklyn-utils-common");
-        log.info("online should be at: "+common);
-        Assert.assertTrue(common.startsWith("http"));
-    }
-
-    @Test(groups="Integration")
-    public void testHistoricHosted() {
-        // NB: this should be a version known to be up at sonatype or maven central, NOT necessarily the current version!
-        String snapshot = MavenRetriever.hostedUrl(MavenArtifact.fromCoordinate("org.apache.brooklyn:brooklyn-utils-common:jar:0.7.0-SNAPSHOT"));
-        log.info("Sample snapshot URL is: "+snapshot);
-        checkValidArchive(snapshot);
-        ResourceUtils.create(this).checkUrlExists(snapshot);
-        
-        // NB: this should be a version known to be up at sonatype or maven central, NOT necessarily the current version!
-        String release = MavenRetriever.hostedUrl(MavenArtifact.fromCoordinate("io.brooklyn:brooklyn-utils-common:jar:0.6.0"));
-        log.info("Sample release URL is: "+release);
-        checkValidArchive(release);
-    }
-
-    private void checkValidArchive(final String url) {
-        // Note have seen response code 500 from repository.apache.org, for
-        //   https://repository.apache.org/service/local/artifact/maven/redirect?r=snapshots&v=0.7.0-SNAPSHOT&g=org.apache.brooklyn&a=brooklyn-utils-common&e=jar
-        // Therefore willing to retry, rather than failing immediately.
-        Asserts.succeedsEventually(new Runnable() {
-            @Override public void run() {
-                try {
-                    byte[] bytes = Streams.readFully(ResourceUtils.create(this).getResourceFromUrl(url));
-                    // confirm this follow redirects!
-                    Assert.assertTrue(bytes.length > 100*1000, "download of "+url+" is suspect ("+Strings.makeSizeString(bytes.length)+")");
-                    // (could also check it is a zip etc)
-                } catch (Exception e) {
-                    throw Exceptions.propagate(e);
-                }
-            }});
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/ResourceUtilsHttpTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/ResourceUtilsHttpTest.java b/core/src/test/java/brooklyn/util/ResourceUtilsHttpTest.java
deleted file mode 100644
index daac00a..0000000
--- a/core/src/test/java/brooklyn/util/ResourceUtilsHttpTest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.http.HttpException;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.localserver.RequestBasicAuth;
-import org.apache.http.localserver.ResponseBasicUnauthorized;
-import org.apache.http.protocol.HttpContext;
-import org.apache.http.protocol.HttpRequestHandler;
-import org.apache.http.protocol.ResponseServer;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import brooklyn.test.TestHttpRequestHandler;
-import brooklyn.test.TestHttpServer;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.text.Strings;
-
-public class ResourceUtilsHttpTest {
-    private ResourceUtils utils;
-    private TestHttpServer server;
-    private String baseUrl;
-
-    @BeforeClass(alwaysRun=true)
-    public void setUp() throws Exception {
-        utils = ResourceUtils.create(this, "mycontext");
-        server = new TestHttpServer()
-            .interceptor(new ResponseServer())
-            .interceptor(new ResponseBasicUnauthorized())
-            .interceptor(new RequestBasicAuth())
-            .handler("/simple", new TestHttpRequestHandler().response("OK"))
-            .handler("/empty", new TestHttpRequestHandler().code(HttpStatus.SC_NO_CONTENT))
-            .handler("/missing", new TestHttpRequestHandler().code(HttpStatus.SC_NOT_FOUND).response("Missing"))
-            .handler("/redirect", new TestHttpRequestHandler().code(HttpStatus.SC_MOVED_TEMPORARILY).response("Redirect").header("Location", "/simple"))
-            .handler("/cycle", new TestHttpRequestHandler().code(HttpStatus.SC_MOVED_TEMPORARILY).response("Redirect").header("Location", "/cycle"))
-            .handler("/secure", new TestHttpRequestHandler().code(HttpStatus.SC_MOVED_TEMPORARILY).response("Redirect").header("Location", "https://0.0.0.0/"))
-            .handler("/auth", new AuthHandler("test", "test", "OK"))
-            .handler("/auth_escape", new AuthHandler("test@me:/", "test", "OK"))
-            .handler("/auth_escape2", new AuthHandler("test@me:test", "", "OK"))
-            .handler("/no_credentials", new CheckNoCredentials())
-            .start();
-        baseUrl = server.getUrl();
-    }
-
-    @AfterClass(alwaysRun=true)
-    public void tearDown() throws Exception {
-        server.stop();
-    }
-
-    @Test
-    public void testGet() throws Exception {
-        InputStream stream = utils.getResourceFromUrl(baseUrl + "/simple");
-        assertEquals(Streams.readFullyString(stream), "OK");
-    }
-
-    @Test
-    public void testGetEmpty() throws Exception {
-        InputStream stream = utils.getResourceFromUrl(baseUrl + "/empty");
-        assertEquals(Streams.readFullyString(stream), "");
-    }
-
-    @Test
-    public void testGetProtected() throws Exception {
-        String url = baseUrl.replace("http://", "http://test:test@") + "/auth";
-        InputStream stream = utils.getResourceFromUrl(url);
-        assertEquals(Streams.readFullyString(stream), "OK");
-    }
-
-    @Test
-    public void testGetProtectedEscape() throws Exception {
-        String url = baseUrl.replace("http://", "http://test%40me%3A%2F:test@") + "/auth_escape";
-        InputStream stream = utils.getResourceFromUrl(url);
-        assertEquals(Streams.readFullyString(stream), "OK");
-    }
-
-    @Test
-    public void testGetProtectedEscape2() throws Exception {
-        String url = baseUrl.replace("http://", "http://test%40me%3Atest@") + "/auth_escape2";
-        InputStream stream = utils.getResourceFromUrl(url);
-        assertEquals(Streams.readFullyString(stream), "OK");
-    }
-
-    @Test(expectedExceptions = RuntimeException.class)
-    public void testProtectedFailsWithoutCredentials() throws Exception {
-        utils.getResourceFromUrl(baseUrl + "/auth");
-    }
-
-    @Test
-    public void testInvalidCredentialsNotPassed() throws Exception {
-        String url = baseUrl + "/no_credentials?no:auth@needed";
-        InputStream stream = utils.getResourceFromUrl(url);
-        assertEquals(Streams.readFullyString(stream), "OK");
-    }
-
-    @Test
-    public void testRedirect() throws Exception {
-        InputStream stream = utils.getResourceFromUrl(baseUrl + "/redirect");
-        assertEquals(Streams.readFullyString(stream), "OK");
-    }
-
-    @Test(expectedExceptions = RuntimeException.class)
-    public void testCycleRedirect() throws Exception {
-        InputStream stream = utils.getResourceFromUrl(baseUrl + "/cycle");
-        assertEquals(Streams.readFullyString(stream), "OK");
-    }
-
-    @Test(expectedExceptions = RuntimeException.class)
-    public void testGetMissing() throws Exception {
-        utils.getResourceFromUrl(baseUrl + "/missing");
-    }
-
-    @Test(expectedExceptions = RuntimeException.class)
-    public void testFollowsProtoChange() throws Exception {
-        utils.getResourceFromUrl(baseUrl + "/secure");
-    }
-
-    // See https://github.com/brooklyncentral/brooklyn/issues/1338
-    @Test(groups={"Integration"})
-    public void testResourceFromUrlFollowsRedirect() throws Exception {
-        String contents = new ResourceUtils(this).getResourceAsString("http://bit.ly/brooklyn-visitors-creation-script");
-        assertFalse(contents.contains("bit.ly"), "contents="+contents);
-    }
-
-    private static class AuthHandler implements HttpRequestHandler {
-        private String username;
-        private String password;
-        private String responseBody;
-
-        public AuthHandler(String username, String password, String response) {
-            this.username = username;
-            this.password = password;
-            this.responseBody = response;
-        }
-
-        @Override
-        public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
-            String creds = (String) context.getAttribute("creds");
-            if (creds == null || !creds.equals(getExpectedCredentials())) {
-                response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
-            } else {
-                response.setEntity(new StringEntity(responseBody));
-            }
-        }
-
-        private String getExpectedCredentials() {
-            if (Strings.isEmpty(password)) {
-                return username;
-            } else {
-                return username + ":" + password;
-            }
-        }
-
-    }
-
-    private static class CheckNoCredentials implements HttpRequestHandler {
-
-        @Override
-        public void handle(HttpRequest request, HttpResponse response,
-                HttpContext context) throws HttpException, IOException {
-            String creds = (String) context.getAttribute("creds");
-            if (creds == null) {
-                response.setEntity(new StringEntity("OK"));
-            } else {
-                response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
-            }
-        }
-
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/ResourceUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/ResourceUtilsTest.java b/core/src/test/java/brooklyn/util/ResourceUtilsTest.java
deleted file mode 100644
index de54ebb..0000000
--- a/core/src/test/java/brooklyn/util/ResourceUtilsTest.java
+++ /dev/null
@@ -1,189 +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;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import brooklyn.util.net.Urls;
-import brooklyn.util.os.Os;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.text.Identifiers;
-
-import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.io.Files;
-
-public class ResourceUtilsTest {
-
-    private static final Logger log = LoggerFactory.getLogger(ResourceUtilsTest.class);
-    
-    private String tempFileContents = "abc";
-    private ResourceUtils utils;
-    private File tempFile;
-    
-    @BeforeClass(alwaysRun=true)
-    public void setUp() throws Exception {
-        utils = ResourceUtils.create(this, "mycontext");
-        tempFile = Os.writeToTempFile(new ByteArrayInputStream(tempFileContents.getBytes()), "resourceutils-test", ".txt");
-    }
-    
-    @AfterClass(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (tempFile != null) tempFile.delete();
-    }
-
-    @Test
-    public void testWriteStreamToTempFile() throws Exception {
-        File tempFileLocal = Os.writeToTempFile(new ByteArrayInputStream("mycontents".getBytes()), "resourceutils-test", ".txt");
-        try {
-            List<String> lines = Files.readLines(tempFileLocal, Charsets.UTF_8);
-            assertEquals(lines, ImmutableList.of("mycontents"));
-        } finally {
-            tempFileLocal.delete();
-        }
-    }
-
-    @Test
-    public void testPropertiesStreamToTempFile() throws Exception {
-        Properties props = new Properties();
-        props.setProperty("mykey", "myval");
-        File tempFileLocal = Os.writePropertiesToTempFile(props, "resourceutils-test", ".txt");
-        FileInputStream fis = null;
-        try {
-            fis = new FileInputStream(tempFileLocal);
-            Properties props2 = new Properties();
-            props2.load(fis);
-            assertEquals(props2.getProperty("mykey"), "myval");
-        } finally {
-            Streams.closeQuietly(fis);
-            tempFileLocal.delete();
-        }
-    }
-
-    @Test
-    public void testGetResourceViaClasspathWithPrefix() throws Exception {
-        InputStream stream = utils.getResourceFromUrl("classpath://brooklyn/config/sample.properties");
-        assertNotNull(stream);
-    }
-    
-    @Test
-    public void testGetResourceViaClasspathWithoutPrefix() throws Exception {
-        InputStream stream = utils.getResourceFromUrl("/brooklyn/config/sample.properties");
-        assertNotNull(stream);
-    }
-
-    @Test
-    public void testGetResourceViaFileWithPrefix() throws Exception {
-        // The correct format for file URLs is file:///<absolute path>.
-        // On UNIX file:///tmp.
-        // On Windows both file:/C:/temp and file:///C:/temp are supported by Java, 
-        // while Windows itself supports the latter only. 
-        // Note that file://C:/temp is *wrong*, because C: is interpreted as the host
-        InputStream stream = utils.getResourceFromUrl(tempFile.toURI().toURL().toString());
-        assertEquals(Streams.readFullyString(stream), tempFileContents);
-    }
-    
-    @Test
-    public void testGetResourceViaFileWithoutPrefix() throws Exception {
-        InputStream stream = utils.getResourceFromUrl(tempFile.getAbsolutePath());
-        assertEquals(Streams.readFullyString(stream), tempFileContents);
-    }
-
-    @Test
-    public void testClassLoaderDir() throws Exception {
-        String d = utils.getClassLoaderDir();
-        log.info("Found resource "+this+" in: "+d);
-        assertTrue(new File(d, "brooklyn/util/").exists());
-    }
-
-    @Test
-    public void testClassLoaderDirFromJar() throws Exception {
-        String d = utils.getClassLoaderDir("java/lang/Object.class");
-        log.info("Found Object in: "+d);
-        assertTrue(d.toLowerCase().endsWith(".jar"));
-    }
-
-    @Test
-    public void testClassLoaderDirFromJarWithSlash() throws Exception {
-        String d = utils.getClassLoaderDir("/java/lang/Object.class");
-        log.info("Found Object in: "+d);
-        assertTrue(d.toLowerCase().endsWith(".jar"));
-    }
-
-    @Test(expectedExceptions={NoSuchElementException.class})
-    public void testClassLoaderDirNotFound() throws Exception {
-        String d = utils.getClassLoaderDir("/somewhere/not/found/XXX.xxx");
-        // above should fail
-        log.warn("Uh oh found imaginary resource in: "+d);
-    }
-
-    @Test(groups="Integration")
-    public void testGetResourceViaSftp() throws Exception {
-        InputStream stream = utils.getResourceFromUrl("sftp://localhost:"+tempFile.getAbsolutePath());
-        assertEquals(Streams.readFullyString(stream), tempFileContents);
-    }
-    
-    @Test(groups="Integration")
-    public void testGetResourceViaSftpWithUsername() throws Exception {
-        String user = System.getProperty("user.name");
-        InputStream stream = utils.getResourceFromUrl("sftp://"+user+"@localhost:"+tempFile.getAbsolutePath());
-        assertEquals(Streams.readFullyString(stream), tempFileContents);
-    }
-
-    @Test
-    public void testDataUrl() throws Exception {
-        assertEquals(utils.getResourceAsString("data:,hello"), "hello");
-        assertEquals(utils.getResourceAsString("data:,hello%20world"), "hello world");
-        // above is correct. below are not valid ... but we accept them anyway
-        assertEquals(utils.getResourceAsString("data:hello"), "hello");
-        assertEquals(utils.getResourceAsString("data://hello"), "hello");
-        assertEquals(utils.getResourceAsString("data:hello world"), "hello world");
-        assertEquals(utils.getResourceAsString(Urls.asDataUrlBase64("hello world")), "hello world");
-        
-        String longString = Identifiers.makeRandomId(256);
-        for (int a=32; a<128; a++) longString += (char)a;
-        assertEquals(utils.getResourceAsString(Urls.asDataUrlBase64(longString)), longString);
-    }
-
-    @Test
-    public void testGetResources() {
-        Iterable<URL> manifests = ResourceUtils.create().getResources("META-INF/MANIFEST.MF");
-        assertFalse(Iterables.isEmpty(manifests));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/config/ConfigBagTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/config/ConfigBagTest.java b/core/src/test/java/brooklyn/util/config/ConfigBagTest.java
deleted file mode 100644
index 93cf6ed..0000000
--- a/core/src/test/java/brooklyn/util/config/ConfigBagTest.java
+++ /dev/null
@@ -1,191 +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.config;
-
-import static org.testng.Assert.assertEquals;
-
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.time.Duration;
-
-public class ConfigBagTest {
-
-    @SuppressWarnings("unused")
-    private static final Logger log = LoggerFactory.getLogger(ConfigBagTest.class);
-    
-    private static final ConfigKey<String> K1 = ConfigKeys.newStringConfigKey("k1");
-    private static final ConfigKey<String> K2 = ConfigKeys.newStringConfigKey("k2");
-    private static final ConfigKey<String> K3 = ConfigKeys.newStringConfigKey("k3");
-    
-    @Test
-    public void testPutAndGet() {
-        ConfigBag bag = ConfigBag.newInstance();
-        bag.put(K1, "v1");
-        assertEquals(bag.get(K1), "v1");
-    }
-    
-    @Test
-    public void testPutStringAndGet() {
-        ConfigBag bag = ConfigBag.newInstance();
-        bag.putAsStringKey(K1.getName(), "v1");
-        assertEquals(bag.get(K1), "v1");
-    }
-    
-    @Test
-    public void testUnused() {
-        ConfigBag bag = ConfigBag.newInstance();
-        bag.put(K1, "v1");
-        bag.put(K2, "v2a");
-        assertEquals(bag.get(K1), "v1");
-        assertEquals(bag.getUnusedConfig().size(), 1);
-        assertEquals(bag.peek(K2), "v2a");
-        assertEquals(bag.getUnusedConfig().size(), 1);
-        assertEquals(bag.get(K2), "v2a");
-        Assert.assertTrue(bag.getUnusedConfig().isEmpty());
-    }
-
-    @Test
-    public void testOrder() {
-        ConfigBag bag = ConfigBag.newInstance();
-        bag.put(K1, "v1");
-        bag.put(K2, "v2");
-        bag.put(K3, "v3");
-        Assert.assertEquals(MutableList.copyOf(bag.getAllConfig().keySet()), MutableList.of(K1.getName(), K2.getName(), K3.getName()));
-        Assert.assertEquals(MutableList.copyOf(bag.getAllConfig().values()), MutableList.of("v1", "v2", "v3"));
-    }
-        
-    @Test
-    public void testCopyOverwriteAndGet() {
-        ConfigBag bag1 = ConfigBag.newInstance();
-        bag1.put(K1, "v1");
-        bag1.put(K2, "v2a");
-        bag1.put(K3, "v3");
-        assertEquals(bag1.get(K1), "v1");
-        
-        ConfigBag bag2 = ConfigBag.newInstanceCopying(bag1).putAll(MutableMap.of(K2, "v2b"));
-        assertEquals(bag1.getUnusedConfig().size(), 2);
-        assertEquals(bag2.getUnusedConfig().size(), 2);
-        
-        assertEquals(bag2.get(K1), "v1");
-        assertEquals(bag1.get(K2), "v2a");
-        assertEquals(bag1.getUnusedConfig().size(), 1);
-        assertEquals(bag2.getUnusedConfig().size(), 2);
-        
-        assertEquals(bag2.get(K2), "v2b");
-        assertEquals(bag2.getUnusedConfig().size(), 1);
-        
-        assertEquals(bag2.get(K3), "v3");
-        assertEquals(bag2.getUnusedConfig().size(), 0);
-        assertEquals(bag1.getUnusedConfig().size(), 1);
-    }
-    
-    @Test
-    public void testCopyExtendingAndGet() {
-        ConfigBag bag1 = ConfigBag.newInstance();
-        bag1.put(K1, "v1");
-        bag1.put(K2, "v2a");
-        bag1.put(K3, "v3");
-        assertEquals(bag1.get(K1), "v1");
-        
-        ConfigBag bag2 = ConfigBag.newInstanceExtending(bag1, null).putAll(MutableMap.of(K2, "v2b"));
-        assertEquals(bag1.getUnusedConfig().size(), 2);
-        assertEquals(bag2.getUnusedConfig().size(), 2, "unused are: "+bag2.getUnusedConfig());
-        
-        assertEquals(bag2.get(K1), "v1");
-        assertEquals(bag1.get(K2), "v2a");
-        assertEquals(bag1.getUnusedConfig().size(), 1);
-        assertEquals(bag2.getUnusedConfig().size(), 2);
-        
-        assertEquals(bag2.get(K2), "v2b");
-        assertEquals(bag2.getUnusedConfig().size(), 1);
-        
-        assertEquals(bag2.get(K3), "v3");
-        assertEquals(bag2.getUnusedConfig().size(), 0);
-        // when extended, the difference is that parent is also marked
-        assertEquals(bag1.getUnusedConfig().size(), 0);
-    }
-
-    @Test
-    public void testConcurrent() throws InterruptedException {
-        ConfigBag bag = ConfigBag.newInstance();
-        bag.put(K1, "v1");
-        bag.put(K2, "v2");
-        bag.put(K3, "v3");
-        runConcurrentTest(bag, 10, Duration.millis(50));
-    }
-    
-    @Test(groups="Integration")
-    public void testConcurrentBig() throws InterruptedException {
-        ConfigBag bag = ConfigBag.newInstance();
-        bag.put(K1, "v1");
-        bag.put(K2, "v2");
-        bag.put(K3, "v3");
-        runConcurrentTest(bag, 20, Duration.seconds(5));
-    }
-    
-    private void runConcurrentTest(final ConfigBag bag, int numThreads, Duration time) throws InterruptedException {
-        List<Thread> threads = MutableList.of();
-        final Map<Thread,Exception> exceptions = new ConcurrentHashMap<Thread,Exception>();
-        final AtomicInteger successes = new AtomicInteger();
-        for (int i=0; i<numThreads; i++) {
-            Thread t = new Thread() {
-                @Override
-                public void run() {
-                    try {
-                        while (!interrupted()) {
-                            if (Math.random()<0.9)
-                                bag.put(ConfigKeys.newStringConfigKey("k"+((int)(10*Math.random()))), "v"+((int)(10*Math.random())));
-                            if (Math.random()<0.8)
-                                bag.get(ConfigKeys.newStringConfigKey("k"+((int)(10*Math.random()))));
-                            if (Math.random()<0.2)
-                                bag.copy(bag);
-                            if (Math.random()<0.6)
-                                bag.remove(ConfigKeys.newStringConfigKey("k"+((int)(10*Math.random()))));
-                            successes.incrementAndGet();
-                        }
-                    } catch (Exception e) {
-                        exceptions.put(Thread.currentThread(), e);
-                        Exceptions.propagateIfFatal(e);
-                    }
-                }
-            };
-            t.setName("ConfigBagTest-concurrent-thread-"+i);
-            threads.add(t);
-        }
-        for (Thread t: threads) t.start();
-        time.countdownTimer().waitForExpiry();
-        for (Thread t: threads) t.interrupt();
-        for (Thread t: threads) t.join();
-        Assert.assertTrue(exceptions.isEmpty(), "Got "+exceptions.size()+"/"+numThreads+" exceptions ("+successes.get()+" successful): "+exceptions);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/crypto/SecureKeysAndSignerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/crypto/SecureKeysAndSignerTest.java b/core/src/test/java/brooklyn/util/crypto/SecureKeysAndSignerTest.java
deleted file mode 100644
index 1bdb0a3..0000000
--- a/core/src/test/java/brooklyn/util/crypto/SecureKeysAndSignerTest.java
+++ /dev/null
@@ -1,166 +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.crypto;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.nio.charset.Charset;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.crypto.SecureKeys.PassphraseProblem;
-import brooklyn.util.os.Os;
-
-import com.google.common.io.Files;
-
-public class SecureKeysAndSignerTest {
-
-    // a bit slow, so marked as integration (but possibly due to leftover rebind-cleanup, benign failures writing to /tmp/xx)
-    @Test(groups="Integration")
-    public void testGenerateSignedKeys() throws Exception {
-        FluentKeySigner signer = new FluentKeySigner("the-root").
-            validForYears(2).
-            selfsign();
-        X509Certificate signerCert = signer.getAuthorityCertificate();
-
-        KeyPair aKey = SecureKeys.newKeyPair();
-        X509Certificate aCert = signer.newCertificateFor("A", aKey);
-        
-        KeyPair bKey = SecureKeys.newKeyPair();
-        X509Certificate bCert = signer.newCertificateFor("B", bKey);
-
-        FluentKeySigner selfSigner1 = new FluentKeySigner("self1").selfsign();
-        X509Certificate selfCert1 = selfSigner1.getAuthorityCertificate();
-
-        SecureKeys.getTrustManager(aCert).checkClientTrusted(new X509Certificate[] { aCert }, "RSA");
-        SecureKeys.getTrustManager(signerCert).checkClientTrusted(new X509Certificate[] { signerCert }, "RSA");
-        
-        try {
-            SecureKeys.getTrustManager(aCert).checkClientTrusted(new X509Certificate[] { bCert }, "RSA");
-            Assert.fail("Trust manager for A should not accept B");
-        } catch (CertificateException e) { /* expected */ }
-        
-//        SecureKeys.getTrustManager(signerCert).checkClientTrusted(new X509Certificate[] { aCert }, "RSA");
-        // NB, the above failes; we have to convert to a canonical implementation, handled by the following
-        
-        Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(signerCert, signerCert));
-        Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(aCert, signerCert));
-        Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(bCert, signerCert));
-        Assert.assertFalse(SecureKeys.isCertificateAuthorizedBy(signerCert, aCert));
-        Assert.assertFalse(SecureKeys.isCertificateAuthorizedBy(bCert, aCert));
-        
-        Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(selfCert1, selfCert1));
-        Assert.assertFalse(SecureKeys.isCertificateAuthorizedBy(selfCert1, signerCert));
-    }
-
-    @Test
-    public void testInjectCertificateAuthority() throws Exception {
-        KeyPair caKey = SecureKeys.newKeyPair();
-        X509Certificate caCert = new FluentKeySigner("the-root", caKey).selfsign().getAuthorityCertificate();
-
-        FluentKeySigner signer = new FluentKeySigner(caCert, caKey);
-        Assert.assertEquals("the-root", signer.getCommonName());
-        
-        KeyPair aKey = SecureKeys.newKeyPair();
-        X509Certificate aCert = signer.newCertificateFor("A", aKey);
-        
-        Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(aCert, caCert));
-    }
-
-    @Test
-    public void testReadRsaKey() throws Exception {
-        KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa.pem"), null);
-        checkNonTrivial(key);
-    }
-
-    @Test(expectedExceptions=IllegalStateException.class)
-    public void testReadRsaPublicKeyAsPemFails() throws Exception {
-        // should fail; see next test
-        SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa.pem.pub"), null);
-    }
-    
-    @Test
-    public void testReadRsaPublicKeyAsAuthKeysWorks() throws Exception {
-        PublicKey key = AuthorizedKeysParser.decodePublicKey(
-            ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/util/crypto/sample_rsa.pem.pub"));
-        KeyPair fromPem = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa.pem"), null);        
-        Assert.assertEquals(key, fromPem.getPublic());
-    }
-
-    @Test
-    public void testEncodeDecodeRsaPublicKey() throws Exception {
-        String data = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/util/crypto/sample_rsa.pem.pub");
-        PublicKey key = AuthorizedKeysParser.decodePublicKey(data);
-        String data2 = AuthorizedKeysParser.encodePublicKey(key);
-        Assert.assertTrue(data.contains(data2), "Expected to find '"+data2+"' in '"+data+"'");
-        PublicKey key2 = AuthorizedKeysParser.decodePublicKey(data2);
-        Assert.assertEquals(key2, key);
-    }
-
-    @Test
-    public void testEncodeDecodeDsaPublicKey() throws Exception {
-        String data = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/util/crypto/sample_dsa.pem.pub");
-        PublicKey key = AuthorizedKeysParser.decodePublicKey(data);
-        String data2 = AuthorizedKeysParser.encodePublicKey(key);
-        Assert.assertTrue(data.contains(data2), "Expected to find '"+data2+"' in '"+data+"'");
-        PublicKey key2 = AuthorizedKeysParser.decodePublicKey(data2);
-        Assert.assertEquals(key2, key);
-    }
-
-    @Test
-    public void testReadDsaKey() throws Exception {
-        KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_dsa.pem"), null);
-        checkNonTrivial(key);
-    }
-
-    @Test(expectedExceptions=Exception.class)
-    public void testCantReadRsaPassphraseKeyWithoutPassphrase() throws Exception {
-        KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa_passphrase.pem"), null);
-        checkNonTrivial(key);
-    }
-
-    @Test(expectedExceptions=PassphraseProblem.class)
-    public void testReadRsaPassphraseWithoutKeyFails() throws Exception {
-        SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa_passphrase.pem"), null);
-    }
-    
-    @Test
-    public void testReadRsaPassphraseKeyAndWriteWithoutPassphrase() throws Exception {
-        KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa_passphrase.pem"), "passphrase");
-        checkNonTrivial(key);
-        File f = Os.newTempFile(getClass(), "brooklyn-sample_rsa_passphrase_without_passphrase.pem");
-        Files.write(SecureKeys.stringPem(key), f, Charset.defaultCharset());
-        KeyPair key2 = SecureKeys.readPem(new FileInputStream(f), null);
-        checkNonTrivial(key2);
-        Assert.assertEquals(key2.getPrivate().getEncoded(), key.getPrivate().getEncoded());
-        Assert.assertEquals(key2.getPublic().getEncoded(), key.getPublic().getEncoded());
-    }
-
-    private void checkNonTrivial(KeyPair key) {
-        Assert.assertNotEquals(key.getPrivate().getEncoded().length, 0);
-        Assert.assertNotEquals(key.getPublic().getEncoded().length, 0);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/file/ArchiveBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/file/ArchiveBuilderTest.java b/core/src/test/java/brooklyn/util/file/ArchiveBuilderTest.java
deleted file mode 100644
index 6469f5a..0000000
--- a/core/src/test/java/brooklyn/util/file/ArchiveBuilderTest.java
+++ /dev/null
@@ -1,193 +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.file;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-import javax.annotation.Nullable;
-
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import brooklyn.util.collections.MutableSet;
-import brooklyn.util.os.Os;
-import brooklyn.util.text.Identifiers;
-
-import com.google.common.base.Charsets;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.io.Files;
-
-/**
- * Test the operation of the {@link ArchiveBuilder} class.
- */
-@Test
-public class ArchiveBuilderTest {
-
-    private File parentDir, tmpDir, tmpDir2;
-    private Predicate<ZipEntry> isDirectory = new Predicate<ZipEntry>() {
-                @Override
-                public boolean apply(@Nullable ZipEntry input) {
-                    return input.isDirectory();
-                }
-            };
-
-    @BeforeClass
-    public void createTmpDirAndFiles() throws IOException {
-        parentDir = Os.newTempDir(getClass().getSimpleName());
-        Os.deleteOnExitRecursively(parentDir);
-        tmpDir = new File(parentDir, Identifiers.makeRandomId(4));
-        Os.mkdirs(tmpDir);
-        Files.write("abcdef", new File(tmpDir, "data01.txt"), Charsets.US_ASCII);
-        Files.write("123456", new File(tmpDir, "data02.txt"), Charsets.US_ASCII);
-        Files.write("qqqqqq", new File(tmpDir, "data03.txt"), Charsets.US_ASCII);
-        
-        tmpDir2 = new File(parentDir, Identifiers.makeRandomId(4));
-        Os.mkdirs(tmpDir2);
-        Files.write("zzzzzz", new File(tmpDir2, "data04.txt"), Charsets.US_ASCII);
-    }
-    
-    @Test
-    public void testCreateZipFromDir() throws Exception {
-        File archive = ArchiveBuilder.zip().addDirContentsAt(tmpDir, ".").create();
-        archive.deleteOnExit();
-
-        List<ZipEntry> entries = Lists.newArrayList();
-        ZipInputStream input = new ZipInputStream(new FileInputStream(archive));
-        ZipEntry entry = input.getNextEntry();
-        while (entry != null) {
-            entries.add(entry);
-            entry = input.getNextEntry();
-        }
-        assertEquals(entries.size(), 4);
-        Iterable<ZipEntry> directories = Iterables.filter(entries, isDirectory);
-        Iterable<ZipEntry> files = Iterables.filter(entries, Predicates.not(isDirectory));
-        assertEquals(Iterables.size(directories), 1);
-        assertEquals(Iterables.size(files), 3);
-        String dirName = Iterables.getOnlyElement(directories).getName();
-        assertEquals(dirName, "./");
-        
-        Set<String> names = MutableSet.of();
-        for (ZipEntry file : files) {
-            assertTrue(file.getName().startsWith(dirName));
-            names.add(file.getName());
-        }
-        assertTrue(names.contains("./data01.txt"));
-        assertFalse(names.contains("./data04.txt"));
-        input.close();
-    }
-
-    @Test
-    public void testCreateZipFromTwoDirs() throws Exception {
-        File archive = ArchiveBuilder.zip().addDirContentsAt(tmpDir, ".").addDirContentsAt(tmpDir2, ".").create();
-        archive.deleteOnExit();
-
-        List<ZipEntry> entries = Lists.newArrayList();
-        ZipInputStream input = new ZipInputStream(new FileInputStream(archive));
-        ZipEntry entry = input.getNextEntry();
-        while (entry != null) {
-            entries.add(entry);
-            entry = input.getNextEntry();
-        }
-        assertEquals(entries.size(), 5);
-        Iterable<ZipEntry> directories = Iterables.filter(entries, isDirectory);
-        Iterable<ZipEntry> files = Iterables.filter(entries, Predicates.not(isDirectory));
-        assertEquals(Iterables.size(directories), 1);
-        assertEquals(Iterables.size(files), 4);
-        String dirName = Iterables.getOnlyElement(directories).getName();
-        assertEquals(dirName, "./");
-        
-        Set<String> names = MutableSet.of();
-        for (ZipEntry file : files) {
-            assertTrue(file.getName().startsWith(dirName));
-            names.add(file.getName());
-        }
-        assertTrue(names.contains("./data01.txt"));
-        assertTrue(names.contains("./data04.txt"));
-        input.close();
-    }
-    @Test
-    public void testCreateZipFromFiles() throws Exception {
-        ArchiveBuilder builder = ArchiveBuilder.zip();
-        for (String fileName : Arrays.asList("data01.txt", "data02.txt", "data03.txt")) {
-            builder.addAt(new File(tmpDir, fileName), ".");
-        }
-        File archive = builder.create();
-        archive.deleteOnExit();
-
-        List<ZipEntry> entries = Lists.newArrayList();
-        ZipInputStream input = new ZipInputStream(new FileInputStream(archive));
-        ZipEntry entry = input.getNextEntry();
-        while (entry != null) {
-            entries.add(entry);
-            entry = input.getNextEntry();
-        }
-        assertEquals(entries.size(), 3);
-        Iterable<ZipEntry> directories = Iterables.filter(entries, isDirectory);
-        Iterable<ZipEntry> files = Iterables.filter(entries, Predicates.not(isDirectory));
-        assertTrue(Iterables.isEmpty(directories));
-        assertEquals(Iterables.size(files), 3);
-        for (ZipEntry file : files) {
-            assertTrue(file.getName().startsWith(Os.mergePathsUnix(".", "data")));
-        }
-        input.close();
-    }
-
-    @Test
-    public void testCreateZipFromFilesWithBaseDir() throws Exception {
-        ArchiveBuilder builder = ArchiveBuilder.zip();
-        String baseDir = tmpDir.getName();
-        for (String fileName : Arrays.asList("data01.txt", "data02.txt", "data03.txt")) {
-            builder.addFromLocalBaseDir(parentDir, Os.mergePaths(baseDir, fileName));
-        }
-        File archive = builder.create();
-        archive.deleteOnExit();
-
-        List<ZipEntry> entries = Lists.newArrayList();
-        ZipInputStream input = new ZipInputStream(new FileInputStream(archive));
-        ZipEntry entry = input.getNextEntry();
-        while (entry != null) {
-            entries.add(entry);
-            entry = input.getNextEntry();
-        }
-        assertEquals(entries.size(), 3);
-        Iterable<ZipEntry> directories = Iterables.filter(entries, isDirectory);
-        Iterable<ZipEntry> files = Iterables.filter(entries, Predicates.not(isDirectory));
-        assertTrue(Iterables.isEmpty(directories));
-        assertEquals(Iterables.size(files), 3);
-        for (ZipEntry file : files) {
-            assertTrue(file.getName().startsWith(Os.mergePathsUnix(".", baseDir)));
-        }
-        input.close();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/file/ArchiveUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/file/ArchiveUtilsTest.java b/core/src/test/java/brooklyn/util/file/ArchiveUtilsTest.java
deleted file mode 100644
index 6715444..0000000
--- a/core/src/test/java/brooklyn/util/file/ArchiveUtilsTest.java
+++ /dev/null
@@ -1,135 +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.file;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-
-import java.io.File;
-import java.util.Map;
-
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.BrooklynAppUnitTestSupport;
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.os.Os;
-
-import com.google.api.client.repackaged.com.google.common.base.Joiner;
-import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.Files;
-
-// Test are integration, because relies on ssh/scp via SshMachineLocation
-public class ArchiveUtilsTest extends BrooklynAppUnitTestSupport {
-    
-    private SshMachineLocation machine;
-    private ResourceUtils resourceUtils;
-
-    private Map<String, String> archiveContents = ImmutableMap.of("a.txt", "mya");
-    private File destDir;
-    private File origZip;
-    private File origJar;
-
-    @BeforeClass(alwaysRun=true)
-    public void setUpClass() throws Exception {
-        origZip = newZip(archiveContents);
-        origJar = Os.newTempFile(ArchiveUtilsTest.class, ".jar");
-        Files.copy(origZip, origJar);
-    }
-    
-    @AfterClass(alwaysRun=true)
-    public void tearDownClass() throws Exception {
-        if (origZip != null) origZip.delete();
-        if (origJar != null) origJar.delete();
-    }
-
-    @BeforeMethod(alwaysRun=true)
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        machine = app.newLocalhostProvisioningLocation().obtain();
-        resourceUtils = ResourceUtils.create(ArchiveUtilsTest.class);
-        destDir = Os.newTempDir(getClass().getSimpleName());
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
-        if (destDir != null) Os.deleteRecursively(destDir);
-    }
-    
-    @Test(groups="Integration")
-    public void testDeployZipWithNoOptionalArgsSupplied() throws Exception {
-        boolean result = ArchiveUtils.deploy(resourceUtils, ImmutableMap.<String, Object>of(), origZip.getAbsolutePath(), machine, destDir.getAbsolutePath(), true, null, null);
-        assertTrue(result);
-        assertFilesEqual(new File(destDir, origZip.getName()), origZip);
-        assertSubFilesEqual(destDir, archiveContents);
-    }
-    
-    @Test(groups="Integration")
-    public void testDeployZipDeletingArchiveAfterUnpack() throws Exception {
-        boolean result = ArchiveUtils.deploy(resourceUtils, ImmutableMap.<String, Object>of(), origZip.getAbsolutePath(), machine, destDir.getAbsolutePath(), false, null, null);
-        assertTrue(result);
-        assertFalse(new File(destDir, origZip.getName()).exists());
-        assertSubFilesEqual(destDir, archiveContents);
-    }
-    
-    @Test(groups="Integration")
-    public void testDeployJarNotUnpacked() throws Exception {
-        ArchiveUtils.deploy(origJar.getAbsolutePath(), machine, destDir.getAbsolutePath());
-        assertFilesEqual(new File(destDir, origJar.getName()), origJar);
-    }
-    
-    @Test(groups="Integration")
-    public void testDeployExplicitDestFile() throws Exception {
-        String destFile = "custom-destFile.jar";
-        ArchiveUtils.deploy(origJar.getAbsolutePath(), machine, destDir.getAbsolutePath(), destFile);
-        assertFilesEqual(new File(destDir, destFile), origJar);
-    }
-    
-    private File newZip(Map<String, String> files) throws Exception {
-        File parentDir = Os.newTempDir(getClass().getSimpleName()+"-archive");
-        for (Map.Entry<String, String> entry : files.entrySet()) {
-            File subFile = new File(Os.mergePaths(parentDir.getAbsolutePath(), entry.getKey()));
-            subFile.getParentFile().mkdirs();
-            Files.write(entry.getValue(), subFile, Charsets.UTF_8);
-        }
-        return ArchiveBuilder.zip().addDirContentsAt(parentDir, ".").create();
-    }
-    
-    private void assertFilesEqual(File f1, File f2) throws Exception {
-        byte[] bytes1 = Files.asByteSource(f1).read();
-        byte[] bytes2 = Files.asByteSource(f1).read();
-        assertEquals(bytes1, bytes2, "f1="+f1+"; f2="+f2);
-    }
-    
-    private void assertSubFilesEqual(File parentDir, Map<String, String> files) throws Exception {
-        for (Map.Entry<String, String> entry : archiveContents.entrySet()) {
-            File subFile = new File(Os.mergePaths(parentDir.getAbsolutePath(), entry.getKey()));
-            assertEquals(Joiner.on("\n").join(Files.readLines(subFile, Charsets.UTF_8)), entry.getValue());
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/flags/MethodCoercionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/flags/MethodCoercionsTest.java b/core/src/test/java/brooklyn/util/flags/MethodCoercionsTest.java
deleted file mode 100644
index 4d06ca4..0000000
--- a/core/src/test/java/brooklyn/util/flags/MethodCoercionsTest.java
+++ /dev/null
@@ -1,146 +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.flags;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.guava.Maybe;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import java.lang.reflect.Method;
-import java.util.List;
-
-import static org.testng.Assert.*;
-
-public class MethodCoercionsTest {
-
-    private Method singleParameterMethod;
-    private Method multiParameterMethod;
-    private Method singleCollectionParameterMethod;
-
-    @BeforeClass
-    public void testFixtureSetUp() {
-        try {
-            singleParameterMethod = TestClass.class.getMethod("singleParameterMethod", int.class);
-            multiParameterMethod = TestClass.class.getMethod("multiParameterMethod", boolean.class, int.class);
-            singleCollectionParameterMethod = TestClass.class.getMethod("singleCollectionParameterMethod", List.class);
-        } catch (NoSuchMethodException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    @Test
-    public void testMatchSingleParameterMethod() throws Exception {
-        Predicate<Method> predicate = MethodCoercions.matchSingleParameterMethod("singleParameterMethod", "42");
-        assertTrue(predicate.apply(singleParameterMethod));
-        assertFalse(predicate.apply(multiParameterMethod));
-        assertFalse(predicate.apply(singleCollectionParameterMethod));
-    }
-
-    @Test
-    public void testTryFindAndInvokeSingleParameterMethod() throws Exception {
-        TestClass instance = new TestClass();
-        Maybe<?> maybe = MethodCoercions.tryFindAndInvokeSingleParameterMethod(instance, "singleParameterMethod", "42");
-        assertTrue(maybe.isPresent());
-        assertTrue(instance.wasSingleParameterMethodCalled());
-    }
-
-    @Test
-    public void testMatchMultiParameterMethod() throws Exception {
-        Predicate<Method> predicate = MethodCoercions.matchMultiParameterMethod("multiParameterMethod", ImmutableList.of("true", "42"));
-        assertFalse(predicate.apply(singleParameterMethod));
-        assertTrue(predicate.apply(multiParameterMethod));
-        assertFalse(predicate.apply(singleCollectionParameterMethod));
-    }
-
-    @Test
-    public void testTryFindAndInvokeMultiParameterMethod() throws Exception {
-        TestClass instance = new TestClass();
-        Maybe<?> maybe = MethodCoercions.tryFindAndInvokeMultiParameterMethod(instance, "multiParameterMethod", ImmutableList.of("true", "42"));
-        assertTrue(maybe.isPresent());
-        assertTrue(instance.wasMultiParameterMethodCalled());
-    }
-
-    @Test
-    public void testTryFindAndInvokeBestMatchingMethod() throws Exception {
-        TestClass instance = new TestClass();
-        Maybe<?> maybe = MethodCoercions.tryFindAndInvokeBestMatchingMethod(instance, "singleParameterMethod", "42");
-        assertTrue(maybe.isPresent());
-        assertTrue(instance.wasSingleParameterMethodCalled());
-
-        instance = new TestClass();
-        maybe = MethodCoercions.tryFindAndInvokeBestMatchingMethod(instance, "multiParameterMethod", ImmutableList.of("true", "42"));
-        assertTrue(maybe.isPresent());
-        assertTrue(instance.wasMultiParameterMethodCalled());
-
-        instance = new TestClass();
-        maybe = MethodCoercions.tryFindAndInvokeBestMatchingMethod(instance, "singleCollectionParameterMethod", ImmutableList.of("fred", "joe"));
-        assertTrue(maybe.isPresent());
-        assertTrue(instance.wasSingleCollectionParameterMethodCalled());
-    }
-/*
-    @Test
-    public void testMatchSingleCollectionParameterMethod() throws Exception {
-        Predicate<Method> predicate = MethodCoercions.matchSingleCollectionParameterMethod("singleCollectionParameterMethod", ImmutableList.of("42"));
-        assertFalse(predicate.apply(singleParameterMethod));
-        assertFalse(predicate.apply(multiParameterMethod));
-        assertTrue(predicate.apply(singleCollectionParameterMethod));
-    }
-
-    @Test
-    public void testTryFindAndInvokeSingleCollectionParameterMethod() throws Exception {
-        TestClass instance = new TestClass();
-        Maybe<?> maybe = MethodCoercions.tryFindAndInvokeSingleCollectionParameterMethod(instance, "singleCollectionParameterMethod", ImmutableList.of("42"));
-        assertTrue(maybe.isPresent());
-        assertTrue(instance.wasSingleCollectionParameterMethodCalled());
-    }
-*/
-    public static class TestClass {
-
-        private boolean singleParameterMethodCalled;
-        private boolean multiParameterMethodCalled;
-        private boolean singleCollectionParameterMethodCalled;
-
-        public void singleParameterMethod(int parameter) {
-            singleParameterMethodCalled = true;
-        }
-
-        public void multiParameterMethod(boolean parameter1, int parameter2) {
-            multiParameterMethodCalled = true;
-        }
-
-        public void singleCollectionParameterMethod(List<String> parameter) {
-            singleCollectionParameterMethodCalled = true;
-        }
-
-        public boolean wasSingleParameterMethodCalled() {
-            return singleParameterMethodCalled;
-        }
-
-        public boolean wasMultiParameterMethodCalled() {
-            return multiParameterMethodCalled;
-        }
-
-        public boolean wasSingleCollectionParameterMethodCalled() {
-            return singleCollectionParameterMethodCalled;
-        }
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/http/BetterMockWebServer.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/http/BetterMockWebServer.java b/core/src/test/java/brooklyn/util/http/BetterMockWebServer.java
deleted file mode 100644
index fb76903..0000000
--- a/core/src/test/java/brooklyn/util/http/BetterMockWebServer.java
+++ /dev/null
@@ -1,138 +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.http;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.Proxy;
-import java.net.URL;
-
-import javax.net.ssl.SSLSocketFactory;
-
-import com.google.common.base.Throwables;
-import com.google.mockwebserver.Dispatcher;
-import com.google.mockwebserver.MockResponse;
-import com.google.mockwebserver.MockWebServer;
-import com.google.mockwebserver.RecordedRequest;
-
-/** like MockWebServer (and delegating) but:
- * <li> allows subclassing
- * <li> easy way to create instance which returns localhost for {@link #getHostName()}
- *      (since otherwise you can get failures on networks which misconfigure hostname) 
- * */
-public class BetterMockWebServer {
-
-    final MockWebServer delegate = new MockWebServer();
-    String hostname = null;
-    boolean isHttps = false;
-    
-    public static BetterMockWebServer newInstanceLocalhost() {
-        return new BetterMockWebServer().setHostName("localhost");
-    }
-    
-    /** use {@link #newInstanceLocalhost()} or subclass */
-    protected BetterMockWebServer() {}
-
-    public BetterMockWebServer setHostName(String hostname) {
-        this.hostname = hostname;
-        return this;
-    }
-
-
-    // --- delegate methods (unchanged)
-    
-    public void enqueue(MockResponse response) {
-        delegate.enqueue(response);
-    }
-
-    public boolean equals(Object obj) {
-        return delegate.equals(obj);
-    }
-
-    public String getCookieDomain() {
-        return delegate.getCookieDomain();
-    }
-
-    public String getHostName() {
-        if (hostname!=null) return hostname;
-        return delegate.getHostName();
-    }
-
-    public int getPort() {
-        return delegate.getPort();
-    }
-
-    public int getRequestCount() {
-        return delegate.getRequestCount();
-    }
-
-    public URL getUrl(String path) {
-        try {
-            return isHttps
-                ? new URL("https://" + getHostName() + ":" + getPort() + path)
-                : new URL("http://" + getHostName() + ":" + getPort() + path);
-        } catch (MalformedURLException e) {
-            throw Throwables.propagate(e);
-        }
-    }
-
-    public int hashCode() {
-        return delegate.hashCode();
-    }
-
-    public void play() throws IOException {
-        delegate.play();
-    }
-
-    public void play(int port) throws IOException {
-        delegate.play(port);
-    }
-
-    public void setBodyLimit(int maxBodyLength) {
-        delegate.setBodyLimit(maxBodyLength);
-    }
-
-    public void setDispatcher(Dispatcher dispatcher) {
-        delegate.setDispatcher(dispatcher);
-    }
-
-    public void shutdown() throws IOException {
-        delegate.shutdown();
-    }
-
-    public RecordedRequest takeRequest() throws InterruptedException {
-        return delegate.takeRequest();
-    }
-
-    public Proxy toProxyAddress() {
-        return delegate.toProxyAddress();
-    }
-
-    public String toString() {
-        return delegate.toString();
-    }
-
-    public void useHttps(SSLSocketFactory sslSocketFactory, boolean tunnelProxy) {
-        isHttps = true;
-        delegate.useHttps(sslSocketFactory, tunnelProxy);
-    }
-    
-    
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/http/HttpToolIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/http/HttpToolIntegrationTest.java b/core/src/test/java/brooklyn/util/http/HttpToolIntegrationTest.java
deleted file mode 100644
index dc9fc31..0000000
--- a/core/src/test/java/brooklyn/util/http/HttpToolIntegrationTest.java
+++ /dev/null
@@ -1,98 +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.http;
-
-import static org.testng.Assert.assertTrue;
-
-import java.net.URI;
-
-import org.apache.http.client.HttpClient;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.test.HttpService;
-
-import com.google.common.collect.ImmutableMap;
-
-public class HttpToolIntegrationTest {
-
-    // TODO Expand test coverage for credentials etc
-    
-    private HttpService httpService;
-    private HttpService httpsService;
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        httpService = new HttpService(PortRanges.fromString("9000+"), false).start();
-        httpsService = new HttpService(PortRanges.fromString("9000+"), true).start();
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (httpService != null) httpService.shutdown();
-        if (httpsService != null) httpsService.shutdown();
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testHttpGet() throws Exception {
-        URI baseUri = new URI(httpService.getUrl());
-
-        HttpClient client = HttpTool.httpClientBuilder().build();
-        HttpToolResponse result = HttpTool.httpGet(client, baseUri, ImmutableMap.<String,String>of());
-        assertTrue(new String(result.getContent()).contains("Hello, World"), "val="+new String(result.getContent()));
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testHttpRedirect() throws Exception {
-        URI baseUri = new URI(httpService.getUrl() + "hello/redirectAbsolute");
-
-        HttpClient client = HttpTool.httpClientBuilder().laxRedirect(true).build();
-        HttpToolResponse result = HttpTool.httpGet(client, baseUri, ImmutableMap.<String,String>of());
-        assertTrue(new String(result.getContent()).contains("Hello, World"), "val="+new String(result.getContent()));
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testHttpPost() throws Exception {
-        URI baseUri = new URI(httpService.getUrl());
-
-        HttpClient client = HttpTool.httpClientBuilder().build();
-        HttpToolResponse result = HttpTool.httpPost(client, baseUri, ImmutableMap.<String,String>of(), new byte[0]);
-        assertTrue(new String(result.getContent()).contains("Hello, World"), "val="+new String(result.getContent()));
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testHttpsGetWithTrustAll() throws Exception {
-        URI baseUri = new URI(httpsService.getUrl());
-
-        HttpClient client = HttpTool.httpClientBuilder().https(true).trustAll().build();
-        HttpToolResponse result = HttpTool.httpGet(client, baseUri, ImmutableMap.<String,String>of());
-        assertTrue(new String(result.getContent()).contains("Hello, World"), "val="+new String(result.getContent()));
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testHttpsPostWithTrustSelfSigned() throws Exception {
-        URI baseUri = new URI(httpsService.getUrl());
-
-        HttpClient client = HttpTool.httpClientBuilder().https(true).trustSelfSigned().build();
-        HttpToolResponse result = HttpTool.httpPost(client, baseUri, ImmutableMap.<String,String>of(), new byte[0]);
-        assertTrue(new String(result.getContent()).contains("Hello, World"), "val="+new String(result.getContent()));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/FlagUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/FlagUtilsTest.java b/core/src/test/java/brooklyn/util/internal/FlagUtilsTest.java
deleted file mode 100644
index ed98dba..0000000
--- a/core/src/test/java/brooklyn/util/internal/FlagUtilsTest.java
+++ /dev/null
@@ -1,314 +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.internal;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-
-import java.lang.reflect.Field;
-import java.net.InetAddress;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.brooklyn.api.entity.trait.Configurable;
-import org.apache.brooklyn.api.management.Task;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.annotations.Test;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.config.ConfigKey.HasConfigKey;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.SetFromFlag;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-
-public class FlagUtilsTest {
-
-    public static final Logger log = LoggerFactory.getLogger(FlagUtilsTest.class);
-    
-    @Test
-    public void testGetAllFields() {
-        log.info("types {}", FlagUtils.getAllAssignableTypes(Baz.class));
-        assertEquals(FlagUtils.getAllAssignableTypes(Baz.class), ImmutableList.of(Baz.class, Foo.class, Bar.class));
-        List<Field> fs = FlagUtils.getAllFields(Baz.class);
-        for (Field f : fs) {
-            log.info("field {}    {}", f.getName(), f);
-        }
-        List<String> fsn = ImmutableList.copyOf(Iterables.transform(fs, new Function<Field, String>() {
-            @Override public String apply(Field f) {
-                return f.getName();
-            }}));
-        assertTrue(fsn.indexOf("A") >= 0);
-        assertTrue(fsn.indexOf("w") > fsn.indexOf("A")); 
-        assertTrue(fsn.indexOf("x") > fsn.indexOf("A") );
-        assertTrue(fsn.indexOf("yNotY") > fsn.indexOf("A")); 
-        assertTrue(fsn.indexOf("Z") > fsn.indexOf("yNotY") );
-    }    
-    
-    @Test
-    public void testSetFieldsFromFlags() {
-        Foo f = new Foo();
-        Map<?,?> m = MutableMap.of("w", 3, "x", 1, "y", 7, "z", 9);
-        Map<?, ?> unused = FlagUtils.setFieldsFromFlags(m, f);
-        assertEquals(f.w, 3);
-        assertEquals(f.x, 1);
-        assertEquals(f.yNotY, 7);
-        assertEquals(unused, ImmutableMap.of("z", 9));
-        Map<?,?> m2 = FlagUtils.getFieldsWithValues(f);
-        m.remove("z");
-        assertEquals(m2, m);
-    }
-    
-    @Test
-    public void testCollectionCoercionOnSetFromFlags() {
-        WithSpecialFieldTypes s = new WithSpecialFieldTypes();
-        Map<?,?> m = ImmutableMap.of("set", ImmutableSet.of(1));
-        FlagUtils.setFieldsFromFlags(m, s);
-        assertEquals(s.set, ImmutableSet.of(1));
-    }
-
-    @Test
-    public void testInetAddressCoercionOnSetFromFlags() {
-        WithSpecialFieldTypes s = new WithSpecialFieldTypes();
-        Map<?,?> m = ImmutableMap.of("inet", "127.0.0.1");
-        FlagUtils.setFieldsFromFlags(m, s);
-        assertEquals(s.inet.getHostAddress(), "127.0.0.1");
-    }
-
-    @Test
-    public void testNonImmutableField() {
-        Foo f = new Foo();
-        FlagUtils.setFieldsFromFlags(ImmutableMap.of("w", 8), f);
-        assertEquals(f.w, 8);
-        FlagUtils.setFieldsFromFlags(ImmutableMap.of("w", 9), f);
-        assertEquals(f.w, 9);
-    }
-
-    @Test
-    public void testImmutableIntField() {
-        Foo f = new Foo();
-        FlagUtils.setFieldsFromFlags(ImmutableMap.of("x", 8), f);
-        assertEquals(f.x, 8);
-        boolean succeededWhenShouldntHave = false; 
-        try {
-            FlagUtils.setFieldsFromFlags(ImmutableMap.of("x", 9), f);
-            succeededWhenShouldntHave = true;
-        } catch (IllegalStateException e) {
-            //expected
-        }
-        assertFalse(succeededWhenShouldntHave);
-        assertEquals(f.x, 8);
-    }
-
-    @Test
-    public void testImmutableObjectField() {
-        WithImmutableNonNullableObject o = new WithImmutableNonNullableObject();
-        FlagUtils.setFieldsFromFlags(ImmutableMap.of("a", "a", "b", "b"), o);
-        assertEquals(o.a, "a");
-        assertEquals(o.b, "b");
-        
-        FlagUtils.setFieldsFromFlags(ImmutableMap.of("a", "a2"), o);
-        assertEquals(o.a, "a2");
-        
-        boolean succeededWhenShouldntHave = false;
-        try {
-            FlagUtils.setFieldsFromFlags(ImmutableMap.of("b", "b2"), o);
-            succeededWhenShouldntHave = true;
-        } catch (IllegalStateException e) {
-            //expected
-        }
-        assertFalse(succeededWhenShouldntHave);
-        assertEquals(o.b, "b");
-    }
-
-    @Test
-    public void testNonNullable() {
-        WithImmutableNonNullableObject o = new WithImmutableNonNullableObject();
-        //allowed
-        FlagUtils.setFieldsFromFlags(MutableMap.of("a", null), o);
-        assertEquals(o.a, null);
-        assertEquals(o.b, null);
-        //not allowed
-        boolean succeededWhenShouldntHave = false;
-        try {
-            FlagUtils.setFieldsFromFlags(MutableMap.of("b", null), o);
-            succeededWhenShouldntHave = true;
-        } catch (IllegalArgumentException e) {
-            //expected
-        }
-        assertFalse(succeededWhenShouldntHave);
-        assertEquals(o.b, null);
-    }
-    
-    @Test
-    public void testGetAnnotatedFields() throws Exception {
-        Map<Field, SetFromFlag> fm = FlagUtils.getAnnotatedFields(WithImmutableNonNullableObject.class);
-        assertEquals(fm.keySet().size(), 2);
-        assertTrue(fm.get(WithImmutableNonNullableObject.class.getDeclaredField("b")).immutable());
-    }
-
-    @Test
-    public void testCheckRequired() {
-        WithImmutableNonNullableObject f = new WithImmutableNonNullableObject();
-        FlagUtils.setFieldsFromFlags(ImmutableMap.of("a", "a is a"), f);
-        assertEquals(f.a, "a is a");
-        assertEquals(f.b, null);
-        int exceptions = 0;
-        try {
-            FlagUtils.checkRequiredFields(f);
-        } catch (IllegalStateException e) {
-            exceptions++;
-        }
-        assertEquals(exceptions, 1);
-    }
-
-    @Test
-    public void testSetConfigKeys() {
-        FooCK f = new FooCK();
-        Map<?,?> unused = FlagUtils.setFieldsFromFlags(ImmutableMap.of("f1", 9, "ck1", "do-set", "ck2", "dont-set", "c3", "do-set"), f);
-        assertEquals(f.bag.get(FooCK.CK1), "do-set");
-        assertEquals(f.bag.get(FooCK.CK3), "do-set");
-        assertEquals(f.f1, 9);
-        assertEquals(f.bag.containsKey(FooCK.CK2), false);
-        assertEquals(unused, ImmutableMap.of("ck2", "dont-set"));
-    }
-    
-    @Test
-    public void testSetAllConfigKeys() {
-        FooCK f = new FooCK();
-        Map<?,?> unused = FlagUtils.setAllConfigKeys(ImmutableMap.of("f1", 9, "ck1", "do-set", "ck2", "do-set-2", "c3", "do-set"), f, true);
-        assertEquals(f.bag.get(FooCK.CK1), "do-set");
-        assertEquals(f.bag.get(FooCK.CK3), "do-set");
-        assertEquals(f.bag.containsKey(FooCK.CK2), true);
-        assertEquals(f.bag.get(FooCK.CK2), "do-set-2");
-        assertEquals(unused, ImmutableMap.of("f1", 9));
-    }
-
-    @Test
-    public void testSetFromConfigKeys() {
-        FooCK f = new FooCK();
-        Map<?, ?> unused = FlagUtils.setFieldsFromFlags(ImmutableMap.of(new BasicConfigKey<Integer>(Integer.class, "f1"), 9, "ck1", "do-set", "ck2", "dont-set"), f);
-        assertEquals(f.bag.get(FooCK.CK1), "do-set");
-        assertEquals(f.f1, 9);
-        assertEquals(f.bag.containsKey(FooCK.CK2), false);
-        assertEquals(unused, ImmutableMap.of("ck2", "dont-set"));
-    }
-
-    public static class Foo {
-        @SetFromFlag
-        int w;
-        
-        @SetFromFlag(immutable=true)
-        private int x;
-        
-        @SetFromFlag("y")
-        public int yNotY;
-    }
-    
-    public static interface Bar {
-        static final String Z = "myzval";
-    }
-    
-    public static class Baz extends Foo implements Bar {
-        @SuppressWarnings("unused")  //inspected by reflection
-        private static int A;
-    }
-    
-    public static class WithImmutableNonNullableObject {
-        @SetFromFlag
-        Object a;
-        @SetFromFlag(immutable=true, nullable=false)
-        public Object b;
-    }
-    
-    public static class WithSpecialFieldTypes {
-        @SetFromFlag Set<?> set;
-        @SetFromFlag InetAddress inet;
-    }
-    
-    public static class FooCK implements Configurable {
-        @SetFromFlag
-        public static ConfigKey<String> CK1 = ConfigKeys.newStringConfigKey("ck1");
-        
-        public static ConfigKey<String> CK2 = ConfigKeys.newStringConfigKey("ck2");
-
-        @SetFromFlag("c3")
-        public static ConfigKey<String> CK3 = ConfigKeys.newStringConfigKey("ck3");
-
-        @SetFromFlag
-        int f1;
-        
-        ConfigBag bag = new ConfigBag();
-        BasicConfigurationSupport configSupport = new BasicConfigurationSupport();
-        
-        @Override
-        public ConfigurationSupport config() {
-            return configSupport;
-        }
-        
-        public <T> T setConfig(ConfigKey<T> key, T val) {
-            return config().set(key, val);
-        }
-        
-        private class BasicConfigurationSupport implements ConfigurationSupport {
-            @Override
-            public <T> T get(ConfigKey<T> key) {
-                return bag.get(key);
-            }
-
-            @Override
-            public <T> T get(HasConfigKey<T> key) {
-                return get(key.getConfigKey());
-            }
-
-            @Override
-            public <T> T set(ConfigKey<T> key, T val) {
-                T old = bag.get(key);
-                bag.configure(key, val);
-                return old;
-            }
-
-            @Override
-            public <T> T set(HasConfigKey<T> key, T val) {
-                return set(key.getConfigKey(), val);
-            }
-
-            @Override
-            public <T> T set(ConfigKey<T> key, Task<T> val) {
-                throw new UnsupportedOperationException();
-            }
-
-            @Override
-            public <T> T set(HasConfigKey<T> key, Task<T> val) {
-                return set(key.getConfigKey(), val);
-            }
-        }
-    }
-}
\ No newline at end of file


[31/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/system/internal/ExecWithLoggingHelpers.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/system/internal/ExecWithLoggingHelpers.java b/core/src/main/java/brooklyn/util/task/system/internal/ExecWithLoggingHelpers.java
deleted file mode 100644
index c2b8907..0000000
--- a/core/src/main/java/brooklyn/util/task/system/internal/ExecWithLoggingHelpers.java
+++ /dev/null
@@ -1,200 +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.system.internal;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.util.List;
-import java.util.Map;
-
-import org.slf4j.Logger;
-
-import brooklyn.config.ConfigKey;
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.internal.ssh.ShellAbstractTool;
-import brooklyn.util.internal.ssh.ShellTool;
-import brooklyn.util.stream.StreamGobbler;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Function;
-import com.google.common.base.Throwables;
-
-public abstract class ExecWithLoggingHelpers {
-
-    public static final ConfigKey<OutputStream> STDOUT = SshMachineLocation.STDOUT;
-    public static final ConfigKey<OutputStream> STDERR = SshMachineLocation.STDERR;
-    public static final ConfigKey<Boolean> NO_STDOUT_LOGGING = SshMachineLocation.NO_STDOUT_LOGGING;
-    public static final ConfigKey<Boolean> NO_STDERR_LOGGING = SshMachineLocation.NO_STDERR_LOGGING;
-    public static final ConfigKey<String> LOG_PREFIX = SshMachineLocation.LOG_PREFIX;
-
-    protected final String shortName;
-    protected Logger commandLogger = null;
-    
-    public interface ExecRunner {
-        public int exec(ShellTool ssh, Map<String,?> flags, List<String> cmds, Map<String,?> env);
-    }
-
-    protected abstract <T> T execWithTool(MutableMap<String, Object> toolCreationAndConnectionProperties, Function<ShellTool, T> runMethodOnTool);
-    protected abstract void preExecChecks();
-    protected abstract String getTargetName();
-    protected abstract String constructDefaultLoggingPrefix(ConfigBag execFlags);
-
-    /** takes a very short name for use in blocking details, e.g. SSH or Process */
-    public ExecWithLoggingHelpers(String shortName) {
-        this.shortName = shortName;
-    }
-
-    public ExecWithLoggingHelpers logger(Logger commandLogger) {
-        this.commandLogger = commandLogger;
-        return this;
-    }
-    
-    public int execScript(Map<String,?> props, String summaryForLogging, List<String> commands, Map<String,?> env) {
-        // TODO scriptHeader are the extra commands we expect the SshTool/ShellTool to add.
-        // Would be better if could get this from the ssh-tool, rather than assuming it will behave as
-        // we expect.
-        String scriptHeader = ShellAbstractTool.getOptionalVal(props, ShellTool.PROP_SCRIPT_HEADER);
-        
-        return execWithLogging(props, summaryForLogging, commands, env, scriptHeader, new ExecRunner() {
-                @Override public int exec(ShellTool ssh, Map<String, ?> flags, List<String> cmds, Map<String, ?> env) {
-                    return ssh.execScript(flags, cmds, env);
-                }});
-    }
-
-    protected static <T> T getOptionalVal(Map<String,?> map, ConfigKey<T> keyC) {
-        if (keyC==null) return null;
-        String key = keyC.getName();
-        if (map!=null && map.containsKey(key)) {
-            return TypeCoercions.coerce(map.get(key), keyC.getTypeToken());
-        } else {
-            return keyC.getDefaultValue();
-        }
-    }
-
-    public int execCommands(Map<String,?> props, String summaryForLogging, List<String> commands, Map<String,?> env) {
-        return execWithLogging(props, summaryForLogging, commands, env, new ExecRunner() {
-                @Override public int exec(ShellTool tool, Map<String,?> flags, List<String> cmds, Map<String,?> env) {
-                    return tool.execCommands(flags, cmds, env);
-                }});
-    }
-
-    public int execWithLogging(Map<String,?> props, final String summaryForLogging, final List<String> commands,
-            final Map<String,?> env, final ExecRunner execCommand) {
-        return execWithLogging(props, summaryForLogging, commands, env, null, execCommand);
-    }
-    
-    @SuppressWarnings("resource")
-    public int execWithLogging(Map<String,?> props, final String summaryForLogging, final List<String> commands,
-            final Map<String,?> env, String expectedCommandHeaders, final ExecRunner execCommand) {
-        if (commandLogger!=null && commandLogger.isDebugEnabled()) {
-            String allcmds = (Strings.isBlank(expectedCommandHeaders) ? "" : expectedCommandHeaders + " ; ") + Strings.join(commands, " ; ");
-            commandLogger.debug("{}, initiating "+shortName.toLowerCase()+" on machine {}{}: {}",
-                    new Object[] {summaryForLogging, getTargetName(),
-                    env!=null && !env.isEmpty() ? " (env "+env+")": "", allcmds});
-        }
-
-        if (commands.isEmpty()) {
-            if (commandLogger!=null && commandLogger.isDebugEnabled())
-                commandLogger.debug("{}, on machine {}, ending: no commands to run", summaryForLogging, getTargetName());
-            return 0;
-        }
-
-        final ConfigBag execFlags = new ConfigBag().putAll(props);
-        // some props get overridden in execFlags, so remove them from the tool flags
-        final ConfigBag toolFlags = new ConfigBag().putAll(props).removeAll(
-                LOG_PREFIX, STDOUT, STDERR, ShellTool.PROP_NO_EXTRA_OUTPUT);
-
-        execFlags.configure(ShellTool.PROP_SUMMARY, summaryForLogging);
-        
-        PipedOutputStream outO = null;
-        PipedOutputStream outE = null;
-        StreamGobbler gO=null, gE=null;
-        try {
-            preExecChecks();
-            
-            String logPrefix = execFlags.get(LOG_PREFIX);
-            if (logPrefix==null) logPrefix = constructDefaultLoggingPrefix(execFlags);
-
-            if (!execFlags.get(NO_STDOUT_LOGGING)) {
-                PipedInputStream insO = new PipedInputStream();
-                outO = new PipedOutputStream(insO);
-
-                String stdoutLogPrefix = "["+(logPrefix != null ? logPrefix+":stdout" : "stdout")+"] ";
-                gO = new StreamGobbler(insO, execFlags.get(STDOUT), commandLogger).setLogPrefix(stdoutLogPrefix);
-                gO.start();
-
-                execFlags.put(STDOUT, outO);
-            }
-
-            if (!execFlags.get(NO_STDERR_LOGGING)) {
-                PipedInputStream insE = new PipedInputStream();
-                outE = new PipedOutputStream(insE);
-
-                String stderrLogPrefix = "["+(logPrefix != null ? logPrefix+":stderr" : "stderr")+"] ";
-                gE = new StreamGobbler(insE, execFlags.get(STDERR), commandLogger).setLogPrefix(stderrLogPrefix);
-                gE.start();
-
-                execFlags.put(STDERR, outE);
-            }
-
-            Tasks.setBlockingDetails(shortName+" executing, "+summaryForLogging);
-            try {
-                return execWithTool(MutableMap.copyOf(toolFlags.getAllConfig()), new Function<ShellTool, Integer>() {
-                    public Integer apply(ShellTool tool) {
-                        int result = execCommand.exec(tool, MutableMap.copyOf(execFlags.getAllConfig()), commands, env);
-                        if (commandLogger!=null && commandLogger.isDebugEnabled()) 
-                            commandLogger.debug("{}, on machine {}, completed: return status {}",
-                                    new Object[] {summaryForLogging, getTargetName(), result});
-                        return result;
-                    }});
-
-            } finally {
-                Tasks.setBlockingDetails(null);
-            }
-
-        } catch (IOException e) {
-            if (commandLogger!=null && commandLogger.isDebugEnabled()) 
-                commandLogger.debug("{}, on machine {}, failed: {}", new Object[] {summaryForLogging, getTargetName(), e});
-            throw Throwables.propagate(e);
-        } finally {
-            // Must close the pipedOutStreams, otherwise input will never read -1 so StreamGobbler thread would never die
-            if (outO!=null) try { outO.flush(); } catch (IOException e) {}
-            if (outE!=null) try { outE.flush(); } catch (IOException e) {}
-            Streams.closeQuietly(outO);
-            Streams.closeQuietly(outE);
-
-            try {
-                if (gE!=null) { gE.join(); }
-                if (gO!=null) { gO.join(); }
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                Throwables.propagate(e);
-            }
-        }
-
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/system/internal/SystemProcessTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/system/internal/SystemProcessTaskFactory.java b/core/src/main/java/brooklyn/util/task/system/internal/SystemProcessTaskFactory.java
deleted file mode 100644
index e6eb831..0000000
--- a/core/src/main/java/brooklyn/util/task/system/internal/SystemProcessTaskFactory.java
+++ /dev/null
@@ -1,131 +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.system.internal;
-
-import java.io.File;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.internal.ssh.ShellTool;
-import brooklyn.util.internal.ssh.process.ProcessTool;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-
-import com.google.common.base.Function;
-
-public class SystemProcessTaskFactory<T extends SystemProcessTaskFactory<T,RET>,RET> extends AbstractProcessTaskFactory<T, RET> {
-
-    private static final Logger log = LoggerFactory.getLogger(SystemProcessTaskFactory.class);
-    
-    // FIXME Plum this through?!
-    private File directory;
-    private Boolean loginShell;
-
-    public SystemProcessTaskFactory(String ...commands) {
-        super(commands);
-    }
-    
-    public T directory(File directory) {
-        markDirty();
-        this.directory = directory;
-        return self();
-    }
-    
-    public T loginShell(boolean loginShell) {
-        markDirty();
-        this.loginShell = loginShell;
-        return self();
-    }
-    
-    @Override
-    public T machine(SshMachineLocation machine) {
-        log.warn("Not permitted to set machines on "+this+" (ignoring - "+machine+")");
-        if (log.isDebugEnabled())
-            log.debug("Source of attempt to set machines on "+this+" ("+machine+")",
-                    new Throwable("Source of attempt to set machines on "+this+" ("+machine+")"));
-        return self();
-    }
-
-    @Override
-    public ProcessTaskWrapper<RET> newTask() {
-        return new SystemProcessTaskWrapper();
-    }
-
-    protected class SystemProcessTaskWrapper extends ProcessTaskWrapper<RET> {
-        protected final String taskTypeShortName;
-        
-        public SystemProcessTaskWrapper() {
-            this("Process");
-        }
-        public SystemProcessTaskWrapper(String taskTypeShortName) {
-            super(SystemProcessTaskFactory.this);
-            this.taskTypeShortName = taskTypeShortName;
-        }
-        @Override
-        protected ConfigBag getConfigForRunning() {
-            ConfigBag result = super.getConfigForRunning();
-            if (directory != null) config.put(ProcessTool.PROP_DIRECTORY, directory.getAbsolutePath());
-            if (loginShell != null) config.put(ProcessTool.PROP_LOGIN_SHELL, loginShell);
-            return result;
-        }
-        @Override
-        protected void run(ConfigBag config) {
-            if (Boolean.FALSE.equals(this.runAsScript)) {
-                this.exitCode = newExecWithLoggingHelpers().execCommands(config.getAllConfig(), getSummary(), getCommands(), getShellEnvironment());
-            } else { // runScript = null or TRUE
-                this.exitCode = newExecWithLoggingHelpers().execScript(config.getAllConfig(), getSummary(), getCommands(), getShellEnvironment());
-            }
-        }
-        @Override
-        protected String taskTypeShortName() { return taskTypeShortName; }
-    }
-    
-    protected ExecWithLoggingHelpers newExecWithLoggingHelpers() {
-        return new ExecWithLoggingHelpers("Process") {
-            @Override
-            protected <U> U execWithTool(MutableMap<String, Object> props, Function<ShellTool, U> task) {
-                // properties typically passed to both
-                if (log.isDebugEnabled() && props!=null && !props.isEmpty())
-                    log.debug("Ignoring flags "+props+" when running "+this);
-                return task.apply(new ProcessTool());
-            }
-            @Override
-            protected void preExecChecks() {}
-            @Override
-            protected String constructDefaultLoggingPrefix(ConfigBag execFlags) {
-                return "system.exec";
-            }
-            @Override
-            protected String getTargetName() {
-                return "local host";
-            }
-        }.logger(log);
-    }
-
-    /** concrete instance (for generics) */
-    public static class ConcreteSystemProcessTaskFactory<RET> extends SystemProcessTaskFactory<ConcreteSystemProcessTaskFactory<RET>, RET> {
-        public ConcreteSystemProcessTaskFactory(String ...commands) {
-            super(commands);
-        }
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/text/DataUriSchemeParser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/text/DataUriSchemeParser.java b/core/src/main/java/brooklyn/util/text/DataUriSchemeParser.java
deleted file mode 100644
index 393ab20..0000000
--- a/core/src/main/java/brooklyn/util/text/DataUriSchemeParser.java
+++ /dev/null
@@ -1,267 +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.text;
-
-import java.io.ByteArrayInputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.MalformedURLException;
-import java.net.URLDecoder;
-import java.nio.charset.Charset;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import brooklyn.util.exceptions.Exceptions;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.BaseEncoding;
-//import com.sun.jersey.core.util.Base64;
-
-/** implementation (currently hokey) of RFC-2397 data: URI scheme.
- * see: http://stackoverflow.com/questions/12353552/any-rfc-2397-data-uri-parser-for-java */
-public class DataUriSchemeParser {
-
-    public static final String PROTOCOL_PREFIX = "data:";
-    public static final String DEFAULT_MIME_TYPE = "text/plain";
-    public static final String DEFAULT_CHARSET = "US-ASCII";
-    
-    private final String url;
-    private int parseIndex = 0;
-    private boolean isParsed = false;
-    private boolean allowMissingComma = false;
-    private boolean allowSlashesAfterColon = false;
-    private boolean allowOtherLaxities = false;
-    
-    private String mimeType;
-    private byte[] data;
-    private Map<String,String> parameters = new LinkedHashMap<String,String>();
-
-    public DataUriSchemeParser(String url) {
-        this.url = Preconditions.checkNotNull(url, "url");
-    }
-
-    // ---- static conveniences -----
-    
-    public static String toString(String url) {
-        return new DataUriSchemeParser(url).lax().parse().getDataAsString();
-    }
-
-    public static byte[] toBytes(String url) {
-        return new DataUriSchemeParser(url).lax().parse().getData();
-    }
-
-    // ---- accessors (once it is parsed) -----------
-    
-    public String getCharset() {
-        String charset = parameters.get("charset");
-        if (charset!=null) return charset;
-        return DEFAULT_CHARSET;
-    }
-
-    public String getMimeType() {
-        assertParsed();
-        if (mimeType!=null) return mimeType;
-        return DEFAULT_MIME_TYPE;
-    }
-    
-    public Map<String, String> getParameters() {
-        return ImmutableMap.<String, String>copyOf(parameters);
-    }
-
-    public byte[] getData() {
-        assertParsed();
-        return data;
-    }
-    
-    public ByteArrayInputStream getDataAsInputStream() {
-        return new ByteArrayInputStream(getData());
-    }
-
-    public String getDataAsString() {
-        return new String(getData(), Charset.forName(getCharset()));
-    }
-
-    // ---- config ------------------
-    
-    public synchronized DataUriSchemeParser lax() {
-        return allowMissingComma(true).allowSlashesAfterColon(true).allowOtherLaxities(true);
-    }
-        
-    public synchronized DataUriSchemeParser allowMissingComma(boolean allowMissingComma) {
-        assertNotParsed();
-        this.allowMissingComma = allowMissingComma;
-        return this;
-    }
-    
-    public synchronized DataUriSchemeParser allowSlashesAfterColon(boolean allowSlashesAfterColon) {
-        assertNotParsed();
-        this.allowSlashesAfterColon = allowSlashesAfterColon;
-        return this;
-    }
-    
-    private synchronized DataUriSchemeParser allowOtherLaxities(boolean allowOtherLaxities) {
-        assertNotParsed();
-        this.allowOtherLaxities = allowOtherLaxities;
-        return this;
-    }
-    
-    private void assertNotParsed() {
-        if (isParsed) throw new IllegalStateException("Operation not permitted after parsing");
-    }
-
-    private void assertParsed() {
-        if (!isParsed) throw new IllegalStateException("Operation not permitted before parsing");
-    }
-
-    public synchronized DataUriSchemeParser parse() {
-        try {
-            return parseChecked();
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-    
-    public synchronized DataUriSchemeParser parseChecked() throws UnsupportedEncodingException, MalformedURLException {
-        if (isParsed) return this;
-        
-        skipOptional(PROTOCOL_PREFIX);
-        if (allowSlashesAfterColon)
-            while (skipOptional("/")) ;
-        
-        if (allowMissingComma && remainder().indexOf(',')==-1) {
-            mimeType = DEFAULT_MIME_TYPE;
-            parameters.put("charset", DEFAULT_CHARSET);
-        } else {        
-            parseMediaType();
-            parseParameterOrParameterValues();
-            skipRequired(",");
-        }
-        
-        parseData();
-        
-        isParsed = true;
-        return this;
-    }
-
-    private void parseMediaType() throws MalformedURLException {
-        if (remainder().startsWith(";") || remainder().startsWith(","))
-            return;
-        int slash = remainder().indexOf("/");
-        if (slash==-1) throw new MalformedURLException("Missing required '/' in MIME type of data: URL");
-        String type = read(slash);
-        skipRequired("/");
-        int next = nextSemiOrComma();
-        String subtype = read(next);
-        mimeType = type+"/"+subtype;
-    }
-
-    private String read(int next) {
-        String result = remainder().substring(0, next);
-        parseIndex += next;
-        return result;
-    }
-
-    private int nextSemiOrComma() throws MalformedURLException {
-        int semi = remainder().indexOf(';');
-        int comma = remainder().indexOf(',');
-        if (semi<0 && comma<0) throw new MalformedURLException("Missing required ',' in data: URL");
-        if (semi<0) return comma;
-        if (comma<0) return semi;
-        return Math.min(semi, comma);
-    }
-
-    private void parseParameterOrParameterValues() throws MalformedURLException {
-        while (true) {
-            if (!remainder().startsWith(";")) return;
-            parseIndex++;
-            int eq = remainder().indexOf('=');
-            String word, value;
-            int nextSemiOrComma = nextSemiOrComma();
-            if (eq==-1 || eq>nextSemiOrComma) {
-                word = read(nextSemiOrComma);
-                value = null;
-            } else {
-                word = read(eq);
-                if (remainder().startsWith("\"")) {
-                    // is quoted
-                    parseIndex++;
-                    int nextUnescapedQuote = nextUnescapedQuote();
-                    value = "\"" + read(nextUnescapedQuote);
-                } else {
-                    value = read(nextSemiOrComma());
-                }
-            }
-            parameters.put(word, value);
-        }
-    }
-
-    private int nextUnescapedQuote() throws MalformedURLException {
-        int i=0;
-        String r = remainder();
-        boolean escaped = false;
-        while (i<r.length()) {
-            if (escaped) {
-                escaped = false;
-            } else {
-                if (r.charAt(i)=='"') return i;
-                if (r.charAt(i)=='\\') escaped = true;
-            }
-            i++;
-        }
-        throw new MalformedURLException("Unclosed double-quote in data: URL");
-    }
-
-    private void parseData() throws UnsupportedEncodingException, MalformedURLException {
-        if (parameters.containsKey("base64")) {
-            checkNoParamValue("base64");
-            data = BaseEncoding.base64().decode(remainder());
-        } else if (parameters.containsKey("base64url")) {
-            checkNoParamValue("base64url");
-            data = BaseEncoding.base64Url().decode(remainder());
-        } else {
-            data = URLDecoder.decode(remainder(), getCharset()).getBytes(Charset.forName(getCharset()));
-        }
-    }
-
-    private void checkNoParamValue(String param) throws MalformedURLException {
-        if (allowOtherLaxities) return; 
-        String value = parameters.get(param);
-        if (value!=null)
-            throw new MalformedURLException(param+" parameter must not take a value ("+value+") in data: URL");
-    }
-
-    private String remainder() {
-        return url.substring(parseIndex);
-    }
-
-    private boolean skipOptional(String word) {
-        if (remainder().startsWith(word)) {
-            parseIndex += word.length();
-            return true;
-        }
-        return false;
-    }
-
-    private void skipRequired(String word) throws MalformedURLException {
-        if (!remainder().startsWith(word))
-            throw new MalformedURLException("Missing required '"+word+"' at position "+parseIndex+" of data: URL");
-        parseIndex += word.length();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/text/TemplateProcessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/text/TemplateProcessor.java b/core/src/main/java/brooklyn/util/text/TemplateProcessor.java
deleted file mode 100644
index eb0c2ad..0000000
--- a/core/src/main/java/brooklyn/util/text/TemplateProcessor.java
+++ /dev/null
@@ -1,397 +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.text;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.util.Map;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.drivers.EntityDriver;
-import org.apache.brooklyn.api.event.AttributeSensor;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.event.basic.DependentConfiguration;
-import brooklyn.event.basic.Sensors;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-
-import com.google.common.base.Charsets;
-import com.google.common.collect.Iterables;
-import com.google.common.io.Files;
-
-import freemarker.cache.StringTemplateLoader;
-import freemarker.template.Configuration;
-import freemarker.template.ObjectWrapper;
-import freemarker.template.Template;
-import freemarker.template.TemplateHashModel;
-import freemarker.template.TemplateModel;
-import freemarker.template.TemplateModelException;
-
-/** A variety of methods to assist in Freemarker template processing,
- * including passing in maps with keys flattened (dot-separated namespace),
- * and accessing {@link ManagementContext} brooklyn.properties 
- * and {@link Entity}, {@link EntityDriver}, and {@link Location} methods and config.
- * <p>
- * See {@link #processTemplateContents(String, ManagementContextInternal, Map)} for
- * a description of how management access is done.
- */
-public class TemplateProcessor {
-
-    private static final Logger log = LoggerFactory.getLogger(TemplateProcessor.class);
-
-    protected static TemplateModel wrapAsTemplateModel(Object o) throws TemplateModelException {
-        if (o instanceof Map) return new DotSplittingTemplateModel((Map<?,?>)o);
-        return ObjectWrapper.DEFAULT_WRAPPER.wrap(o);
-    }
-    
-    /** @deprecated since 0.7.0 use {@link #processTemplateFile(String, Map)} */ @Deprecated
-    public static String processTemplate(String templateFileName, Map<String, ? extends Object> substitutions) {
-        return processTemplateFile(templateFileName, substitutions);
-    }
-    
-    /** As per {@link #processTemplateContents(String, Map)}, but taking a file. */
-    public static String processTemplateFile(String templateFileName, Map<String, ? extends Object> substitutions) {
-        String templateContents;
-        try {
-            templateContents = Files.toString(new File(templateFileName), Charsets.UTF_8);
-        } catch (IOException e) {
-            log.warn("Error loading file " + templateFileName, e);
-            throw Exceptions.propagate(e);
-        }
-        return processTemplateContents(templateContents, substitutions);
-    }
-
-    /** @deprecated since 0.7.0 use {@link #processTemplateFile(String, EntityDriver, Map)} */ @Deprecated
-    public static String processTemplate(String templateFileName, EntityDriver driver, Map<String, ? extends Object> extraSubstitutions) {
-        return processTemplateFile(templateFileName, driver, extraSubstitutions);
-    }
-    
-    /** Processes template contents according to {@link EntityAndMapTemplateModel}. */
-    public static String processTemplateFile(String templateFileName, EntityDriver driver, Map<String, ? extends Object> extraSubstitutions) {
-        String templateContents;
-        try {
-            templateContents = Files.toString(new File(templateFileName), Charsets.UTF_8);
-        } catch (IOException e) {
-            log.warn("Error loading file " + templateFileName, e);
-            throw Exceptions.propagate(e);
-        }
-        return processTemplateContents(templateContents, driver, extraSubstitutions);
-    }
-
-    /** Processes template contents according to {@link EntityAndMapTemplateModel}. */
-    public static String processTemplateContents(String templateContents, EntityDriver driver, Map<String,? extends Object> extraSubstitutions) {
-        return processTemplateContents(templateContents, new EntityAndMapTemplateModel(driver, extraSubstitutions));
-    }
-
-    /** Processes template contents according to {@link EntityAndMapTemplateModel}. */
-    public static String processTemplateContents(String templateContents, ManagementContext managementContext, Map<String,? extends Object> extraSubstitutions) {
-        return processTemplateContents(templateContents, new EntityAndMapTemplateModel(managementContext, extraSubstitutions));
-    }
-
-    /**
-     * A Freemarker {@link TemplateHashModel} which will correctly handle entries of the form "a.b" in this map,
-     * matching against template requests for "${a.b}".
-     * <p>
-     * Freemarker requests "a" in a map when given such a request, and expects that to point to a map
-     * with a key "b". This model provides such maps even for "a.b" in a map.
-     * <p>
-     * However if "a" <b>and</b> "a.b" are in the map, this will <b>not</b> currently do the deep mapping.
-     * (It does not have enough contextual information from Freemarker to handle this case.) */
-    public static final class DotSplittingTemplateModel implements TemplateHashModel {
-        protected final Map<?,?> map;
-
-        protected DotSplittingTemplateModel(Map<?,?> map) {
-            this.map = map;
-        }
-
-        @Override
-        public boolean isEmpty() { return map!=null && map.isEmpty(); }
-
-        public boolean contains(String key) {
-            if (map==null) return false;
-            if (map.containsKey(key)) return true;
-            for (Map.Entry<?,?> entry: map.entrySet()) {
-                String k = Strings.toString(entry.getKey());
-                if (k.startsWith(key+".")) {
-                    // contains this prefix
-                    return true;
-                }
-            }
-            return false;
-        }
-        
-        @Override
-        public TemplateModel get(String key) throws TemplateModelException {
-            if (map==null) return null;
-            try {
-                if (map.containsKey(key)) 
-                    return wrapAsTemplateModel( map.get(key) );
-                
-                Map<String,Object> result = MutableMap.of();
-                for (Map.Entry<?,?> entry: map.entrySet()) {
-                    String k = Strings.toString(entry.getKey());
-                    if (k.startsWith(key+".")) {
-                        String k2 = Strings.removeFromStart(k, key+".");
-                        result.put(k2, entry.getValue());
-                    }
-                }
-                if (!result.isEmpty()) 
-                        return wrapAsTemplateModel( result );
-                
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                throw new IllegalStateException("Error accessing config '"+key+"'"+": "+e, e);
-            }
-            
-            return null;
-        }
-        
-        @Override
-        public String toString() {
-            return getClass().getName()+"["+map+"]";
-        }
-    }
-    
-    /** FreeMarker {@link TemplateHashModel} which resolves keys inside the given entity or management context.
-     * Callers are required to include dots for dot-separated keys.
-     * Freemarker will only due this when in inside bracket notation in an outer map, as in <code>${outer['a.b.']}</code>; 
-     * as a result this is intended only for use by {@link EntityAndMapTemplateModel} where 
-     * a caller has used bracked notation, as in <code>${mgmt['key.subkey']}</code>. */
-    protected static final class EntityConfigTemplateModel implements TemplateHashModel {
-        protected final EntityInternal entity;
-        protected final ManagementContext mgmt;
-
-        protected EntityConfigTemplateModel(EntityInternal entity) {
-            this.entity = entity;
-            this.mgmt = entity.getManagementContext();
-        }
-
-        protected EntityConfigTemplateModel(ManagementContext mgmt) {
-            this.entity = null;
-            this.mgmt = mgmt;
-        }
-
-        @Override
-        public boolean isEmpty() { return false; }
-
-        @Override
-        public TemplateModel get(String key) throws TemplateModelException {
-            try {
-                Object result = null;
-                
-                if (entity!=null)
-                    result = entity.getConfig(ConfigKeys.builder(Object.class).name(key).build());
-                if (result==null && mgmt!=null)
-                    result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());
-                
-                if (result!=null)
-                    return wrapAsTemplateModel( result );
-                
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                throw new IllegalStateException("Error accessing config '"+key+"'"
-                    + (entity!=null ? " on "+entity : "")+": "+e, e);
-            }
-            
-            return null;
-        }
-        
-        @Override
-        public String toString() {
-            return getClass().getName()+"["+entity+"]";
-        }
-    }
-
-    protected final static class EntityAttributeTemplateModel implements TemplateHashModel {
-        protected final EntityInternal entity;
-
-        protected EntityAttributeTemplateModel(EntityInternal entity) {
-            this.entity = entity;
-        }
-
-        @Override
-        public boolean isEmpty() throws TemplateModelException {
-            return false;
-        }
-
-        @Override
-        public TemplateModel get(String key) throws TemplateModelException {
-            Object result;
-            try {
-                result = Entities.submit(entity, DependentConfiguration.attributeWhenReady(entity,
-                        Sensors.builder(Object.class, key).persistence(AttributeSensor.SensorPersistenceMode.NONE).build())).get();
-            } catch (Exception e) {
-                throw Exceptions.propagate(e);
-            }
-            if (result == null) {
-                return null;
-            } else {
-                return wrapAsTemplateModel(result);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return getClass().getName()+"["+entity+"]";
-        }
-    }
-
-    /**
-     * Provides access to config on an entity or management context, using
-     * <code>${config['entity.config.key']}</code> or <code>${mgmt['brooklyn.properties.key']}</code> notation,
-     * and also allowing access to <code>getX()</code> methods on entity (interface) or driver
-     * using <code>${entity.x}</code> or <code><${driver.x}</code>.
-     * Optional extra properties can be supplied, treated as per {@link DotSplittingTemplateModel}.
-     */
-    protected static final class EntityAndMapTemplateModel implements TemplateHashModel {
-        protected final EntityInternal entity;
-        protected final EntityDriver driver;
-        protected final ManagementContext mgmt;
-        protected final DotSplittingTemplateModel extraSubstitutionsModel;
-
-        protected EntityAndMapTemplateModel(ManagementContext mgmt, Map<String,? extends Object> extraSubstitutions) {
-            this.entity = null;
-            this.driver = null;
-            this.mgmt = mgmt;
-            this.extraSubstitutionsModel = new DotSplittingTemplateModel(extraSubstitutions);
-        }
-
-        protected EntityAndMapTemplateModel(EntityDriver driver, Map<String,? extends Object> extraSubstitutions) {
-            this.driver = driver;
-            this.entity = (EntityInternal) driver.getEntity();
-            this.mgmt = entity.getManagementContext();
-            this.extraSubstitutionsModel = new DotSplittingTemplateModel(extraSubstitutions);
-        }
-
-        protected EntityAndMapTemplateModel(EntityInternal entity, Map<String,? extends Object> extraSubstitutions) {
-            this.entity = entity;
-            this.driver = null;
-            this.mgmt = entity.getManagementContext();
-            this.extraSubstitutionsModel = new DotSplittingTemplateModel(extraSubstitutions);
-        }
-
-        @Override
-        public boolean isEmpty() { return false; }
-
-        @Override
-        public TemplateModel get(String key) throws TemplateModelException {
-            if (extraSubstitutionsModel.contains(key))
-                return wrapAsTemplateModel( extraSubstitutionsModel.get(key) );
-
-            if ("entity".equals(key) && entity!=null)
-                return wrapAsTemplateModel( entity );
-            if ("config".equals(key)) {
-                if (entity!=null)
-                    return new EntityConfigTemplateModel(entity);
-                else
-                    return new EntityConfigTemplateModel(mgmt);
-            }
-            if ("mgmt".equals(key)) {
-                return new EntityConfigTemplateModel(mgmt);
-            }
-
-            if ("driver".equals(key) && driver!=null)
-                return wrapAsTemplateModel( driver );
-            if ("location".equals(key)) {
-                if (driver!=null && driver.getLocation()!=null)
-                    return wrapAsTemplateModel( driver.getLocation() );
-                if (entity!=null)
-                    return wrapAsTemplateModel( Iterables.getOnlyElement( entity.getLocations() ) );
-            }
-            if ("attribute".equals(key)) {
-                return new EntityAttributeTemplateModel(entity);
-            }
-            
-            if (mgmt!=null) {
-                // TODO deprecated in 0.7.0, remove after next version
-                // ie not supported to access global props without qualification
-                Object result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());
-                if (result!=null) { 
-                    log.warn("Deprecated access of global brooklyn.properties value for "+key+"; should be qualified with 'mgmt.'");
-                    return wrapAsTemplateModel( result );
-                }
-            }
-            
-            if ("javaSysProps".equals(key))
-                return wrapAsTemplateModel( System.getProperties() );
-
-            return null;
-        }
-        
-        @Override
-        public String toString() {
-            return getClass().getName()+"["+(entity!=null ? entity : mgmt)+"]";
-        }
-    }
-
-    /** Processes template contents with the given items in scope as per {@link EntityAndMapTemplateModel}. */
-    public static String processTemplateContents(String templateContents, final EntityInternal entity, Map<String,? extends Object> extraSubstitutions) {
-        return processTemplateContents(templateContents, new EntityAndMapTemplateModel(entity, extraSubstitutions));
-    }
-    
-    /** Processes template contents using the given map, passed to freemarker,
-     * with dot handling as per {@link DotSplittingTemplateModel}. */
-    public static String processTemplateContents(String templateContents, final Map<String, ? extends Object> substitutions) {
-        TemplateHashModel root;
-        try {
-            root = substitutions != null
-                ? (TemplateHashModel)wrapAsTemplateModel(substitutions)
-                : null;
-        } catch (TemplateModelException e) {
-            throw new IllegalStateException("Unable to set up TemplateHashModel to parse template, given "+substitutions+": "+e, e);
-        }
-        
-        return processTemplateContents(templateContents, root);
-    }
-    
-    /** Processes template contents against the given {@link TemplateHashModel}. */
-    public static String processTemplateContents(String templateContents, final TemplateHashModel substitutions) {
-        try {
-            Configuration cfg = new Configuration();
-            StringTemplateLoader templateLoader = new StringTemplateLoader();
-            templateLoader.putTemplate("config", templateContents);
-            cfg.setTemplateLoader(templateLoader);
-            Template template = cfg.getTemplate("config");
-
-            // TODO could expose CAMP '$brooklyn:' style dsl, based on template.createProcessingEnvironment
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            Writer out = new OutputStreamWriter(baos);
-            template.process(substitutions, out);
-            out.flush();
-
-            return new String(baos.toByteArray());
-        } catch (Exception e) {
-            log.warn("Error processing template (propagating): "+e, e);
-            log.debug("Template which could not be parsed (causing "+e+") is:"
-                + (Strings.isMultiLine(templateContents) ? "\n"+templateContents : templateContents));
-            throw Exceptions.propagate(e);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/CompilerIndependentOuterClassFieldMapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/CompilerIndependentOuterClassFieldMapper.java b/core/src/main/java/brooklyn/util/xstream/CompilerIndependentOuterClassFieldMapper.java
deleted file mode 100644
index f9a1f00..0000000
--- a/core/src/main/java/brooklyn/util/xstream/CompilerIndependentOuterClassFieldMapper.java
+++ /dev/null
@@ -1,166 +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.xstream;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.thoughtworks.xstream.core.Caching;
-import com.thoughtworks.xstream.mapper.Mapper;
-import com.thoughtworks.xstream.mapper.MapperWrapper;
-
-/**
- * <p>Compiler independent outer class field mapper.</p>
- * <p>Different compilers generate different indexes for the names of outer class reference
- *    field (this$N) leading to deserialization errors.</p>
- * <ul>
- *   <li> eclipse-[groovy-]compiler counts all outer static classes
- *   <li> OpenJDK/Oracle/IBM compiler starts at 0, regardless of the nesting level
- * </ul>
- * <p>The mapper will be able to update field names for instances with a single this$N
- *    field only (including those from parent classes).</p>
- * <p>For difference between generated field names compare
- *    {@code src/test/java/brooklyn/util/xstream/compiler_compatibility_eclipse.xml} and
- *    {@code src/test/java/brooklyn/util/xstream/compiler_compatibility_oracle.xml},
- *    generated from {@code brooklyn.util.xstream.CompilerCompatibilityTest}</p>
- * <p>JLS 1.1 relevant section, copied verbatim for a lack of reliable URL:</p>
- * <blockquote>
- *  <p>Java 1.1 compilers are strongly encouraged, though not required, to use the
- *     following naming conventions when implementing inner classes. Compilers may
- *     not use synthetic names of the forms defined here for any other purposes.</p>
- *  <p>A synthetic field pointing to the outermost enclosing instance is named this$0.
- *     The next-outermost enclosing instance is this$1, and so forth. (At most one such
- *     field is necessary in any given inner class.) A synthetic field containing a copy
- *     of a constant v is named val$v. These fields are final.</p>
- * </blockquote>
- * <p>Currently available at
- *    http://web.archive.org/web/20000830111107/http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html</p>
- */
-public class CompilerIndependentOuterClassFieldMapper extends MapperWrapper implements Caching {
-    public static final Logger LOG = LoggerFactory.getLogger(CompilerIndependentOuterClassFieldMapper.class);
-
-    private static final String OUTER_CLASS_FIELD_PREFIX = "this$";
-
-    private final Map<String, Collection<String>> classOuterFields = new ConcurrentHashMap<String, Collection<String>>();
-
-    public CompilerIndependentOuterClassFieldMapper(Mapper wrapped) {
-        super(wrapped);
-        classOuterFields.put(Object.class.getName(), Collections.<String>emptyList());
-    }
-
-    @Override
-    public String realMember(@SuppressWarnings("rawtypes") Class type, String serialized) {
-        // Let com.thoughtworks.xstream.mapper.OuterClassMapper also run on the input.
-        String serializedFieldName = super.realMember(type, serialized);
-
-        if (serializedFieldName.startsWith(OUTER_CLASS_FIELD_PREFIX)) {
-            Collection<String> compiledFieldNames = findOuterClassFieldNames(type);
-            if (compiledFieldNames.size() == 0) {
-                throw new IllegalStateException("Unable to find any outer class fields in " + type + ", searching specifically for " + serializedFieldName);
-            }
-
-            Set<String> uniqueFieldNames = new HashSet<String>(compiledFieldNames);
-            String deserializeFieldName;
-            if (!compiledFieldNames.contains(serializedFieldName)) {
-                String msg =
-                        "Unable to find outer class field " + serializedFieldName + " in class " + type + ". " +
-                        "This could be caused by " +
-                        "1) changing the class (or one of its parents) to a static or " +
-                        "2) moving the class to a different lexical level (enclosing classes) or " +
-                        "3) using a different compiler (i.e eclipse vs oracle) at the time the object was serialized. ";
-                if (uniqueFieldNames.size() == 1) {
-                    // Try to fix the field naming only for the case with a single field or 
-                    // multiple fields with the same name, in which case XStream puts defined-in
-                    // for the field declared in super.
-                    //
-                    // We don't have access to the XML elements from here to check for same name
-                    // so we check the target class instead. This should work most of the time, but
-                    // if code is recompiled in such a way that the new instance has fields with
-                    // different names, where only the field of the extending class is renamed and
-                    // the super field is not, then the instance will be deserialized incorrectly -
-                    // the super field will be assigned both times. If the field type is incompatible
-                    // then a casting exception will be thrown, if it's the same then only the warning
-                    // below will indicate of a possible problem down the line - most probably NPE on
-                    // the this$N field.
-                    deserializeFieldName = compiledFieldNames.iterator().next();
-                    LOG.warn(msg + "Will use the field " + deserializeFieldName + " instead.");
-                } else {
-                    // Multiple fields with differing names case - don't try to fix it.
-                    // Better fail with an explicit error, and have someone fix it manually,
-                    // than try to fix it here non-reliably and have it fail down the line
-                    // with some unrelated error.
-                    // XStream will fail later with a field not found exception.
-                    LOG.error(msg + "Will fail with a field not found exception. " +
-                            "Edit the persistence state manually and update the field names. "+
-                            "Existing field names are " + uniqueFieldNames);
-                    deserializeFieldName = serializedFieldName;
-                }
-            } else {
-                if (uniqueFieldNames.size() > 1) {
-                    // Log at debug level as the actual problem would occur in very specific cases. Only
-                    // useful when the compiler is changed, otherwise leads to false positives.
-                    LOG.debug("Deserializing the non-static class " + type + " with multiple outer class fields " + uniqueFieldNames + ". " +
-                            "When changing compilers it's possible that the instance won't be able to be deserialized due to changed outer class field names. " +
-                            "In those cases deserialization could fail with field not found exception or class cast exception following this log line.");
-                }
-                deserializeFieldName = serializedFieldName;
-            }
-
-            return deserializeFieldName;
-        } else {
-            return serializedFieldName;
-        }
-    }
-    
-    private Collection<String> findOuterClassFieldNames(Class<?> type) {
-        Collection<String> fields = classOuterFields.get(type.getName());
-        if (fields == null) {
-            fields = new ArrayList<String>();
-            addOuterClassFields(type, fields);
-            classOuterFields.put(type.getName(), fields);
-        }
-        return fields;
-    }
-    
-    private void addOuterClassFields(Class<?> type, Collection<String> fields) {
-        for (Field field : type.getDeclaredFields()) {
-            if (field.isSynthetic()) {
-                fields.add(field.getName());
-            }
-        }
-        if (type.getSuperclass() != null) {
-            addOuterClassFields(type.getSuperclass(), fields);
-        }
-    }
-
-    @Override
-    public void flushCache() {
-        classOuterFields.keySet().retainAll(Collections.singletonList(Object.class.getName()));
-    }
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/EnumCaseForgivingConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/EnumCaseForgivingConverter.java b/core/src/main/java/brooklyn/util/xstream/EnumCaseForgivingConverter.java
deleted file mode 100644
index 96bafe6..0000000
--- a/core/src/main/java/brooklyn/util/xstream/EnumCaseForgivingConverter.java
+++ /dev/null
@@ -1,60 +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.xstream;
-
-import brooklyn.util.exceptions.Exceptions;
-
-import com.thoughtworks.xstream.converters.UnmarshallingContext;
-import com.thoughtworks.xstream.converters.enums.EnumConverter;
-import com.thoughtworks.xstream.io.HierarchicalStreamReader;
-
-/** ... except this doesn't seem to get applied when we think it should
- * (normal xstream.resgisterConverter doesn't apply to enums) */
-public class EnumCaseForgivingConverter extends EnumConverter {
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
-        Class type = context.getRequiredType();
-        if (type.getSuperclass() != Enum.class) {
-            type = type.getSuperclass(); // polymorphic enums
-        }
-        String token = reader.getValue();
-        // this is the new bit (overriding superclass to accept case-insensitive)
-        return resolve(type, token);
-    }
-
-    public static <T extends Enum<T>> T resolve(Class<T> type, String token) {
-        try {
-            return Enum.valueOf(type, token.toUpperCase());
-        } catch (Exception e) {
-            
-            // new stuff here:  try reading case insensitive
-            
-            Exceptions.propagateIfFatal(e);
-            try {
-                for (T v: type.getEnumConstants())
-                    if (v.name().equalsIgnoreCase(token)) return v;
-                throw e;
-            } catch (Exception e2) {
-                throw Exceptions.propagate(e2);
-            }
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/EnumCaseForgivingSingleValueConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/EnumCaseForgivingSingleValueConverter.java b/core/src/main/java/brooklyn/util/xstream/EnumCaseForgivingSingleValueConverter.java
deleted file mode 100644
index a7aba8c..0000000
--- a/core/src/main/java/brooklyn/util/xstream/EnumCaseForgivingSingleValueConverter.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.xstream;
-
-import com.thoughtworks.xstream.converters.enums.EnumSingleValueConverter;
-
-public class EnumCaseForgivingSingleValueConverter extends EnumSingleValueConverter {
-
-    private final Class enumType;
-
-    public EnumCaseForgivingSingleValueConverter(Class type) {
-        super(type);
-        enumType = type;
-    }
-
-    public Object fromString(String str) {
-        return EnumCaseForgivingConverter.resolve(enumType, str);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/ImmutableListConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/ImmutableListConverter.java b/core/src/main/java/brooklyn/util/xstream/ImmutableListConverter.java
deleted file mode 100644
index df33142..0000000
--- a/core/src/main/java/brooklyn/util/xstream/ImmutableListConverter.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.xstream;
-
-import java.util.Collection;
-
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.thoughtworks.xstream.converters.UnmarshallingContext;
-import com.thoughtworks.xstream.converters.collections.CollectionConverter;
-import com.thoughtworks.xstream.io.HierarchicalStreamReader;
-import com.thoughtworks.xstream.mapper.Mapper;
-
-public class ImmutableListConverter extends CollectionConverter {
-
-    public ImmutableListConverter(Mapper mapper) {
-        super(mapper);
-    }
-
-    @Override
-    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
-        return ImmutableList.class.isAssignableFrom(type);
-    }
-
-    // marshalling is the same
-    // so is unmarshalling the entries
-
-    // only differences are creating the overarching collection, which we do after the fact
-    // (optimizing format on disk as opposed to in-memory), and we discard null values 
-    // to avoid failing entirely.
-    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
-        Collection<?> collection = Lists.newArrayList();
-        populateCollection(reader, context, collection);
-        return ImmutableList.copyOf(Iterables.filter(collection, Predicates.notNull()));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/ImmutableMapConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/ImmutableMapConverter.java b/core/src/main/java/brooklyn/util/xstream/ImmutableMapConverter.java
deleted file mode 100644
index aa8ca4c..0000000
--- a/core/src/main/java/brooklyn/util/xstream/ImmutableMapConverter.java
+++ /dev/null
@@ -1,56 +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.xstream;
-
-import java.util.Map;
-import java.util.Map.Entry;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.thoughtworks.xstream.converters.UnmarshallingContext;
-import com.thoughtworks.xstream.io.HierarchicalStreamReader;
-import com.thoughtworks.xstream.mapper.Mapper;
-
-public class ImmutableMapConverter extends MapConverter {
-
-    public ImmutableMapConverter(Mapper mapper) {
-        super(mapper);
-    }
-
-    @Override
-    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
-        return ImmutableMap.class.isAssignableFrom(type);
-    }
-
-    // marshalling is the same
-    // so is unmarshalling the entries
-
-    // only differences are creating the overarching collection, which we do after the fact
-    // (optimizing format on disk as opposed to in-memory), and we discard null key/values 
-    // to avoid failing entirely.
-    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
-        Map<?, ?> map = Maps.newLinkedHashMap();
-        populateMap(reader, context, map);
-        return ImmutableMap.copyOf(Maps.filterEntries(map, new Predicate<Map.Entry<?,?>>() {
-                @Override public boolean apply(Entry<?, ?> input) {
-                    return input != null && input.getKey() != null && input.getValue() != null;
-                }}));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/ImmutableSetConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/ImmutableSetConverter.java b/core/src/main/java/brooklyn/util/xstream/ImmutableSetConverter.java
deleted file mode 100644
index 11ee3ce..0000000
--- a/core/src/main/java/brooklyn/util/xstream/ImmutableSetConverter.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.xstream;
-
-import java.util.Collection;
-
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.thoughtworks.xstream.converters.UnmarshallingContext;
-import com.thoughtworks.xstream.converters.collections.CollectionConverter;
-import com.thoughtworks.xstream.io.HierarchicalStreamReader;
-import com.thoughtworks.xstream.mapper.Mapper;
-
-public class ImmutableSetConverter extends CollectionConverter {
-
-    public ImmutableSetConverter(Mapper mapper) {
-        super(mapper);
-    }
-
-    @Override
-    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
-        return ImmutableSet.class.isAssignableFrom(type);
-    }
-
-    // marshalling is the same
-    // so is unmarshalling the entries
-
-    // only differences are creating the overarching collection, which we do after the fact
-    // (optimizing format on disk as opposed to in-memory), and we discard null values 
-    // to avoid failing entirely.
-    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
-        Collection<?> collection = Lists.newArrayList();
-        populateCollection(reader, context, collection);
-        return ImmutableSet.copyOf(Iterables.filter(collection, Predicates.notNull()));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/Inet4AddressConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/Inet4AddressConverter.java b/core/src/main/java/brooklyn/util/xstream/Inet4AddressConverter.java
deleted file mode 100644
index dbd704b..0000000
--- a/core/src/main/java/brooklyn/util/xstream/Inet4AddressConverter.java
+++ /dev/null
@@ -1,65 +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.xstream;
-
-import java.net.Inet4Address;
-import java.net.UnknownHostException;
-
-import brooklyn.util.exceptions.Exceptions;
-
-import com.thoughtworks.xstream.converters.Converter;
-import com.thoughtworks.xstream.converters.MarshallingContext;
-import com.thoughtworks.xstream.converters.UnmarshallingContext;
-import com.thoughtworks.xstream.io.HierarchicalStreamReader;
-import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
-
-public class Inet4AddressConverter implements Converter {
-
-    @Override
-    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
-        return type.equals(Inet4Address.class);
-    }
-
-    @Override
-    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
-        Inet4Address addr = (Inet4Address) source;
-        writer.setValue(addr.getHostName()+"/"+addr.getHostAddress());
-    }
-
-    @Override
-    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
-        String hostSlashAddress = reader.getValue();
-        int i = hostSlashAddress.indexOf('/');
-        try {
-            if (i==-1) {
-                return Inet4Address.getByName(hostSlashAddress);
-            } else {
-                String host = hostSlashAddress.substring(0, i);
-                String addrS = hostSlashAddress.substring(i+1);
-                byte[] addr = new byte[4];
-                String[] addrSI = addrS.split("\\.");
-                for (int k=0; k<4; k++) addr[k] = (byte)(int)Integer.valueOf(addrSI[k]);
-                return Inet4Address.getByAddress(host, addr);
-            }
-        } catch (UnknownHostException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/MapConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/MapConverter.java b/core/src/main/java/brooklyn/util/xstream/MapConverter.java
deleted file mode 100644
index 5ee2a6a..0000000
--- a/core/src/main/java/brooklyn/util/xstream/MapConverter.java
+++ /dev/null
@@ -1,104 +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.xstream;
-
-import java.util.ConcurrentModificationException;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.thoughtworks.xstream.converters.MarshallingContext;
-import com.thoughtworks.xstream.converters.UnmarshallingContext;
-import com.thoughtworks.xstream.core.ReferencingMarshallingContext;
-import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
-import com.thoughtworks.xstream.io.HierarchicalStreamReader;
-import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
-import com.thoughtworks.xstream.mapper.Mapper;
-
-/** equivalent to super, but cleaner methods, overridable, logging, and some retries */
-public class MapConverter extends com.thoughtworks.xstream.converters.collections.MapConverter {
-
-    private static final Logger log = LoggerFactory.getLogger(MapConverter.class);
-    
-    public MapConverter(Mapper mapper) {
-        super(mapper);
-    }
-
-    @SuppressWarnings({ "rawtypes" })
-    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
-        Map map = (Map) source;
-        try {
-            for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
-                Map.Entry entry = (Map.Entry) iterator.next();
-                marshalEntry(writer, context, entry);
-            }
-        } catch (ConcurrentModificationException e) {
-            log.debug("Map "
-                // seems there is no non-deprecated way to get the path...
-                + (context instanceof ReferencingMarshallingContext ? "at "+((ReferencingMarshallingContext)context).currentPath() : "")
-                + "["+source+"] modified while serializing; will fail, and retry may be attempted");
-            throw e;
-            // would be nice to attempt to re-serialize being slightly more defensive, as code below;
-            // but the code above may have written partial data so that is dangerous, we could have corrupted output. 
-            // if we could mark and restore in the output stream then we could do this below (but we don't have that in our stream),
-            // or we could try this copying code in the first instance (but that's slow);
-            // error is rare most of the time (e.g. attribute being updated) so we bail on this whole attempt
-            // and simply try serializing the map-owner (e.g. an entity) again.
-//            ImmutableList entries = ImmutableList.copyOf(map.entrySet());
-//            for (Iterator iterator = entries.iterator(); iterator.hasNext();) {
-//                Map.Entry entry = (Map.Entry) iterator.next();
-//                marshalEntry(writer, context, entry);                
-//            }
-        }
-    }
-
-    protected String getEntryNodeName() { return mapper().serializedClass(Map.Entry.class); }
-    
-    protected void marshalEntry(HierarchicalStreamWriter writer, MarshallingContext context, Map.Entry entry) {
-        ExtendedHierarchicalStreamWriterHelper.startNode(writer, getEntryNodeName(), Map.Entry.class);
-
-        writeItem(entry.getKey(), context, writer);
-        writeItem(entry.getValue(), context, writer);
-
-        writer.endNode();
-    }
-
-    protected void populateMap(HierarchicalStreamReader reader, UnmarshallingContext context, Map map) {
-        while (reader.hasMoreChildren()) {
-            reader.moveDown();
-            unmarshalEntry(reader, context, map);
-            reader.moveUp();
-        }
-    }
-
-    protected void unmarshalEntry(HierarchicalStreamReader reader, UnmarshallingContext context, Map map) {
-        reader.moveDown();
-        Object key = readItem(reader, context, map);
-        reader.moveUp();
-
-        reader.moveDown();
-        Object value = readItem(reader, context, map);
-        reader.moveUp();
-
-        map.put(key, value);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/MutableSetConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/MutableSetConverter.java b/core/src/main/java/brooklyn/util/xstream/MutableSetConverter.java
deleted file mode 100644
index 3432751..0000000
--- a/core/src/main/java/brooklyn/util/xstream/MutableSetConverter.java
+++ /dev/null
@@ -1,44 +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.xstream;
-
-import brooklyn.util.collections.MutableSet;
-
-import com.thoughtworks.xstream.converters.collections.CollectionConverter;
-import com.thoughtworks.xstream.mapper.Mapper;
-
-public class MutableSetConverter extends CollectionConverter {
-
-    // Although this class seems pointless (!), without registering an explicit converter for MutableSet then the
-    // declaration for Set interferes, causing MutableSet.map field to be null on deserialization.
-    
-    public MutableSetConverter(Mapper mapper) {
-        super(mapper);
-    }
-
-    @Override
-    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
-        return MutableSet.class.isAssignableFrom(type);
-    }
-
-    @Override
-    protected Object createCollection(Class type) {
-        return new MutableSet<Object>();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/StringKeyMapConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/StringKeyMapConverter.java b/core/src/main/java/brooklyn/util/xstream/StringKeyMapConverter.java
deleted file mode 100644
index 26aba7a..0000000
--- a/core/src/main/java/brooklyn/util/xstream/StringKeyMapConverter.java
+++ /dev/null
@@ -1,134 +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.xstream;
-
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.text.Identifiers;
-
-import com.thoughtworks.xstream.converters.MarshallingContext;
-import com.thoughtworks.xstream.converters.UnmarshallingContext;
-import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
-import com.thoughtworks.xstream.io.HierarchicalStreamReader;
-import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
-import com.thoughtworks.xstream.mapper.Mapper;
-
-/** converter which simplifies representation of a map for string-based keys,
- * to <key>value</key>, or <entry key="key" type="string">value</entry> 
- * @author alex
- *
- */
-public class StringKeyMapConverter extends MapConverter {
-
-    private static final Logger log = LoggerFactory.getLogger(StringKeyMapConverter.class);
-    
-    // full stop is technically allowed ... goes against "best practice" ... 
-    // but simplifies property maps, and is used elsewhere in xstream's repn
-    final static String VALID_XML_NODE_NAME_CHARS = Identifiers.JAVA_GOOD_NONSTART_CHARS + ".";
-
-    final static String VALID_XML_NODE_NAME_START_CHARS = Identifiers.JAVA_GOOD_START_CHARS + ".";
-
-    public StringKeyMapConverter(Mapper mapper) {
-        super(mapper);
-    }
-    
-    protected boolean isKeyValidForNodeName(String key) {
-        // return false to always write as <entry key="key" ...; otherwise only use that when key is not valid xml
-        return Identifiers.isValidToken(key, VALID_XML_NODE_NAME_START_CHARS, VALID_XML_NODE_NAME_CHARS);
-    }
-    
-    public boolean canConvert(Class type) {
-        return super.canConvert(type) || type.getName().equals(MutableMap.class.getName());
-    }
-    
-    @Override
-    protected void marshalEntry(HierarchicalStreamWriter writer, MarshallingContext context, Entry entry) {
-        if (entry.getKey() instanceof String) {
-            marshalStringKey(writer, context, entry);
-        } else {
-            super.marshalEntry(writer, context, entry);
-        }
-    }
-    
-    protected void marshalStringKey(HierarchicalStreamWriter writer, MarshallingContext context, Entry entry) {
-        String key = (String)entry.getKey();
-        String entryNodeName = getEntryNodeName();
-        boolean useKeyAsNodeName = (!key.equals(entryNodeName) && isKeyValidForNodeName(key));
-        if (useKeyAsNodeName) entryNodeName = key;
-        ExtendedHierarchicalStreamWriterHelper.startNode(writer, entryNodeName, Map.Entry.class);
-        if (!useKeyAsNodeName)
-            writer.addAttribute("key", key);
-        
-        Object value = entry.getValue();
-        if (entry.getValue()!=null && isInlineableType(value.getClass())) {
-            if (!(value instanceof String))
-                writer.addAttribute("type", mapper().serializedClass(entry.getValue().getClass()));
-            if (entry.getValue().getClass().isEnum())
-                writer.setValue(((Enum)entry.getValue()).name());
-            else
-                writer.setValue(""+entry.getValue());
-        } else {
-            writeItem(entry.getValue(), context, writer);
-        }
-        
-        writer.endNode();
-    }
-
-    protected boolean isInlineableType(Class<?> type) {
-        return TypeCoercions.isPrimitiveOrBoxer(type) || String.class.equals(type) || type.isEnum();
-    }
-    
-    @Override
-    protected void unmarshalEntry(HierarchicalStreamReader reader, UnmarshallingContext context, Map map) {
-        String key = reader.getNodeName(); 
-        if (key.equals(getEntryNodeName())) key = reader.getAttribute("key");
-        if (key==null) {
-            super.unmarshalEntry(reader, context, map);
-        } else {
-            unmarshalStringKey(reader, context, map, key);
-        }
-    }
-
-    protected void unmarshalStringKey(HierarchicalStreamReader reader, UnmarshallingContext context, Map map, String key) {
-        String type = reader.getAttribute("type");
-        Object value;
-        if (type==null && reader.hasMoreChildren()) {
-            reader.moveDown();
-            value = readItem(reader, context, map);
-            reader.moveUp();
-        } else {
-            Class typeC = type!=null ? mapper().realClass(type) : String.class;
-            try {
-                value = TypeCoercions.coerce(reader.getValue(), typeC);
-            } catch (Exception e) {
-                log.warn("FAILED to coerce "+reader.getValue()+" to "+typeC+": "+e);
-                throw Exceptions.propagate(e);
-            }
-        }
-        map.put(key, value);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/xstream/XmlSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/xstream/XmlSerializer.java b/core/src/main/java/brooklyn/util/xstream/XmlSerializer.java
deleted file mode 100644
index 7220254..0000000
--- a/core/src/main/java/brooklyn/util/xstream/XmlSerializer.java
+++ /dev/null
@@ -1,97 +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.xstream;
-
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.collections.MutableSet;
-
-import com.google.common.collect.ImmutableList;
-import com.thoughtworks.xstream.XStream;
-import com.thoughtworks.xstream.mapper.MapperWrapper;
-
-public class XmlSerializer<T> {
-
-    protected final XStream xstream;
-    
-    public XmlSerializer() {
-        xstream = new XStream() {
-            @Override
-            protected MapperWrapper wrapMapper(MapperWrapper next) {
-                MapperWrapper result = super.wrapMapper(next);
-                return XmlSerializer.this.wrapMapper(result);
-            }
-        };
-        
-        // list as array list is default
-        xstream.alias("map", Map.class, LinkedHashMap.class);
-        xstream.alias("set", Set.class, LinkedHashSet.class);
-        
-        xstream.registerConverter(new StringKeyMapConverter(xstream.getMapper()), /* priority */ 10);
-        xstream.alias("MutableMap", MutableMap.class);
-        xstream.alias("MutableSet", MutableSet.class);
-        xstream.alias("MutableList", MutableList.class);
-        
-        // Needs an explicit MutableSet converter!
-        // Without it, the alias for "set" seems to interfere with the MutableSet.map field, so it gets
-        // a null field on deserialization.
-        xstream.registerConverter(new MutableSetConverter(xstream.getMapper()));
-        
-        xstream.aliasType("ImmutableList", ImmutableList.class);
-        xstream.registerConverter(new ImmutableListConverter(xstream.getMapper()));
-        xstream.registerConverter(new ImmutableSetConverter(xstream.getMapper()));
-        xstream.registerConverter(new ImmutableMapConverter(xstream.getMapper()));
-
-        xstream.registerConverter(new EnumCaseForgivingConverter());
-        xstream.registerConverter(new Inet4AddressConverter());
-    }
-    
-    protected MapperWrapper wrapMapper(MapperWrapper next) {
-        return new CompilerIndependentOuterClassFieldMapper(next);
-    }
-
-    public void serialize(Object object, Writer writer) {
-        xstream.toXML(object, writer);
-    }
-
-    @SuppressWarnings("unchecked")
-    public T deserialize(Reader xml) {
-        return (T) xstream.fromXML(xml);
-    }
-
-    public String toString(T memento) {
-        Writer writer = new StringWriter();
-        serialize(memento, writer);
-        return writer.toString();
-    }
-
-    public T fromString(String xml) {
-        return deserialize(new StringReader(xml));
-    }
-
-}


[33/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ForwardingTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ForwardingTask.java b/core/src/main/java/brooklyn/util/task/ForwardingTask.java
deleted file mode 100644
index 3bc3427..0000000
--- a/core/src/main/java/brooklyn/util/task/ForwardingTask.java
+++ /dev/null
@@ -1,325 +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 java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.brooklyn.api.management.Task;
-
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ForwardingObject;
-import com.google.common.util.concurrent.ExecutionList;
-import com.google.common.util.concurrent.ListenableFuture;
-
-public abstract class ForwardingTask<T> extends ForwardingObject implements TaskInternal<T> {
-
-    /** Constructor for use by subclasses. */
-    protected ForwardingTask() {}
-
-    @Override
-    protected abstract TaskInternal<T> delegate();
-
-    @Override
-    public void addListener(Runnable listener, Executor executor) {
-        delegate().addListener(listener, executor);
-    }
-
-    @Override
-    public boolean cancel(boolean arg0) {
-        return delegate().cancel(arg0);
-    }
-
-    @Override
-    public T get() throws InterruptedException, ExecutionException {
-        return delegate().get();
-    }
-
-    @Override
-    public T get(long arg0, TimeUnit arg1) throws InterruptedException, ExecutionException, TimeoutException {
-        return delegate().get(arg0, arg1);
-    }
-
-    @Override
-    public boolean isCancelled() {
-        return delegate().isCancelled();
-    }
-
-    @Override
-    public boolean isDone() {
-        return delegate().isDone();
-    }
-
-    @Override
-    public Task<T> asTask() {
-        return delegate().asTask();
-    }
-
-    @Override
-    public String getId() {
-        return delegate().getId();
-    }
-
-    @Override
-    public Set<Object> getTags() {
-        return delegate().getTags();
-    }
-
-    @Override
-    public long getSubmitTimeUtc() {
-        return delegate().getSubmitTimeUtc();
-    }
-
-    @Override
-    public long getStartTimeUtc() {
-        return delegate().getStartTimeUtc();
-    }
-
-    @Override
-    public long getEndTimeUtc() {
-        return delegate().getEndTimeUtc();
-    }
-
-    @Override
-    public String getDisplayName() {
-        return delegate().getDisplayName();
-    }
-
-    @Override
-    public String getDescription() {
-        return delegate().getDescription();
-    }
-
-    @Override
-    public Task<?> getSubmittedByTask() {
-        return delegate().getSubmittedByTask();
-    }
-
-    @Override
-    public Thread getThread() {
-        return delegate().getThread();
-    }
-
-    @Override
-    public boolean isSubmitted() {
-        return delegate().isSubmitted();
-    }
-
-    @Override
-    public boolean isBegun() {
-        return delegate().isBegun();
-    }
-
-    @Override
-    public boolean isError() {
-        return delegate().isError();
-    }
-
-    @Override
-    public void blockUntilStarted() {
-        delegate().blockUntilStarted();
-    }
-
-    @Override
-    public void blockUntilEnded() {
-        delegate().blockUntilEnded();
-    }
-
-    @Override
-    public boolean blockUntilEnded(Duration timeout) {
-        return delegate().blockUntilEnded(timeout);
-    }
-
-    @Override
-    public String getStatusSummary() {
-        return delegate().getStatusSummary();
-    }
-
-    @Override
-    public String getStatusDetail(boolean multiline) {
-        return delegate().getStatusDetail(multiline);
-    }
-
-    @Override
-    public T get(Duration duration) throws InterruptedException, ExecutionException, TimeoutException {
-        return delegate().get(duration);
-    }
-
-    @Override
-    public T getUnchecked() {
-        return delegate().getUnchecked();
-    }
-
-    @Override
-    public T getUnchecked(Duration duration) {
-        return delegate().getUnchecked(duration);
-    }
-
-    @Override
-    public void initInternalFuture(ListenableFuture<T> result) {
-        delegate().initInternalFuture(result);
-    }
-
-    @Override
-    public long getQueuedTimeUtc() {
-        return delegate().getQueuedTimeUtc();
-    }
-
-    @Override
-    public Future<T> getInternalFuture() {
-        return delegate().getInternalFuture();
-    }
-
-    @Override
-    public boolean isQueued() {
-        return delegate().isQueued();
-    }
-
-    @Override
-    public boolean isQueuedOrSubmitted() {
-        return delegate().isQueuedOrSubmitted();
-    }
-
-    @Override
-    public boolean isQueuedAndNotSubmitted() {
-        return delegate().isQueuedAndNotSubmitted();
-    }
-
-    @Override
-    public void markQueued() {
-        delegate().markQueued();
-    }
-
-    @Override
-    public boolean cancel() {
-        return delegate().cancel();
-    }
-
-    @Override
-    public boolean blockUntilStarted(Duration timeout) {
-        return delegate().blockUntilStarted(timeout);
-    }
-
-    @Override
-    public String setBlockingDetails(String blockingDetails) {
-        return delegate().setBlockingDetails(blockingDetails);
-    }
-
-    @Override
-    public Task<?> setBlockingTask(Task<?> blockingTask) {
-        return delegate().setBlockingTask(blockingTask);
-    }
-
-    @Override
-    public void resetBlockingDetails() {
-        delegate().resetBlockingDetails();
-    }
-
-    @Override
-    public void resetBlockingTask() {
-        delegate().resetBlockingTask();
-    }
-
-    @Override
-    public String getBlockingDetails() {
-        return delegate().getBlockingDetails();
-    }
-
-    @Override
-    public Task<?> getBlockingTask() {
-        return delegate().getBlockingTask();
-    }
-
-    @Override
-    public void setExtraStatusText(Object extraStatus) {
-        delegate().setExtraStatusText(extraStatus);
-    }
-
-    @Override
-    public Object getExtraStatusText() {
-        return delegate().getExtraStatusText();
-    }
-
-    @Override
-    public void runListeners() {
-        delegate().runListeners();
-    }
-
-    @Override
-    public void setEndTimeUtc(long val) {
-        delegate().setEndTimeUtc(val);
-    }
-
-    @Override
-    public void setThread(Thread thread) {
-        delegate().setThread(thread);
-    }
-
-    @Override
-    public Callable<T> getJob() {
-        return delegate().getJob();
-    }
-
-    @Override
-    public void setJob(Callable<T> job) {
-        delegate().setJob(job);
-    }
-
-    @Override
-    public ExecutionList getListeners() {
-        return delegate().getListeners();
-    }
-
-    @Override
-    public void setSubmitTimeUtc(long currentTimeMillis) {
-        delegate().setSubmitTimeUtc(currentTimeMillis);
-    }
-
-    @Override
-    public void setSubmittedByTask(Task<?> task) {
-        delegate().setSubmittedByTask(task);
-    }
-
-    @Override
-    public Set<Object> getMutableTags() {
-        return delegate().getMutableTags();
-    }
-
-    @Override
-    public void setStartTimeUtc(long currentTimeMillis) {
-        delegate().setStartTimeUtc(currentTimeMillis);
-    }
-
-    @Override
-    public void applyTagModifier(Function<Set<Object>, Void> modifier) {
-        delegate().applyTagModifier(modifier);
-    }
-    
-    @Override
-    public Task<?> getProxyTarget() {
-        return delegate().getProxyTarget();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ListenableForwardingFuture.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ListenableForwardingFuture.java b/core/src/main/java/brooklyn/util/task/ListenableForwardingFuture.java
deleted file mode 100644
index 8111332..0000000
--- a/core/src/main/java/brooklyn/util/task/ListenableForwardingFuture.java
+++ /dev/null
@@ -1,50 +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 java.util.concurrent.Executor;
-import java.util.concurrent.Future;
-
-import com.google.common.util.concurrent.ExecutionList;
-import com.google.common.util.concurrent.ForwardingFuture.SimpleForwardingFuture;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/** Wraps a Future, making it a ListenableForwardingFuture, but with the caller having the resposibility to:
- * <li> invoke the listeners on job completion (success or error)
- * <li> invoke the listeners on cancel */
-public abstract class ListenableForwardingFuture<T> extends SimpleForwardingFuture<T> implements ListenableFuture<T> {
-
-    final ExecutionList listeners;
-    
-    protected ListenableForwardingFuture(Future<T> delegate) {
-        super(delegate);
-        this.listeners = new ExecutionList();
-    }
-
-    protected ListenableForwardingFuture(Future<T> delegate, ExecutionList list) {
-        super(delegate);
-        this.listeners = list;
-    }
-
-    @Override
-    public void addListener(Runnable listener, Executor executor) {
-        listeners.add(listener, executor);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ParallelTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ParallelTask.java b/core/src/main/java/brooklyn/util/task/ParallelTask.java
deleted file mode 100644
index d6e65ab..0000000
--- a/core/src/main/java/brooklyn/util/task/ParallelTask.java
+++ /dev/null
@@ -1,85 +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 java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-
-import org.apache.brooklyn.api.management.Task;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.text.Strings;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-/**
- * Runs {@link Task}s in parallel.
- *
- * No guarantees of order of starting the tasks, but the return value is a
- * {@link List} of the return values of supplied tasks in the same
- * order they were passed as arguments.
- */
-public class ParallelTask<T> extends CompoundTask<T> {
-    public ParallelTask(Object... tasks) { super(tasks); }
-    
-    public ParallelTask(Map<String,?> flags, Collection<? extends Object> tasks) { super(flags, tasks); }
-    public ParallelTask(Collection<? extends Object> tasks) { super(tasks); }
-    
-    public ParallelTask(Map<String,?> flags, Iterable<? extends Object> tasks) { super(flags, ImmutableList.copyOf(tasks)); }
-    public ParallelTask(Iterable<? extends Object> tasks) { super(ImmutableList.copyOf(tasks)); }
-
-    @Override
-    protected List<T> runJobs() throws InterruptedException, ExecutionException {
-        setBlockingDetails("Executing "+
-                (children.size()==1 ? "1 child task" :
-                children.size()+" children tasks in parallel") );
-        for (Task<? extends T> task : children) {
-            submitIfNecessary(task);
-        }
-
-        List<T> result = Lists.newArrayList();
-        List<Exception> exceptions = Lists.newArrayList();
-        for (Task<? extends T> task : children) {
-            T x;
-            try {
-                x = task.get();
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                if (TaskTags.isInessential(task)) {
-                    // ignore exception as it's inessential
-                } else {
-                    exceptions.add(e);
-                }
-                x = null;
-            }
-            result.add(x);
-        }
-        
-        if (exceptions.isEmpty()) {
-            return result;
-        } else {
-            if (result.size()==1 && exceptions.size()==1)
-                throw Exceptions.propagate( exceptions.get(0) );
-            throw Exceptions.propagate(exceptions.size()+" of "+result.size()+" parallel child task"+Strings.s(result.size())+" failed", exceptions);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ScheduledTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ScheduledTask.java b/core/src/main/java/brooklyn/util/task/ScheduledTask.java
deleted file mode 100644
index eabff49..0000000
--- a/core/src/main/java/brooklyn/util/task/ScheduledTask.java
+++ /dev/null
@@ -1,185 +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.util.GroovyJavaMethods.elvis;
-import static brooklyn.util.GroovyJavaMethods.truth;
-
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.brooklyn.api.management.Task;
-
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.time.Duration;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Throwables;
-
-/**
- * A task which runs with a fixed period.
- * <p>
- * Note that some termination logic, including {@link #addListener(Runnable, java.util.concurrent.Executor)},
- * is not precisely defined. 
- */
-// TODO ScheduledTask is a very pragmatic implementation; would be nice to tighten, 
-// reduce external assumptions about internal structure, and clarify "done" semantics
-public class ScheduledTask extends BasicTask {
-    
-    final Callable<Task<?>> taskFactory;
-    /** initial delay before running, set as flag in constructor; defaults to 0 */
-    protected Duration delay;
-    /** time to wait between executions, or null if not to repeat (default), set as flag to constructor;
-     * this may be modified for subsequent submissions by a running task generated by the factory 
-     * using getSubmittedByTask().setPeriod(Duration) */
-    protected Duration period = null;
-    /** optional, set as flag in constructor; defaults to null meaning no limit */
-    protected Integer maxIterations = null;
-    
-    protected int runCount=0;
-    protected Task<?> recentRun, nextRun;
-
-    public int getRunCount() { return runCount; }
-    public ScheduledFuture<?> getNextScheduled() { return (ScheduledFuture<?>)internalFuture; }
-
-    public ScheduledTask(Callable<Task<?>> taskFactory) {
-        this(MutableMap.of(), taskFactory);
-    }
-
-    public ScheduledTask(final Task<?> task) {
-        this(MutableMap.of(), task);
-    }
-
-    public ScheduledTask(Map flags, final Task<?> task){
-        this(flags, new Callable<Task<?>>(){
-            @Override
-            public Task<?> call() throws Exception {
-                return task;
-            }});
-    }
-
-    public ScheduledTask(Map flags, Callable<Task<?>> taskFactory) {
-        super(flags);
-        this.taskFactory = taskFactory;
-        
-        delay = Duration.of(elvis(flags.remove("delay"), 0));
-        period = Duration.of(elvis(flags.remove("period"), null));
-        maxIterations = elvis(flags.remove("maxIterations"), null);
-    }
-    
-    public ScheduledTask delay(Duration d) {
-        this.delay = d;
-        return this;
-    }
-    public ScheduledTask delay(long val) {
-        return delay(Duration.millis(val));
-    }
-
-    public ScheduledTask period(Duration d) {
-        this.period = d;
-        return this;
-    }
-    public ScheduledTask period(long val) {
-        return period(Duration.millis(val));
-    }
-
-    public ScheduledTask maxIterations(int val) {
-        this.maxIterations = val;
-        return this;
-    }
-
-    public Callable<Task<?>> getTaskFactory() {
-        return taskFactory;
-    }
-
-    public Task<?> newTask() {
-        try {
-            return taskFactory.call();
-        } catch (Exception e) {
-            throw Throwables.propagate(e);
-        }
-    }
-    
-    protected String getActiveTaskStatusString(int verbosity) {
-        StringBuilder rv = new StringBuilder("Scheduler");
-        if (runCount>0) rv.append(", iteration "+(runCount+1));
-        if (recentRun!=null) rv.append(", last run "+
-            Duration.sinceUtc(recentRun.getStartTimeUtc())+" ms ago");
-        if (truth(getNextScheduled())) {
-            Duration untilNext = Duration.millis(getNextScheduled().getDelay(TimeUnit.MILLISECONDS));
-            if (untilNext.isPositive())
-                rv.append(", next in "+untilNext);
-            else 
-                rv.append(", next imminent");
-        }
-        return rv.toString();
-    }
-    
-    @Override
-    public boolean isDone() {
-        return isCancelled() || (maxIterations!=null && maxIterations <= runCount) || (period==null && nextRun!=null && nextRun.isDone());
-    }
-    
-    public synchronized void blockUntilFirstScheduleStarted() {
-        // TODO Assumes that maxIterations is not negative!
-        while (true) {
-            if (isCancelled()) throw new CancellationException();
-            if (recentRun==null)
-                try {
-                    wait();
-                } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt();
-                    Throwables.propagate(e);
-                }
-            if (recentRun!=null) return;
-        }
-    }
-    
-    public void blockUntilEnded() {
-        while (!isDone()) super.blockUntilEnded();
-    }
-
-    /** gets the value of the most recently run task */
-    public Object get() throws InterruptedException, ExecutionException {
-        blockUntilStarted();
-        blockUntilFirstScheduleStarted();
-        return (truth(recentRun)) ? recentRun.get() : internalFuture.get();
-    }
-    
-    @Override
-    public synchronized boolean cancel(boolean mayInterrupt) {
-        boolean result = super.cancel(mayInterrupt);
-        if (nextRun!=null) {
-            nextRun.cancel(mayInterrupt);
-            notifyAll();
-        }
-        return result;
-    }
-    
-    /** internal method used to allow callers to wait for underlying tasks to finished in the case of cancellation 
-     * @param duration */ 
-    @Beta
-    public boolean blockUntilNextRunFinished(Duration timeout) {
-        return Tasks.blockUntilInternalTasksEnded(nextRun, timeout);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/SequentialTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/SequentialTask.java b/core/src/main/java/brooklyn/util/task/SequentialTask.java
deleted file mode 100644
index e739eb0..0000000
--- a/core/src/main/java/brooklyn/util/task/SequentialTask.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-
-import org.apache.brooklyn.api.management.Task;
-
-import com.google.common.collect.ImmutableList;
-
-
-/** runs tasks in order, waiting for one to finish before starting the next; return value here is TBD;
- * (currently is all the return values of individual tasks, but we
- * might want some pipeline support and eventually only to return final value...) */
-public class SequentialTask<T> extends CompoundTask<T> {
-
-    public SequentialTask(Object... tasks) { super(tasks); }
-    
-    public SequentialTask(Map<String,?> flags, Collection<? extends Object> tasks) { super(flags, tasks); }
-    public SequentialTask(Collection<? extends Object> tasks) { super(tasks); }
-    
-    public SequentialTask(Map<String,?> flags, Iterable<? extends Object> tasks) { super(flags, ImmutableList.copyOf(tasks)); }
-    public SequentialTask(Iterable<? extends Object> tasks) { super(ImmutableList.copyOf(tasks)); }
-    
-    protected List<T> runJobs() throws InterruptedException, ExecutionException {
-        setBlockingDetails("Executing "+
-                (children.size()==1 ? "1 child task" :
-                children.size()+" children tasks sequentially") );
-
-        List<T> result = new ArrayList<T>();
-        for (Task<? extends T> task : children) {
-            submitIfNecessary(task);
-            // throw exception (and cancel subsequent tasks) on error
-            result.add(task.get());
-        }
-        return result;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/SingleThreadedScheduler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/SingleThreadedScheduler.java b/core/src/main/java/brooklyn/util/task/SingleThreadedScheduler.java
deleted file mode 100644
index a48bac8..0000000
--- a/core/src/main/java/brooklyn/util/task/SingleThreadedScheduler.java
+++ /dev/null
@@ -1,216 +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 java.util.Queue;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.brooklyn.api.management.Task;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Instances of this class ensures that {@link Task}s execute with in-order
- * single-threaded semantics.
- *
- * Tasks can be presented through {@link #submit(Callable)}. The order of execution is the
- * sumbission order.
- * <p>
- * This implementation does so by blocking on a {@link ConcurrentLinkedQueue}, <em>after</em>
- * the task is started in a thread (and {@link Task#isBegun()} returns true), but (of course)
- * <em>before</em> the {@link TaskInternal#getJob()} actually gets invoked.
- */
-public class SingleThreadedScheduler implements TaskScheduler, CanSetName {
-    private static final Logger LOG = LoggerFactory.getLogger(SingleThreadedScheduler.class);
-    
-    private final Queue<QueuedSubmission<?>> order = new ConcurrentLinkedQueue<QueuedSubmission<?>>();
-    private int queueSize = 0;
-    private final AtomicBoolean running = new AtomicBoolean(false);
-    
-    private ExecutorService executor;
-
-    private String name;
-    
-    @Override
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    @Override
-    public String toString() {
-        return name!=null ? "SingleThreadedScheduler["+name+"]" : super.toString();
-    }
-    
-    @Override
-    public void injectExecutor(ExecutorService executor) {
-        this.executor = executor;
-    }
-
-    @Override
-    public synchronized <T> Future<T> submit(Callable<T> c) {
-        if (running.compareAndSet(false, true)) {
-            return executeNow(c);
-        } else {
-            WrappingFuture<T> f = new WrappingFuture<T>();
-            order.add(new QueuedSubmission<T>(c, f));
-            queueSize++;
-            if (queueSize>0 && (queueSize == 50 || (queueSize<=500 && (queueSize%100)==0) || (queueSize%1000)==0) && queueSize!=lastSizeWarn) {
-                LOG.warn("{} is backing up, {} tasks queued", this, queueSize);
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("Task queue backing up detail, queue "+this+"; task context is "+Tasks.current()+"; latest task is "+c+"; first task is "+order.peek());
-                }
-                lastSizeWarn = queueSize;
-            }
-            return f;
-        }
-    }
-    int lastSizeWarn = 0;
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private synchronized void onEnd() {
-        boolean done = false;
-        while (!done) {
-            if (order.isEmpty()) {
-                running.set(false);
-                done = true;
-            } else {
-                QueuedSubmission<?> qs = order.remove();
-                queueSize--;
-                if (!qs.f.isCancelled()) {
-                    Future future = executeNow(qs.c);
-                    qs.f.setDelegate(future);
-                    done = true;
-                }
-            }
-        }
-    }
-
-    private synchronized <T> Future<T> executeNow(final Callable<T> c) {
-        return executor.submit(new Callable<T>() {
-            @Override public T call() throws Exception {
-                try {
-                    return c.call();
-                } finally {
-                    onEnd();
-                }
-            }});
-    }
-    
-    
-    private static class QueuedSubmission<T> {
-        final Callable<T> c;
-        final WrappingFuture<T> f;
-        
-        QueuedSubmission(Callable<T> c, WrappingFuture<T> f) {
-            this.c = c;
-            this.f = f;
-        }
-        
-        @Override
-        public String toString() {
-            return "QueuedSubmission["+c+"]@"+Integer.toHexString(System.identityHashCode(this));
-        }
-    }
-    
-    /**
-     * A future, where the task may not yet have been submitted to the real executor.
-     * It delegates to the real future if present, and otherwise waits for that to appear
-     */
-    private static class WrappingFuture<T> implements Future<T> {
-        private volatile Future<T> delegate;
-        private boolean cancelled;
-        
-        void setDelegate(Future<T> delegate) {
-            synchronized (this) {
-                this.delegate = delegate;
-                notifyAll();
-            }
-        }
-        
-        @Override public boolean cancel(boolean mayInterruptIfRunning) {
-            if (delegate != null) {
-                return delegate.cancel(mayInterruptIfRunning);
-            } else {
-                cancelled = true;
-                synchronized (this) {
-                    notifyAll();
-                }
-                return true;
-            }
-        }
-        
-        @Override public boolean isCancelled() {
-            if (delegate != null) {
-                return delegate.isCancelled();
-            } else {
-                return cancelled;
-            }
-        }
-        
-        @Override public boolean isDone() {
-            return (delegate != null) ? delegate.isDone() : cancelled;
-        }
-        
-        @Override public T get() throws CancellationException, ExecutionException, InterruptedException {
-            if (cancelled) {
-                throw new CancellationException();
-            } else if (delegate != null) {
-                return delegate.get();
-            } else {
-                synchronized (this) {
-                    while (delegate == null && !cancelled) {
-                        wait();
-                    }
-                }
-                return get();
-            }
-        }
-        
-        @Override public T get(long timeout, TimeUnit unit) throws CancellationException, ExecutionException, InterruptedException, TimeoutException {
-            long endtime = System.currentTimeMillis()+unit.toMillis(timeout);
-            
-            if (cancelled) {
-                throw new CancellationException();
-            } else if (delegate != null) {
-                return delegate.get(timeout, unit);
-            } else if (System.currentTimeMillis() >= endtime) {
-                throw new TimeoutException();
-            } else {
-                synchronized (this) {
-                    while (delegate == null && !cancelled && System.currentTimeMillis() < endtime) {
-                        long remaining = endtime - System.currentTimeMillis();
-                        if (remaining > 0) {
-                            wait(remaining);
-                        }
-                    }
-                }
-                long remaining = endtime - System.currentTimeMillis();
-                return get(remaining, TimeUnit.MILLISECONDS);
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/TaskBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/TaskBuilder.java b/core/src/main/java/brooklyn/util/task/TaskBuilder.java
deleted file mode 100644
index ecd4d4f..0000000
--- a/core/src/main/java/brooklyn/util/task/TaskBuilder.java
+++ /dev/null
@@ -1,184 +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 java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskAdaptable;
-import org.apache.brooklyn.api.management.TaskFactory;
-import org.apache.brooklyn.api.management.TaskQueueingContext;
-
-import brooklyn.util.JavaGroovyEquivalents;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.collections.MutableSet;
-
-import com.google.common.collect.Iterables;
-
-/** Convenience for creating tasks; note that DynamicSequentialTask is the default */
-public class TaskBuilder<T> {
-
-    String name = null;
-    String description = null;
-    Callable<T> body = null;
-    Boolean swallowChildrenFailures = null;
-    List<TaskAdaptable<?>> children = MutableList.of();
-    Set<Object> tags = MutableSet.of();
-    Map<String,Object> flags = MutableMap.of();
-    Boolean dynamic = null;
-    boolean parallel = false;
-    
-    public static <T> TaskBuilder<T> builder() {
-        return new TaskBuilder<T>();
-    }
-    
-    public TaskBuilder<T> name(String name) {
-        this.name = name;
-        return this;
-    }
-    
-    public TaskBuilder<T> description(String description) {
-        this.description = description;
-        return this;
-    }
-    
-    /** whether task that is built has been explicitly specified to be a dynamic task 
-     * (ie a Task which is also a {@link TaskQueueingContext}
-     * whereby new tasks can be added after creation */
-    public TaskBuilder<T> dynamic(boolean dynamic) {
-        this.dynamic = dynamic;
-        return this;
-    }
-    
-    /** whether task that is built should be parallel; cannot (currently) also be dynamic */
-    public TaskBuilder<T> parallel(boolean parallel) {
-        this.parallel = parallel;
-        return this;
-    }
-    
-    public TaskBuilder<T> body(Callable<T> body) {
-        this.body = body;
-        return this;
-    }
-    
-    /** sets up a dynamic task not to fail even if children fail */
-    public TaskBuilder<T> swallowChildrenFailures(boolean swallowChildrenFailures) {
-        this.swallowChildrenFailures = swallowChildrenFailures;
-        return this;
-    }
-    
-    public TaskBuilder<T> body(Runnable body) {
-        this.body = JavaGroovyEquivalents.<T>toCallable(body);
-        return this;
-    }
-
-    /** adds a child to the given task; the semantics of how the child is executed is set using
-     * {@link #dynamic(boolean)} and {@link #parallel(boolean)} */
-    public TaskBuilder<T> add(TaskAdaptable<?> child) {
-        children.add(child);
-        return this;
-    }
-
-    public TaskBuilder<T> addAll(Iterable<? extends TaskAdaptable<?>> additionalChildren) {
-        Iterables.addAll(children, additionalChildren);
-        return this;
-    }
-
-    public TaskBuilder<T> add(TaskAdaptable<?>... additionalChildren) {
-        children.addAll(Arrays.asList(additionalChildren));
-        return this;
-    }
-
-    /** adds a tag to the given task */
-    public TaskBuilder<T> tag(Object tag) {
-        tags.add(tag);
-        return this;
-    }
-    
-    /** adds a flag to the given task */
-    public TaskBuilder<T> flag(String flag, Object value) {
-        flags.put(flag, value);
-        return this;
-    }
-
-    /** adds the given flags to the given task */
-    public TaskBuilder<T> flags(Map<String,Object> flags) {
-        this.flags.putAll(flags);
-        return this;
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public Task<T> build() {
-        MutableMap<String, Object> taskFlags = MutableMap.copyOf(flags);
-        if (name!=null) taskFlags.put("displayName", name);
-        if (description!=null) taskFlags.put("description", description);
-        if (!tags.isEmpty()) taskFlags.put("tags", tags);
-        
-        if (Boolean.FALSE.equals(dynamic) && children.isEmpty()) {
-            if (swallowChildrenFailures!=null)
-                throw new IllegalArgumentException("Cannot set swallowChildrenFailures for non-dynamic task: "+this);
-            return new BasicTask<T>(taskFlags, body);
-        }
-        
-        // prefer dynamic set unless (a) user has said not dynamic, or (b) it's parallel (since there is no dynamic parallel yet)
-        // dynamic has better cancel (will interrupt the thread) and callers can submit tasks flexibly;
-        // however dynamic uses an extra thread and task and is noisy for contexts which don't need it
-        if (Boolean.TRUE.equals(dynamic) || (dynamic==null && !parallel)) {
-            if (parallel)
-                throw new UnsupportedOperationException("No implementation of parallel dynamic aggregate task available");
-            DynamicSequentialTask<T> result = new DynamicSequentialTask<T>(taskFlags, body);
-            if (swallowChildrenFailures!=null && swallowChildrenFailures.booleanValue()) result.swallowChildrenFailures();
-            for (TaskAdaptable t: children)
-                result.queue(t.asTask());
-            return result;
-        }
-        
-        // T must be of type List<V> for these to be valid
-        if (body != null) {
-            throw new UnsupportedOperationException("No implementation of non-dynamic task with both body and children");
-        }
-        if (swallowChildrenFailures!=null) {
-            throw new IllegalArgumentException("Cannot set swallowChildrenFailures for non-dynamic task: "+this);
-        }
-        
-        if (parallel)
-            return new ParallelTask(taskFlags, children);
-        else
-            return new SequentialTask(taskFlags, children);
-    }
-
-    /** returns a a factory based on this builder */
-    public TaskFactory<Task<T>> buildFactory() {
-        return new TaskFactory<Task<T>>() {
-            public Task<T> newTask() {
-                return build();
-            }
-        };
-    }
-    
-    @Override
-    public String toString() {
-        return super.toString()+"["+name+"]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/TaskInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/TaskInternal.java b/core/src/main/java/brooklyn/util/task/TaskInternal.java
deleted file mode 100644
index 51dbddb..0000000
--- a/core/src/main/java/brooklyn/util/task/TaskInternal.java
+++ /dev/null
@@ -1,125 +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 java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-import org.apache.brooklyn.api.management.ExecutionManager;
-import org.apache.brooklyn.api.management.Task;
-
-import brooklyn.util.time.Duration;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Function;
-import com.google.common.util.concurrent.ExecutionList;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * All tasks being passed to the {@link ExecutionManager} should implement this.
- * Users are strongly encouraged to use (or extend) {@link BasicTask}, rather than
- * implementing a task from scratch.
- * 
- * The methods on this interface will change in subsequent releases. Because this is
- * marked as beta, the normal deprecation policy for these methods does not apply.
- * 
- * @author aled
- */
-@Beta
-public interface TaskInternal<T> extends Task<T> {
-    
-    /** sets the internal future object used to record the association to a job submitted to an {@link ExecutorService} */
-    void initInternalFuture(ListenableFuture<T> result);
-
-    /** returns the underlying future where this task's results will come in; see {@link #initInternalFuture(ListenableFuture)} */
-    Future<T> getInternalFuture();
-    
-    /** if the job is queued for submission (e.g. by another task) it can indicate that fact (and time) here;
-     * note tasks can (and often are) submitted without any queueing, in which case this value may be -1 */
-    long getQueuedTimeUtc();
-    
-    boolean isQueuedOrSubmitted();
-    boolean isQueuedAndNotSubmitted();
-    boolean isQueued();
-
-    /** marks the task as queued for execution */
-    void markQueued();
-
-    boolean cancel();
-    
-    boolean blockUntilStarted(Duration timeout);
-
-    /** allows a task user to specify why a task is blocked; for use immediately before a blocking/wait,
-     * and typically cleared immediately afterwards; referenced by management api to inspect a task
-     * which is blocking
-     * <p>
-     * returns previous details, in case caller wishes to recall and restore it (e.g. if it is doing a sub-blocking)
-     */
-    String setBlockingDetails(String blockingDetails);
-
-    /** as {@link #setBlockingDetails(String)} but records a task which is blocking,
-     * for use e.g. in a gui to navigate to the current active subtask
-     * <p>
-     * returns previous blocking task, in case caller wishes to recall and restore it
-     */
-    Task<?> setBlockingTask(Task<?> blockingTask);
-    
-    void resetBlockingDetails();
-    
-    void resetBlockingTask();
-
-    /** returns a textual message giving details while the task is blocked */
-    String getBlockingDetails();
-    
-    /** returns a task that this task is blocked on */
-    Task<?> getBlockingTask();
-    
-    void setExtraStatusText(Object extraStatus);
-    
-    Object getExtraStatusText();
-
-    void runListeners();
-
-    void setEndTimeUtc(long val);
-
-    void setThread(Thread thread);
-
-    Callable<T> getJob();
-    
-    void setJob(Callable<T> job);
-
-    ExecutionList getListeners();
-
-    void setSubmitTimeUtc(long currentTimeMillis);
-
-    void setSubmittedByTask(Task<?> task);
-    
-    Set<Object> getMutableTags();
-
-    void setStartTimeUtc(long currentTimeMillis);
-
-    void applyTagModifier(Function<Set<Object>,Void> modifier);
-    
-    /** if a task is a proxy for another one (used mainly for internal tasks),
-     * this returns the "real" task represented by this one */
-    Task<?> getProxyTarget();
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/TaskScheduler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/TaskScheduler.java b/core/src/main/java/brooklyn/util/task/TaskScheduler.java
deleted file mode 100644
index a10e63a..0000000
--- a/core/src/main/java/brooklyn/util/task/TaskScheduler.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-import org.apache.brooklyn.api.management.Task;
-
-/**
- * The scheduler is an internal mechanism to decorate {@link Task}s.
- *
- * It can control how the tasks are scheduled for execution (e.g. single-threaded execution,
- * prioritised, etc).
- */
-public interface TaskScheduler {
-    
-    public void injectExecutor(ExecutorService executor);
-
-    /**
-     * Called by {@link BasicExecutionManager} to schedule tasks.
-     */
-    public <T> Future<T> submit(Callable<T> c);
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/TaskTags.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/TaskTags.java b/core/src/main/java/brooklyn/util/task/TaskTags.java
deleted file mode 100644
index a9da252..0000000
--- a/core/src/main/java/brooklyn/util/task/TaskTags.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task;
-
-import java.util.Set;
-
-import javax.annotation.Nullable;
-
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskAdaptable;
-
-import com.google.common.base.Function;
-
-public class TaskTags {
-
-    /** marks a task which is allowed to fail without failing his parent */
-    public static final String INESSENTIAL_TASK = "inessential";
-
-    /** marks a task which is a subtask of another */
-    public static final String SUB_TASK_TAG = "SUB-TASK";
-
-    public static void addTagDynamically(TaskAdaptable<?> task, final Object tag) {
-        ((BasicTask<?>)task.asTask()).applyTagModifier(new Function<Set<Object>, Void>() {
-            public Void apply(@Nullable Set<Object> input) {
-                input.add(tag);
-                return null;
-            }
-        });
-    }
-    
-    public static void addTagsDynamically(TaskAdaptable<?> task, final Object tag1, final Object ...tags) {
-        ((BasicTask<?>)task.asTask()).applyTagModifier(new Function<Set<Object>, Void>() {
-            public Void apply(@Nullable Set<Object> input) {
-                input.add(tag1);
-                for (Object tag: tags) input.add(tag);
-                return null;
-            }
-        });
-    }
-
-    
-    public static boolean isInessential(Task<?> task) {
-        return hasTag(task, INESSENTIAL_TASK);
-    }
-
-    public static boolean hasTag(Task<?> task, Object tag) {
-        return task.getTags().contains(tag);
-    }
-    
-    public static <U,V extends TaskAdaptable<U>> V markInessential(V task) {
-        addTagDynamically(task, INESSENTIAL_TASK);
-        return task;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/Tasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/Tasks.java b/core/src/main/java/brooklyn/util/task/Tasks.java
deleted file mode 100644
index c25dd19..0000000
--- a/core/src/main/java/brooklyn/util/task/Tasks.java
+++ /dev/null
@@ -1,488 +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 java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
-
-import javax.annotation.Nullable;
-
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.HasTaskChildren;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskAdaptable;
-import org.apache.brooklyn.api.management.TaskFactory;
-import org.apache.brooklyn.api.management.TaskQueueingContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.exceptions.ReferenceWithError;
-import brooklyn.util.repeat.Repeater;
-import brooklyn.util.time.CountdownTimer;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Supplier;
-import com.google.common.collect.Iterables;
-
-public class Tasks {
-    
-    private static final Logger log = LoggerFactory.getLogger(Tasks.class);
-    
-    /** convenience for setting "blocking details" on any task where the current thread is running;
-     * typically invoked prior to a wait, for transparency to a user;
-     * then invoked with 'null' just after the wait */
-    public static String setBlockingDetails(String description) {
-        Task<?> current = current();
-        if (current instanceof TaskInternal)
-            return ((TaskInternal<?>)current).setBlockingDetails(description);
-        return null;
-    }
-    public static void resetBlockingDetails() {
-        Task<?> current = current();
-        if (current instanceof TaskInternal)
-            ((TaskInternal<?>)current).resetBlockingDetails(); 
-    }
-    public static Task<?> setBlockingTask(Task<?> blocker) {
-        Task<?> current = current();
-        if (current instanceof TaskInternal)
-            return ((TaskInternal<?>)current).setBlockingTask(blocker);
-        return null;
-    }
-    public static void resetBlockingTask() {
-        Task<?> current = current();
-        if (current instanceof TaskInternal)
-            ((TaskInternal<?>)current).resetBlockingTask(); 
-    }
-    
-    /** convenience for setting "blocking details" on any task where the current thread is running,
-     * while the passed code is executed; often used from groovy as
-     * <pre>{@code withBlockingDetails("sleeping 5s") { Thread.sleep(5000); } }</pre>
-     * If code block is null, the description is set until further notice (not cleareed). */
-    @SuppressWarnings("rawtypes")
-    public static <T> T withBlockingDetails(String description, Callable<T> code) throws Exception {
-        Task current = current();
-        if (code==null) {
-            log.warn("legacy invocation of withBlockingDetails with null code block, ignoring");
-            return null;
-        }
-        String prevBlockingDetails = null;
-        if (current instanceof TaskInternal) {
-            prevBlockingDetails = ((TaskInternal)current).setBlockingDetails(description);
-        } 
-        try {
-            return code.call();
-        } finally {
-            if (current instanceof TaskInternal)
-                ((TaskInternal)current).setBlockingDetails(prevBlockingDetails); 
-        }
-    }
-
-    /** the {@link Task} where the current thread is executing, if executing in a Task, otherwise null;
-     * if the current task is a proxy, this returns the target of that proxy */
-    @SuppressWarnings("rawtypes")
-    public static Task current() { 
-        return getFinalProxyTarget(BasicExecutionManager.getPerThreadCurrentTask().get());
-    }
-
-    public static Task<?> getFinalProxyTarget(Task<?> task) {
-        if (task==null) return null;
-        Task<?> proxy = ((TaskInternal<?>)task).getProxyTarget();
-        if (proxy==null || proxy.equals(task)) return task;
-        return getFinalProxyTarget(proxy);
-    }
-    
-    /** creates a {@link ValueResolver} instance which allows significantly more customization than
-     * the various {@link #resolveValue(Object, Class, ExecutionContext)} methods here */
-    public static <T> ValueResolver<T> resolving(Object v, Class<T> type) {
-        return new ValueResolver<T>(v, type);
-    }
-
-    public static ValueResolver.ResolverBuilderPretype resolving(Object v) {
-        return new ValueResolver.ResolverBuilderPretype(v);
-    }
-
-    /** @see #resolveValue(Object, Class, ExecutionContext, String) */
-    public static <T> T resolveValue(Object v, Class<T> type, @Nullable ExecutionContext exec) throws ExecutionException, InterruptedException {
-        return new ValueResolver<T>(v, type).context(exec).get();
-    }
-    
-    /** attempt to resolve the given value as the given type, waiting on futures, submitting if necessary,
-     * and coercing as allowed by TypeCoercions;
-     * contextMessage (optional) will be displayed in status reports while it waits (e.g. the name of the config key being looked up).
-     * if no execution context supplied (null) this method will throw an exception if the object is an unsubmitted task */
-    public static <T> T resolveValue(Object v, Class<T> type, @Nullable ExecutionContext exec, String contextMessage) throws ExecutionException, InterruptedException {
-        return new ValueResolver<T>(v, type).context(exec).description(contextMessage).get();
-    }
-    
-    /**
-     * @see #resolveDeepValue(Object, Class, ExecutionContext, String)
-     */
-    public static Object resolveDeepValue(Object v, Class<?> type, ExecutionContext exec) throws ExecutionException, InterruptedException {
-        return resolveDeepValue(v, type, exec, null);
-    }
-
-    /**
-     * Resolves the given object, blocking on futures and coercing it to the given type. If the object is a 
-     * map or iterable (or a list of map of maps, etc, etc) then walks these maps/iterables to convert all of 
-     * their values to the given type. For example, the following will return a list containing a map with "1"="true":
-     * 
-     *   {@code Object result = resolveDeepValue(ImmutableList.of(ImmutableMap.of(1, true)), String.class, exec)} 
-     *
-     * To perform a deep conversion of futures contained within Iterables or Maps without coercion of each element,
-     * the type should normally be Object, not the type of the collection. This differs from
-     * {@link #resolveValue(Object, Class, ExecutionContext, String)} which will accept Map and Iterable
-     * as the required type.
-     */
-    public static <T> T resolveDeepValue(Object v, Class<T> type, ExecutionContext exec, String contextMessage) throws ExecutionException, InterruptedException {
-        return new ValueResolver<T>(v, type).context(exec).deep(true).description(contextMessage).get();
-    }
-
-    /** sets extra status details on the current task, if possible (otherwise does nothing).
-     * the extra status is presented in Task.getStatusDetails(true)
-     */
-    public static void setExtraStatusDetails(String notes) {
-        Task<?> current = current();
-        if (current instanceof TaskInternal)
-            ((TaskInternal<?>)current).setExtraStatusText(notes); 
-    }
-
-    public static <T> TaskBuilder<T> builder() {
-        return TaskBuilder.<T>builder();
-    }
-    
-    private static Task<?>[] asTasks(TaskAdaptable<?> ...tasks) {
-        Task<?>[] result = new Task<?>[tasks.length];
-        for (int i=0; i<tasks.length; i++)
-            result[i] = tasks[i].asTask();
-        return result;
-    }
-
-    public static Task<List<?>> parallel(TaskAdaptable<?> ...tasks) {
-        return parallelInternal("parallelised tasks", asTasks(tasks));
-    }
-    public static Task<List<?>> parallel(String name, TaskAdaptable<?> ...tasks) {
-        return parallelInternal(name, asTasks(tasks));
-    }
-    public static Task<List<?>> parallel(Iterable<? extends TaskAdaptable<?>> tasks) {
-        return parallel(asTasks(Iterables.toArray(tasks, TaskAdaptable.class)));
-    }
-    public static Task<List<?>> parallel(String name, Iterable<? extends TaskAdaptable<?>> tasks) {
-        return parallelInternal(name, asTasks(Iterables.toArray(tasks, TaskAdaptable.class)));
-    }
-    private static Task<List<?>> parallelInternal(String name, Task<?>[] tasks) {
-        return Tasks.<List<?>>builder().name(name).parallel(true).add(tasks).build();
-    }
-
-    public static Task<List<?>> sequential(TaskAdaptable<?> ...tasks) {
-        return sequentialInternal("sequential tasks", asTasks(tasks));
-    }
-    public static Task<List<?>> sequential(String name, TaskAdaptable<?> ...tasks) {
-        return sequentialInternal(name, asTasks(tasks));
-    }
-    public static TaskFactory<?> sequential(TaskFactory<?> ...taskFactories) {
-        return sequentialInternal("sequential tasks", taskFactories);
-    }
-    public static TaskFactory<?> sequential(String name, TaskFactory<?> ...taskFactories) {
-        return sequentialInternal(name, taskFactories);
-    }
-    public static Task<List<?>> sequential(List<? extends TaskAdaptable<?>> tasks) {
-        return sequential(asTasks(Iterables.toArray(tasks, TaskAdaptable.class)));
-    }
-    public static Task<List<?>> sequential(String name, List<? extends TaskAdaptable<?>> tasks) {
-        return sequential(name, asTasks(Iterables.toArray(tasks, TaskAdaptable.class)));
-    }
-    private static Task<List<?>> sequentialInternal(String name, Task<?>[] tasks) {
-        return Tasks.<List<?>>builder().name(name).parallel(false).add(tasks).build();
-    }
-    private static TaskFactory<?> sequentialInternal(final String name, final TaskFactory<?> ...taskFactories) {
-        return new TaskFactory<TaskAdaptable<?>>() {
-            @Override
-            public TaskAdaptable<?> newTask() {
-                TaskBuilder<List<?>> tb = Tasks.<List<?>>builder().name(name).parallel(false);
-                for (TaskFactory<?> tf: taskFactories)
-                    tb.add(tf.newTask().asTask());
-                return tb.build();
-            }
-        };
-    }
-
-    /** returns the first tag found on the given task which matches the given type, looking up the submission hierarachy if necessary */
-    @SuppressWarnings("unchecked")
-    public static <T> T tag(@Nullable Task<?> task, Class<T> type, boolean recurseHierarchy) {
-        // support null task to make it easier for callers to walk hierarchies
-        if (task==null) return null;
-        for (Object tag: task.getTags())
-            if (type.isInstance(tag)) return (T)tag;
-        if (!recurseHierarchy) return null;
-        return tag(task.getSubmittedByTask(), type, true);
-    }
-    
-    public static boolean isAncestorCancelled(Task<?> t) {
-        if (t==null) return false;
-        if (t.isCancelled()) return true;
-        return isAncestorCancelled(t.getSubmittedByTask());
-    }
-
-    public static boolean isQueued(TaskAdaptable<?> task) {
-        return ((TaskInternal<?>)task.asTask()).isQueued();
-    }
-
-    public static boolean isSubmitted(TaskAdaptable<?> task) {
-        return ((TaskInternal<?>)task.asTask()).isSubmitted();
-    }
-    
-    public static boolean isQueuedOrSubmitted(TaskAdaptable<?> task) {
-        return ((TaskInternal<?>)task.asTask()).isQueuedOrSubmitted();
-    }
-    
-    /**
-     * Adds the given task to the given context. Does not throw an exception if the addition fails.
-     * @return true if the task was added, false otherwise.
-     */
-    public static boolean tryQueueing(TaskQueueingContext adder, TaskAdaptable<?> task) {
-        if (task==null || isQueued(task))
-            return false;
-        try {
-            adder.queue(task.asTask());
-            return true;
-        } catch (Exception e) {
-            if (log.isDebugEnabled())
-                log.debug("Could not add task "+task+" at "+adder+": "+e);
-            return false;
-        }        
-    }
-    
-    /** see also {@link #resolving(Object)} which gives much more control about submission, timeout, etc */
-    public static <T> Supplier<T> supplier(final TaskAdaptable<T> task) {
-        return new Supplier<T>() {
-            @Override
-            public T get() {
-                return task.asTask().getUnchecked();
-            }
-        };
-    }
-    
-    /** return all children tasks of the given tasks, if it has children, else empty list */
-    public static Iterable<Task<?>> children(Task<?> task) {
-        if (task instanceof HasTaskChildren)
-            return ((HasTaskChildren)task).getChildren();
-        return Collections.emptyList();
-    }
-    
-    /** returns failed tasks */
-    public static Iterable<Task<?>> failed(Iterable<Task<?>> subtasks) {
-        return Iterables.filter(subtasks, new Predicate<Task<?>>() {
-            @Override
-            public boolean apply(Task<?> input) {
-                return input.isError();
-            }
-        });
-    }
-    
-    /** returns the task, its children, and all its children, and so on;
-     * @param root task whose descendants should be iterated
-     * @param parentFirst whether to put parents before children or after
-     */
-    public static Iterable<Task<?>> descendants(Task<?> root, final boolean parentFirst) {
-        Iterable<Task<?>> descs = Iterables.concat(Iterables.transform(Tasks.children(root), new Function<Task<?>,Iterable<Task<?>>>() {
-            @Override
-            public Iterable<Task<?>> apply(Task<?> input) {
-                return descendants(input, parentFirst);
-            }
-        }));
-        if (parentFirst) return Iterables.concat(Collections.singleton(root), descs);
-        else return Iterables.concat(descs, Collections.singleton(root));
-    }
-
-    /** returns the error thrown by the task if {@link Task#isError()}, or null if no error or not done */
-    public static Throwable getError(Task<?> t) {
-        if (t==null) return null;
-        if (!t.isDone()) return null;
-        if (t.isCancelled()) return new CancellationException();
-        try {
-            t.get();
-            return null;
-        } catch (Throwable error) {
-            // do not propagate as we are pretty much guaranteed above that it wasn't this
-            // thread which originally threw the error
-            return error;
-        }
-    }
-    public static Task<Void> fail(final String name, final Throwable optionalError) {
-        return Tasks.<Void>builder().dynamic(false).name(name).body(new Runnable() { public void run() { 
-            if (optionalError!=null) throw Exceptions.propagate(optionalError); else throw new RuntimeException("Failed: "+name);
-        } }).build();
-    }
-    public static Task<Void> warning(final String message, final Throwable optionalError) {
-        log.warn(message);
-        return TaskTags.markInessential(fail(message, optionalError));
-    }
-
-    /** marks the current task inessential; this mainly matters if the task is running in a parent
-     * {@link TaskQueueingContext} and we don't want the parent to fail if this task fails
-     * <p>
-     * no-op (silently ignored) if not in a task */
-    public static void markInessential() {
-        Task<?> task = Tasks.current();
-        if (task==null) {
-            TaskQueueingContext qc = DynamicTasks.getTaskQueuingContext();
-            if (qc!=null) task = qc.asTask();
-        }
-        if (task!=null) {
-            TaskTags.markInessential(task);
-        }
-    }
-    
-    /** causes failures in subtasks of the current task not to fail the parent;
-     * no-op if not in a {@link TaskQueueingContext}.
-     * <p>
-     * essentially like a {@link #markInessential()} on all tasks in the current 
-     * {@link TaskQueueingContext}, including tasks queued subsequently */
-    @Beta
-    public static void swallowChildrenFailures() {
-        Preconditions.checkNotNull(DynamicTasks.getTaskQueuingContext(), "Task queueing context required here");
-        TaskQueueingContext qc = DynamicTasks.getTaskQueuingContext();
-        if (qc!=null) {
-            qc.swallowChildrenFailures();
-        }
-    }
-
-    /** as {@link TaskTags#addTagDynamically(TaskAdaptable, Object)} but for current task, skipping if no current task */
-    public static void addTagDynamically(Object tag) {
-        Task<?> t = Tasks.current();
-        if (t!=null) TaskTags.addTagDynamically(t, tag);
-    }
-    
-    /** 
-     * Workaround for limitation described at {@link Task#cancel(boolean)};
-     * internal method used to allow callers to wait for underlying tasks to finished in the case of cancellation.
-     * <p> 
-     * It is irritating that {@link FutureTask} sync's object clears the runner thread, 
-     * so even if {@link BasicTask#getInternalFuture()} is used, there is no means of determining if the underlying object is done.
-     * The {@link Task#getEndTimeUtc()} seems the only way.
-     *  
-     * @return true if tasks ended; false if timed out
-     **/ 
-    @Beta
-    public static boolean blockUntilInternalTasksEnded(Task<?> t, Duration timeout) {
-        CountdownTimer timer = timeout.countdownTimer();
-        
-        if (t==null)
-            return true;
-        
-        if (t instanceof ScheduledTask) {
-            boolean result = ((ScheduledTask)t).blockUntilNextRunFinished(timer.getDurationRemaining());
-            if (!result) return false;
-        }
-
-        t.blockUntilEnded(timer.getDurationRemaining());
-        
-        while (true) {
-            if (t.getEndTimeUtc()>=0) return true;
-            // above should be sufficient; but just in case, trying the below
-            Thread tt = t.getThread();
-            if (t instanceof ScheduledTask) {
-                ((ScheduledTask)t).blockUntilNextRunFinished(timer.getDurationRemaining());
-                return true;
-            } else {
-                if (tt==null || !tt.isAlive()) {
-                    if (!t.isCancelled()) {
-                        // may happen for a cancelled task, interrupted after submit but before start
-                        log.warn("Internal task thread is dead or null ("+tt+") but task not ended: "+t.getEndTimeUtc()+" ("+t+")");
-                    }
-                    return true;
-                }
-            }
-            if (timer.isExpired())
-                return false;
-            Time.sleep(Repeater.DEFAULT_REAL_QUICK_PERIOD);
-        }
-    }
-    
-    /** returns true if either the current thread or the current task is interrupted/cancelled */
-    public static boolean isInterrupted() {
-        if (Thread.currentThread().isInterrupted()) return true;
-        Task<?> t = current();
-        if (t==null) return false;
-        return t.isCancelled();
-    }
-
-    private static class WaitForRepeaterCallable implements Callable<Boolean> {
-        protected Repeater repeater;
-        protected boolean requireTrue;
-
-        public WaitForRepeaterCallable(Repeater repeater, boolean requireTrue) {
-            this.repeater = repeater;
-            this.requireTrue = requireTrue;
-        }
-
-        @Override
-        public Boolean call() {
-            ReferenceWithError<Boolean> result;
-            Tasks.setBlockingDetails(repeater.getDescription());
-            try {
-               result = repeater.runKeepingError();
-            } finally {
-                Tasks.resetBlockingDetails();
-            }
-
-            if (Boolean.TRUE.equals(result.getWithoutError()))
-                return true;
-            if (result.hasError()) 
-                throw Exceptions.propagate(result.getError());
-            if (requireTrue)
-                throw new IllegalStateException("timeout - "+repeater.getDescription());
-            return false;
-        }
-    }
-
-    /** @return a {@link TaskBuilder} which tests whether the repeater terminates with success in its configured timeframe,
-     * returning true or false depending on whether repeater succeed */
-    public static TaskBuilder<Boolean> testing(Repeater repeater) {
-        return Tasks.<Boolean>builder().body(new WaitForRepeaterCallable(repeater, false))
-            .name("waiting for condition")
-            .description("Testing whether " + getTimeoutString(repeater) + ": "+repeater.getDescription());
-    }
-
-    /** @return a {@link TaskBuilder} which requires that the repeater terminate with success in its configured timeframe,
-     * throwing if it does not */
-    public static TaskBuilder<?> requiring(Repeater repeater) {
-        return Tasks.<Boolean>builder().body(new WaitForRepeaterCallable(repeater, true))
-            .name("waiting for condition")
-            .description("Requiring " + getTimeoutString(repeater) + ": " + repeater.getDescription());
-    }
-    
-    private static String getTimeoutString(Repeater repeater) {
-        Duration timeout = repeater.getTimeLimit();
-        if (timeout==null || Duration.PRACTICALLY_FOREVER.equals(timeout))
-            return "eventually";
-        return "in "+timeout;
-    }
-
-}


[39/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/file/ArchiveTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/file/ArchiveTasks.java b/core/src/main/java/brooklyn/util/file/ArchiveTasks.java
deleted file mode 100644
index b183f62..0000000
--- a/core/src/main/java/brooklyn/util/file/ArchiveTasks.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.file;
-
-import java.util.Map;
-
-import org.apache.brooklyn.api.management.TaskAdaptable;
-import org.apache.brooklyn.api.management.TaskFactory;
-
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.net.Urls;
-import brooklyn.util.task.Tasks;
-
-public class ArchiveTasks {
-
-    /** as {@link #deploy(ResourceUtils, Map, String, SshMachineLocation, String, String, String)} with the most common parameters */
-    public static TaskFactory<?> deploy(final ResourceUtils optionalResolver, final String archiveUrl, final SshMachineLocation machine, final String destDir) {
-        return deploy(optionalResolver, null, archiveUrl, machine, destDir, false, null, null);
-    }
-    
-    /** returns a task which installs and unpacks the given archive, as per {@link ArchiveUtils#deploy(ResourceUtils, Map, String, SshMachineLocation, String, String, String)};
-     * if allowNonarchivesOrKeepArchiveAfterDeploy is false, this task will fail if the item is not an archive;
-     * in cases where the download type is not clear in the URL but is known by the caller, supply a optionalDestFile including the appropriate file extension */
-    public static TaskFactory<?> deploy(final ResourceUtils resolver, final Map<String, ?> props, final String archiveUrl, final SshMachineLocation machine, final String destDir, final boolean allowNonarchivesOrKeepArchiveAfterDeploy, final String optionalTmpDir, final String optionalDestFile) {
-        return new TaskFactory<TaskAdaptable<?>>() {
-            @Override
-            public TaskAdaptable<?> newTask() {
-                return Tasks.<Void>builder().name("deploying "+Urls.getBasename(archiveUrl)).description("installing "+archiveUrl+" and unpacking to "+destDir).body(new Runnable() {
-                    @Override
-                    public void run() {
-                        boolean unpacked = ArchiveUtils.deploy(resolver, props, archiveUrl, machine, destDir, allowNonarchivesOrKeepArchiveAfterDeploy, optionalTmpDir, optionalDestFile);
-                        if (!unpacked && !allowNonarchivesOrKeepArchiveAfterDeploy) {
-                            throw new IllegalStateException("Unable to unpack archive from "+archiveUrl+"; not able to infer archive type");
-                        }
-                    }
-                }).build();
-            }
-        };
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/file/ArchiveUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/file/ArchiveUtils.java b/core/src/main/java/brooklyn/util/file/ArchiveUtils.java
deleted file mode 100644
index d072b70..0000000
--- a/core/src/main/java/brooklyn/util/file/ArchiveUtils.java
+++ /dev/null
@@ -1,351 +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.file;
-
-import static java.lang.String.format;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.EnumSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.javalang.StackTraceSimplifier;
-import brooklyn.util.net.Urls;
-import brooklyn.util.os.Os;
-import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ssh.SshTasks;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Charsets;
-import com.google.common.base.Preconditions;
-import com.google.common.io.Files;
-
-public class ArchiveUtils {
-
-    private static final Logger log = LoggerFactory.getLogger(ArchiveUtils.class);
-
-    // TODO Make this a ConfigKey on the machine location
-    /** Number of attempts when copying a file to a remote server. */
-    public static final int NUM_RETRIES_FOR_COPYING = 5;
-
-    /**
-     * The types of archive that are supported by Brooklyn.
-     */
-    public static enum ArchiveType {
-        TAR,
-        TGZ,
-        TBZ,
-        ZIP,
-        JAR,
-        WAR,
-        EAR,
-        UNKNOWN;
-
-        /**
-         * Zip format archives used by Java.
-         */
-        public static Set<ArchiveType> ZIP_ARCHIVES = EnumSet.of(ArchiveType.ZIP, ArchiveType.JAR, ArchiveType.WAR, ArchiveType.EAR);
-
-        public static ArchiveUtils.ArchiveType of(String filename) {
-            if (filename == null) return null;
-            String ext = Files.getFileExtension(filename);
-            try {
-                return valueOf(ext.toUpperCase());
-            } catch (IllegalArgumentException iae) {
-                if (filename.toLowerCase().endsWith(".tar.gz")) {
-                    return TGZ;
-                } else if (filename.toLowerCase().endsWith(".tar.bz") ||
-                        filename.toLowerCase().endsWith(".tar.bz2") ||
-                        filename.toLowerCase().endsWith(".tar.xz")) {
-                    return TBZ;
-                } else {
-                    return UNKNOWN;
-                }
-            }
-        }
-
-        @Override
-        public String toString() {
-            if (UNKNOWN.equals(this)) {
-                return "";
-            } else {
-                return name().toLowerCase();
-            }
-        }
-    }
-
-    /**
-     * Returns the list of commands used to install support for an archive with the given name.
-     */
-    public static List<String> installCommands(String fileName) {
-        List<String> commands = new LinkedList<String>();
-        switch (ArchiveType.of(fileName)) {
-            case TAR:
-            case TGZ:
-            case TBZ:
-                commands.add(BashCommands.INSTALL_TAR);
-                break;
-            case ZIP:
-                commands.add(BashCommands.INSTALL_UNZIP);
-                break;
-            case JAR:
-            case WAR:
-            case EAR:
-            case UNKNOWN:
-                break;
-        }
-        return commands;
-    }
-
-    /**
-     * Returns the list of commands used to extract the contents of the archive with the given name.
-     * <p>
-     * Optionally, Java archives of type
-     *
-     * @see #extractCommands(String, String)
-     */
-    public static List<String> extractCommands(String fileName, String sourceDir, String targetDir, boolean extractJar) {
-        return extractCommands(fileName, sourceDir, targetDir, extractJar, true);
-    }
-    
-    /** as {@link #extractCommands(String, String, String, boolean)}, but also with option to keep the original */
-    public static List<String> extractCommands(String fileName, String sourceDir, String targetDir, boolean extractJar, boolean keepOriginal) {
-        List<String> commands = new LinkedList<String>();
-        commands.add("cd " + targetDir);
-        String sourcePath = Os.mergePathsUnix(sourceDir, fileName);
-        switch (ArchiveType.of(fileName)) {
-            case TAR:
-                commands.add("tar xvf " + sourcePath);
-                break;
-            case TGZ:
-                commands.add("tar xvfz " + sourcePath);
-                break;
-            case TBZ:
-                commands.add("tar xvfj " + sourcePath);
-                break;
-            case ZIP:
-                commands.add("unzip " + sourcePath);
-                break;
-            case JAR:
-            case WAR:
-            case EAR:
-                if (extractJar) {
-                    commands.add("jar -xvf " + sourcePath);
-                    break;
-                }
-            case UNKNOWN:
-                if (!sourcePath.equals(Urls.mergePaths(targetDir, fileName))) {
-                    commands.add("cp " + sourcePath + " " + targetDir);
-                } else {
-                    keepOriginal = true;
-                    // else we'd just end up deleting it!
-                    // this branch will often lead to errors in any case, see the allowNonarchivesOrKeepArchiveAfterDeploy parameter 
-                    // in ArchiveTasks which calls through to here and then fails in the case corresponding to this code branch
-                }
-                break;
-        }
-        if (!keepOriginal && !commands.isEmpty())
-            commands.add("rm "+sourcePath);
-        return commands;
-    }
-
-    /**
-     * Returns the list of commands used to extract the contents of the archive with the given name.
-     * <p>
-     * The archive will be extracted in its current directory unless it is a Java archive of type {@code .jar},
-     * {@code .war} or {@code .ear}, which will be left as is.
-     *
-     * @see #extractCommands(String, String, String, boolean)
-     */
-    public static List<String> extractCommands(String fileName, String sourceDir) {
-        return extractCommands(fileName, sourceDir, ".", false);
-    }
-
-    /**
-     * Deploys an archive file to a remote machine and extracts the contents.
-     */
-    public static void deploy(String archiveUrl, SshMachineLocation machine, String destDir) {
-        deploy(MutableMap.<String, Object>of(), archiveUrl, machine, destDir);
-    }
-
-    /**
-     * Deploys an archive file to a remote machine and extracts the contents.
-     * <p>
-     * Copies the archive file from the given URL to the destination directory and extracts
-     * the contents. If the URL is a local directory, the contents are packaged as a Zip archive first.
-     *
-     * @see #deploy(String, SshMachineLocation, String, String)
-     * @see #deploy(Map, String, SshMachineLocation, String, String, String)
-     */
-    public static void deploy(Map<String, ?> props, String archiveUrl, SshMachineLocation machine, String destDir) {
-        if (Urls.isDirectory(archiveUrl)) {
-            File zipFile = ArchiveBuilder.zip().entry(".", Urls.toFile(archiveUrl)).create();
-            archiveUrl = zipFile.getAbsolutePath();
-        }
-
-        // Determine filename
-        String destFile = archiveUrl.contains("?") ? archiveUrl.substring(0, archiveUrl.indexOf('?')) : archiveUrl;
-        destFile = destFile.substring(destFile.lastIndexOf('/') + 1);
-
-        deploy(props, archiveUrl, machine, destDir, destFile);
-    }
-
-    /**
-     * Deploys an archive file to a remote machine and extracts the contents.
-     * <p>
-     * Copies the archive file from the given URL to a file in the destination directory and extracts
-     * the contents.
-     *
-     * @see #deploy(String, SshMachineLocation, String)
-     * @see #deploy(Map, String, SshMachineLocation, String, String, String)
-     */
-    public static void deploy(String archiveUrl, SshMachineLocation machine, String destDir, String destFile) {
-        deploy(MutableMap.<String, Object>of(), archiveUrl, machine, destDir, destDir, destFile);
-    }
-    public static void deploy(Map<String, ?> props, String archiveUrl, SshMachineLocation machine, String destDir, String destFile) {
-        deploy(props, archiveUrl, machine, destDir, destDir, destFile);
-    }
-    public static void deploy(Map<String, ?> props, String archiveUrl, SshMachineLocation machine, String tmpDir, String destDir, String destFile) {
-        deploy(null, props, archiveUrl, machine, destDir, true, tmpDir, destFile);
-    }
-    
-    /**
-     * Deploys an archive file to a remote machine and extracts the contents.
-     * <p>
-     * Copies the archive file from the given URL to a file in a temporary directory and extracts
-     * the contents in the destination directory. For Java archives of type {@code .jar},
-     * {@code .war} or {@code .ear} the file is simply copied.
-     * 
-     * @return true if the archive is downloaded AND unpacked; false if it is downloaded but not unpacked; 
-     * throws if there was an error downloading or, for known archive types, unpacking.
-     *
-     * @see #deploy(String, SshMachineLocation, String)
-     * @see #deploy(Map, String, SshMachineLocation, String, String, String)
-     * @see #install(SshMachineLocation, String, String, int)
-     */
-    public static boolean deploy(ResourceUtils resolver, Map<String, ?> props, String archiveUrl, SshMachineLocation machine, String destDir, boolean keepArchiveAfterUnpacking, String optionalTmpDir, String optionalDestFile) {
-        String destFile = optionalDestFile;
-        if (destFile==null) destFile = Urls.getBasename(Preconditions.checkNotNull(archiveUrl, "archiveUrl"));
-        if (Strings.isBlank(destFile)) 
-            throw new IllegalStateException("Not given filename and cannot infer archive type from '"+archiveUrl+"'");
-        
-        String tmpDir = optionalTmpDir;
-        if (tmpDir==null) tmpDir=Preconditions.checkNotNull(destDir, "destDir");
-        if (props==null) props = MutableMap.of();
-        String destPath = Os.mergePaths(tmpDir, destFile);
-
-        // Use the location mutex to prevent package manager locking issues
-        machine.acquireMutex("installing", "installing archive");
-        try {
-            int result = install(resolver, props, machine, archiveUrl, destPath, NUM_RETRIES_FOR_COPYING);
-            if (result != 0) {
-                throw new IllegalStateException(format("Unable to install archive %s to %s", archiveUrl, machine));
-            }
-
-            // extract, now using task if available
-            MutableList<String> commands = MutableList.copyOf(installCommands(destFile))
-                    .appendAll(extractCommands(destFile, tmpDir, destDir, false, keepArchiveAfterUnpacking));
-            if (DynamicTasks.getTaskQueuingContext()!=null) {
-                result = DynamicTasks.queue(SshTasks.newSshExecTaskFactory(machine, commands.toArray(new String[0])).summary("extracting archive").requiringExitCodeZero()).get();
-            } else {
-                result = machine.execCommands(props, "extracting content", commands);
-            }
-            if (result != 0) {
-                throw new IllegalStateException(format("Failed to expand archive %s on %s", archiveUrl, machine));
-            }
-            return ArchiveType.of(destFile)!=ArchiveType.UNKNOWN;
-        } finally {
-            machine.releaseMutex("installing");
-        }
-    }
-
-    /**
-     * Installs a URL onto a remote machine.
-     *
-     * @see #install(Map, SshMachineLocation, String, String, int)
-     */
-    public static int install(SshMachineLocation machine, String urlToInstall, String target) {
-        return install(MutableMap.<String, Object>of(), machine, urlToInstall, target, NUM_RETRIES_FOR_COPYING);
-    }
-
-    /**
-     * Installs a URL onto a remote machine.
-     *
-     * @see #install(SshMachineLocation, String, String)
-     * @see SshMachineLocation#installTo(Map, String, String)
-     */
-    public static int install(Map<String, ?> props, SshMachineLocation machine, String urlToInstall, String target, int numAttempts) {
-        return install(null, props, machine, urlToInstall, target, numAttempts);
-    }
-    
-    public static int install(ResourceUtils resolver, Map<String, ?> props, SshMachineLocation machine, String urlToInstall, String target, int numAttempts) {
-        if (resolver==null) resolver = ResourceUtils.create(machine);
-        Exception lastError = null;
-        int retriesRemaining = numAttempts;
-        int attemptNum = 0;
-        do {
-            attemptNum++;
-            try {
-                Tasks.setBlockingDetails("Installing "+urlToInstall+" at "+machine);
-                // TODO would be nice to have this in a task (and the things within it!)
-                return machine.installTo(resolver, props, urlToInstall, target);
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                lastError = e;
-                String stack = StackTraceSimplifier.toString(e);
-                if (stack.contains("net.schmizz.sshj.sftp.RemoteFile.write")) {
-                    log.warn("Failed to transfer "+urlToInstall+" to "+machine+", retryable error, attempt "+attemptNum+"/"+numAttempts+": "+e);
-                    continue;
-                }
-                log.warn("Failed to transfer "+urlToInstall+" to "+machine+", not a retryable error so failing: "+e);
-                throw Exceptions.propagate(e);
-            } finally {
-                Tasks.resetBlockingDetails();
-            }
-        } while (retriesRemaining --> 0);
-        throw Exceptions.propagate(lastError);
-    }
-
-    /**
-     * Copies the entire contents of a file to a String.
-     *
-     * @see com.google.common.io.Files#toString(File, java.nio.charset.Charset)
-     */
-    public static String readFullyString(File sourceFile) {
-        try {
-            return Files.toString(sourceFile, Charsets.UTF_8);
-        } catch (IOException ioe) {
-            throw Exceptions.propagate(ioe);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/flags/ClassCoercionException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/flags/ClassCoercionException.java b/core/src/main/java/brooklyn/util/flags/ClassCoercionException.java
deleted file mode 100644
index 17385ac..0000000
--- a/core/src/main/java/brooklyn/util/flags/ClassCoercionException.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.flags;
-
-/**
- * Thrown to indicate that {@link TypeCoercions} could not cast an object from one
- * class to another.
- */
-public class ClassCoercionException extends ClassCastException {
-    public ClassCoercionException() {
-        super();
-    }
-
-    /**
-     * Constructs a <code>ClassCoercionException</code> with the specified
-     * detail message.
-     *
-     * @param s the detail message.
-     */
-    public ClassCoercionException(String s) {
-        super(s);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/flags/FlagUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/flags/FlagUtils.java b/core/src/main/java/brooklyn/util/flags/FlagUtils.java
deleted file mode 100644
index 0e596ac..0000000
--- a/core/src/main/java/brooklyn/util/flags/FlagUtils.java
+++ /dev/null
@@ -1,587 +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.flags;
-
-import static brooklyn.util.GroovyJavaMethods.elvis;
-import static brooklyn.util.GroovyJavaMethods.truth;
-import static com.google.common.base.Preconditions.checkNotNull;
-import groovy.lang.Closure;
-import groovy.lang.GroovyObject;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-
-import org.apache.brooklyn.api.entity.trait.Configurable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.config.ConfigKey.HasConfigKey;
-import brooklyn.util.GroovyJavaMethods;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.guava.Maybe;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.base.Throwables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-
-/** class to help transfer values passed as named arguments to other well-known variables/fields/objects;
- * see the test case for example usage */
-public class FlagUtils {
-
-    public static final Logger log = LoggerFactory.getLogger(FlagUtils.class);
-    
-    private FlagUtils() {}
-    
-    /** see {@link #setFieldsFromFlags(Object o, ConfigBag)} */
-    public static Map<?, ?> setPublicFieldsFromFlags(Map<?, ?> flags, Object o) {
-        return setFieldsFromFlagsInternal(o, Arrays.asList(o.getClass().getFields()), flags, null, true);
-    }
-
-    /** see {@link #setFieldsFromFlags(Object, ConfigBag)} */
-    public static Map<?, ?> setFieldsFromFlags(Map<?, ?> flags, Object o) {
-        return setFieldsFromFlagsInternal(o, getAllFields(o.getClass()), flags, null, true);
-    }
-    
-    /** sets all fields (including private and static, local and inherited) annotated {@link SetFromFlag} on the given object, 
-     * from the given flags map, returning just those flag-value pairs passed in which do not correspond to SetFromFlags fields 
-     * annotated ConfigKey and HasConfigKey fields are _configured_ (and we assume the object in that case is {@link Configurable});
-     * keys should be ConfigKey, HasConfigKey, or String;
-     * default values are also applied unless that is specified false on one of the variants of this method which takes such an argument
-     */
-    public static void setFieldsFromFlags(Object o, ConfigBag configBag) {
-        setFieldsFromFlagsInternal(o, getAllFields(o.getClass()), configBag.getAllConfig(), configBag, true);
-    }
-
-    /** as {@link #setFieldsFromFlags(Object, ConfigBag)}, but allowing control over whether default values should be set */
-    public static void setFieldsFromFlags(Object o, ConfigBag configBag, boolean setDefaultVals) {
-        setFieldsFromFlagsInternal(o, getAllFields(o.getClass()), configBag.getAllConfig(), configBag, setDefaultVals);
-    }
-
-    /** as {@link #setFieldsFromFlags(Object, ConfigBag)}, but specifying a subset of flags to use */
-    public static void setFieldsFromFlagsWithBag(Object o, Map<?,?> flags, ConfigBag configBag, boolean setDefaultVals) {
-        setFieldsFromFlagsInternal(o, getAllFields(o.getClass()), flags, configBag, setDefaultVals);
-    }
-
-    /**
-     * Sets the field with the given flag (if it exists) to the given value.
-     * Will attempt to coerce the value to the required type.
-     * Will respect "nullable" on the SetFromFlag annotation.
-     * 
-     * @throws IllegalArgumentException If fieldVal is null and the SetFromFlag annotation set nullable=false
-     */
-    public static boolean setFieldFromFlag(Object o, String flagName, Object fieldVal) {
-        return setFieldFromFlagInternal(checkNotNull(flagName, "flagName"), fieldVal, o, getAllFields(o.getClass()));
-    }
-    
-    /** get all fields (including private and static) on the given object and all supertypes, 
-     * that are annotated with SetFromFlags. 
-     */
-    public static Map<String, ?> getFieldsWithFlags(Object o) {
-        return getFieldsWithFlagsInternal(o, getAllFields(o.getClass()));
-    }
-    
-    /**
-     * Finds the {@link Field} on the given object annotated with the given name flag.
-     */
-    public static Field findFieldForFlag(String flagName, Object o) {
-        return findFieldForFlagInternal(flagName, o, getAllFields(o.getClass()));
-    }
-
-    /** get all fields (including private and static) and their values on the given object and all supertypes, 
-     * where the field is annotated with SetFromFlags. 
-     */
-    public static Map<String, Object> getFieldsWithFlagsExcludingModifiers(Object o, int excludingModifiers) {
-        List<Field> filteredFields = Lists.newArrayList();
-        for (Field contender : getAllFields(o.getClass())) {
-            if ((contender.getModifiers() & excludingModifiers) == 0) {
-                filteredFields.add(contender);
-            }
-        }
-        return getFieldsWithFlagsInternal(o, filteredFields);
-    }
-    
-    /** get all fields with the given modifiers, and their values on the given object and all supertypes, 
-     * where the field is annotated with SetFromFlags. 
-     */
-    public static Map<String, Object> getFieldsWithFlagsWithModifiers(Object o, int requiredModifiers) {
-        List<Field> filteredFields = Lists.newArrayList();
-        for (Field contender : getAllFields(o.getClass())) {
-            if ((contender.getModifiers() & requiredModifiers) == requiredModifiers) {
-                filteredFields.add(contender);
-            }
-        }
-        return getFieldsWithFlagsInternal(o, filteredFields);
-    }
-    
-    /** sets _all_ accessible _{@link ConfigKey}_ and {@link HasConfigKey} fields on the given object, 
-     * using the indicated flags/config-bag 
-     * @deprecated since 0.7.0 use {@link #setAllConfigKeys(Map, Configurable, boolean)} */
-    public static Map<String, ?> setAllConfigKeys(Map<String, ?> flagsOrConfig, Configurable instance) {
-        return setAllConfigKeys(flagsOrConfig, instance, false);
-    }
-    /** sets _all_ accessible _{@link ConfigKey}_ and {@link HasConfigKey} fields on the given object, 
-     * using the indicated flags/config-bag */
-    public static Map<String, ?> setAllConfigKeys(Map<String, ?> flagsOrConfig, Configurable instance, boolean includeFlags) {
-        ConfigBag bag = new ConfigBag().putAll(flagsOrConfig);
-        setAllConfigKeys(instance, bag, includeFlags);
-        return bag.getUnusedConfigMutable();
-    }
-    
-    /** sets _all_ accessible _{@link ConfigKey}_ and {@link HasConfigKey} fields on the given object, 
-     * using the indicated flags/config-bag 
-    * @deprecated since 0.7.0 use {@link #setAllConfigKeys(Configurable, ConfigBag, boolean)} */
-    public static void setAllConfigKeys(Configurable o, ConfigBag bag) {
-        setAllConfigKeys(o, bag, false);
-    }
-    /** sets _all_ accessible _{@link ConfigKey}_ and {@link HasConfigKey} fields on the given object, 
-     * using the indicated flags/config-bag */
-    public static void setAllConfigKeys(Configurable o, ConfigBag bag, boolean includeFlags) {
-        for (Field f: getAllFields(o.getClass())) {
-            ConfigKey<?> key = getFieldAsConfigKey(o, f);
-            if (key!=null) {
-                FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(f, key, bag);
-                if ((includeFlags && record.isValuePresent()) || record.getConfigKeyMaybeValue().isPresent()) {
-                    setField(o, f, record.getValueOrNullPreferringConfigKey(), null);
-                }
-            }
-        }
-    }
-    
-    public static class FlagConfigKeyAndValueRecord {
-        private String flagName = null;
-        private ConfigKey<?> configKey = null;
-        private Maybe<Object> flagValue = Maybe.absent();
-        private Maybe<Object> configKeyValue = Maybe.absent();
-        
-        public String getFlagName() {
-            return flagName;
-        }
-        public ConfigKey<?> getConfigKey() {
-            return configKey;
-        }
-        public Maybe<Object> getFlagMaybeValue() {
-            return flagValue;
-        }
-        public Maybe<Object> getConfigKeyMaybeValue() {
-            return configKeyValue;
-        }
-        public Object getValueOrNullPreferringConfigKey() {
-            return getConfigKeyMaybeValue().or(getFlagMaybeValue()).orNull();
-        }
-        public Object getValueOrNullPreferringFlag() {
-            return getFlagMaybeValue().or(getConfigKeyMaybeValue()).orNull();
-        }
-        /** true if value is present for either flag or config key */
-        public boolean isValuePresent() {
-            return flagValue.isPresent() || configKeyValue.isPresent();
-        }
-        
-        @Override
-        public String toString() {
-            return Objects.toStringHelper(this).omitNullValues()
-                .add("flag", flagName)
-                .add("configKey", configKey)
-                .add("flagValue", flagValue.orNull())
-                .add("configKeyValue", configKeyValue.orNull())
-                .toString();
-        }
-    }
-    
-    /** gets all the flags/keys in the given config bag which are applicable to the given type's config keys and flags */
-    public static <T> List<FlagConfigKeyAndValueRecord> findAllFlagsAndConfigKeys(T optionalInstance, Class<? extends T> type, ConfigBag input) {
-        List<FlagConfigKeyAndValueRecord> output = new ArrayList<FlagUtils.FlagConfigKeyAndValueRecord>();
-        for (Field f: getAllFields(type)) {
-            ConfigKey<?> key = getFieldAsConfigKey(optionalInstance, f);
-            FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(f, key, input);
-            if (record.isValuePresent())
-                output.add(record);
-        }
-        return output;
-    }
-
-    /** returns the flag/config-key record for the given input */
-    private static FlagConfigKeyAndValueRecord getFlagConfigKeyRecord(Field f, ConfigKey<?> key, ConfigBag input) {
-        FlagConfigKeyAndValueRecord result = new FlagConfigKeyAndValueRecord(); 
-        result.configKey = key;
-        if (key!=null && input.containsKey(key))
-            result.configKeyValue = Maybe.<Object>of(input.getStringKey(key.getName()));
-        SetFromFlag flag = f.getAnnotation(SetFromFlag.class);
-        if (flag!=null) {
-            result.flagName = flag.value();
-            if (input.containsKey(flag.value()))
-                result.flagValue = Maybe.of(input.getStringKey(flag.value()));
-        }
-        return result;
-    }
-
-    /** returns all fields on the given class, superclasses, and interfaces thereof, in that order of preference,
-     * (excluding fields on Object) */
-    public static List<Field> getAllFields(Class<?> base, Closure<Boolean> filter) {
-        return getAllFields(base, GroovyJavaMethods.<Field>predicateFromClosure(filter));
-    }
-    public static List<Field> getAllFields(Class<?> base) {
-        return getAllFields(base, Predicates.<Field>alwaysTrue());
-    }
-    public static List<Field> getAllFields(Class<?> base, Predicate<Field> filter) {
-        return getLocalFields(getAllAssignableTypes(base), filter);
-    }
-    /** returns all fields explicitly declared on the given classes */
-    public static List<Field> getLocalFields(List<Class<?>> classes) {
-        return getLocalFields(classes, Predicates.<Field>alwaysTrue());
-    }
-    public static List<Field> getLocalFields(List<Class<?>> classes, Closure<Boolean> filter) {
-        return getLocalFields(classes, GroovyJavaMethods.<Field>predicateFromClosure(filter));
-    }
-    public static List<Field> getLocalFields(List<Class<?>> classes, Predicate<Field> filter) {
-        List<Field> fields = Lists.newArrayList();
-        for (Class<?> c : classes) {
-            for (Field f : c.getDeclaredFields()) {
-                if (filter.apply(f)) fields.add(f);
-            }
-        }
-        return fields;
-    }
-    
-    /** returns base, superclasses, then interfaces */
-    public static List<Class<?>> getAllAssignableTypes(Class<?> base) {
-        return getAllAssignableTypes(base, new Predicate<Class<?>>() {
-            @Override public boolean apply(Class<?> it) {
-                return (it != Object.class) && (it != GroovyObject.class);
-            }
-        });
-    }
-    public static List<Class<?>> getAllAssignableTypes(Class<?> base, Closure<Boolean> filter) {
-        return getAllAssignableTypes(base, GroovyJavaMethods.<Class<?>>predicateFromClosure(filter));
-    }
-    public static List<Class<?>> getAllAssignableTypes(Class<?> base, Predicate<Class<?>> filter) {
-        List<Class<?>> classes = Lists.newArrayList();
-        for (Class<?> c = base; c != null; c = c.getSuperclass()) {
-            if (filter.apply(c)) classes.add(c);
-        }
-        for (int i=0; i<classes.size(); i++) {
-            for (Class<?> interf : classes.get(i).getInterfaces()) {
-                if (filter.apply(interf) && !(classes.contains(interf))) classes.add(interf);
-            }
-        }
-        return classes;
-    }
-    
-    private static Map<String, Object> getFieldsWithFlagsInternal(Object o, Collection<Field> fields) {
-        Map<String, Object> result = Maps.newLinkedHashMap();
-        for (Field f: fields) {
-            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
-            if (cf != null) {
-                String flagName = elvis(cf.value(), f.getName());
-                if (truth(flagName)) {
-                    result.put(flagName, getField(o, f));
-                } else {
-                    log.warn("Ignoring field {} of object {} as no flag name available", f, o);
-                }
-            }
-        }
-        return result;
-    }
-
-    private static Field findFieldForFlagInternal(String flagName, Object o, Collection<Field> fields) {
-        for (Field f: fields) {
-            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
-            if (cf != null) {
-                String contenderName = elvis(cf.value(), f.getName());
-                if (flagName.equals(contenderName)) {
-                    return f;
-                }
-            }
-        }
-        throw new NoSuchElementException("Field with flag "+flagName+" not found on "+o+" of type "+(o != null ? o.getClass() : null));
-    }
-
-    private static boolean setFieldFromFlagInternal(String flagName, Object fieldVal, Object o, Collection<Field> fields) {
-        for (Field f: fields) {
-            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
-            if (cf != null && flagName.equals(elvis(cf.value(), f.getName()))) {
-                setField(o, f, fieldVal, cf);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private static Map<String, ?> setFieldsFromFlagsInternal(Object o, Collection<Field> fields, Map<?,?> flagsOrConfig, ConfigBag bag, boolean setDefaultVals) {
-        if (bag==null) bag = new ConfigBag().putAll(flagsOrConfig);
-        for (Field f: fields) {
-            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
-            if (cf!=null) setFieldFromConfig(o, f, bag, cf, setDefaultVals);
-        }
-        return bag.getUnusedConfigMutable();
-    }
-
-    private static void setFieldFromConfig(Object o, Field f, ConfigBag bag, SetFromFlag optionalAnnotation, boolean setDefaultVals) {
-        String flagName = optionalAnnotation==null ? null : (String)elvis(optionalAnnotation.value(), f.getName());
-        // prefer flag name, if present
-        if (truth(flagName) && bag.containsKey(flagName)) {
-            setField(o, f, bag.getStringKey(flagName), optionalAnnotation);
-            return;
-        }
-        // first check whether it is a key
-        ConfigKey<?> key = getFieldAsConfigKey(o, f);
-        if (key!=null && bag.containsKey(key)) {
-            Object uncoercedValue = bag.getStringKey(key.getName());
-            setField(o, f, uncoercedValue, optionalAnnotation);
-            return;
-        }
-        if (setDefaultVals && optionalAnnotation!=null && truth(optionalAnnotation.defaultVal())) {
-            Object oldValue;
-            try {
-                f.setAccessible(true);
-                oldValue = f.get(o);
-                if (oldValue==null || oldValue.equals(getDefaultValueForType(f.getType()))) {
-                    setField(o, f, optionalAnnotation.defaultVal(), optionalAnnotation);
-                }
-            } catch (Exception e) {
-                Exceptions.propagate(e);
-            }
-            return;
-        }
-    }
-
-    /** returns the given field as a config key, if it is an accessible config key, otherwise null */
-    private static ConfigKey<?> getFieldAsConfigKey(Object optionalInstance, Field f) {
-        if (optionalInstance==null) {
-            if ((f.getModifiers() & Modifier.STATIC)==0)
-                // non-static field on null instance, can't be set
-                return null;
-        }
-        if (ConfigKey.class.isAssignableFrom(f.getType())) {
-            return (ConfigKey<?>) getField(optionalInstance, f);
-        } else if (HasConfigKey.class.isAssignableFrom(f.getType())) {
-            return ((HasConfigKey<?>)getField(optionalInstance, f)).getConfigKey();
-        }
-        return null;
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static void setConfig(Object objectOfField, ConfigKey<?> key, Object value, SetFromFlag optionalAnnotation) {
-        if (objectOfField instanceof Configurable) {
-            ((Configurable)objectOfField).setConfig((ConfigKey)key, value);
-            return;
-        } else {
-            if (optionalAnnotation==null) {
-                log.warn("Cannot set key "+key.getName()+" on "+objectOfField+": containing class is not Configurable");
-            } else if (!key.getName().equals(optionalAnnotation.value())) {
-                log.warn("Cannot set key "+key.getName()+" on "+objectOfField+" from flag "+optionalAnnotation.value()+": containing class is not Configurable");
-            } else {
-                // if key and flag are the same, then it will probably happen automatically
-                if (log.isDebugEnabled())
-                    log.debug("Cannot set key "+key.getName()+" on "+objectOfField+" from flag "+optionalAnnotation.value()+": containing class is not Configurable");
-            }
-            return;
-        }
-    }
-    
-    /** sets the field to the value, after checking whether the given value can be set 
-     * respecting the constraints of the annotation 
-     */
-    public static void setField(Object objectOfField, Field f, Object value, SetFromFlag optionalAnnotation) {
-        try {
-            ConfigKey<?> key = getFieldAsConfigKey(objectOfField, f);
-            if (key!=null) {
-                setConfig(objectOfField, key, value, optionalAnnotation);
-                return;
-            }
-            
-            if (!f.isAccessible()) f.setAccessible(true);
-            if (optionalAnnotation!=null && optionalAnnotation.immutable()) {
-                Object oldValue = f.get(objectOfField);
-                if (!Objects.equal(oldValue, getDefaultValueForType(f.getType())) && oldValue != value) {
-                    throw new IllegalStateException("Forbidden modification to immutable field "+
-                        f+" in "+objectOfField+": attempting to change to "+value+" when was already "+oldValue);
-                }
-            }
-            if (optionalAnnotation!=null && !optionalAnnotation.nullable() && value==null) {
-                throw new IllegalArgumentException("Forbidden null assignment to non-nullable field "+
-                        f+" in "+objectOfField);
-            }
-            if (optionalAnnotation!=null && (f.getModifiers() & Modifier.STATIC)==Modifier.STATIC)
-                log.warn("Setting static field "+f+" in "+objectOfField+" from flag "+optionalAnnotation.value()+": discouraged");
-
-            Object newValue;
-            try {
-                newValue = TypeCoercions.coerce(value, f.getType());
-            } catch (Exception e) {
-                throw new IllegalArgumentException("Cannot set "+f+" in "+objectOfField+" from type "+value.getClass()+" ("+value+"): "+e, e);
-            }
-            f.set(objectOfField, newValue);
-            if (log.isTraceEnabled()) log.trace("FlagUtils for "+objectOfField+", setting field="+f.getName()+"; val="+value+"; newVal="+newValue+"; key="+key);
-
-        } catch (IllegalAccessException e) {
-            throw Throwables.propagate(e);
-        }
-    }
-
-    /** gets the value of the field. 
-     */
-    public static Object getField(Object objectOfField, Field f) {
-        try {
-            if (!f.isAccessible()) f.setAccessible(true);
-            return f.get(objectOfField);
-        } catch (IllegalAccessException e) {
-            throw Throwables.propagate(e);
-        }
-    }
-    
-    /** returns the default/inital value that is assigned to fields of the givien type;
-     * if the type is not primitive this value is null;
-     * for primitive types it is obvious but not AFAIK programmatically visible
-     * (e.g. 0 for int, false for boolean)  
-     */
-    public static Object getDefaultValueForType(Class<?> t) {
-        if (!t.isPrimitive()) return null;
-        if (t==Integer.TYPE) return (int)0;
-        if (t==Long.TYPE) return (long)0;
-        if (t==Double.TYPE) return (double)0;
-        if (t==Float.TYPE) return (float)0;
-        if (t==Byte.TYPE) return (byte)0;
-        if (t==Short.TYPE) return (short)0;
-        if (t==Character.TYPE) return (char)0;
-        if (t==Boolean.TYPE) return false;
-        //should never happen
-        throw new IllegalStateException("Class "+t+" is an unknown primitive.");
-    }
-
-    /** returns a map of all fields which are annotated 'SetFromFlag', along with the annotation */
-    public static Map<Field,SetFromFlag> getAnnotatedFields(Class<?> type) {
-        Map<Field, SetFromFlag> result = Maps.newLinkedHashMap();
-        for (Field f: getAllFields(type)) {
-            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
-            if (truth(cf)) result.put(f, cf);
-        }
-        return result;
-    }
-
-    /** returns a map of all {@link ConfigKey} fields which are annotated 'SetFromFlag', along with the annotation */
-    public static Map<ConfigKey<?>,SetFromFlag> getAnnotatedConfigKeys(Class<?> type) {
-        Map<ConfigKey<?>, SetFromFlag> result = Maps.newLinkedHashMap();
-        List<Field> fields = getAllFields(type, new Predicate<Field>() {
-            @Override public boolean apply(Field f) {
-                return (f != null) && ConfigKey.class.isAssignableFrom(f.getType()) && ((f.getModifiers() & Modifier.STATIC)!=0);
-            }});
-        for (Field f: fields) {
-            SetFromFlag cf = f.getAnnotation(SetFromFlag.class);
-            if (cf != null) {
-                ConfigKey<?> key = getFieldAsConfigKey(null, f);
-                if (key != null) {
-                    result.put(key, cf);
-                }
-            }
-        }
-        return result;
-    }
-
-    /** returns a map of all fields which are annotated 'SetFromFlag' with their current values;
-     * useful if you want to clone settings from one object
-     */
-    public static Map<String,Object> getFieldsWithValues(Object o) {
-        try {
-            Map<String, Object> result = Maps.newLinkedHashMap();
-            for (Map.Entry<Field, SetFromFlag> entry : getAnnotatedFields(o.getClass()).entrySet()) {
-                Field f = entry.getKey();
-                SetFromFlag cf = entry.getValue();
-                String flagName = elvis(cf.value(), f.getName());
-                if (truth(flagName)) {
-                    if (!f.isAccessible()) f.setAccessible(true);
-                    result.put(flagName, f.get(o));
-                }
-            }
-            return result;
-        } catch (IllegalAccessException e) {
-            throw Throwables.propagate(e);
-        }
-    }
-        
-    /**
-     * @throws an IllegalStateException if there are fields required (nullable=false) which are unset 
-     * @throws wrapped IllegalAccessException
-     */
-    public static void checkRequiredFields(Object o) {
-        try {
-            Set<String> unsetFields = Sets.newLinkedHashSet();
-            for (Map.Entry<Field, SetFromFlag> entry : getAnnotatedFields(o.getClass()).entrySet()) {
-                Field f = entry.getKey();
-                SetFromFlag cf = entry.getValue();
-                if (!cf.nullable()) {
-                    String flagName = elvis(cf.value(), f.getName());
-                    if (!f.isAccessible()) f.setAccessible(true);
-                    Object v = f.get(o);
-                    if (v==null) unsetFields.add(flagName);
-                }
-            }
-            if (truth(unsetFields)) {
-                throw new IllegalStateException("Missing required "+(unsetFields.size()>1 ? "fields" : "field")+": "+unsetFields);
-            }
-        } catch (IllegalAccessException e) {
-            throw Throwables.propagate(e);
-        }
-    }
-
-//    /** sets all fields in target annotated with @SetFromFlag using the configuration in the given config bag */
-//    public static void setFieldsFromConfigFlags(Object target, ConfigBag configBag) {
-//        setFieldsFromConfigFlags(target, configBag.getAllConfig(), configBag);
-//    }
-//
-//    
-//    /** sets all fields in target annotated with @SetFromFlag using the configuration in the given configToUse,
-//     * marking used in the given configBag */
-//    public static void setFieldsFromConfigFlags(Object target, Map<?,?> configToUse, ConfigBag configBag) {
-//        for (Map.Entry<?,?> entry: configToUse.entrySet()) {
-//            setFieldFromConfigFlag(target, entry.getKey(), entry.getValue(), configBag);
-//        }
-//    }
-//
-//    public static void setFieldFromConfigFlag(Object target, Object key, Object value, ConfigBag optionalConfigBag) {
-//        String name = null;
-//        if (key instanceof String) name = (String)key;
-//        else if (key instanceof ConfigKey<?>) name = ((ConfigKey<?>)key).getName();
-//        else if (key instanceof HasConfigKey<?>) name = ((HasConfigKey<?>)key).getConfigKey().getName();
-//        else {
-//            if (key!=null) {
-//                log.warn("Invalid config type "+key.getClass().getCanonicalName()+" ("+key+") when configuring "+target+"; ignoring");
-//            }
-//            return;
-//        }
-//        if (setFieldFromFlag(name, value, target)) {
-//            if (optionalConfigBag!=null)
-//                optionalConfigBag.markUsed(name);
-//        }
-//    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/flags/MethodCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/flags/MethodCoercions.java b/core/src/main/java/brooklyn/util/flags/MethodCoercions.java
deleted file mode 100644
index c9f00fe..0000000
--- a/core/src/main/java/brooklyn/util/flags/MethodCoercions.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.flags;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.guava.Maybe;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import com.google.common.reflect.TypeToken;
-
-import javax.annotation.Nullable;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * A way of binding a loosely-specified method call into a strongly-typed Java method call.
- */
-public class MethodCoercions {
-
-    /**
-     * Returns a predicate that matches a method with the given name, and a single parameter that
-     * {@link brooklyn.util.flags.TypeCoercions#tryCoerce(Object, com.google.common.reflect.TypeToken)} can process
-     * from the given argument.
-     *
-     * @param methodName name of the method
-     * @param argument argument that is intended to be given
-     * @return a predicate that will match a compatible method
-     */
-    public static Predicate<Method> matchSingleParameterMethod(final String methodName, final Object argument) {
-        checkNotNull(methodName, "methodName");
-        checkNotNull(argument, "argument");
-
-        return new Predicate<Method>() {
-            @Override
-            public boolean apply(@Nullable Method input) {
-                if (input == null) return false;
-                if (!input.getName().equals(methodName)) return false;
-                Type[] parameterTypes = input.getGenericParameterTypes();
-                return parameterTypes.length == 1
-                        && TypeCoercions.tryCoerce(argument, TypeToken.of(parameterTypes[0])).isPresentAndNonNull();
-
-            }
-        };
-    }
-
-    /**
-     * Tries to find a single-parameter method with a parameter compatible with (can be coerced to) the argument, and
-     * invokes it.
-     *
-     * @param instance the object to invoke the method on
-     * @param methodName the name of the method to invoke
-     * @param argument the argument to the method's parameter.
-     * @return the result of the method call, or {@link brooklyn.util.guava.Maybe#absent()} if method could not be matched.
-     */
-    public static Maybe<?> tryFindAndInvokeSingleParameterMethod(final Object instance, final String methodName, final Object argument) {
-        Class<?> clazz = instance.getClass();
-        Iterable<Method> methods = Arrays.asList(clazz.getMethods());
-        Optional<Method> matchingMethod = Iterables.tryFind(methods, matchSingleParameterMethod(methodName, argument));
-        if (matchingMethod.isPresent()) {
-            Method method = matchingMethod.get();
-            try {
-                Type paramType = method.getGenericParameterTypes()[0];
-                Object coercedArgument = TypeCoercions.coerce(argument, TypeToken.of(paramType));
-                return Maybe.of(method.invoke(instance, coercedArgument));
-            } catch (IllegalAccessException | InvocationTargetException e) {
-                throw Exceptions.propagate(e);
-            }
-        } else {
-            return Maybe.absent();
-        }
-    }
-
-    /**
-     * Returns a predicate that matches a method with the given name, and parameters that
-     * {@link brooklyn.util.flags.TypeCoercions#tryCoerce(Object, com.google.common.reflect.TypeToken)} can process
-     * from the given list of arguments.
-     *
-     * @param methodName name of the method
-     * @param arguments arguments that is intended to be given
-     * @return a predicate that will match a compatible method
-     */
-    public static Predicate<Method> matchMultiParameterMethod(final String methodName, final List<?> arguments) {
-        checkNotNull(methodName, "methodName");
-        checkNotNull(arguments, "arguments");
-
-        return new Predicate<Method>() {
-            @Override
-            public boolean apply(@Nullable Method input) {
-                if (input == null) return false;
-                if (!input.getName().equals(methodName)) return false;
-                int numOptionParams = arguments.size();
-                Type[] parameterTypes = input.getGenericParameterTypes();
-                if (parameterTypes.length != numOptionParams) return false;
-
-                for (int paramCount = 0; paramCount < numOptionParams; paramCount++) {
-                    if (!TypeCoercions.tryCoerce(((List) arguments).get(paramCount),
-                            TypeToken.of(parameterTypes[paramCount])).isPresentAndNonNull()) return false;
-                }
-                return true;
-            }
-        };
-    }
-
-    /**
-     * Tries to find a multiple-parameter method with each parameter compatible with (can be coerced to) the
-     * corresponding argument, and invokes it.
-     *
-     * @param instance the object to invoke the method on
-     * @param methodName the name of the method to invoke
-     * @param argument a list of the arguments to the method's parameters.
-     * @return the result of the method call, or {@link brooklyn.util.guava.Maybe#absent()} if method could not be matched.
-     */
-    public static Maybe<?> tryFindAndInvokeMultiParameterMethod(final Object instance, final String methodName, final List<?> arguments) {
-        Class<?> clazz = instance.getClass();
-        Iterable<Method> methods = Arrays.asList(clazz.getMethods());
-        Optional<Method> matchingMethod = Iterables.tryFind(methods, matchMultiParameterMethod(methodName, arguments));
-        if (matchingMethod.isPresent()) {
-            Method method = matchingMethod.get();
-            try {
-                int numOptionParams = ((List)arguments).size();
-                Object[] coercedArguments = new Object[numOptionParams];
-                for (int paramCount = 0; paramCount < numOptionParams; paramCount++) {
-                    Object argument = arguments.get(paramCount);
-                    Type paramType = method.getGenericParameterTypes()[paramCount];
-                    coercedArguments[paramCount] = TypeCoercions.coerce(argument, TypeToken.of(paramType));
-                }
-                return Maybe.of(method.invoke(instance, coercedArguments));
-            } catch (IllegalAccessException | InvocationTargetException e) {
-                throw Exceptions.propagate(e);
-            }
-        } else {
-            return Maybe.absent();
-        }
-    }
-
-    /**
-     * Tries to find a method with each parameter compatible with (can be coerced to) the corresponding argument, and invokes it.
-     *
-     * @param instance the object to invoke the method on
-     * @param methodName the name of the method to invoke
-     * @param argument a list of the arguments to the method's parameters, or a single argument for a single-parameter method.
-     * @return the result of the method call, or {@link brooklyn.util.guava.Maybe#absent()} if method could not be matched.
-     */
-    public static Maybe<?> tryFindAndInvokeBestMatchingMethod(final Object instance, final String methodName, final Object argument) {
-        if (argument instanceof List) {
-            List<?> arguments = (List<?>) argument;
-
-            // ambiguous case: we can't tell if the user is using the multi-parameter syntax, or the single-parameter
-            // syntax for a method which takes a List parameter. So we try one, then fall back to the other.
-
-            Maybe<?> maybe = tryFindAndInvokeMultiParameterMethod(instance, methodName, arguments);
-            if (maybe.isAbsent())
-                maybe = tryFindAndInvokeSingleParameterMethod(instance, methodName, argument);
-
-            return maybe;
-        } else {
-            return tryFindAndInvokeSingleParameterMethod(instance, methodName, argument);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/flags/SetFromFlag.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/flags/SetFromFlag.java b/core/src/main/java/brooklyn/util/flags/SetFromFlag.java
deleted file mode 100644
index c7d588e..0000000
--- a/core/src/main/java/brooklyn/util/flags/SetFromFlag.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.flags;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Annotation to indicate that a variable may be set through the use of a named argument,
- * looking for the name specified here or inferred from the annotated field/argument/object.
- * <p>
- * This is used to automate the processing where named arguments are passed in constructors
- * and other methods, and the values of those named arguments should be transferred to
- * other known fields/arguments/objects at runtime.
- * <p>
- * Fields on a class are typically set from values in a map with a call to
- * {@link FlagUtils#setFieldsFromFlags(java.util.Map, Object)}.
- * That method (and related, in the same class) will attend to the arguments here.
- */
-@Retention(RetentionPolicy.RUNTIME)
-public @interface SetFromFlag {
-
-    /** the flag (key) which should be used to find the value; if empty defaults to field/argument/object name */
-    String value() default "";
-    
-    /** whether the object should not be changed once set; defaults to false
-     * <p>
-     * this is partially tested for in many routines, but not all;
-     * when nullable=false the testing (when done) is guaranteed.
-     * however if nullable is allowed we do not distinguish between null and unset
-     * so explicitly setting null then setting to a value is not detected as an illegal mutating.
-     */
-    boolean immutable() default false;
-    
-    /** whether the object is required & should not be set to null; defaults to true.
-     * (there is no 'required' parameter, but setting nullable false then invoking 
-     * e.g. {@link FlagUtils#checkRequiredFields(Object)} has the effect of requiring a value)
-     * <p>
-     * code should call that method explicitly to enforce nullable false;
-     * errors are not done during a call to setFieldsFromFlags 
-     * because fields may be initialised in multiple passes.) 
-     * <p>
-     * this is partially tested for in many routines, but not all
-     */
-    boolean nullable() default true;
-
-    /** The default value, if it is not explicitly set.
-     * <p>
-     * The value will be coerced from String where required, for types supported by {@link TypeCoercions}.
-     * <p>
-     * The field will be initialised with its default value on the first call to setFieldsFromFlags
-     * (or related).  (The field will not be initialised if that method is not called.) 
-     */
-    String defaultVal() default "";
-}


[03/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/policy

Posted by ha...@apache.org.
[BROOKLYN-162] Refactor package in ./core/policy


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

Branch: refs/heads/master
Commit: 6eacb3c38337d3c8733221b89b42fea3cdd93a23
Parents: a4c0e5f
Author: Hadrian Zbarcea <ha...@apache.org>
Authored: Mon Aug 17 02:03:16 2015 -0400
Committer: Hadrian Zbarcea <ha...@apache.org>
Committed: Mon Aug 17 14:52:04 2015 -0400

----------------------------------------------------------------------
 .../main/java/brooklyn/basic/BrooklynTypes.java |   2 +-
 .../enricher/basic/AbstractEnricher.java        |   2 +-
 .../brooklyn/entity/basic/AbstractEntity.java   |   6 +-
 .../group/AbstractMembershipTrackingPolicy.java |   2 +-
 .../entity/proxying/InternalEntityFactory.java  |   2 +-
 .../entity/proxying/InternalPolicyFactory.java  |   2 +-
 .../AbstractBrooklynObjectRebindSupport.java    |   2 +-
 .../entity/rebind/BasicEntityRebindSupport.java |   2 +-
 .../entity/rebind/BasicPolicyRebindSupport.java |   3 +-
 .../brooklyn/entity/rebind/RebindIteration.java |   2 +-
 .../entity/rebind/dto/MementosGenerators.java   |   2 +-
 .../java/brooklyn/event/feed/AbstractFeed.java  |   2 +-
 .../policy/basic/AbstractEntityAdjunct.java     | 510 -------------------
 .../brooklyn/policy/basic/AbstractPolicy.java   | 119 -----
 .../java/brooklyn/policy/basic/AdjunctType.java | 174 -------
 .../brooklyn/policy/basic/ConfigMapImpl.java    | 140 -----
 .../policy/basic/GeneralPurposePolicy.java      |  36 --
 .../java/brooklyn/policy/basic/Policies.java    |  73 ---
 .../policy/basic/PolicyDynamicType.java         |  44 --
 .../policy/basic/PolicyTypeSnapshot.java        |  40 --
 .../policy/basic/AbstractEntityAdjunct.java     | 510 +++++++++++++++++++
 .../core/policy/basic/AbstractPolicy.java       | 119 +++++
 .../brooklyn/core/policy/basic/AdjunctType.java | 174 +++++++
 .../core/policy/basic/ConfigMapImpl.java        | 140 +++++
 .../core/policy/basic/GeneralPurposePolicy.java |  36 ++
 .../brooklyn/core/policy/basic/Policies.java    |  73 +++
 .../core/policy/basic/PolicyDynamicType.java    |  44 ++
 .../core/policy/basic/PolicyTypeSnapshot.java   |  40 ++
 .../entity/EntityPreManagementTest.java         |   2 +-
 .../brooklyn/entity/basic/EntitySpecTest.java   |   2 +-
 .../entity/basic/PolicyRegistrationTest.java    |   2 +-
 .../entity/group/GroupPickUpEntitiesTest.java   |   2 +-
 .../entity/rebind/RebindCatalogItemTest.java    |   3 +-
 .../entity/rebind/RebindFailuresTest.java       |   2 +-
 .../entity/rebind/RebindPolicyTest.java         |   2 +-
 .../brooklyn/policy/basic/BasicPolicyTest.java  |  89 ----
 .../brooklyn/policy/basic/EnricherTypeTest.java |  59 ---
 .../brooklyn/policy/basic/PolicyConfigTest.java | 202 --------
 .../policy/basic/PolicySubscriptionTest.java    | 125 -----
 .../brooklyn/policy/basic/PolicyTypeTest.java   |  58 ---
 .../java/brooklyn/test/policy/TestPolicy.java   |   2 +-
 .../core/policy/basic/BasicPolicyTest.java      |  90 ++++
 .../core/policy/basic/EnricherTypeTest.java     |  59 +++
 .../core/policy/basic/PolicyConfigTest.java     | 202 ++++++++
 .../policy/basic/PolicySubscriptionTest.java    | 128 +++++
 .../core/policy/basic/PolicyTypeTest.java       |  59 +++
 .../policy/os/AdvertiseWinrmLoginPolicy.java    |   3 +-
 .../brooklyn/policy/os/CreateUserPolicy.java    |   2 +-
 .../policy/autoscaling/AutoScalerPolicy.java    |   2 +-
 .../policy/followthesun/FollowTheSunPolicy.java |   2 +-
 .../policy/ha/AbstractFailureDetector.java      |   2 +-
 .../policy/ha/ConditionalSuspendPolicy.java     |   2 +-
 .../brooklyn/policy/ha/ServiceReplacer.java     |   2 +-
 .../brooklyn/policy/ha/ServiceRestarter.java    |   2 +-
 .../loadbalancing/LoadBalancingPolicy.java      |   2 +-
 .../camp/brooklyn/TestReferencingPolicy.java    |   2 +-
 .../apache/brooklyn/cli/lister/ClassFinder.java |   2 +-
 .../brooklyn/rest/resources/PolicyResource.java |   4 +-
 .../rest/transform/PolicyTransformer.java       |   2 +-
 .../rest/util/BrooklynRestResourceUtils.java    |   2 +-
 .../rest/testing/mocks/CapitalizePolicy.java    |   3 +-
 .../testing/mocks/RestMockSimplePolicy.java     |   2 +-
 .../util/BrooklynRestResourceUtilsTest.java     |   2 +-
 .../java/brooklyn/osgi/tests/SimplePolicy.java  |   2 +-
 .../brooklyn/osgi/tests/more/MorePolicy.java    |   3 +-
 .../brooklyn/osgi/tests/more/MorePolicy.java    |   2 +-
 66 files changed, 1716 insertions(+), 1718 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/basic/BrooklynTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/basic/BrooklynTypes.java b/core/src/main/java/brooklyn/basic/BrooklynTypes.java
index 64dbcd0..780349f 100644
--- a/core/src/main/java/brooklyn/basic/BrooklynTypes.java
+++ b/core/src/main/java/brooklyn/basic/BrooklynTypes.java
@@ -26,11 +26,11 @@ import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.policy.basic.PolicyDynamicType;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.basic.EnricherDynamicType;
 import brooklyn.entity.basic.EntityDynamicType;
-import brooklyn.policy.basic.PolicyDynamicType;
 import brooklyn.util.exceptions.Exceptions;
 
 import com.google.common.collect.Maps;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java b/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java
index 5bca143..651ea56 100644
--- a/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java
+++ b/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.mementos.EnricherMemento;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.EnricherType;
+import org.apache.brooklyn.core.policy.basic.AbstractEntityAdjunct;
 import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 import brooklyn.config.ConfigKey;
@@ -36,7 +37,6 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.rebind.BasicEnricherRebindSupport;
-import brooklyn.policy.basic.AbstractEntityAdjunct;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.Maps;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java b/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
index 4bdae46..b004754 100644
--- a/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
+++ b/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
@@ -60,6 +60,9 @@ import org.apache.brooklyn.core.management.internal.EffectorUtils;
 import org.apache.brooklyn.core.management.internal.EntityManagementSupport;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.management.internal.SubscriptionTracker;
+import org.apache.brooklyn.core.policy.basic.AbstractEntityAdjunct;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
+import org.apache.brooklyn.core.policy.basic.AbstractEntityAdjunct.AdjunctTagSupport;
 import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.core.util.flags.FlagUtils;
 import org.apache.brooklyn.core.util.flags.TypeCoercions;
@@ -88,9 +91,6 @@ import brooklyn.internal.storage.impl.BasicReference;
 
 import org.apache.brooklyn.location.basic.Locations;
 
-import brooklyn.policy.basic.AbstractEntityAdjunct;
-import brooklyn.policy.basic.AbstractEntityAdjunct.AdjunctTagSupport;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/entity/group/AbstractMembershipTrackingPolicy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/group/AbstractMembershipTrackingPolicy.java b/core/src/main/java/brooklyn/entity/group/AbstractMembershipTrackingPolicy.java
index 5647975..400506f 100644
--- a/core/src/main/java/brooklyn/entity/group/AbstractMembershipTrackingPolicy.java
+++ b/core/src/main/java/brooklyn/entity/group/AbstractMembershipTrackingPolicy.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,7 +38,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.DynamicGroup;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.javalang.JavaClassNames;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
index 1c1a920..eb601ed 100644
--- a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
+++ b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
@@ -37,6 +37,7 @@ import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.FlagUtils;
 import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
@@ -48,7 +49,6 @@ import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
index 7c53404..c174dcb 100644
--- a/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
+++ b/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
@@ -27,12 +27,12 @@ import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.basic.AbstractEntity;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
index 19a6cd6..000a651 100644
--- a/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
@@ -22,12 +22,12 @@ import org.apache.brooklyn.api.entity.rebind.RebindContext;
 import org.apache.brooklyn.api.entity.rebind.RebindSupport;
 import org.apache.brooklyn.api.mementos.Memento;
 import org.apache.brooklyn.api.policy.EntityAdjunct;
+import org.apache.brooklyn.core.policy.basic.AbstractEntityAdjunct.AdjunctTagSupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.basic.AbstractBrooklynObject;
 import brooklyn.entity.rebind.dto.MementosGenerators;
-import brooklyn.policy.basic.AbstractEntityAdjunct.AdjunctTagSupport;
 import brooklyn.util.text.Strings;
 
 public abstract class AbstractBrooklynObjectRebindSupport<T extends Memento> implements RebindSupport<T> {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/entity/rebind/BasicEntityRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/BasicEntityRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/BasicEntityRebindSupport.java
index 3a29595..f059a83 100644
--- a/core/src/main/java/brooklyn/entity/rebind/BasicEntityRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/BasicEntityRebindSupport.java
@@ -31,6 +31,7 @@ import org.apache.brooklyn.api.entity.rebind.RebindContext;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.mementos.EntityMemento;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -41,7 +42,6 @@ import brooklyn.entity.basic.AbstractGroupImpl;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.rebind.dto.MementosGenerators;
 import brooklyn.event.feed.AbstractFeed;
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.exceptions.Exceptions;
 
 import com.google.common.base.Throwables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/entity/rebind/BasicPolicyRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/BasicPolicyRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/BasicPolicyRebindSupport.java
index 4623225..e511ce2 100644
--- a/core/src/main/java/brooklyn/entity/rebind/BasicPolicyRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/BasicPolicyRebindSupport.java
@@ -20,11 +20,10 @@ package brooklyn.entity.rebind;
 
 import org.apache.brooklyn.api.entity.rebind.RebindContext;
 import org.apache.brooklyn.api.mementos.PolicyMemento;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.core.util.flags.FlagUtils;
 
-import brooklyn.policy.basic.AbstractPolicy;
-
 public class BasicPolicyRebindSupport extends AbstractBrooklynObjectRebindSupport<PolicyMemento> {
 
     private final AbstractPolicy policy;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
index 67d20ae..eb1a461 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
@@ -73,6 +73,7 @@ import org.apache.brooklyn.core.management.internal.EntityManagerInternal;
 import org.apache.brooklyn.core.management.internal.LocationManagerInternal;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.management.internal.ManagementTransitionMode;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.flags.FlagUtils;
 
 import brooklyn.config.BrooklynLogging;
@@ -92,7 +93,6 @@ import brooklyn.event.feed.AbstractFeed;
 import org.apache.brooklyn.location.basic.AbstractLocation;
 import org.apache.brooklyn.location.basic.LocationInternal;
 
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java b/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
index 5c52788..301211d 100644
--- a/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
+++ b/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
@@ -50,6 +50,7 @@ import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.EntityAdjunct;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.core.catalog.internal.CatalogItemDo;
+import org.apache.brooklyn.core.policy.basic.AbstractPolicy;
 import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.core.util.flags.FlagUtils;
 
@@ -64,7 +65,6 @@ import brooklyn.event.feed.AbstractFeed;
 
 import org.apache.brooklyn.location.basic.LocationInternal;
 
-import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableMap;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/event/feed/AbstractFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/AbstractFeed.java b/core/src/main/java/brooklyn/event/feed/AbstractFeed.java
index 23037a7..59ed4f7 100644
--- a/core/src/main/java/brooklyn/event/feed/AbstractFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/AbstractFeed.java
@@ -27,6 +27,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.rebind.RebindSupport;
 import org.apache.brooklyn.api.mementos.FeedMemento;
 import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
+import org.apache.brooklyn.core.policy.basic.AbstractEntityAdjunct;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,7 +35,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.rebind.BasicFeedRebindSupport;
-import brooklyn.policy.basic.AbstractEntityAdjunct;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java b/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java
deleted file mode 100644
index afa015f..0000000
--- a/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java
+++ /dev/null
@@ -1,510 +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.policy.basic;
-
-import static brooklyn.util.GroovyJavaMethods.truth;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.Group;
-import org.apache.brooklyn.api.entity.basic.EntityLocal;
-import org.apache.brooklyn.api.entity.trait.Configurable;
-import org.apache.brooklyn.api.event.AttributeSensor;
-import org.apache.brooklyn.api.event.Sensor;
-import org.apache.brooklyn.api.event.SensorEventListener;
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.SubscriptionContext;
-import org.apache.brooklyn.api.management.SubscriptionHandle;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.policy.EntityAdjunct;
-import org.apache.brooklyn.core.management.internal.SubscriptionTracker;
-import org.apache.brooklyn.core.util.config.ConfigBag;
-import org.apache.brooklyn.core.util.flags.FlagUtils;
-import org.apache.brooklyn.core.util.flags.SetFromFlag;
-import org.apache.brooklyn.core.util.flags.TypeCoercions;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.basic.AbstractBrooklynObject;
-import brooklyn.basic.BrooklynObjectInternal;
-import brooklyn.config.ConfigKey;
-import brooklyn.config.ConfigKey.HasConfigKey;
-import brooklyn.config.ConfigMap;
-import brooklyn.enricher.basic.AbstractEnricher;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.text.Strings;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-
-
-/**
- * Common functionality for policies and enrichers
- */
-public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject implements BrooklynObjectInternal, EntityAdjunct, Configurable {
-    private static final Logger log = LoggerFactory.getLogger(AbstractEntityAdjunct.class);
-
-    private boolean _legacyNoConstructionInit;
-
-    /**
-     * @deprecated since 0.7.0; leftover properties are put into config, since when coming from yaml this is normal.
-     */
-    @Deprecated
-    protected Map<String,Object> leftoverProperties = Maps.newLinkedHashMap();
-
-    protected transient ExecutionContext execution;
-
-    private final BasicConfigurationSupport config = new BasicConfigurationSupport();
-    
-    /**
-     * The config values of this entity. Updating this map should be done
-     * via {@link #config()}.
-     * 
-     * @deprecated since 0.7.0; use {@link #config()} instead; this field may be made private or deleted in a future release.
-     */
-    @Deprecated
-    protected final ConfigMapImpl configsInternal = new ConfigMapImpl(this);
-
-    /**
-     * @deprecated since 0.7.0; use {@link #getAdjunctType()} instead; this field may be made private or deleted in a future release.
-     */
-    @Deprecated
-    protected final AdjunctType adjunctType = new AdjunctType(this);
-
-    @SetFromFlag
-    protected String name;
-    
-    protected transient EntityLocal entity;
-    
-    /** not for direct access; refer to as 'subscriptionTracker' via getter so that it is initialized */
-    protected transient SubscriptionTracker _subscriptionTracker;
-    
-    private AtomicBoolean destroyed = new AtomicBoolean(false);
-    
-    @SetFromFlag(value="uniqueTag")
-    protected String uniqueTag;
-
-    public AbstractEntityAdjunct() {
-        this(Collections.emptyMap());
-    }
-    
-    public AbstractEntityAdjunct(@SuppressWarnings("rawtypes") Map properties) {
-        super(properties);
-        _legacyNoConstructionInit = (properties != null) && Boolean.TRUE.equals(properties.get("noConstructionInit"));
-        
-        if (isLegacyConstruction()) {
-            AbstractBrooklynObject checkWeGetThis = configure(properties);
-            assert this.equals(checkWeGetThis) : this+" configure method does not return itself; returns "+checkWeGetThis+" instead of "+this;
-
-            boolean deferConstructionChecks = (properties.containsKey("deferConstructionChecks") && TypeCoercions.coerce(properties.get("deferConstructionChecks"), Boolean.class));
-            if (!deferConstructionChecks) {
-                FlagUtils.checkRequiredFields(this);
-            }
-        }
-    }
-
-    /**
-     * @deprecated since 0.7.0; only used for legacy brooklyn types where constructor is called directly
-     */
-    @Override
-    @Deprecated
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public AbstractEntityAdjunct configure(Map flags) {
-        // TODO only set on first time through
-        boolean isFirstTime = true;
-        
-        // allow config keys, and fields, to be set from these flags if they have a SetFromFlag annotation
-        // or if the value is a config key
-        for (Iterator<Map.Entry> iter = flags.entrySet().iterator(); iter.hasNext();) {
-            Map.Entry entry = iter.next();
-            if (entry.getKey() instanceof ConfigKey) {
-                ConfigKey key = (ConfigKey)entry.getKey();
-                if (adjunctType.getConfigKeys().contains(key)) {
-                    setConfig(key, entry.getValue());
-                } else {
-                    log.warn("Unknown configuration key {} for policy {}; ignoring", key, this);
-                    iter.remove();
-                }
-            }
-        }
-
-        ConfigBag bag = new ConfigBag().putAll(flags);
-        FlagUtils.setFieldsFromFlags(this, bag, isFirstTime);
-        FlagUtils.setAllConfigKeys(this, bag, false);
-        leftoverProperties.putAll(bag.getUnusedConfig());
-
-        //replace properties _contents_ with leftovers so subclasses see leftovers only
-        flags.clear();
-        flags.putAll(leftoverProperties);
-        leftoverProperties = flags;
-        
-        if (!truth(name) && flags.containsKey("displayName")) {
-            //TODO inconsistent with entity and location, where name is legacy and displayName is encouraged!
-            //'displayName' is a legacy way to refer to a policy's name
-            Preconditions.checkArgument(flags.get("displayName") instanceof CharSequence, "'displayName' property should be a string");
-            setDisplayName(flags.remove("displayName").toString());
-        }
-        
-        // set leftover flags should as config items; particularly useful when these have come from a brooklyn.config map 
-        for (Object flag: flags.keySet()) {
-            ConfigKey<Object> key = ConfigKeys.newConfigKey(Object.class, Strings.toString(flag));
-            if (config().getRaw(key).isPresent()) {
-                log.warn("Config '"+flag+"' on "+this+" conflicts with key already set; ignoring");
-            } else {
-                config().set(key, flags.get(flag));
-            }
-        }
-        
-        return this;
-    }
-    
-    /**
-     * Used for legacy-style policies/enrichers on rebind, to indicate that init() should not be called.
-     * Will likely be deleted in a future release; should not be called apart from by framework code.
-     */
-    @Beta
-    protected boolean isLegacyNoConstructionInit() {
-        return _legacyNoConstructionInit;
-    }
-
-    @Override
-    public ConfigurationSupportInternal config() {
-        return config;
-    }
-
-    private class BasicConfigurationSupport implements ConfigurationSupportInternal {
-
-        @Override
-        public <T> T get(ConfigKey<T> key) {
-            return configsInternal.getConfig(key);
-        }
-
-        @Override
-        public <T> T get(HasConfigKey<T> key) {
-            return get(key.getConfigKey());
-        }
-
-        @SuppressWarnings("unchecked")
-        @Override
-        public <T> T set(ConfigKey<T> key, T val) {
-            if (entity != null && isRunning()) {
-                doReconfigureConfig(key, val);
-            }
-            T result = (T) configsInternal.setConfig(key, val);
-            onChanged();
-            return result;
-        }
-
-        @Override
-        public <T> T set(HasConfigKey<T> key, T val) {
-            return setConfig(key.getConfigKey(), val);
-        }
-
-        @SuppressWarnings("unchecked")
-        @Override
-        public <T> T set(ConfigKey<T> key, Task<T> val) {
-            if (entity != null && isRunning()) {
-                // TODO Support for AbstractEntityAdjunct
-                throw new UnsupportedOperationException();
-            }
-            T result = (T) configsInternal.setConfig(key, val);
-            onChanged();
-            return result;
-        }
-
-        @Override
-        public <T> T set(HasConfigKey<T> key, Task<T> val) {
-            return set(key.getConfigKey(), val);
-        }
-
-        @Override
-        public ConfigBag getBag() {
-            return getLocalBag();
-        }
-
-        @Override
-        public ConfigBag getLocalBag() {
-            return ConfigBag.newInstance(configsInternal.getAllConfig());
-        }
-
-        @Override
-        public Maybe<Object> getRaw(ConfigKey<?> key) {
-            return configsInternal.getConfigRaw(key, true);
-        }
-
-        @Override
-        public Maybe<Object> getRaw(HasConfigKey<?> key) {
-            return getRaw(key.getConfigKey());
-        }
-
-        @Override
-        public Maybe<Object> getLocalRaw(ConfigKey<?> key) {
-            return configsInternal.getConfigRaw(key, false);
-        }
-
-        @Override
-        public Maybe<Object> getLocalRaw(HasConfigKey<?> key) {
-            return getLocalRaw(key.getConfigKey());
-        }
-
-        @Override
-        public void addToLocalBag(Map<String, ?> vals) {
-            configsInternal.addToLocalBag(vals);
-        }
-        
-        @Override
-        public void removeFromLocalBag(String key) {
-            configsInternal.removeFromLocalBag(key);
-        }
-        
-        @Override
-        public void refreshInheritedConfig() {
-            // no-op for location
-        }
-        
-        @Override
-        public void refreshInheritedConfigOfChildren() {
-            // no-op for location
-        }
-    }
-
-    public <T> T getConfig(ConfigKey<T> key) {
-        return config().get(key);
-    }
-    
-    protected <K> K getRequiredConfig(ConfigKey<K> key) {
-        K result = config().get(key);
-        if (result==null) 
-            throw new NullPointerException("Value required for '"+key.getName()+"' in "+this);
-        return result;
-    }
-
-    @Override
-    @Deprecated
-    public <T> T setConfig(ConfigKey<T> key, T val) {
-        return config().set(key, val);
-    }
-    
-    // TODO make immutable
-    /** for inspection only */
-    @Beta
-    @Deprecated
-    public ConfigMap getConfigMap() {
-        return configsInternal;
-    }
-    
-    /**
-     * Invoked whenever a config change is applied after management is started.
-     * Default implementation throws an exception to disallow the change. 
-     * Can be overridden to return (allowing the change) or to make other changes 
-     * (if necessary), and of course it can do this selectively and call the super to disallow any others. */
-    protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) {
-        throw new UnsupportedOperationException("reconfiguring "+key+" unsupported for "+this);
-    }
-    
-    @Override
-    protected void onTagsChanged() {
-        onChanged();
-    }
-    
-    protected abstract void onChanged();
-    
-    protected AdjunctType getAdjunctType() {
-        return adjunctType;
-    }
-    
-    @Override
-    public String getDisplayName() {
-        if (name!=null && name.length()>0) return name;
-        return getClass().getCanonicalName();
-    }
-    
-    public void setDisplayName(String name) {
-        this.name = name;
-    }
-
-    public void setEntity(EntityLocal entity) {
-        if (destroyed.get()) throw new IllegalStateException("Cannot set entity on a destroyed entity adjunct");
-        this.entity = entity;
-        if (entity!=null && getCatalogItemId() == null) {
-            setCatalogItemId(entity.getCatalogItemId());
-        }
-    }
-    
-    /** @deprecated since 0.7.0 only {@link AbstractEnricher} has emit convenience */
-    protected <T> void emit(Sensor<T> sensor, Object val) {
-        checkState(entity != null, "entity must first be set");
-        if (val == Entities.UNCHANGED) {
-            return;
-        }
-        if (val == Entities.REMOVE) {
-            ((EntityInternal)entity).removeAttribute((AttributeSensor<T>) sensor);
-            return;
-        }
-        
-        T newVal = TypeCoercions.coerce(val, sensor.getTypeToken());
-        if (sensor instanceof AttributeSensor) {
-            entity.setAttribute((AttributeSensor<T>)sensor, newVal);
-        } else { 
-            entity.emit(sensor, newVal);
-        }
-    }
-
-    protected synchronized SubscriptionTracker getSubscriptionTracker() {
-        if (_subscriptionTracker!=null) return _subscriptionTracker;
-        if (entity==null) return null;
-        _subscriptionTracker = new SubscriptionTracker(((EntityInternal)entity).getManagementSupport().getSubscriptionContext());
-        return _subscriptionTracker;
-    }
-    
-    /** @see SubscriptionContext#subscribe(Entity, Sensor, SensorEventListener) */
-    protected <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        if (!checkCanSubscribe()) return null;
-        return getSubscriptionTracker().subscribe(producer, sensor, listener);
-    }
-
-    /** @see SubscriptionContext#subscribe(Entity, Sensor, SensorEventListener) */
-    protected <T> SubscriptionHandle subscribeToMembers(Group producerGroup, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        if (!checkCanSubscribe(producerGroup)) return null;
-        return getSubscriptionTracker().subscribeToMembers(producerGroup, sensor, listener);
-    }
-
-    /** @see SubscriptionContext#subscribe(Entity, Sensor, SensorEventListener) */
-    protected <T> SubscriptionHandle subscribeToChildren(Entity producerParent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        if (!checkCanSubscribe(producerParent)) return null;
-        return getSubscriptionTracker().subscribeToChildren(producerParent, sensor, listener);
-    }
-
-    /** @deprecated since 0.7.0 use {@link #checkCanSubscribe(Entity)} */
-    @Deprecated
-    protected boolean check(Entity requiredEntity) {
-        return checkCanSubscribe(requiredEntity);
-    }
-    /** returns false if deleted, throws exception if invalid state, otherwise true.
-     * okay if entity is not yet managed (but not if entity is no longer managed). */
-    protected boolean checkCanSubscribe(Entity producer) {
-        if (destroyed.get()) return false;
-        if (producer==null) throw new IllegalStateException(this+" given a null target for subscription");
-        if (entity==null) throw new IllegalStateException(this+" cannot subscribe to "+producer+" because it is not associated to an entity");
-        if (((EntityInternal)entity).getManagementSupport().isNoLongerManaged()) throw new IllegalStateException(this+" cannot subscribe to "+producer+" because the associated entity "+entity+" is no longer managed");
-        return true;
-    }
-    protected boolean checkCanSubscribe() {
-        if (destroyed.get()) return false;
-        if (entity==null) throw new IllegalStateException(this+" cannot subscribe because it is not associated to an entity");
-        if (((EntityInternal)entity).getManagementSupport().isNoLongerManaged()) throw new IllegalStateException(this+" cannot subscribe because the associated entity "+entity+" is no longer managed");
-        return true;
-    }
-        
-    /**
-     * Unsubscribes the given producer.
-     *
-     * @see SubscriptionContext#unsubscribe(SubscriptionHandle)
-     */
-    protected boolean unsubscribe(Entity producer) {
-        if (destroyed.get()) return false;
-        return getSubscriptionTracker().unsubscribe(producer);
-    }
-
-    /**
-    * Unsubscribes the given producer.
-    *
-    * @see SubscriptionContext#unsubscribe(SubscriptionHandle)
-    */
-   protected boolean unsubscribe(Entity producer, SubscriptionHandle handle) {
-       if (destroyed.get()) return false;
-       return getSubscriptionTracker().unsubscribe(producer, handle);
-   }
-
-    /**
-    * @return a list of all subscription handles
-    */
-    protected Collection<SubscriptionHandle> getAllSubscriptions() {
-        SubscriptionTracker tracker = getSubscriptionTracker();
-        return (tracker != null) ? tracker.getAllSubscriptions() : Collections.<SubscriptionHandle>emptyList();
-    }
-    
-    /** 
-     * Unsubscribes and clears all managed subscriptions; is called by the owning entity when a policy is removed
-     * and should always be called by any subclasses overriding this method
-     */
-    public void destroy() {
-        destroyed.set(true);
-        SubscriptionTracker tracker = getSubscriptionTracker();
-        if (tracker != null) tracker.unsubscribeAll();
-    }
-    
-    @Override
-    public boolean isDestroyed() {
-        return destroyed.get();
-    }
-    
-    @Override
-    public boolean isRunning() {
-        return !isDestroyed();
-    }
-
-    @Override
-    public String getUniqueTag() {
-        return uniqueTag;
-    }
-
-    public TagSupport tags() {
-        return new AdjunctTagSupport();
-    }
-
-    public class AdjunctTagSupport extends BasicTagSupport {
-        @Override
-        public Set<Object> getTags() {
-            ImmutableSet.Builder<Object> rb = ImmutableSet.builder().addAll(super.getTags());
-            if (getUniqueTag()!=null) rb.add(getUniqueTag());
-            return rb.build();
-        }
-        public String getUniqueTag() {
-            return AbstractEntityAdjunct.this.getUniqueTag();
-        }
-        public void setUniqueTag(String uniqueTag) {
-            AbstractEntityAdjunct.this.uniqueTag = uniqueTag;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(getClass()).omitNullValues()
-                .add("name", name)
-                .add("uniqueTag", uniqueTag)
-                .add("running", isRunning())
-                .add("entity", entity)
-                .add("id", getId())
-                .toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/policy/basic/AbstractPolicy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/policy/basic/AbstractPolicy.java b/core/src/main/java/brooklyn/policy/basic/AbstractPolicy.java
deleted file mode 100644
index e935e4b..0000000
--- a/core/src/main/java/brooklyn/policy/basic/AbstractPolicy.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.policy.basic;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.brooklyn.api.entity.rebind.RebindSupport;
-import org.apache.brooklyn.api.entity.trait.Configurable;
-import org.apache.brooklyn.api.mementos.PolicyMemento;
-import org.apache.brooklyn.api.policy.Policy;
-import org.apache.brooklyn.api.policy.PolicyType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.rebind.BasicPolicyRebindSupport;
-
-import com.google.common.base.Objects;
-
-/**
- * Base {@link Policy} implementation; all policies should extend this or its children
- */
-public abstract class AbstractPolicy extends AbstractEntityAdjunct implements Policy, Configurable {
-    @SuppressWarnings("unused")
-    private static final Logger log = LoggerFactory.getLogger(AbstractPolicy.class);
-
-    protected String policyStatus;
-    protected AtomicBoolean suspended = new AtomicBoolean(false);
-
-    private final PolicyDynamicType policyType;
-    
-    public AbstractPolicy() {
-        this(Collections.emptyMap());
-    }
-    
-    public AbstractPolicy(Map<?,?> flags) {
-        super(flags);
-        
-        // TODO Don't let `this` reference escape during construction
-        policyType = new PolicyDynamicType(this);
-        
-        if (isLegacyConstruction() && !isLegacyNoConstructionInit()) {
-            init();
-        }
-    }
-
-    @Override
-    public PolicyType getPolicyType() {
-        return policyType.getSnapshot();
-    }
-
-    @Override
-    public void suspend() {
-        suspended.set(true);
-    }
-
-    @Override
-    public void resume() {
-        suspended.set(false);
-    }
-
-    @Override
-    public boolean isSuspended() {
-        if (suspended==null) {
-            // only if accessed during construction in super, e.g. by a call to toString in configure
-            return true;
-        }
-        return suspended.get();
-    }
-
-    @Override
-    public void destroy(){
-        suspend();
-        super.destroy();
-    }
-
-    @Override
-    public boolean isRunning() {
-        return !isSuspended() && !isDestroyed();
-    }
-
-    @Override
-    protected void onChanged() {
-        // currently changes simply trigger re-persistence; there is no intermediate listener as we do for EntityChangeListener
-        if (getManagementContext() != null) {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(this);
-        }
-    }
-    
-    @Override
-    public RebindSupport<PolicyMemento> getRebindSupport() {
-        return new BasicPolicyRebindSupport(this);
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(getClass())
-                .add("name", name)
-                .add("running", isRunning())
-                .toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/policy/basic/AdjunctType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/policy/basic/AdjunctType.java b/core/src/main/java/brooklyn/policy/basic/AdjunctType.java
deleted file mode 100644
index f53f822..0000000
--- a/core/src/main/java/brooklyn/policy/basic/AdjunctType.java
+++ /dev/null
@@ -1,174 +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.policy.basic;
-
-import java.io.Serializable;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.brooklyn.api.policy.EntityAdjunct;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.config.ConfigKey.HasConfigKey;
-
-import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-
-/**
- * This is the actual type of a policy instance at runtime.
- */
-public class AdjunctType implements Serializable {
-    private static final long serialVersionUID = -662979234559595903L;
-
-    private static final Logger LOG = LoggerFactory.getLogger(AdjunctType.class);
-
-    private final String name;
-    private final Map<String, ConfigKey<?>> configKeys;
-    private final Set<ConfigKey<?>> configKeysSet;
-
-    public AdjunctType(AbstractEntityAdjunct adjunct) {
-        this(adjunct.getClass(), adjunct);
-    }
-    
-    protected AdjunctType(Class<? extends EntityAdjunct> clazz) {
-        this(clazz, null);
-    }
-    
-    private AdjunctType(Class<? extends EntityAdjunct> clazz, AbstractEntityAdjunct adjunct) {
-        name = clazz.getCanonicalName();
-        configKeys = Collections.unmodifiableMap(findConfigKeys(clazz, null));
-        configKeysSet = ImmutableSet.copyOf(this.configKeys.values());
-        if (LOG.isTraceEnabled())
-            LOG.trace("Policy {} config keys: {}", name, Joiner.on(", ").join(configKeys.keySet()));
-    }
-    
-    AdjunctType(String name, Map<String, ConfigKey<?>> configKeys) {
-        this.name = name;
-        this.configKeys = ImmutableMap.copyOf(configKeys);
-        this.configKeysSet = ImmutableSet.copyOf(this.configKeys.values());
-    }
-
-    public String getName() {
-        return name;
-    }
-    
-    public Set<ConfigKey<?>> getConfigKeys() {
-        return configKeysSet;
-    }
-    
-    public ConfigKey<?> getConfigKey(String name) {
-        return configKeys.get(name);
-    }
-    
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(name, configKeys);
-    }
-    
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (getClass() != obj.getClass()) return false;
-        AdjunctType o = (AdjunctType) obj;
-        if (!Objects.equal(name, o.getName())) return false;
-        if (!Objects.equal(getConfigKeys(), o.getConfigKeys())) return false;
-        return true;
-    }
-    
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(name)
-                .add("configKeys", configKeys)
-                .toString();
-    }
-    
-    /**
-     * Finds the config keys defined on the entity's class, statics and optionally any non-static (discouraged).
-     */
-    // TODO Remove duplication from EntityDynamicType
-    protected static Map<String,ConfigKey<?>> findConfigKeys(Class<? extends EntityAdjunct> clazz, EntityAdjunct optionalInstance) {
-        try {
-            Map<String,ConfigKey<?>> result = Maps.newLinkedHashMap();
-            Map<String,Field> configFields = Maps.newLinkedHashMap();
-            for (Field f : clazz.getFields()) {
-                boolean isConfigKey = ConfigKey.class.isAssignableFrom(f.getType());
-                if (!isConfigKey) {
-                    if (!HasConfigKey.class.isAssignableFrom(f.getType())) {
-                        // neither ConfigKey nor HasConfigKey
-                        continue;
-                    }
-                }
-                if (!Modifier.isStatic(f.getModifiers())) {
-                    // require it to be static or we have an instance
-                    LOG.warn("Discouraged use of non-static config key "+f+" defined in " + (optionalInstance!=null ? optionalInstance : clazz));
-                    if (optionalInstance==null) continue;
-                }
-                ConfigKey<?> k = isConfigKey ? (ConfigKey<?>) f.get(optionalInstance) : 
-                    ((HasConfigKey<?>)f.get(optionalInstance)).getConfigKey();
-
-                Field alternativeField = configFields.get(k.getName());
-                // Allow overriding config keys (e.g. to set default values) when there is an assignable-from relationship between classes
-                Field definitiveField = alternativeField != null ? inferSubbestField(alternativeField, f) : f;
-                boolean skip = false;
-                if (definitiveField != f) {
-                    // If they refer to the _same_ instance, just keep the one we already have
-                    if (alternativeField.get(optionalInstance) == f.get(optionalInstance)) skip = true;
-                }
-                if (skip) {
-                    //nothing
-                } else if (definitiveField == f) {
-                    result.put(k.getName(), k);
-                    configFields.put(k.getName(), f);
-                } else if (definitiveField != null) {
-                    if (LOG.isDebugEnabled()) LOG.debug("multiple definitions for config key {} on {}; preferring that in sub-class: {} to {}", new Object[] {
-                            k.getName(), optionalInstance!=null ? optionalInstance : clazz, alternativeField, f});
-                } else if (definitiveField == null) {
-                    LOG.warn("multiple definitions for config key {} on {}; preferring {} to {}", new Object[] {
-                            k.getName(), optionalInstance!=null ? optionalInstance : clazz, alternativeField, f});
-                }
-            }
-            
-            return result;
-        } catch (IllegalAccessException e) {
-            throw Throwables.propagate(e);
-        }
-    }
-    
-    /**
-     * Gets the field that is in the sub-class; or null if one field does not come from a sub-class of the other field's class
-     */
-    // TODO Remove duplication from EntityDynamicType
-    private static Field inferSubbestField(Field f1, Field f2) {
-        Class<?> c1 = f1.getDeclaringClass();
-        Class<?> c2 = f2.getDeclaringClass();
-        boolean isSuper1 = c1.isAssignableFrom(c2);
-        boolean isSuper2 = c2.isAssignableFrom(c1);
-        return (isSuper1) ? (isSuper2 ? null : f2) : (isSuper2 ? f1 : null);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java b/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java
deleted file mode 100644
index 467eb05..0000000
--- a/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.policy.basic;
-
-import static brooklyn.util.GroovyJavaMethods.elvis;
-
-import java.util.Collections;
-import java.util.Map;
-
-import org.apache.brooklyn.api.entity.basic.EntityLocal;
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.core.util.flags.TypeCoercions;
-import org.apache.brooklyn.core.util.internal.ConfigKeySelfExtracting;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Maps;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.config.internal.AbstractConfigMapImpl;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.entity.basic.Sanitizer;
-import brooklyn.event.basic.StructuredConfigKey;
-import brooklyn.util.guava.Maybe;
-
-public class ConfigMapImpl extends AbstractConfigMapImpl {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ConfigMapImpl.class);
-
-    /** policy against which config resolution / task execution will occur */
-    private final AbstractEntityAdjunct adjunct;
-
-    /*
-     * TODO An alternative implementation approach would be to have:
-     *   setParent(Entity o, Map<ConfigKey,Object> inheritedConfig=[:])
-     * The idea is that the parent could in theory decide explicitly what in its config
-     * would be shared.
-     * I (Aled) am undecided as to whether that would be better...
-     * 
-     * (Alex) i lean toward the config key getting to make the decision
-     */
-
-    public ConfigMapImpl(AbstractEntityAdjunct adjunct) {
-        this.adjunct = Preconditions.checkNotNull(adjunct, "AbstractEntityAdjunct must be specified");
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public <T> T getConfig(ConfigKey<T> key, T defaultValue) {
-        // FIXME What about inherited task in config?!
-        //              alex says: think that should work, no?
-        // FIXME What if someone calls getConfig on a task, before setting parent app?
-        //              alex says: not supported (throw exception, or return the task)
-        
-        // In case this entity class has overridden the given key (e.g. to set default), then retrieve this entity's key
-        // TODO If ask for a config value that's not in our configKeys, should we really continue with rest of method and return key.getDefaultValue?
-        //      e.g. SshBasedJavaAppSetup calls setAttribute(JMX_USER), which calls getConfig(JMX_USER)
-        //           but that example doesn't have a default...
-        ConfigKey<T> ownKey = adjunct!=null ? (ConfigKey<T>)elvis(adjunct.getAdjunctType().getConfigKey(key.getName()), key) : key;
-        
-        // Don't use groovy truth: if the set value is e.g. 0, then would ignore set value and return default!
-        if (ownKey instanceof ConfigKeySelfExtracting) {
-            if (((ConfigKeySelfExtracting<T>)ownKey).isSet(ownConfig)) {
-                // FIXME Should we support config from futures? How to get execution context before setEntity?
-                EntityLocal entity = adjunct.entity;
-                ExecutionContext exec = (entity != null) ? ((EntityInternal)entity).getExecutionContext() : null;
-                return ((ConfigKeySelfExtracting<T>)ownKey).extractValue(ownConfig, exec);
-            }
-        } else {
-            LOG.warn("Config key {} of {} is not a ConfigKeySelfExtracting; cannot retrieve value; returning default", ownKey, this);
-        }
-        return TypeCoercions.coerce((defaultValue != null) ? defaultValue : ownKey.getDefaultValue(), key.getTypeToken());
-    }
-    
-    @Override
-    public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) {
-        if (ownConfig.containsKey(key)) return Maybe.of(ownConfig.get(key));
-        return Maybe.absent();
-    }
-    
-    /** returns the config of this policy */
-    @Override
-    public Map<ConfigKey<?>,Object> getAllConfig() {
-        // Don't use ImmutableMap because valide for values to be null
-        return Collections.unmodifiableMap(Maps.newLinkedHashMap(ownConfig));
-    }
-
-    public Object setConfig(ConfigKey<?> key, Object v) {
-        Object val = coerceConfigVal(key, v);
-        if (key instanceof StructuredConfigKey) {
-            return ((StructuredConfigKey)key).applyValueToMap(val, ownConfig);
-        } else {
-            return ownConfig.put(key, val);
-        }
-    }
-    
-    public void addToLocalBag(Map<String, ?> vals) {
-        for (Map.Entry<String, ?> entry : vals.entrySet()) {
-            setConfig(ConfigKeys.newConfigKey(Object.class, entry.getKey()), entry.getValue());
-        }
-    }
-
-    public void removeFromLocalBag(String key) {
-        ownConfig.remove(key);
-    }
-
-    @Override
-    public ConfigMapImpl submap(Predicate<ConfigKey<?>> filter) {
-        ConfigMapImpl m = new ConfigMapImpl(adjunct);
-        for (Map.Entry<ConfigKey<?>,Object> entry: ownConfig.entrySet())
-            if (filter.apply(entry.getKey()))
-                m.ownConfig.put(entry.getKey(), entry.getValue());
-        return m;
-    }
-
-    @Override
-    public String toString() {
-        return super.toString()+"[own="+Sanitizer.sanitize(ownConfig)+"]";
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/policy/basic/GeneralPurposePolicy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/policy/basic/GeneralPurposePolicy.java b/core/src/main/java/brooklyn/policy/basic/GeneralPurposePolicy.java
deleted file mode 100644
index 0600006..0000000
--- a/core/src/main/java/brooklyn/policy/basic/GeneralPurposePolicy.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.policy.basic;
-
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * @deprecated since 0.7.0; will be either deleted or moved to tests
- */
-@Deprecated
-public class GeneralPurposePolicy extends AbstractPolicy {
-    public GeneralPurposePolicy() {
-        this(Collections.emptyMap());
-    }
-    public GeneralPurposePolicy(Map properties) {
-        super(properties);
-    }
-}
-

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/policy/basic/Policies.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/policy/basic/Policies.java b/core/src/main/java/brooklyn/policy/basic/Policies.java
deleted file mode 100644
index 887546a..0000000
--- a/core/src/main/java/brooklyn/policy/basic/Policies.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.policy.basic;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.basic.EntityLocal;
-import org.apache.brooklyn.api.event.Sensor;
-import org.apache.brooklyn.api.event.SensorEvent;
-import org.apache.brooklyn.api.event.SensorEventListener;
-import org.apache.brooklyn.api.policy.Policy;
-
-import groovy.lang.Closure;
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.policy.basic.AbstractPolicy;
-
-@SuppressWarnings({"rawtypes","unchecked"})
-public class Policies {
-
-    public static SensorEventListener listenerFromValueClosure(final Closure code) {
-        return new SensorEventListener() {
-            @Override
-            public void onEvent(SensorEvent event) {
-                code.call(event.getValue());
-            }
-        };
-    }
-    
-    public static <T> Policy newSingleSensorValuePolicy(final Sensor<T> sensor, final Closure code) {
-        return new AbstractPolicy() {
-            @Override
-            public void setEntity(EntityLocal entity) {
-                super.setEntity(entity);
-                entity.subscribe(entity, sensor, listenerFromValueClosure(code));
-            }
-        };
-    }
-    
-    public static <S,T> Policy newSingleSensorValuePolicy(final Entity remoteEntity, final Sensor<T> remoteSensor, 
-            final Closure code) {
-        return new AbstractPolicy() {
-            @Override
-            public void setEntity(EntityLocal entity) {
-                super.setEntity(entity);
-                entity.subscribe(remoteEntity, remoteSensor, listenerFromValueClosure(code));
-            }
-        };
-    }
-
-    public static Lifecycle getPolicyStatus(Policy p) {
-        if (p.isRunning()) return Lifecycle.RUNNING;
-        if (p.isDestroyed()) return Lifecycle.DESTROYED;
-        if (p.isSuspended()) return Lifecycle.STOPPED;
-        // TODO could policy be in an error state?
-        return Lifecycle.CREATED;        
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/policy/basic/PolicyDynamicType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/policy/basic/PolicyDynamicType.java b/core/src/main/java/brooklyn/policy/basic/PolicyDynamicType.java
deleted file mode 100644
index b6d3c6d..0000000
--- a/core/src/main/java/brooklyn/policy/basic/PolicyDynamicType.java
+++ /dev/null
@@ -1,44 +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.policy.basic;
-
-import org.apache.brooklyn.api.policy.Policy;
-import org.apache.brooklyn.api.policy.PolicyType;
-
-import brooklyn.basic.BrooklynDynamicType;
-
-public class PolicyDynamicType extends BrooklynDynamicType<Policy, AbstractPolicy> {
-
-    public PolicyDynamicType(Class<? extends Policy> type) {
-        super(type);
-    }
-    
-    public PolicyDynamicType(AbstractPolicy policy) {
-        super(policy);
-    }
-    
-    public PolicyType getSnapshot() {
-        return (PolicyType) super.getSnapshot();
-    }
-
-    @Override
-    protected PolicyTypeSnapshot newSnapshot() {
-        return new PolicyTypeSnapshot(name, value(configKeys));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/brooklyn/policy/basic/PolicyTypeSnapshot.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/policy/basic/PolicyTypeSnapshot.java b/core/src/main/java/brooklyn/policy/basic/PolicyTypeSnapshot.java
deleted file mode 100644
index b6d2f33..0000000
--- a/core/src/main/java/brooklyn/policy/basic/PolicyTypeSnapshot.java
+++ /dev/null
@@ -1,40 +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.policy.basic;
-
-import java.util.Map;
-
-import org.apache.brooklyn.api.policy.PolicyType;
-
-import brooklyn.basic.BrooklynTypeSnapshot;
-import brooklyn.config.ConfigKey;
-
-public class PolicyTypeSnapshot extends BrooklynTypeSnapshot implements PolicyType {
-    private static final long serialVersionUID = 4670930188951106009L;
-    
-    PolicyTypeSnapshot(String name, Map<String, ConfigKey<?>> configKeys) {
-        super(name, configKeys);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        return (obj instanceof PolicyTypeSnapshot) && super.equals(obj);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6eacb3c3/core/src/main/java/org/apache/brooklyn/core/policy/basic/AbstractEntityAdjunct.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/policy/basic/AbstractEntityAdjunct.java b/core/src/main/java/org/apache/brooklyn/core/policy/basic/AbstractEntityAdjunct.java
new file mode 100644
index 0000000..eda106f
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/policy/basic/AbstractEntityAdjunct.java
@@ -0,0 +1,510 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.policy.basic;
+
+import static brooklyn.util.GroovyJavaMethods.truth;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.Group;
+import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.api.entity.trait.Configurable;
+import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.api.event.Sensor;
+import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.SubscriptionContext;
+import org.apache.brooklyn.api.management.SubscriptionHandle;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.policy.EntityAdjunct;
+import org.apache.brooklyn.core.management.internal.SubscriptionTracker;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.basic.AbstractBrooklynObject;
+import brooklyn.basic.BrooklynObjectInternal;
+import brooklyn.config.ConfigKey;
+import brooklyn.config.ConfigKey.HasConfigKey;
+import brooklyn.config.ConfigMap;
+import brooklyn.enricher.basic.AbstractEnricher;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.text.Strings;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+
+/**
+ * Common functionality for policies and enrichers
+ */
+public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject implements BrooklynObjectInternal, EntityAdjunct, Configurable {
+    private static final Logger log = LoggerFactory.getLogger(AbstractEntityAdjunct.class);
+
+    private boolean _legacyNoConstructionInit;
+
+    /**
+     * @deprecated since 0.7.0; leftover properties are put into config, since when coming from yaml this is normal.
+     */
+    @Deprecated
+    protected Map<String,Object> leftoverProperties = Maps.newLinkedHashMap();
+
+    protected transient ExecutionContext execution;
+
+    private final BasicConfigurationSupport config = new BasicConfigurationSupport();
+    
+    /**
+     * The config values of this entity. Updating this map should be done
+     * via {@link #config()}.
+     * 
+     * @deprecated since 0.7.0; use {@link #config()} instead; this field may be made private or deleted in a future release.
+     */
+    @Deprecated
+    protected final ConfigMapImpl configsInternal = new ConfigMapImpl(this);
+
+    /**
+     * @deprecated since 0.7.0; use {@link #getAdjunctType()} instead; this field may be made private or deleted in a future release.
+     */
+    @Deprecated
+    protected final AdjunctType adjunctType = new AdjunctType(this);
+
+    @SetFromFlag
+    protected String name;
+    
+    protected transient EntityLocal entity;
+    
+    /** not for direct access; refer to as 'subscriptionTracker' via getter so that it is initialized */
+    protected transient SubscriptionTracker _subscriptionTracker;
+    
+    private AtomicBoolean destroyed = new AtomicBoolean(false);
+    
+    @SetFromFlag(value="uniqueTag")
+    protected String uniqueTag;
+
+    public AbstractEntityAdjunct() {
+        this(Collections.emptyMap());
+    }
+    
+    public AbstractEntityAdjunct(@SuppressWarnings("rawtypes") Map properties) {
+        super(properties);
+        _legacyNoConstructionInit = (properties != null) && Boolean.TRUE.equals(properties.get("noConstructionInit"));
+        
+        if (isLegacyConstruction()) {
+            AbstractBrooklynObject checkWeGetThis = configure(properties);
+            assert this.equals(checkWeGetThis) : this+" configure method does not return itself; returns "+checkWeGetThis+" instead of "+this;
+
+            boolean deferConstructionChecks = (properties.containsKey("deferConstructionChecks") && TypeCoercions.coerce(properties.get("deferConstructionChecks"), Boolean.class));
+            if (!deferConstructionChecks) {
+                FlagUtils.checkRequiredFields(this);
+            }
+        }
+    }
+
+    /**
+     * @deprecated since 0.7.0; only used for legacy brooklyn types where constructor is called directly
+     */
+    @Override
+    @Deprecated
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public AbstractEntityAdjunct configure(Map flags) {
+        // TODO only set on first time through
+        boolean isFirstTime = true;
+        
+        // allow config keys, and fields, to be set from these flags if they have a SetFromFlag annotation
+        // or if the value is a config key
+        for (Iterator<Map.Entry> iter = flags.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = iter.next();
+            if (entry.getKey() instanceof ConfigKey) {
+                ConfigKey key = (ConfigKey)entry.getKey();
+                if (adjunctType.getConfigKeys().contains(key)) {
+                    setConfig(key, entry.getValue());
+                } else {
+                    log.warn("Unknown configuration key {} for policy {}; ignoring", key, this);
+                    iter.remove();
+                }
+            }
+        }
+
+        ConfigBag bag = new ConfigBag().putAll(flags);
+        FlagUtils.setFieldsFromFlags(this, bag, isFirstTime);
+        FlagUtils.setAllConfigKeys(this, bag, false);
+        leftoverProperties.putAll(bag.getUnusedConfig());
+
+        //replace properties _contents_ with leftovers so subclasses see leftovers only
+        flags.clear();
+        flags.putAll(leftoverProperties);
+        leftoverProperties = flags;
+        
+        if (!truth(name) && flags.containsKey("displayName")) {
+            //TODO inconsistent with entity and location, where name is legacy and displayName is encouraged!
+            //'displayName' is a legacy way to refer to a policy's name
+            Preconditions.checkArgument(flags.get("displayName") instanceof CharSequence, "'displayName' property should be a string");
+            setDisplayName(flags.remove("displayName").toString());
+        }
+        
+        // set leftover flags should as config items; particularly useful when these have come from a brooklyn.config map 
+        for (Object flag: flags.keySet()) {
+            ConfigKey<Object> key = ConfigKeys.newConfigKey(Object.class, Strings.toString(flag));
+            if (config().getRaw(key).isPresent()) {
+                log.warn("Config '"+flag+"' on "+this+" conflicts with key already set; ignoring");
+            } else {
+                config().set(key, flags.get(flag));
+            }
+        }
+        
+        return this;
+    }
+    
+    /**
+     * Used for legacy-style policies/enrichers on rebind, to indicate that init() should not be called.
+     * Will likely be deleted in a future release; should not be called apart from by framework code.
+     */
+    @Beta
+    protected boolean isLegacyNoConstructionInit() {
+        return _legacyNoConstructionInit;
+    }
+
+    @Override
+    public ConfigurationSupportInternal config() {
+        return config;
+    }
+
+    private class BasicConfigurationSupport implements ConfigurationSupportInternal {
+
+        @Override
+        public <T> T get(ConfigKey<T> key) {
+            return configsInternal.getConfig(key);
+        }
+
+        @Override
+        public <T> T get(HasConfigKey<T> key) {
+            return get(key.getConfigKey());
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> T set(ConfigKey<T> key, T val) {
+            if (entity != null && isRunning()) {
+                doReconfigureConfig(key, val);
+            }
+            T result = (T) configsInternal.setConfig(key, val);
+            onChanged();
+            return result;
+        }
+
+        @Override
+        public <T> T set(HasConfigKey<T> key, T val) {
+            return setConfig(key.getConfigKey(), val);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> T set(ConfigKey<T> key, Task<T> val) {
+            if (entity != null && isRunning()) {
+                // TODO Support for AbstractEntityAdjunct
+                throw new UnsupportedOperationException();
+            }
+            T result = (T) configsInternal.setConfig(key, val);
+            onChanged();
+            return result;
+        }
+
+        @Override
+        public <T> T set(HasConfigKey<T> key, Task<T> val) {
+            return set(key.getConfigKey(), val);
+        }
+
+        @Override
+        public ConfigBag getBag() {
+            return getLocalBag();
+        }
+
+        @Override
+        public ConfigBag getLocalBag() {
+            return ConfigBag.newInstance(configsInternal.getAllConfig());
+        }
+
+        @Override
+        public Maybe<Object> getRaw(ConfigKey<?> key) {
+            return configsInternal.getConfigRaw(key, true);
+        }
+
+        @Override
+        public Maybe<Object> getRaw(HasConfigKey<?> key) {
+            return getRaw(key.getConfigKey());
+        }
+
+        @Override
+        public Maybe<Object> getLocalRaw(ConfigKey<?> key) {
+            return configsInternal.getConfigRaw(key, false);
+        }
+
+        @Override
+        public Maybe<Object> getLocalRaw(HasConfigKey<?> key) {
+            return getLocalRaw(key.getConfigKey());
+        }
+
+        @Override
+        public void addToLocalBag(Map<String, ?> vals) {
+            configsInternal.addToLocalBag(vals);
+        }
+        
+        @Override
+        public void removeFromLocalBag(String key) {
+            configsInternal.removeFromLocalBag(key);
+        }
+        
+        @Override
+        public void refreshInheritedConfig() {
+            // no-op for location
+        }
+        
+        @Override
+        public void refreshInheritedConfigOfChildren() {
+            // no-op for location
+        }
+    }
+
+    public <T> T getConfig(ConfigKey<T> key) {
+        return config().get(key);
+    }
+    
+    protected <K> K getRequiredConfig(ConfigKey<K> key) {
+        K result = config().get(key);
+        if (result==null) 
+            throw new NullPointerException("Value required for '"+key.getName()+"' in "+this);
+        return result;
+    }
+
+    @Override
+    @Deprecated
+    public <T> T setConfig(ConfigKey<T> key, T val) {
+        return config().set(key, val);
+    }
+    
+    // TODO make immutable
+    /** for inspection only */
+    @Beta
+    @Deprecated
+    public ConfigMap getConfigMap() {
+        return configsInternal;
+    }
+    
+    /**
+     * Invoked whenever a config change is applied after management is started.
+     * Default implementation throws an exception to disallow the change. 
+     * Can be overridden to return (allowing the change) or to make other changes 
+     * (if necessary), and of course it can do this selectively and call the super to disallow any others. */
+    protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) {
+        throw new UnsupportedOperationException("reconfiguring "+key+" unsupported for "+this);
+    }
+    
+    @Override
+    protected void onTagsChanged() {
+        onChanged();
+    }
+    
+    protected abstract void onChanged();
+    
+    protected AdjunctType getAdjunctType() {
+        return adjunctType;
+    }
+    
+    @Override
+    public String getDisplayName() {
+        if (name!=null && name.length()>0) return name;
+        return getClass().getCanonicalName();
+    }
+    
+    public void setDisplayName(String name) {
+        this.name = name;
+    }
+
+    public void setEntity(EntityLocal entity) {
+        if (destroyed.get()) throw new IllegalStateException("Cannot set entity on a destroyed entity adjunct");
+        this.entity = entity;
+        if (entity!=null && getCatalogItemId() == null) {
+            setCatalogItemId(entity.getCatalogItemId());
+        }
+    }
+    
+    /** @deprecated since 0.7.0 only {@link AbstractEnricher} has emit convenience */
+    protected <T> void emit(Sensor<T> sensor, Object val) {
+        checkState(entity != null, "entity must first be set");
+        if (val == Entities.UNCHANGED) {
+            return;
+        }
+        if (val == Entities.REMOVE) {
+            ((EntityInternal)entity).removeAttribute((AttributeSensor<T>) sensor);
+            return;
+        }
+        
+        T newVal = TypeCoercions.coerce(val, sensor.getTypeToken());
+        if (sensor instanceof AttributeSensor) {
+            entity.setAttribute((AttributeSensor<T>)sensor, newVal);
+        } else { 
+            entity.emit(sensor, newVal);
+        }
+    }
+
+    protected synchronized SubscriptionTracker getSubscriptionTracker() {
+        if (_subscriptionTracker!=null) return _subscriptionTracker;
+        if (entity==null) return null;
+        _subscriptionTracker = new SubscriptionTracker(((EntityInternal)entity).getManagementSupport().getSubscriptionContext());
+        return _subscriptionTracker;
+    }
+    
+    /** @see SubscriptionContext#subscribe(Entity, Sensor, SensorEventListener) */
+    protected <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        if (!checkCanSubscribe()) return null;
+        return getSubscriptionTracker().subscribe(producer, sensor, listener);
+    }
+
+    /** @see SubscriptionContext#subscribe(Entity, Sensor, SensorEventListener) */
+    protected <T> SubscriptionHandle subscribeToMembers(Group producerGroup, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        if (!checkCanSubscribe(producerGroup)) return null;
+        return getSubscriptionTracker().subscribeToMembers(producerGroup, sensor, listener);
+    }
+
+    /** @see SubscriptionContext#subscribe(Entity, Sensor, SensorEventListener) */
+    protected <T> SubscriptionHandle subscribeToChildren(Entity producerParent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        if (!checkCanSubscribe(producerParent)) return null;
+        return getSubscriptionTracker().subscribeToChildren(producerParent, sensor, listener);
+    }
+
+    /** @deprecated since 0.7.0 use {@link #checkCanSubscribe(Entity)} */
+    @Deprecated
+    protected boolean check(Entity requiredEntity) {
+        return checkCanSubscribe(requiredEntity);
+    }
+    /** returns false if deleted, throws exception if invalid state, otherwise true.
+     * okay if entity is not yet managed (but not if entity is no longer managed). */
+    protected boolean checkCanSubscribe(Entity producer) {
+        if (destroyed.get()) return false;
+        if (producer==null) throw new IllegalStateException(this+" given a null target for subscription");
+        if (entity==null) throw new IllegalStateException(this+" cannot subscribe to "+producer+" because it is not associated to an entity");
+        if (((EntityInternal)entity).getManagementSupport().isNoLongerManaged()) throw new IllegalStateException(this+" cannot subscribe to "+producer+" because the associated entity "+entity+" is no longer managed");
+        return true;
+    }
+    protected boolean checkCanSubscribe() {
+        if (destroyed.get()) return false;
+        if (entity==null) throw new IllegalStateException(this+" cannot subscribe because it is not associated to an entity");
+        if (((EntityInternal)entity).getManagementSupport().isNoLongerManaged()) throw new IllegalStateException(this+" cannot subscribe because the associated entity "+entity+" is no longer managed");
+        return true;
+    }
+        
+    /**
+     * Unsubscribes the given producer.
+     *
+     * @see SubscriptionContext#unsubscribe(SubscriptionHandle)
+     */
+    protected boolean unsubscribe(Entity producer) {
+        if (destroyed.get()) return false;
+        return getSubscriptionTracker().unsubscribe(producer);
+    }
+
+    /**
+    * Unsubscribes the given producer.
+    *
+    * @see SubscriptionContext#unsubscribe(SubscriptionHandle)
+    */
+   protected boolean unsubscribe(Entity producer, SubscriptionHandle handle) {
+       if (destroyed.get()) return false;
+       return getSubscriptionTracker().unsubscribe(producer, handle);
+   }
+
+    /**
+    * @return a list of all subscription handles
+    */
+    protected Collection<SubscriptionHandle> getAllSubscriptions() {
+        SubscriptionTracker tracker = getSubscriptionTracker();
+        return (tracker != null) ? tracker.getAllSubscriptions() : Collections.<SubscriptionHandle>emptyList();
+    }
+    
+    /** 
+     * Unsubscribes and clears all managed subscriptions; is called by the owning entity when a policy is removed
+     * and should always be called by any subclasses overriding this method
+     */
+    public void destroy() {
+        destroyed.set(true);
+        SubscriptionTracker tracker = getSubscriptionTracker();
+        if (tracker != null) tracker.unsubscribeAll();
+    }
+    
+    @Override
+    public boolean isDestroyed() {
+        return destroyed.get();
+    }
+    
+    @Override
+    public boolean isRunning() {
+        return !isDestroyed();
+    }
+
+    @Override
+    public String getUniqueTag() {
+        return uniqueTag;
+    }
+
+    public TagSupport tags() {
+        return new AdjunctTagSupport();
+    }
+
+    public class AdjunctTagSupport extends BasicTagSupport {
+        @Override
+        public Set<Object> getTags() {
+            ImmutableSet.Builder<Object> rb = ImmutableSet.builder().addAll(super.getTags());
+            if (getUniqueTag()!=null) rb.add(getUniqueTag());
+            return rb.build();
+        }
+        public String getUniqueTag() {
+            return AbstractEntityAdjunct.this.getUniqueTag();
+        }
+        public void setUniqueTag(String uniqueTag) {
+            AbstractEntityAdjunct.this.uniqueTag = uniqueTag;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(getClass()).omitNullValues()
+                .add("name", name)
+                .add("uniqueTag", uniqueTag)
+                .add("running", isRunning())
+                .add("entity", entity)
+                .add("id", getId())
+                .toString();
+    }
+}



[16/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java b/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
deleted file mode 100644
index df0330a..0000000
--- a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
+++ /dev/null
@@ -1,177 +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.internal.ssh.sshj;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.List;
-
-import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.internal.ssh.SshAbstractTool.SshAction;
-import brooklyn.util.internal.ssh.sshj.SshjTool.ShellAction;
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-
-/**
- * Tests for async-exec with {@link SshjTool}, where it stubs out the actual ssh commands
- * to return a controlled sequence of responses.
- */
-public class SshjToolAsyncStubIntegrationTest {
-
-    static class InjectedResult {
-        Predicate<SshjTool.ShellAction> expected;
-        Function<SshjTool.ShellAction, Integer> result;
-        
-        InjectedResult(Predicate<SshjTool.ShellAction> expected, Function<SshjTool.ShellAction, Integer> result) {
-            this.expected = expected;
-            this.result = result;
-        }
-    }
-    
-    private SshjTool tool;
-    private List<InjectedResult> sequence;
-    int counter = 0;
-    private boolean origFeatureEnablement;
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
-        sequence = Lists.newArrayList();
-        counter = 0;
-        
-        tool = new SshjTool(ImmutableMap.<String,Object>of("host", "localhost")) {
-            @SuppressWarnings("unchecked")
-            protected <T, C extends SshAction<T>> T acquire(C action, int sshTries, Duration sshTriesTimeout) {
-                if (action instanceof SshjTool.ShellAction) {
-                    SshjTool.ShellAction shellAction = (SshjTool.ShellAction) action;
-                    InjectedResult injectedResult = sequence.get(counter);
-                    assertTrue(injectedResult.expected.apply(shellAction), "counter="+counter+"; cmds="+shellAction.commands);
-                    counter++;
-                    return (T) injectedResult.result.apply(shellAction);
-                }
-                return super.acquire(action, sshTries, sshTriesTimeout);
-            }
-        };
-    }
-
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        try {
-            if (tool != null) tool.disconnect();
-        } finally {
-            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
-        }
-    }
-    
-    private Predicate<SshjTool.ShellAction> containsCmd(final String cmd) {
-        return new Predicate<SshjTool.ShellAction>() {
-            @Override public boolean apply(ShellAction input) {
-                return input != null && input.commands.toString().contains(cmd);
-            }
-        };
-    }
-    
-    private Function<SshjTool.ShellAction, Integer> returning(final int result, final String stdout, final String stderr) {
-        return new Function<SshjTool.ShellAction, Integer>() {
-            @Override public Integer apply(ShellAction input) {
-                try {
-                    if (stdout != null && input.out != null) input.out.write(stdout.getBytes());
-                    if (stderr != null && input.err != null) input.err.write(stderr.getBytes());
-                } catch (IOException e) {
-                    throw Exceptions.propagate(e);
-                }
-                return result;
-            }
-        };
-    }
-    
-    @Test(groups="Integration")
-    public void testPolls() throws Exception {
-        sequence = ImmutableList.of(
-                new InjectedResult(containsCmd("nohup"), returning(0, "", "")),
-                new InjectedResult(containsCmd("# Long poll"), returning(0, "mystringToStdout", "mystringToStderr")));
-
-        runTest(0, "mystringToStdout", "mystringToStderr");
-        assertEquals(counter, sequence.size());
-    }
-    
-    @Test(groups="Integration")
-    public void testPollsAndReturnsNonZeroExitCode() throws Exception {
-        sequence = ImmutableList.of(
-                new InjectedResult(containsCmd("nohup"), returning(0, "", "")),
-                new InjectedResult(containsCmd("# Long poll"), returning(123, "mystringToStdout", "mystringToStderr")),
-                new InjectedResult(containsCmd("# Retrieve status"), returning(0, "123", "")));
-
-        runTest(123, "mystringToStdout", "mystringToStderr");
-        assertEquals(counter, sequence.size());
-    }
-    
-    @Test(groups="Integration")
-    public void testPollsRepeatedly() throws Exception {
-        sequence = ImmutableList.of(
-                new InjectedResult(containsCmd("nohup"), returning(0, "", "")),
-                new InjectedResult(containsCmd("# Long poll"), returning(125, "mystringToStdout", "mystringToStderr")),
-                new InjectedResult(containsCmd("# Retrieve status"), returning(0, "", "")),
-                new InjectedResult(containsCmd("# Long poll"), returning(125, "mystringToStdout2", "mystringToStderr2")),
-                new InjectedResult(containsCmd("# Retrieve status"), returning(0, "", "")),
-                new InjectedResult(containsCmd("# Long poll"), returning(-1, "mystringToStdout3", "mystringToStderr3")),
-                new InjectedResult(containsCmd("# Long poll"), returning(125, "mystringToStdout4", "mystringToStderr4")),
-                new InjectedResult(containsCmd("# Retrieve status"), returning(0, "", "")),
-                new InjectedResult(containsCmd("# Long poll"), returning(0, "mystringToStdout5", "mystringToStderr5")));
-
-        runTest(0,
-                "mystringToStdout"+"mystringToStdout2"+"mystringToStdout3"+"mystringToStdout4"+"mystringToStdout5",
-                "mystringToStderr"+"mystringToStderr2"+"mystringToStderr3"+"mystringToStderr4"+"mystringToStderr5");
-        assertEquals(counter, sequence.size());
-    }
-    
-    protected void runTest(int expectedExit, String expectedStdout, String expectedStderr) throws Exception {
-        List<String> cmds = ImmutableList.of("abc");
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        ByteArrayOutputStream err = new ByteArrayOutputStream();
-        int exitCode = tool.execScript(
-                ImmutableMap.of(
-                        "out", out, 
-                        "err", err, 
-                        SshjTool.PROP_EXEC_ASYNC.getName(), true, 
-                        SshjTool.PROP_NO_EXTRA_OUTPUT.getName(), true,
-                        SshjTool.PROP_EXEC_ASYNC_POLLING_TIMEOUT.getName(), Duration.ONE_MILLISECOND), 
-                cmds, 
-                ImmutableMap.<String,String>of());
-        String outStr = new String(out.toByteArray());
-        String errStr = new String(err.toByteArray());
-
-        assertEquals(exitCode, expectedExit);
-        assertEquals(outStr.trim(), expectedStdout);
-        assertEquals(errStr.trim(), expectedStderr);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolIntegrationTest.java b/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolIntegrationTest.java
deleted file mode 100644
index f1e354c..0000000
--- a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolIntegrationTest.java
+++ /dev/null
@@ -1,313 +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.internal.ssh.sshj;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
-import net.schmizz.sshj.connection.channel.direct.Session;
-
-import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
-import org.testng.annotations.Test;
-
-import brooklyn.test.Asserts;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.exceptions.RuntimeTimeoutException;
-import brooklyn.util.internal.ssh.SshException;
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.internal.ssh.SshToolAbstractIntegrationTest;
-import brooklyn.util.os.Os;
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-/**
- * Test the operation of the {@link SshJschTool} utility class.
- */
-public class SshjToolIntegrationTest extends SshToolAbstractIntegrationTest {
-
-    @Override
-    protected SshTool newUnregisteredTool(Map<String,?> flags) {
-        return new SshjTool(flags);
-    }
-
-    // TODO requires vt100 terminal emulation to work?
-    @Test(enabled = false, groups = {"Integration"})
-    public void testExecShellWithCommandTakingStdin() throws Exception {
-        // Uses `tee` to redirect stdin to the given file; cntr-d (i.e. char 4) stops tee with exit code 0
-        String content = "blah blah";
-        String out = execShellDirectWithTerminalEmulation("tee "+remoteFilePath, content, ""+(char)4, "echo file contents: `cat "+remoteFilePath+"`");
-
-        assertTrue(out.contains("file contents: blah blah"), "out="+out);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testGivesUpAfterMaxRetries() throws Exception {
-        final AtomicInteger callCount = new AtomicInteger();
-        
-        final SshTool localtool = new SshjTool(ImmutableMap.of("sshTries", 3, "host", "localhost", "privateKeyFile", "~/.ssh/id_rsa")) {
-            protected SshAction<Session> newSessionAction() {
-                callCount.incrementAndGet();
-                throw new RuntimeException("Simulating ssh execution failure");
-            }
-        };
-        
-        tools.add(localtool);
-        try {
-            localtool.execScript(ImmutableMap.<String,Object>of(), ImmutableList.of("true"));
-            fail();
-        } catch (SshException e) {
-            if (!e.toString().contains("out of retries")) throw e;
-            assertEquals(callCount.get(), 3);
-        }
-    }
-
-    @Test(groups = {"Integration"})
-    public void testReturnsOnSuccessWhenRetrying() throws Exception {
-        final AtomicInteger callCount = new AtomicInteger();
-        final int successOnAttempt = 2;
-        final SshTool localtool = new SshjTool(ImmutableMap.of("sshTries", 3, "host", "localhost", "privateKeyFile", "~/.ssh/id_rsa")) {
-            protected SshAction<Session> newSessionAction() {
-                callCount.incrementAndGet();
-                if (callCount.incrementAndGet() >= successOnAttempt) {
-                    return super.newSessionAction();
-                } else {
-                    throw new RuntimeException("Simulating ssh execution failure");
-                }
-            }
-        };
-        
-        tools.add(localtool);
-        localtool.execScript(ImmutableMap.<String,Object>of(), ImmutableList.of("true"));
-        assertEquals(callCount.get(), successOnAttempt);
-    }
-
-    @Test(groups = {"Integration"})
-    public void testGivesUpAfterMaxTime() throws Exception {
-        final AtomicInteger callCount = new AtomicInteger();
-        final SshTool localtool = new SshjTool(ImmutableMap.of("sshTriesTimeout", 1000, "host", "localhost", "privateKeyFile", "~/.ssh/id_rsa")) {
-            protected SshAction<Session> newSessionAction() {
-                callCount.incrementAndGet();
-                try {
-                    Thread.sleep(600);
-                } catch (InterruptedException e) {
-                    throw Exceptions.propagate(e);
-                }
-                throw new RuntimeException("Simulating ssh execution failure");
-            }
-        };
-        
-        tools.add(localtool);
-        try {
-            localtool.execScript(ImmutableMap.<String,Object>of(), ImmutableList.of("true"));
-            fail();
-        } catch (RuntimeTimeoutException e) {
-            if (!e.toString().contains("out of time")) throw e;
-            assertEquals(callCount.get(), 2);
-        }
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testUsesCustomLocalTempDir() throws Exception {
-        class SshjToolForTest extends SshjTool {
-            public SshjToolForTest(Map<String, ?> map) {
-                super(map);
-            }
-            public File getLocalTempDir() {
-                return localTempDir;
-            }
-        };
-        
-        final SshjToolForTest localtool = new SshjToolForTest(ImmutableMap.<String, Object>of("host", "localhost"));
-        assertNotNull(localtool.getLocalTempDir());
-        assertEquals(localtool.getLocalTempDir(), new File(Os.tidyPath(SshjTool.PROP_LOCAL_TEMP_DIR.getDefaultValue())));
-        
-        String customTempDir = Os.tmp();
-        final SshjToolForTest localtool2 = new SshjToolForTest(ImmutableMap.of(
-                "host", "localhost", 
-                SshjTool.PROP_LOCAL_TEMP_DIR.getName(), customTempDir));
-        assertEquals(localtool2.getLocalTempDir(), new File(customTempDir));
-        
-        String customRelativeTempDir = "~/tmp";
-        final SshjToolForTest localtool3 = new SshjToolForTest(ImmutableMap.of(
-                "host", "localhost", 
-                SshjTool.PROP_LOCAL_TEMP_DIR.getName(), customRelativeTempDir));
-        assertEquals(localtool3.getLocalTempDir(), new File(Os.tidyPath(customRelativeTempDir)));
-    }
-
-    @Test(groups = {"Integration"})
-    public void testAsyncExecStdoutAndStderr() throws Exception {
-        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
-        try {
-            // Include a sleep, to ensure that the contents retrieved in first poll and subsequent polls are appended
-            List<String> cmds = ImmutableList.of(
-                    "echo mystringToStdout",
-                    "echo mystringToStderr 1>&2",
-                    "sleep 5",
-                    "echo mystringPostSleepToStdout",
-                    "echo mystringPostSleepToStderr 1>&2");
-            
-            ByteArrayOutputStream out = new ByteArrayOutputStream();
-            ByteArrayOutputStream err = new ByteArrayOutputStream();
-            int exitCode = tool.execScript(
-                    ImmutableMap.of(
-                            "out", out, 
-                            "err", err, 
-                            SshjTool.PROP_EXEC_ASYNC.getName(), true, 
-                            SshjTool.PROP_NO_EXTRA_OUTPUT.getName(), true,
-                            SshjTool.PROP_EXEC_ASYNC_POLLING_TIMEOUT.getName(), Duration.ONE_SECOND), 
-                    cmds, 
-                    ImmutableMap.<String,String>of());
-            String outStr = new String(out.toByteArray());
-            String errStr = new String(err.toByteArray());
-    
-            assertEquals(exitCode, 0);
-            assertEquals(outStr.trim(), "mystringToStdout\nmystringPostSleepToStdout");
-            assertEquals(errStr.trim(), "mystringToStderr\nmystringPostSleepToStderr");
-        } finally {
-            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
-        }
-    }
-
-    @Test(groups = {"Integration"})
-    public void testAsyncExecReturnsExitCode() throws Exception {
-        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
-        try {
-            int exitCode = tool.execScript(
-                    ImmutableMap.of(SshjTool.PROP_EXEC_ASYNC.getName(), true), 
-                    ImmutableList.of("exit 123"), 
-                    ImmutableMap.<String,String>of());
-            assertEquals(exitCode, 123);
-        } finally {
-            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
-        }
-    }
-
-    @Test(groups = {"Integration"})
-    public void testAsyncExecTimesOut() throws Exception {
-        Stopwatch stopwatch = Stopwatch.createStarted();
-        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
-        try {
-            tool.execScript(
-                ImmutableMap.of(SshjTool.PROP_EXEC_ASYNC.getName(), true, SshjTool.PROP_EXEC_TIMEOUT.getName(), Duration.millis(1)), 
-                ImmutableList.of("sleep 60"), 
-                ImmutableMap.<String,String>of());
-            fail();
-        } catch (Exception e) {
-            TimeoutException te = Exceptions.getFirstThrowableOfType(e, TimeoutException.class);
-            if (te == null) throw e;
-        } finally {
-            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
-        }
-        
-        long seconds = stopwatch.elapsed(TimeUnit.SECONDS);
-        assertTrue(seconds < 30, "exec took "+seconds+" seconds");
-    }
-
-    @Test(groups = {"Integration"})
-    public void testAsyncExecAbortsIfProcessFails() throws Exception {
-        final AtomicReference<Throwable> error = new AtomicReference<Throwable>();
-        Thread thread = new Thread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Stopwatch stopwatch = Stopwatch.createStarted();
-                    int exitStatus = tool.execScript(
-                        ImmutableMap.of(SshjTool.PROP_EXEC_ASYNC.getName(), true, SshjTool.PROP_EXEC_TIMEOUT.getName(), Duration.millis(1)), 
-                        ImmutableList.of("sleep 63"), 
-                        ImmutableMap.<String,String>of());
-                    
-                    assertEquals(exitStatus, 143 /* 128 + Signal number (SIGTERM) */);
-                    
-                    long seconds = stopwatch.elapsed(TimeUnit.SECONDS);
-                    assertTrue(seconds < 30, "exec took "+seconds+" seconds");
-                } catch (Throwable t) {
-                    error.set(t);
-                }
-            }});
-        
-        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
-        try {
-            thread.start();
-            
-            Asserts.succeedsEventually(new Runnable() {
-                @Override
-                public void run() {
-                    int exitStatus = tool.execCommands(ImmutableMap.<String,Object>of(), ImmutableList.of("ps aux| grep \"sleep 63\" | grep -v grep"));
-                    assertEquals(exitStatus, 0);
-                }});
-            
-            tool.execCommands(ImmutableMap.<String,Object>of(), ImmutableList.of("ps aux| grep \"sleep 63\" | grep -v grep | awk '{print($2)}' | xargs kill"));
-            
-            thread.join(30*1000);
-            assertFalse(thread.isAlive());
-            if (error.get() != null) {
-                throw Exceptions.propagate(error.get());
-            }
-        } finally {
-            thread.interrupt();
-            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
-        }
-    }
-
-    
-    protected String execShellDirect(List<String> cmds) {
-        return execShellDirect(cmds, ImmutableMap.<String,Object>of());
-    }
-    
-    protected String execShellDirect(List<String> cmds, Map<String,?> env) {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        int exitcode = ((SshjTool)tool).execShellDirect(ImmutableMap.of("out", out), cmds, env);
-        String outstr = new String(out.toByteArray());
-        assertEquals(exitcode, 0, outstr);
-        return outstr;
-    }
-
-    private String execShellDirectWithTerminalEmulation(String... cmds) {
-        return execShellDirectWithTerminalEmulation(Arrays.asList(cmds));
-    }
-    
-    private String execShellDirectWithTerminalEmulation(List<String> cmds) {
-        return execShellDirectWithTerminalEmulation(cmds, ImmutableMap.<String,Object>of());
-    }
-    
-    private String execShellDirectWithTerminalEmulation(List<String> cmds, Map<String,?> env) {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        int exitcode = ((SshjTool)tool).execShellDirect(ImmutableMap.of("allocatePTY", true, "out", out), cmds, env);
-        String outstr = new String(out.toByteArray());
-        assertEquals(exitcode, 0, outstr);
-        return outstr;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolPerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolPerformanceTest.java b/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolPerformanceTest.java
deleted file mode 100644
index 0c79bf1..0000000
--- a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolPerformanceTest.java
+++ /dev/null
@@ -1,44 +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.internal.ssh.sshj;
-
-import java.util.Map;
-
-import org.testng.annotations.Test;
-
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.internal.ssh.SshToolAbstractPerformanceTest;
-
-/**
- * Test the performance of different variants of invoking the sshj tool.
- * 
- * Intended for human-invocation and inspection, to see which parts are most expensive.
- */
-public class SshjToolPerformanceTest extends SshToolAbstractPerformanceTest {
-
-    @Override
-    protected SshTool newSshTool(Map<String,?> flags) {
-        return new SshjTool(flags);
-    }
-    
-    // Need to have at least one test method here (rather than just inherited) for eclipse to recognize it
-    @Test(enabled = false)
-    public void testDummy() throws Exception {
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/mutex/WithMutexesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/mutex/WithMutexesTest.java b/core/src/test/java/brooklyn/util/mutex/WithMutexesTest.java
deleted file mode 100644
index cde25d3..0000000
--- a/core/src/test/java/brooklyn/util/mutex/WithMutexesTest.java
+++ /dev/null
@@ -1,126 +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.mutex;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Map;
-
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-public class WithMutexesTest {
-
-    @Test
-    public void testOneAcquisitionAndRelease() throws InterruptedException {
-        MutexSupport m = new MutexSupport();
-        Map<String, SemaphoreWithOwners> sems;
-        SemaphoreWithOwners s;
-        try {
-            m.acquireMutex("foo", "something foo");
-            sems = m.getAllSemaphores();
-            Assert.assertEquals(sems.size(), 1);
-            s = sems.get("foo");
-            Assert.assertEquals(s.getDescription(), "something foo");
-            Assert.assertEquals(s.getOwningThreads(), Arrays.asList(Thread.currentThread()));
-            Assert.assertEquals(s.getRequestingThreads(), Collections.emptyList());
-            Assert.assertTrue(s.isInUse());
-            Assert.assertTrue(s.isCallingThreadAnOwner());
-        } finally {
-            m.releaseMutex("foo");
-        }
-        Assert.assertFalse(s.isInUse());
-        Assert.assertFalse(s.isCallingThreadAnOwner());
-        Assert.assertEquals(s.getDescription(), "something foo");
-        Assert.assertEquals(s.getOwningThreads(), Collections.emptyList());
-        Assert.assertEquals(s.getRequestingThreads(), Collections.emptyList());
-        
-        sems = m.getAllSemaphores();
-        Assert.assertEquals(sems, Collections.emptyMap());
-    }
-
-    @Test(groups = "Integration")  //just because it takes a wee while
-    public void testBlockingAcquisition() throws InterruptedException {
-        final MutexSupport m = new MutexSupport();
-        m.acquireMutex("foo", "something foo");
-        
-        Assert.assertFalse(m.tryAcquireMutex("foo", "something else"));
-
-        Thread t = new Thread() {
-            public void run() {
-                try {
-                    m.acquireMutex("foo", "thread 2 foo");
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
-                }
-                m.releaseMutex("foo");
-            }
-        };
-        t.start();
-        
-        t.join(500);
-        Assert.assertTrue(t.isAlive());
-        Assert.assertEquals(m.getSemaphore("foo").getRequestingThreads(), Arrays.asList(t));
-
-        m.releaseMutex("foo");
-        
-        t.join(1000);
-        Assert.assertFalse(t.isAlive());
-
-        Assert.assertEquals(m.getAllSemaphores(), Collections.emptyMap());
-    }
-
-    
-    public static class SampleWithMutexesDelegatingMixin implements WithMutexes {
-        
-        /* other behaviour would typically go here... */
-        
-        WithMutexes mutexSupport = new MutexSupport();
-        
-        @Override
-        public void acquireMutex(String mutexId, String description) throws InterruptedException {
-            mutexSupport.acquireMutex(mutexId, description);
-        }
-
-        @Override
-        public boolean tryAcquireMutex(String mutexId, String description) {
-            return mutexSupport.tryAcquireMutex(mutexId, description);
-        }
-
-        @Override
-        public void releaseMutex(String mutexId) {
-            mutexSupport.releaseMutex(mutexId);
-        }
-
-        @Override
-        public boolean hasMutex(String mutexId) {
-            return mutexSupport.hasMutex(mutexId);
-        }
-    }
-    
-    @Test
-    public void testDelegatingMixinPattern() throws InterruptedException {
-        WithMutexes m = new SampleWithMutexesDelegatingMixin();
-        m.acquireMutex("foo", "sample");
-        Assert.assertTrue(m.hasMutex("foo"));
-        Assert.assertFalse(m.hasMutex("bar"));
-        m.releaseMutex("foo");
-        Assert.assertFalse(m.hasMutex("foo"));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/osgi/OsgisTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/osgi/OsgisTest.java b/core/src/test/java/brooklyn/util/osgi/OsgisTest.java
deleted file mode 100644
index 49f8017..0000000
--- a/core/src/test/java/brooklyn/util/osgi/OsgisTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.osgi;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-
-import org.osgi.framework.Version;
-import org.testng.annotations.Test;
-
-import brooklyn.util.osgi.Osgis.VersionedName;
-
-public class OsgisTest {
-
-    @Test
-    public void testParseOsgiIdentifier() throws Exception {
-        assertEquals(Osgis.parseOsgiIdentifier("a.b").get(), new VersionedName("a.b", null));
-        assertEquals(Osgis.parseOsgiIdentifier("a.b:0.1.2").get(), new VersionedName("a.b", Version.parseVersion("0.1.2")));
-        assertEquals(Osgis.parseOsgiIdentifier("a.b:0.0.0.SNAPSHOT").get(), new VersionedName("a.b", Version.parseVersion("0.0.0.SNAPSHOT")));
-        assertFalse(Osgis.parseOsgiIdentifier("a.b:0.notanumber.2").isPresent()); // invalid version
-        assertFalse(Osgis.parseOsgiIdentifier("a.b:0.1.2:3.4.5").isPresent());    // too many colons
-        assertFalse(Osgis.parseOsgiIdentifier("a.b:0.0.0_SNAPSHOT").isPresent()); // invalid version
-        assertFalse(Osgis.parseOsgiIdentifier("").isPresent());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/ssh/BashCommandsIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/ssh/BashCommandsIntegrationTest.java b/core/src/test/java/brooklyn/util/ssh/BashCommandsIntegrationTest.java
deleted file mode 100644
index accac56..0000000
--- a/core/src/test/java/brooklyn/util/ssh/BashCommandsIntegrationTest.java
+++ /dev/null
@@ -1,501 +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.ssh;
-
-import static brooklyn.util.ssh.BashCommands.sudo;
-import static java.lang.String.format;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotEquals;
-import static org.testng.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.net.ServerSocket;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-import org.apache.commons.io.FileUtils;
-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;
-
-import brooklyn.entity.basic.Entities;
-import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.javalang.JavaClassNames;
-import brooklyn.util.net.Networking;
-import brooklyn.util.os.Os;
-import brooklyn.util.task.BasicExecutionContext;
-import brooklyn.util.task.ssh.SshTasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-import brooklyn.util.text.Identifiers;
-import brooklyn.util.text.Strings;
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.Files;
-
-public class BashCommandsIntegrationTest {
-
-    private static final Logger log = LoggerFactory.getLogger(BashCommandsIntegrationTest.class);
-    
-    private ManagementContext mgmt;
-    private BasicExecutionContext exec;
-    
-    private File destFile;
-    private File sourceNonExistantFile;
-    private File sourceFile1;
-    private File sourceFile2;
-    private String sourceNonExistantFileUrl;
-    private String sourceFileUrl1;
-    private String sourceFileUrl2;
-    private SshMachineLocation loc;
-
-    private String localRepoFilename = "localrepofile.txt";
-    private File localRepoBasePath;
-    private File localRepoEntityBasePath;
-    private String localRepoEntityVersionPath;
-    private File localRepoEntityFile;
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        mgmt = new LocalManagementContextForTests();
-        exec = new BasicExecutionContext(mgmt.getExecutionManager());
-        
-        destFile = Os.newTempFile(getClass(), "commoncommands-test-dest.txt");
-        
-        sourceNonExistantFile = new File("/this/does/not/exist/ERQBETJJIG1234");
-        sourceNonExistantFileUrl = sourceNonExistantFile.toURI().toString();
-        
-        sourceFile1 = Os.newTempFile(getClass(), "commoncommands-test.txt");
-        sourceFileUrl1 = sourceFile1.toURI().toString();
-        Files.write("mysource1".getBytes(), sourceFile1);
-        
-        sourceFile2 = Os.newTempFile(getClass(), "commoncommands-test2.txt");
-        sourceFileUrl2 = sourceFile2.toURI().toString();
-        Files.write("mysource2".getBytes(), sourceFile2);
-
-        localRepoEntityVersionPath = JavaClassNames.simpleClassName(this)+"-test-dest-"+Identifiers.makeRandomId(8);
-        localRepoBasePath = new File(format("%s/.brooklyn/repository", System.getProperty("user.home")));
-        localRepoEntityBasePath = new File(localRepoBasePath, localRepoEntityVersionPath);
-        localRepoEntityFile = new File(localRepoEntityBasePath, localRepoFilename);
-        localRepoEntityBasePath.mkdirs();
-        Files.write("mylocal1".getBytes(), localRepoEntityFile);
-
-        loc = mgmt.getLocationManager().createLocation(LocalhostMachineProvisioningLocation.spec()).obtain();
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (sourceFile1 != null) sourceFile1.delete();
-        if (sourceFile2 != null) sourceFile2.delete();
-        if (destFile != null) destFile.delete();
-        if (localRepoEntityFile != null) localRepoEntityFile.delete();
-        if (localRepoEntityBasePath != null) FileUtils.deleteDirectory(localRepoEntityBasePath);
-        if (loc != null) loc.close();
-        if (mgmt != null) Entities.destroyAll(mgmt);
-    }
-    
-    @Test(groups="Integration")
-    public void testSudo() throws Exception {
-        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
-        ByteArrayOutputStream errStream = new ByteArrayOutputStream();
-        String cmd = sudo("whoami");
-        int exitcode = loc.execCommands(ImmutableMap.of("out", outStream, "err", errStream), "test", ImmutableList.of(cmd));
-        String outstr = new String(outStream.toByteArray());
-        String errstr = new String(errStream.toByteArray());
-        
-        assertEquals(exitcode, 0, "out="+outstr+"; err="+errstr);
-        assertTrue(outstr.contains("root"), "out="+outstr+"; err="+errstr);
-    }
-    
-    public void testDownloadUrl() throws Exception {
-        List<String> cmds = BashCommands.commandsToDownloadUrlsAs(
-                ImmutableList.of(sourceFileUrl1), 
-                destFile.getAbsolutePath());
-        int exitcode = loc.execCommands("test", cmds);
-        
-        assertEquals(0, exitcode);
-        assertEquals(Files.readLines(destFile, Charsets.UTF_8), ImmutableList.of("mysource1"));
-    }
-    
-    @Test(groups="Integration")
-    public void testDownloadFirstSuccessfulFile() throws Exception {
-        List<String> cmds = BashCommands.commandsToDownloadUrlsAs(
-                ImmutableList.of(sourceNonExistantFileUrl, sourceFileUrl1, sourceFileUrl2), 
-                destFile.getAbsolutePath());
-        int exitcode = loc.execCommands("test", cmds);
-        
-        assertEquals(0, exitcode);
-        assertEquals(Files.readLines(destFile, Charsets.UTF_8), ImmutableList.of("mysource1"));
-    }
-    
-    @Test(groups="Integration")
-    public void testDownloadToStdout() throws Exception {
-        ProcessTaskWrapper<String> t = SshTasks.newSshExecTaskFactory(loc, 
-                "cd "+destFile.getParentFile().getAbsolutePath(),
-                BashCommands.downloadToStdout(Arrays.asList(sourceFileUrl1))+" | sed s/my/your/")
-            .requiringZeroAndReturningStdout().newTask();
-
-        String result = exec.submit(t).get();
-        assertTrue(result.trim().equals("yoursource1"), "Wrong contents of stdout download: "+result);
-    }
-
-    @Test(groups="Integration")
-    public void testAlternativesWhereFirstSucceeds() throws Exception {
-        ProcessTaskWrapper<Integer> t = SshTasks.newSshExecTaskFactory(loc)
-                .add(BashCommands.alternatives(Arrays.asList("echo first", "exit 88")))
-                .newTask();
-
-        Integer returnCode = exec.submit(t).get();
-        String stdout = t.getStdout();
-        String stderr = t.getStderr();
-        log.info("alternatives for good first command gave: "+returnCode+"; err="+stderr+"; out="+stdout);
-        assertTrue(stdout.contains("first"), "errcode="+returnCode+"; stdout="+stdout+"; stderr="+stderr);
-        assertEquals(returnCode, (Integer)0);
-    }
-
-    @Test(groups="Integration")
-    public void testAlternatives() throws Exception {
-        ProcessTaskWrapper<Integer> t = SshTasks.newSshExecTaskFactory(loc)
-                .add(BashCommands.alternatives(Arrays.asList("asdfj_no_such_command_1", "exit 88")))
-                .newTask();
-
-        Integer returnCode = exec.submit(t).get();
-        log.info("alternatives for bad commands gave: "+returnCode+"; err="+new String(t.getStderr())+"; out="+new String(t.getStdout()));
-        assertEquals(returnCode, (Integer)88);
-    }
-
-    @Test(groups="Integration")
-    public void testRequireTestHandlesFailure() throws Exception {
-        ProcessTaskWrapper<?> t = SshTasks.newSshExecTaskFactory(loc)
-            .add(BashCommands.requireTest("-f "+sourceNonExistantFile.getPath(),
-                    "The requested file does not exist")).newTask();
-
-        exec.submit(t).get();
-        assertNotEquals(t.getExitCode(), (Integer)0);
-        assertTrue(t.getStderr().contains("The requested file"), "Expected message in: "+t.getStderr());
-        assertTrue(t.getStdout().contains("The requested file"), "Expected message in: "+t.getStdout());
-    }
-
-    @Test(groups="Integration")
-    public void testRequireTestHandlesSuccess() throws Exception {
-        ProcessTaskWrapper<?> t = SshTasks.newSshExecTaskFactory(loc)
-            .add(BashCommands.requireTest("-f "+sourceFile1.getPath(),
-                    "The requested file does not exist")).newTask();
-
-        exec.submit(t).get();
-        assertEquals(t.getExitCode(), (Integer)0);
-        assertTrue(t.getStderr().equals(""), "Expected no stderr messages, but got: "+t.getStderr());
-    }
-
-    @Test(groups="Integration")
-    public void testRequireFileHandlesFailure() throws Exception {
-        ProcessTaskWrapper<?> t = SshTasks.newSshExecTaskFactory(loc)
-            .add(BashCommands.requireFile(sourceNonExistantFile.getPath())).newTask();
-
-        exec.submit(t).get();
-        assertNotEquals(t.getExitCode(), (Integer)0);
-        assertTrue(t.getStderr().contains("required file"), "Expected message in: "+t.getStderr());
-        assertTrue(t.getStderr().contains(sourceNonExistantFile.getPath()), "Expected message in: "+t.getStderr());
-        assertTrue(t.getStdout().contains("required file"), "Expected message in: "+t.getStdout());
-        assertTrue(t.getStdout().contains(sourceNonExistantFile.getPath()), "Expected message in: "+t.getStdout());
-    }
-
-    @Test(groups="Integration")
-    public void testRequireFileHandlesSuccess() throws Exception {
-        ProcessTaskWrapper<?> t = SshTasks.newSshExecTaskFactory(loc)
-            .add(BashCommands.requireFile(sourceFile1.getPath())).newTask();
-
-        exec.submit(t).get();
-        assertEquals(t.getExitCode(), (Integer)0);
-        assertTrue(t.getStderr().equals(""), "Expected no stderr messages, but got: "+t.getStderr());
-    }
-
-    @Test(groups="Integration")
-    public void testRequireFailureExitsImmediately() throws Exception {
-        ProcessTaskWrapper<?> t = SshTasks.newSshExecTaskFactory(loc)
-            .add(BashCommands.requireTest("-f "+sourceNonExistantFile.getPath(),
-                    "The requested file does not exist"))
-            .add("echo shouldnae come here").newTask();
-
-        exec.submit(t).get();
-        assertNotEquals(t.getExitCode(), (Integer)0);
-        assertTrue(t.getStderr().contains("The requested file"), "Expected message in: "+t.getStderr());
-        assertTrue(t.getStdout().contains("The requested file"), "Expected message in: "+t.getStdout());
-        Assert.assertFalse(t.getStdout().contains("shouldnae"), "Expected message in: "+t.getStdout());
-    }
-
-    @Test(groups="Integration")
-    public void testPipeMultiline() throws Exception {
-        String output = execRequiringZeroAndReturningStdout(loc,
-                BashCommands.pipeTextTo("hello world\n"+"and goodbye\n", "wc")).get();
-
-        assertEquals(Strings.replaceAllRegex(output, "\\s+", " ").trim(), "3 4 25");
-    }
-
-    @Test(groups="Integration")
-    public void testWaitForFileContentsWhenAbortingOnFail() throws Exception {
-        String fileContent = "mycontents";
-        String cmd = BashCommands.waitForFileContents(destFile.getAbsolutePath(), fileContent, Duration.ONE_SECOND, true);
-
-        int exitcode = loc.execCommands("test", ImmutableList.of(cmd));
-        assertEquals(exitcode, 1);
-        
-        Files.write(fileContent, destFile, Charsets.UTF_8);
-        int exitcode2 = loc.execCommands("test", ImmutableList.of(cmd));
-        assertEquals(exitcode2, 0);
-    }
-
-    @Test(groups="Integration")
-    public void testWaitForFileContentsWhenNotAbortingOnFail() throws Exception {
-        String fileContent = "mycontents";
-        String cmd = BashCommands.waitForFileContents(destFile.getAbsolutePath(), fileContent, Duration.ONE_SECOND, false);
-
-        String output = execRequiringZeroAndReturningStdout(loc, cmd).get();
-        assertTrue(output.contains("Couldn't find"), "output="+output);
-
-        Files.write(fileContent, destFile, Charsets.UTF_8);
-        String output2 = execRequiringZeroAndReturningStdout(loc, cmd).get();
-        assertFalse(output2.contains("Couldn't find"), "output="+output2);
-    }
-    
-    @Test(groups="Integration")
-    public void testWaitForFileContentsWhenContentsAppearAfterStart() throws Exception {
-        String fileContent = "mycontents";
-
-        String cmd = BashCommands.waitForFileContents(destFile.getAbsolutePath(), fileContent, Duration.THIRTY_SECONDS, false);
-        ProcessTaskWrapper<String> t = execRequiringZeroAndReturningStdout(loc, cmd);
-        exec.submit(t);
-        
-        // sleep for long enough to ensure the ssh command is definitely executing
-        Thread.sleep(5*1000);
-        assertFalse(t.isDone());
-        
-        Files.write(fileContent, destFile, Charsets.UTF_8);
-        String output = t.get();
-        assertFalse(output.contains("Couldn't find"), "output="+output);
-    }
-    
-    @Test(groups="Integration", dependsOnMethods="testSudo")
-    public void testWaitForPortFreeWhenAbortingOnTimeout() throws Exception {
-        ServerSocket serverSocket = openServerSocket();
-        try {
-            int port = serverSocket.getLocalPort();
-            String cmd = BashCommands.waitForPortFree(port, Duration.ONE_SECOND, true);
-    
-            int exitcode = loc.execCommands("test", ImmutableList.of(cmd));
-            assertEquals(exitcode, 1);
-            
-            serverSocket.close();
-            assertTrue(Networking.isPortAvailable(port));
-            int exitcode2 = loc.execCommands("test", ImmutableList.of(cmd));
-            assertEquals(exitcode2, 0);
-        } finally {
-            serverSocket.close();
-        }
-    }
-
-    @Test(groups="Integration", dependsOnMethods="testSudo")
-    public void testWaitForPortFreeWhenNotAbortingOnTimeout() throws Exception {
-        ServerSocket serverSocket = openServerSocket();
-        try {
-            int port = serverSocket.getLocalPort();
-            String cmd = BashCommands.waitForPortFree(port, Duration.ONE_SECOND, false);
-    
-            String output = execRequiringZeroAndReturningStdout(loc, cmd).get();
-            assertTrue(output.contains(port+" still in use"), "output="+output);
-    
-            serverSocket.close();
-            assertTrue(Networking.isPortAvailable(port));
-            String output2 = execRequiringZeroAndReturningStdout(loc, cmd).get();
-            assertFalse(output2.contains("still in use"), "output="+output2);
-        } finally {
-            serverSocket.close();
-        }
-    }
-    
-    @Test(groups="Integration", dependsOnMethods="testSudo")
-    public void testWaitForPortFreeWhenFreedAfterStart() throws Exception {
-        ServerSocket serverSocket = openServerSocket();
-        try {
-            int port = serverSocket.getLocalPort();
-    
-            String cmd = BashCommands.waitForPortFree(port, Duration.THIRTY_SECONDS, false);
-            ProcessTaskWrapper<String> t = execRequiringZeroAndReturningStdout(loc, cmd);
-            exec.submit(t);
-            
-            // sleep for long enough to ensure the ssh command is definitely executing
-            Thread.sleep(5*1000);
-            assertFalse(t.isDone());
-            
-            serverSocket.close();
-            assertTrue(Networking.isPortAvailable(port));
-            String output = t.get();
-            assertFalse(output.contains("still in use"), "output="+output);
-        } finally {
-            serverSocket.close();
-        }
-    }
-
-    
-    // Disabled by default because of risk of overriding /etc/hosts in really bad way if doesn't work properly!
-    // As a manual visual inspection test, consider first manually creating /etc/hostname and /etc/sysconfig/network
-    // so that it looks like debian+ubuntu / CentOS/RHEL.
-    @Test(groups={"Integration"}, enabled=false)
-    public void testSetHostnameUnqualified() throws Exception {
-        runSetHostname("br-"+Identifiers.makeRandomId(8).toLowerCase(), null, false);
-    }
-
-    @Test(groups={"Integration"}, enabled=false)
-    public void testSetHostnameQualified() throws Exception {
-        runSetHostname("br-"+Identifiers.makeRandomId(8).toLowerCase()+".brooklyn.incubator.apache.org", null, false);
-    }
-
-    @Test(groups={"Integration"}, enabled=false)
-    public void testSetHostnameNullDomain() throws Exception {
-        runSetHostname("br-"+Identifiers.makeRandomId(8).toLowerCase(), null, true);
-    }
-
-    @Test(groups={"Integration"}, enabled=false)
-    public void testSetHostnameNonNullDomain() throws Exception {
-        runSetHostname("br-"+Identifiers.makeRandomId(8).toLowerCase(), "brooklyn.incubator.apache.org", true);
-    }
-
-    protected void runSetHostname(String newHostname, String newDomain, boolean includeDomain) throws Exception {
-        String fqdn = (includeDomain && Strings.isNonBlank(newDomain)) ? newHostname + "." + newDomain : newHostname;
-        
-        LocalManagementContextForTests mgmt = new LocalManagementContextForTests();
-        SshMachineLocation loc = mgmt.getLocationManager().createLocation(LocalhostMachineProvisioningLocation.spec()).obtain();
-
-        execRequiringZeroAndReturningStdout(loc, sudo("cp /etc/hosts /etc/hosts-orig-testSetHostname")).get();
-        execRequiringZeroAndReturningStdout(loc, BashCommands.ifFileExistsElse0("/etc/hostname", sudo("cp /etc/hostname /etc/hostname-orig-testSetHostname"))).get();
-        execRequiringZeroAndReturningStdout(loc, BashCommands.ifFileExistsElse0("/etc/sysconfig/network", sudo("cp /etc/sysconfig/network /etc/sysconfig/network-orig-testSetHostname"))).get();
-        
-        String origHostname = getHostnameNoArgs(loc);
-        assertTrue(Strings.isNonBlank(origHostname));
-        
-        try {
-            List<String> cmd = (includeDomain) ? BashCommands.setHostname(newHostname, newDomain) : BashCommands.setHostname(newHostname);
-            execRequiringZeroAndReturningStdout(loc, cmd).get();
-
-            String actualHostnameUnqualified = getHostnameUnqualified(loc);
-            String actualHostnameFullyQualified = getHostnameFullyQualified(loc);
-
-            // TODO On OS X at least, we aren't actually setting the domain name; we're just letting 
-            //      the user pass in what the domain name is. We do add this properly to /etc/hosts
-            //      (e.g. first line is "127.0.0.1 br-g4x5wgx8.brooklyn.incubator.apache.org br-g4x5wgx8 localhost")
-            //      but subsequent calls to `hostname -f` returns the unqualified. Similarly, `domainname` 
-            //      returns blank. Therefore we can't assert that it equals our expected val (because we just made  
-            //      it up - "brooklyn.incubator.apache.org").
-            //      assertEquals(actualHostnameFullyQualified, fqdn);
-            assertEquals(actualHostnameUnqualified, Strings.getFragmentBetween(newHostname, null, "."));
-            execRequiringZeroAndReturningStdout(loc, "ping -c1 -n -q "+actualHostnameUnqualified).get();
-            execRequiringZeroAndReturningStdout(loc, "ping -c1 -n -q "+actualHostnameFullyQualified).get();
-            
-            String result = execRequiringZeroAndReturningStdout(loc, "grep -n "+fqdn+" /etc/hosts").get();
-            assertTrue(result.contains("localhost"), "line="+result);
-            log.info("result="+result);
-            
-        } finally {
-            execRequiringZeroAndReturningStdout(loc, sudo("cp /etc/hosts-orig-testSetHostname /etc/hosts")).get();
-            execRequiringZeroAndReturningStdout(loc, BashCommands.ifFileExistsElse0("/etc/hostname-orig-testSetHostname", sudo("cp /etc/hostname-orig-testSetHostname /etc/hostname"))).get();
-            execRequiringZeroAndReturningStdout(loc, BashCommands.ifFileExistsElse0("/etc/sysconfig/network-orig-testSetHostname", sudo("cp /etc/sysconfig/network-orig-testSetHostname /etc/sysconfig/network"))).get();
-            execRequiringZeroAndReturningStdout(loc, sudo("hostname "+origHostname)).get();
-        }
-    }
-
-    // Marked disabled because not safe to run on your normal machine! It modifies /etc/hosts, which is dangerous if things go wrong!
-    @Test(groups={"Integration"}, enabled=false)
-    public void testModifyEtcHosts() throws Exception {
-        LocalManagementContextForTests mgmt = new LocalManagementContextForTests();
-        SshMachineLocation loc = mgmt.getLocationManager().createLocation(LocalhostMachineProvisioningLocation.spec()).obtain();
-
-        execRequiringZeroAndReturningStdout(loc, sudo("cp /etc/hosts /etc/hosts-orig-testModifyEtcHosts")).get();
-        int numLinesOrig = Integer.parseInt(execRequiringZeroAndReturningStdout(loc, "wc -l /etc/hosts").get().trim().split("\\s")[0]);
-        
-        try {
-            String cmd = BashCommands.prependToEtcHosts("1.2.3.4", "myhostnamefor1234.at.start", "myhostnamefor1234b");
-            execRequiringZeroAndReturningStdout(loc, cmd).get();
-            
-            String cmd2 = BashCommands.appendToEtcHosts("5.6.7.8", "myhostnamefor5678.at.end", "myhostnamefor5678");
-            execRequiringZeroAndReturningStdout(loc, cmd2).get();
-            
-            String grepFirst = execRequiringZeroAndReturningStdout(loc, "grep -n myhostnamefor1234 /etc/hosts").get();
-            String grepLast = execRequiringZeroAndReturningStdout(loc, "grep -n myhostnamefor5678 /etc/hosts").get();
-            int numLinesAfter = Integer.parseInt(execRequiringZeroAndReturningStdout(loc, "wc -l /etc/hosts").get().trim().split("\\s")[0]);
-            log.info("result: numLinesBefore="+numLinesOrig+"; numLinesAfter="+numLinesAfter+"; first="+grepFirst+"; last="+grepLast);
-            
-            assertTrue(grepFirst.startsWith("1:") && grepFirst.contains("1.2.3.4 myhostnamefor1234.at.start myhostnamefor1234"), "first="+grepFirst);
-            assertTrue(grepLast.startsWith((numLinesOrig+2)+":") && grepLast.contains("5.6.7.8 myhostnamefor5678.at.end myhostnamefor5678"), "last="+grepLast);
-            assertEquals(numLinesOrig + 2, numLinesAfter, "lines orig="+numLinesOrig+", after="+numLinesAfter);
-        } finally {
-            execRequiringZeroAndReturningStdout(loc, sudo("cp /etc/hosts-orig-testModifyEtcHosts /etc/hosts")).get();
-        }
-    }
-    
-    private String getHostnameNoArgs(SshMachineLocation machine) {
-        String hostnameStdout = execRequiringZeroAndReturningStdout(machine, "echo FOREMARKER; hostname; echo AFTMARKER").get();
-        return Strings.getFragmentBetween(hostnameStdout, "FOREMARKER", "AFTMARKER").trim();
-    }
-
-    private String getHostnameUnqualified(SshMachineLocation machine) {
-        String hostnameStdout = execRequiringZeroAndReturningStdout(machine, "echo FOREMARKER; hostname -s 2> /dev/null || hostname; echo AFTMARKER").get();
-        return Strings.getFragmentBetween(hostnameStdout, "FOREMARKER", "AFTMARKER").trim();
-    }
-
-    private String getHostnameFullyQualified(SshMachineLocation machine) {
-        String hostnameStdout = execRequiringZeroAndReturningStdout(machine, "echo FOREMARKER; hostname --fqdn 2> /dev/null || hostname -f; echo AFTMARKER").get();
-        return Strings.getFragmentBetween(hostnameStdout, "FOREMARKER", "AFTMARKER").trim();
-    }
-
-    private ProcessTaskWrapper<String> execRequiringZeroAndReturningStdout(SshMachineLocation loc, Collection<String> cmds) {
-        return execRequiringZeroAndReturningStdout(loc, cmds.toArray(new String[cmds.size()]));
-    }
-    
-    private ProcessTaskWrapper<String> execRequiringZeroAndReturningStdout(SshMachineLocation loc, String... cmds) {
-        ProcessTaskWrapper<String> t = SshTasks.newSshExecTaskFactory(loc, cmds)
-                .requiringZeroAndReturningStdout().newTask();
-        exec.submit(t);
-        return t;
-    }
-
-    private ServerSocket openServerSocket() {
-        int lowerBound = 40000;
-        int upperBound = 40100;
-        for (int i = lowerBound; i < upperBound; i++) {
-            try {
-                return new ServerSocket(i);
-            } catch (IOException e) {
-                // try next number
-            }
-        }
-        throw new IllegalStateException("No ports available in range "+lowerBound+" to "+upperBound);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/task/BasicTaskExecutionPerformanceTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/BasicTaskExecutionPerformanceTest.java b/core/src/test/java/brooklyn/util/task/BasicTaskExecutionPerformanceTest.java
deleted file mode 100644
index 574c8c7..0000000
--- a/core/src/test/java/brooklyn/util/task/BasicTaskExecutionPerformanceTest.java
+++ /dev/null
@@ -1,206 +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.assertNull;
-import static org.testng.Assert.assertTrue;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.brooklyn.api.management.Task;
-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.util.collections.MutableMap;
-
-import com.google.common.base.Predicate;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-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 BasicTaskExecutionPerformanceTest {
-    private static final Logger log = LoggerFactory.getLogger(BasicTaskExecutionPerformanceTest.class);
- 
-    private static final int TIMEOUT_MS = 10*1000;
-    
-    private BasicExecutionManager em;
-
-    public static final int MAX_OVERHEAD_MS = 1500; // was 750ms but saw 1.3s on buildhive
-    public static final int EARLY_RETURN_GRACE = 25; // saw 13ms early return on jenkins!
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        em = new BasicExecutionManager("mycontext");
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (em != null) em.shutdownNow();
-    }
-    
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testScheduledTaskExecutedAfterDelay() throws Exception {
-        int delay = 100;
-        final CountDownLatch latch = new CountDownLatch(1);
-        
-        Callable<Task<?>> taskFactory = new Callable<Task<?>>() {
-            @Override public Task<?> call() {
-                return new BasicTask<Void>(new Runnable() {
-                    @Override public void run() {
-                        latch.countDown();
-                    }});
-            }};
-        ScheduledTask t = new ScheduledTask(taskFactory).delay(delay);
-
-        Stopwatch stopwatch = Stopwatch.createStarted();
-        em.submit(t);
-        
-        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        long actualDelay = stopwatch.elapsed(TimeUnit.MILLISECONDS);
-        
-        assertTrue(actualDelay > (delay-EARLY_RETURN_GRACE), "actualDelay="+actualDelay+"; delay="+delay);
-        assertTrue(actualDelay < (delay+MAX_OVERHEAD_MS), "actualDelay="+actualDelay+"; delay="+delay);
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testScheduledTaskExecutedAtRegularPeriod() throws Exception {
-        final int period = 100;
-        final int numTimestamps = 4;
-        final CountDownLatch latch = new CountDownLatch(1);
-        final List<Long> timestamps = Collections.synchronizedList(Lists.<Long>newArrayList());
-        final Stopwatch stopwatch = Stopwatch.createStarted();
-        
-        Callable<Task<?>> taskFactory = new Callable<Task<?>>() {
-            @Override public Task<?> call() {
-                return new BasicTask<Void>(new Runnable() {
-                    @Override public void run() {
-                        timestamps.add(stopwatch.elapsed(TimeUnit.MILLISECONDS));
-                        if (timestamps.size() >= numTimestamps) latch.countDown();
-                    }});
-            }};
-        ScheduledTask t = new ScheduledTask(taskFactory).delay(1).period(period);
-        em.submit(t);
-        
-        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        
-        synchronized (timestamps) {
-            long prev = timestamps.get(0);
-            for (long timestamp : timestamps.subList(1, timestamps.size())) {
-                assertTrue(timestamp > prev+period-EARLY_RETURN_GRACE, "timestamps="+timestamps);
-                assertTrue(timestamp < prev+period+MAX_OVERHEAD_MS, "timestamps="+timestamps);
-                prev = timestamp;
-            }
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testCanCancelScheduledTask() throws Exception {
-        final int period = 1;
-        final long checkPeriod = 250;
-        final List<Long> timestamps = Collections.synchronizedList(Lists.<Long>newArrayList());
-        
-        Callable<Task<?>> taskFactory = new Callable<Task<?>>() {
-            @Override public Task<?> call() {
-                return new BasicTask<Void>(new Runnable() {
-                    @Override public void run() {
-                        timestamps.add(System.currentTimeMillis());
-                    }});
-            }};
-        ScheduledTask t = new ScheduledTask(taskFactory).period(period);
-        em.submit(t);
-
-        t.cancel();
-        long cancelTime = System.currentTimeMillis();
-        int countImmediatelyAfterCancel = timestamps.size();
-        Thread.sleep(checkPeriod);
-        int countWellAfterCancel = timestamps.size();
-
-        // should have at most 1 more execution after cancel
-        log.info("testCanCancelScheduledTask saw "+countImmediatelyAfterCancel+" then cancel then "+countWellAfterCancel+" total");                
-        assertTrue(countWellAfterCancel - countImmediatelyAfterCancel <= 2, "timestamps="+timestamps+"; cancelTime="+cancelTime);
-    }
-
-    // Previously, when we used a CopyOnWriteArraySet, performance for submitting new tasks was
-    // terrible, and it degraded significantly as the number of previously executed tasks increased
-    // (e.g. 9s for first 1000; 26s for next 1000; 42s for next 1000).
-    @Test
-    public void testExecutionManagerPerformance() throws Exception {
-        // Was fixed at 1000 tasks, but was running out of virtual memory due to excessive thread creation
-        // on machines which were not able to execute the threads quickly.
-        final int NUM_TASKS = Math.min(500 * Runtime.getRuntime().availableProcessors(), 1000);
-        final int NUM_TIMES = 10;
-        final int MAX_ACCEPTABLE_TIME = 7500; // saw 5601ms on buildhive
-        
-        long tWarmup = execTasksAndWaitForDone(NUM_TASKS, ImmutableList.of("A"));
-        
-        List<Long> times = Lists.newArrayList();
-        for (int i = 1; i <= NUM_TIMES; i++) {
-            times.add(execTasksAndWaitForDone(NUM_TASKS, ImmutableList.of("A")));
-        }
-        
-        Long toobig = Iterables.find(
-                times, 
-                new Predicate<Long>() {
-                    public boolean apply(Long input) {
-                        return input > MAX_ACCEPTABLE_TIME;
-                    }},
-                null);
-        assertNull(toobig, "warmup="+tWarmup+"; times="+times);
-    }
-    
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    private long execTasksAndWaitForDone(int numTasks, List<?> tags) throws Exception {
-        List<Task<?>> tasks = Lists.newArrayList();
-        long startTimestamp = System.currentTimeMillis();
-        for (int i = 1; i < numTasks; i++) {
-            Task<?> t = new BasicTask(Callables.returning(null)); // no-op
-            em.submit(MutableMap.of("tags", tags), t);
-            tasks.add(t);
-        }
-        long submittedTimestamp = System.currentTimeMillis();
-
-        for (Task t : tasks) {
-            t.get();
-        }
-        long endTimestamp = System.currentTimeMillis();
-        long submitTime = submittedTimestamp - startTimestamp;
-        long totalTime = endTimestamp - startTimestamp;
-        
-        log.info("Executed {} tasks; {}ms total; {}ms to submit", new Object[] {numTasks, totalTime, submitTime});
-
-        return totalTime;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/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
deleted file mode 100644
index 40660d4..0000000
--- a/core/src/test/java/brooklyn/util/task/BasicTaskExecutionTest.java
+++ /dev/null
@@ -1,460 +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.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.apache.brooklyn.api.management.Task;
-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.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.getSubmittedByTask());
-        assertEquals(-1, t.submitTimeUtc);
-        assertNull(t.getInternalFuture());
-
-        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.getInternalFuture());
-        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.getSubmittedByTask() );
-        
-        BasicTask<?> submitter = (BasicTask<?>) tb.getSubmittedByTask();
-        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() {
-            }
-        };
-    }
-}


[29/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/config/ConfigBag.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/config/ConfigBag.java b/core/src/main/java/org/apache/brooklyn/core/util/config/ConfigBag.java
new file mode 100644
index 0000000..0152bb9
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/config/ConfigBag.java
@@ -0,0 +1,589 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.config;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.ConcurrentModificationException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.config.ConfigKey.HasConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.javalang.JavaClassNames;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import com.google.common.collect.Sets;
+
+/**
+ * Stores config in such a way that usage can be tracked.
+ * Either {@link ConfigKey} or {@link String} keys can be inserted;
+ * they will be stored internally as strings.
+ * It is recommended to use {@link ConfigKey} instances to access,
+ * although in some cases (such as setting fields from flags, or copying a map)
+ * it may be necessary to mark things as used, or put, when only a string key is available.
+ * <p>
+ * This bag is order-preserving and thread-safe except where otherwise indicated,
+ * currently by synching on this instance (but that behaviour may change).
+ * <p>
+ * @author alex
+ */
+public class ConfigBag {
+
+    private static final Logger log = LoggerFactory.getLogger(ConfigBag.class);
+
+    /** an immutable, empty ConfigBag */
+    public static final ConfigBag EMPTY = new ConfigBag().setDescription("immutable empty config bag").seal();
+    
+    protected String description;
+    
+    private Map<String,Object> config;
+    private final Map<String,Object> unusedConfig;
+    private final boolean live;
+    private boolean sealed = false;
+
+    /** creates a new ConfigBag instance, empty and ready for population */
+    public static ConfigBag newInstance() {
+        return new ConfigBag();
+    }
+
+    /**
+     * Creates an instance that is backed by a "live map" (e.g. storage in a datagrid).
+     * The order-preserving nature of this class is only guaranteed if the
+     * provided storage has those properties. External modifications to the store can cause
+     * {@link ConcurrentModificationException} to be thrown, here or elsewhere. 
+     */
+    public static ConfigBag newLiveInstance(Map<String,Object> storage) {
+        return new ConfigBag(checkNotNull(storage, "storage map must be specified"));
+    }
+
+    public static ConfigBag newInstance(Map<?, ?> config) {
+        ConfigBag result = new ConfigBag();
+        result.putAll(config);
+        return result;
+    }
+
+    /** creates a new ConfigBag instance which includes all of the supplied ConfigBag's values,
+     * but which tracks usage separately (already used values are marked as such,
+     * but uses in the original set will not be marked here, and vice versa) */
+    public static ConfigBag newInstanceCopying(final ConfigBag configBag) {
+        return new ConfigBag().copy(configBag).setDescription(configBag.getDescription());
+    }
+    
+    /** creates a new ConfigBag instance which includes all of the supplied ConfigBag's values,
+     * plus an additional set of &lt;ConfigKey,Object&gt; or &lt;String,Object&gt; pairs
+     * <p>
+     * values from the original set which are used here will be marked as used in the original set
+     * (note: this applies even for values which are overridden and the overridden value is used);
+     * however subsequent uses in the original set will not be marked here
+     */
+    @Beta
+    public static ConfigBag newInstanceExtending(final ConfigBag parentBag) {
+        return new ConfigBagExtendingParent(parentBag);
+    }
+
+    /** @see #newInstanceExtending(ConfigBag) */
+    private static class ConfigBagExtendingParent extends ConfigBag {
+        ConfigBag parentBag;
+        private ConfigBagExtendingParent(ConfigBag parentBag) {
+            this.parentBag = parentBag;
+            copy(parentBag);
+        }
+        @Override
+        public void markUsed(String key) {
+            super.markUsed(key);
+            if (parentBag!=null)
+                parentBag.markUsed(key);
+        }
+    }
+    
+    /** As {@link #newInstanceExtending(ConfigBag)} but also putting the supplied values. */
+    @Beta
+    public static ConfigBag newInstanceExtending(final ConfigBag configBag, Map<?,?> optionalAdditionalValues) {
+        return newInstanceExtending(configBag).putAll(optionalAdditionalValues);
+    }
+
+    /** @deprecated since 0.7.0, not used; kept only for rebind compatibility where the inner class is used 
+     * (now replaced by a static class above) */
+    @Beta @Deprecated
+    public static ConfigBag newInstanceWithInnerClass(final ConfigBag configBag, Map<?,?> optionalAdditionalValues) {
+        return new ConfigBag() {
+            @Override
+            public void markUsed(String key) {
+                super.markUsed(key);
+                configBag.markUsed(key);
+            }
+        }.copy(configBag).putAll(optionalAdditionalValues);
+    }
+
+    public ConfigBag() {
+        config = new LinkedHashMap<String,Object>();
+        unusedConfig = new LinkedHashMap<String,Object>();
+        live = false;
+    }
+    
+    private ConfigBag(Map<String,Object> storage) {
+        this.config = storage;
+        unusedConfig = new LinkedHashMap<String,Object>();
+        live = true;
+    }
+    
+    public ConfigBag setDescription(String description) {
+        if (sealed) 
+            throw new IllegalStateException("Cannot set description to '"+description+"': this config bag has been sealed and is now immutable.");
+        this.description = description;
+        return this;
+    }
+    
+    /** optional description used to provide context for operations */
+    public String getDescription() {
+        return description;
+    }
+    
+    /** current values for all entries 
+     * @return non-modifiable map of strings to object */
+    public synchronized Map<String,Object> getAllConfig() {
+        return MutableMap.copyOf(config).asUnmodifiable();
+    }
+
+    /** current values for all entries in a map where the keys are converted to {@link ConfigKey} instances */
+    public synchronized Map<ConfigKey<?>, ?> getAllConfigAsConfigKeyMap() {
+        Map<ConfigKey<?>,Object> result = MutableMap.of();
+        for (Map.Entry<String,Object> entry: config.entrySet()) {
+            result.put(ConfigKeys.newConfigKey(Object.class, entry.getKey()), entry.getValue());
+        }
+        return result;
+    }
+
+    /** Returns the internal map containing the current values for all entries;
+     * for use where the caller wants to modify this directly and knows it is safe to do so 
+     * <p>
+     * Accesses to the returned map must be synchronized on this bag if the 
+     * thread-safe behaviour is required. */ 
+    public Map<String,Object> getAllConfigMutable() {
+        if (live) {
+            // TODO sealed no longer works as before, because `config` is the backing storage map.
+            // Therefore returning it is dangerous! Even if we were to replace our field with an immutable copy,
+            // the underlying datagrid's map would still be modifiable. We need a way to switch the returned
+            // value's behaviour to sealable (i.e. wrapping the returned map).
+            return (sealed) ? MutableMap.copyOf(config).asUnmodifiable() : config;
+        } else {
+            return config;
+        }
+    }
+
+    /** current values for all entries which have not yet been used 
+     * @return non-modifiable map of strings to object */
+    public synchronized Map<String,Object> getUnusedConfig() {
+        return MutableMap.copyOf(unusedConfig).asUnmodifiable();
+    }
+
+    /** Returns the internal map containing the current values for all entries which have not yet been used;
+     * for use where the caller wants to modify this directly and knows it is safe to do so 
+     * <p>
+     * Accesses to the returned map must be synchronized on this bag if the 
+     * thread-safe behaviour is required. */ 
+    public Map<String,Object> getUnusedConfigMutable() {
+        return unusedConfig;
+    }
+
+    public ConfigBag putAll(Map<?,?> addlConfig) {
+        if (addlConfig==null) return this;
+        for (Map.Entry<?,?> e: addlConfig.entrySet()) {
+            putAsStringKey(e.getKey(), e.getValue());
+        }
+        return this;
+    }
+    
+    public ConfigBag putAll(ConfigBag addlConfig) {
+        return putAll(addlConfig.getAllConfig());
+    }
+    
+    public <T> ConfigBag putIfAbsent(ConfigKey<T> key, T value) {
+        return putIfAbsent(MutableMap.of(key, value));
+    }
+
+    public ConfigBag putAsStringKeyIfAbsent(Object key, Object value) {
+        return putIfAbsent(MutableMap.of(key, value));
+    }
+
+    public synchronized ConfigBag putIfAbsent(Map<?, ?> propertiesToSet) {
+        if (propertiesToSet==null)
+            return this;
+        for (Map.Entry<?, ?> entry: propertiesToSet.entrySet()) {
+            Object key = entry.getKey();
+            if (key instanceof HasConfigKey<?>)
+                key = ((HasConfigKey<?>)key).getConfigKey();
+            if (key instanceof ConfigKey<?>) {
+                if (!containsKey((ConfigKey<?>)key))
+                    putAsStringKey(key, entry.getValue());
+            } else if (key instanceof String) {
+                if (!containsKey((String)key))
+                    putAsStringKey(key, entry.getValue());
+            } else {
+                logInvalidKey(key);
+            }
+        }
+        return this;
+    }
+
+    public ConfigBag putIfAbsent(ConfigBag addlConfig) {
+        return putIfAbsent(addlConfig.getAllConfig());
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public <T> T put(ConfigKey<T> key, T value) {
+        return (T) putStringKey(key.getName(), value);
+    }
+    
+    public <T> ConfigBag putIfNotNull(ConfigKey<T> key, T value) {
+        if (value!=null) put(key, value);
+        return this;
+    }
+
+    public <T> ConfigBag putIfAbsentAndNotNull(ConfigKey<T> key, T value) {
+        if (value!=null) putIfAbsent(key, value);
+        return this;
+    }
+
+    /** as {@link #put(ConfigKey, Object)} but returning this ConfigBag for fluent-style coding */
+    public <T> ConfigBag configure(ConfigKey<T> key, T value) {
+        putStringKey(key.getName(), value);
+        return this;
+    }
+    
+    public <T> ConfigBag configureStringKey(String key, T value) {
+        putStringKey(key, value);
+        return this;
+    }
+    
+    protected synchronized void putAsStringKey(Object key, Object value) {
+        if (key instanceof HasConfigKey<?>) key = ((HasConfigKey<?>)key).getConfigKey();
+        if (key instanceof ConfigKey<?>) key = ((ConfigKey<?>)key).getName();
+        if (key instanceof String) {
+            putStringKey((String)key, value);
+        } else {
+            logInvalidKey(key);
+        }
+    }
+
+    protected void logInvalidKey(Object key) {
+        String message = (key == null ? "Invalid key 'null'" : "Invalid key type "+key.getClass().getCanonicalName()+" ("+key+")") +
+                " being used for configuration, ignoring";
+        log.debug(message, new Throwable("Source of "+message));
+        log.warn(message);
+    }
+    
+    /** recommended to use {@link #put(ConfigKey, Object)} but there are times
+     * (e.g. when copying a map) where we want to put a string key directly 
+     */
+    public synchronized Object putStringKey(String key, Object value) {
+        if (sealed) 
+            throw new IllegalStateException("Cannot insert "+key+"="+value+": this config bag has been sealed and is now immutable.");
+        boolean isNew = !config.containsKey(key);
+        boolean isUsed = !isNew && !unusedConfig.containsKey(key);
+        Object old = config.put(key, value);
+        if (!isUsed) 
+            unusedConfig.put(key, value);
+        //if (!isNew && !isUsed) log.debug("updating config value which has already been used");
+        return old;
+    }
+    public Object putStringKeyIfHasValue(String key, Maybe<?> value) {
+        if (value.isPresent())
+            return putStringKey(key, value.get());
+        return null;
+    }
+    public Object putStringKeyIfNotNull(String key, Object value) {
+        if (value!=null)
+            return putStringKey(key, value);
+        return null;
+    }
+
+    public boolean containsKey(HasConfigKey<?> key) {
+        return containsKey(key.getConfigKey());
+    }
+
+    public boolean containsKey(ConfigKey<?> key) {
+        return containsKey(key.getName());
+    }
+
+    public synchronized boolean containsKey(String key) {
+        return config.containsKey(key);
+    }
+
+    /** returns the value of this config key, falling back to its default (use containsKey to see whether it was contained);
+     * also marks it as having been used (use peek to prevent marking as used)
+     */
+    public <T> T get(ConfigKey<T> key) {
+        return get(key, true);
+    }
+
+    /** gets a value from a string-valued key or null; ConfigKey is preferred, but this is useful in some contexts (e.g. setting from flags) */
+    public Object getStringKey(String key) {
+        return getStringKeyMaybe(key).orNull();
+    }
+    /** gets a {@link Maybe}-wrapped value from a string-valued key; ConfigKey is preferred, but this is useful in some contexts (e.g. setting from flags) */
+    public @Nonnull Maybe<Object> getStringKeyMaybe(String key) {
+        return getStringKeyMaybe(key, true);
+    }
+
+    /** gets a {@link Maybe}-wrapped value from a key, inferring the type of that key (e.g. {@link ConfigKey} or {@link String}) */
+    @Beta
+    public Maybe<Object> getObjKeyMaybe(Object key) {
+        if (key instanceof HasConfigKey<?>) key = ((HasConfigKey<?>)key).getConfigKey();
+        if (key instanceof ConfigKey<?>) key = ((ConfigKey<?>)key).getName();
+        if (key instanceof String) {
+            return getStringKeyMaybe((String)key, true);
+        } else {
+            logInvalidKey(key);
+            return Maybe.absent();
+        }
+    }
+
+    /** like get, but without marking it as used */
+    public <T> T peek(ConfigKey<T> key) {
+        return get(key, false);
+    }
+
+    /** returns the first key in the list for which a value is explicitly set, then defaulting to defaulting value of preferred key */
+    public synchronized <T> T getFirst(ConfigKey<T> preferredKey, ConfigKey<T> ...otherCurrentKeysInOrderOfPreference) {
+        if (containsKey(preferredKey)) 
+            return get(preferredKey);
+        for (ConfigKey<T> key: otherCurrentKeysInOrderOfPreference) {
+            if (containsKey(key)) 
+                return get(key);
+        }
+        return get(preferredKey);
+    }
+
+    /** convenience for @see #getWithDeprecation(ConfigKey[], ConfigKey...) */
+    public Object getWithDeprecation(ConfigKey<?> key, ConfigKey<?> ...deprecatedKeys) {
+        return getWithDeprecation(new ConfigKey[] { key }, deprecatedKeys);
+    }
+
+    /** returns the value for the first key in the list for which a value is set,
+     * warning if any of the deprecated keys have a value which is different to that set on the first set current key
+     * (including warning if a deprecated key has a value but no current key does) */
+    public synchronized Object getWithDeprecation(ConfigKey<?>[] currentKeysInOrderOfPreference, ConfigKey<?> ...deprecatedKeys) {
+        // Get preferred key (or null)
+        ConfigKey<?> preferredKeyProvidingValue = null;
+        Object result = null;
+        boolean found = false;
+        for (ConfigKey<?> key: currentKeysInOrderOfPreference) {
+            if (containsKey(key)) {
+                preferredKeyProvidingValue = key;
+                result = get(preferredKeyProvidingValue);
+                found = true;
+                break;
+            }
+        }
+        
+        // Check if any deprecated keys are set
+        ConfigKey<?> deprecatedKeyProvidingValue = null;
+        Object deprecatedResult = null;
+        boolean foundDeprecated = false;
+        for (ConfigKey<?> deprecatedKey: deprecatedKeys) {
+            Object x = null;
+            boolean foundX = false;
+            if (containsKey(deprecatedKey)) {
+                x = get(deprecatedKey);
+                foundX = true;
+            }
+            if (foundX) {
+                if (found) {
+                    if (!Objects.equal(result, x)) {
+                        log.warn("Conflicting value from deprecated key " +deprecatedKey+", value "+x+
+                                "; using preferred key "+preferredKeyProvidingValue+" value "+result);
+                    } else {
+                        log.info("Deprecated key " +deprecatedKey+" ignored; has same value as preferred key "+preferredKeyProvidingValue+" ("+result+")");
+                    }
+                } else if (foundDeprecated) {
+                    if (!Objects.equal(result, x)) {
+                        log.warn("Conflicting values from deprecated keys: using " +deprecatedKeyProvidingValue+" instead of "+deprecatedKey+
+                                " (value "+deprecatedResult+" instead of "+x+")");
+                    } else {
+                        log.info("Deprecated key " +deprecatedKey+" ignored; has same value as other deprecated key "+preferredKeyProvidingValue+" ("+deprecatedResult+")");
+                    }
+                } else {
+                    // new value, from deprecated key
+                    log.warn("Deprecated key " +deprecatedKey+" detected (supplying value "+x+"), "+
+                            "; recommend changing to preferred key '"+currentKeysInOrderOfPreference[0]+"'; this will not be supported in future versions");
+                    deprecatedResult = x;
+                    deprecatedKeyProvidingValue = deprecatedKey;
+                    foundDeprecated = true;
+                }
+            }
+        }
+        
+        if (found) {
+            return result;
+        } else if (foundDeprecated) {
+            return deprecatedResult;
+        } else {
+            return currentKeysInOrderOfPreference[0].getDefaultValue();
+        }
+    }
+
+    protected <T> T get(ConfigKey<T> key, boolean markUsed) {
+        // TODO for now, no evaluation -- maps / closure content / other smart (self-extracting) keys are NOT supported
+        // (need a clean way to inject that behaviour, as well as desired TypeCoercions)
+        // this method, and the coercion, is not synchronized, nor does it need to be, because the "get" is synchronized. 
+        return coerceFirstNonNullKeyValue(key, getStringKey(key.getName(), markUsed));
+    }
+
+    /** returns the first non-null value to be the type indicated by the key, or the keys default value if no non-null values are supplied */
+    public static <T> T coerceFirstNonNullKeyValue(ConfigKey<T> key, Object ...values) {
+        for (Object o: values)
+            if (o!=null) return TypeCoercions.coerce(o, key.getTypeToken());
+        return TypeCoercions.coerce(key.getDefaultValue(), key.getTypeToken());
+    }
+
+    protected Object getStringKey(String key, boolean markUsed) {
+        return getStringKeyMaybe(key, markUsed).orNull();
+    }
+    protected synchronized Maybe<Object> getStringKeyMaybe(String key, boolean markUsed) {
+        if (config.containsKey(key)) {
+            if (markUsed) markUsed(key);
+            return Maybe.of(config.get(key));
+        }
+        return Maybe.absent();
+    }
+
+    /** indicates that a string key in the config map has been accessed */
+    public synchronized void markUsed(String key) {
+        unusedConfig.remove(key);
+    }
+
+    public synchronized void clear() {
+        if (sealed) 
+            throw new IllegalStateException("Cannot clear this config bag has been sealed and is now immutable.");
+        config.clear();
+        unusedConfig.clear();
+    }
+    
+    public ConfigBag removeAll(ConfigKey<?> ...keys) {
+        for (ConfigKey<?> key: keys) remove(key);
+        return this;
+    }
+
+    public synchronized void remove(ConfigKey<?> key) {
+        remove(key.getName());
+    }
+
+    public ConfigBag removeAll(Iterable<String> keys) {
+        for (String key: keys) remove(key);
+        return this;
+    }
+
+    public synchronized void remove(String key) {
+        if (sealed) 
+            throw new IllegalStateException("Cannot remove "+key+": this config bag has been sealed and is now immutable.");
+        config.remove(key);
+        unusedConfig.remove(key);
+    }
+
+    public ConfigBag copy(ConfigBag other) {
+        // ensure locks are taken in a canonical order to prevent deadlock
+        if (other==null) {
+            synchronized (this) {
+                return copyWhileSynched(other);
+            }
+        }
+        if (System.identityHashCode(other) < System.identityHashCode(this)) {
+            synchronized (other) {
+                synchronized (this) {
+                    return copyWhileSynched(other);
+                }
+            }
+        } else {
+            synchronized (this) {
+                synchronized (other) {
+                    return copyWhileSynched(other);
+                }
+            }
+        }
+    }
+    
+    protected ConfigBag copyWhileSynched(ConfigBag other) {
+        if (sealed) 
+            throw new IllegalStateException("Cannot copy "+other+" to "+this+": this config bag has been sealed and is now immutable.");
+        putAll(other.getAllConfig());
+        markAll(Sets.difference(other.getAllConfig().keySet(), other.getUnusedConfig().keySet()));
+        setDescription(other.getDescription());
+        return this;
+    }
+
+    public synchronized int size() {
+        return config.size();
+    }
+    
+    public synchronized boolean isEmpty() {
+        return config.isEmpty();
+    }
+    
+    public ConfigBag markAll(Iterable<String> usedFlags) {
+        for (String flag: usedFlags)
+            markUsed(flag);
+        return this;
+    }
+
+    public synchronized boolean isUnused(ConfigKey<?> key) {
+        return unusedConfig.containsKey(key.getName());
+    }
+    
+    /** makes this config bag immutable; any attempts to change subsequently 
+     * (apart from marking fields as used) will throw an exception
+     * <p>
+     * copies will be unsealed however
+     * <p>
+     * returns this for convenience (fluent usage) */
+    public ConfigBag seal() {
+        sealed = true;
+        if (live) {
+            // TODO How to ensure sealed?!
+        } else {
+            config = getAllConfig();
+        }
+        return this;
+    }
+
+    // TODO why have both this and mutable
+    /** @see #getAllConfigMutable() */
+    public Map<String, Object> getAllConfigRaw() {
+        return getAllConfigMutable();
+    }
+    
+    @Override
+    public String toString() {
+        return JavaClassNames.simpleClassName(this)+"["+getAllConfigRaw()+"]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/crypto/FluentKeySigner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/crypto/FluentKeySigner.java b/core/src/main/java/org/apache/brooklyn/core/util/crypto/FluentKeySigner.java
new file mode 100644
index 0000000..1d0b030
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/crypto/FluentKeySigner.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 org.apache.brooklyn.core.util.crypto;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.apache.brooklyn.core.internal.BrooklynInitialization;
+import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.bouncycastle.asn1.x509.X509Extension;
+import org.bouncycastle.jce.X509Principal;
+
+import brooklyn.util.exceptions.Exceptions;
+
+/** A fluent API which simplifies generating certificates (signed keys) */
+/* NB - re deprecation - we use deprecated X509V3CertificateGenerator still
+ * because the official replacement, X509v3CertificateBuilder, 
+ * drags in an add'l dependency (bcmail) and is harder to use. */
+public class FluentKeySigner {
+
+    static { BrooklynInitialization.initSecureKeysBouncyCastleProvider(); }
+
+    protected X500Principal issuerPrincipal;
+    protected KeyPair issuerKey;
+
+    protected SecureRandom srand = new SecureRandom();
+    
+    protected Date validityStartDate, validityEndDate;
+    protected BigInteger serialNumber;
+    
+    protected String signatureAlgorithm = "MD5WithRSAEncryption";
+    protected AuthorityKeyIdentifier authorityKeyIdentifier;
+    protected X509Certificate authorityCertificate;
+
+    public FluentKeySigner(X500Principal issuerPrincipal, KeyPair issuerKey) {
+        this.issuerPrincipal = issuerPrincipal;
+        this.issuerKey = issuerKey;
+        validFromDaysAgo(7);
+        validForYears(10);
+    }
+    public FluentKeySigner(String issuerCommonName, KeyPair issuerKey) {
+        this(SecureKeys.getX500PrincipalWithCommonName(issuerCommonName), issuerKey);
+    }
+    
+    public FluentKeySigner(String issuerCommonName) {
+        this(issuerCommonName, SecureKeys.newKeyPair());
+    }
+
+    public FluentKeySigner(X509Certificate caCert, KeyPair caKey) {
+        this(caCert.getIssuerX500Principal(), caKey);
+        authorityCertificate(caCert);
+    }
+    
+    public KeyPair getKey() {
+        return issuerKey;
+    }
+    
+    public X500Principal getPrincipal() {
+        return issuerPrincipal;
+    }
+    
+    @SuppressWarnings("deprecation")
+    public String getCommonName() {
+//        TODO see deprecation note at top of file
+        // for modernising, would RFC4519Style.cn work ?
+        return (String) new X509Principal(issuerPrincipal.getName()).getValues(org.bouncycastle.asn1.x509.X509Name.CN).elementAt(0);
+    }
+    
+    public X509Certificate getAuthorityCertificate() {
+        return authorityCertificate;
+    }
+    
+    public FluentKeySigner validFromDaysAgo(long days) {
+        return validFrom(new Date( (System.currentTimeMillis() / (1000L*60*60*24) - days) * 1000L*60*60*24));            
+    }
+
+    public FluentKeySigner validFrom(Date d) {
+        validityStartDate = d;
+        return this;
+    }
+
+    public FluentKeySigner validForYears(long years) {
+        return validUntil(new Date( (System.currentTimeMillis() / (1000L*60*60*24) + 365*years) * 1000L*60*60*24));            
+    }
+
+    public FluentKeySigner validUntil(Date d) {
+        validityEndDate = d;
+        return this;
+    }
+
+    /** use a hard-coded serial number; or make one up, if null */
+    public FluentKeySigner serialNumber(BigInteger serialNumber) {
+        this.serialNumber = serialNumber;
+        return this;
+    }
+
+    public FluentKeySigner signatureAlgorithm(String signatureAlgorithm) {
+        this.signatureAlgorithm = signatureAlgorithm;
+        return this;
+    }
+
+    @SuppressWarnings("deprecation")
+    public FluentKeySigner authorityCertificate(X509Certificate certificate) {
+        try {
+            authorityKeyIdentifier(new org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure(certificate));
+            this.authorityCertificate = certificate;
+            return this;
+        } catch (CertificateParsingException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    public FluentKeySigner authorityKeyIdentifier(AuthorityKeyIdentifier authorityKeyIdentifier) {
+        this.authorityKeyIdentifier = authorityKeyIdentifier;
+        return this;
+    }
+    
+    public FluentKeySigner selfsign() {
+        if (authorityCertificate!=null) throw new IllegalStateException("Signer already has certificate");
+        authorityCertificate(newCertificateFor(getCommonName(), getKey()));
+        return this;
+    }
+
+    // TODO see note re deprecation at start of file
+    @SuppressWarnings("deprecation")
+    public X509Certificate newCertificateFor(X500Principal subject, PublicKey keyToCertify) {
+        try {
+            org.bouncycastle.x509.X509V3CertificateGenerator v3CertGen = new org.bouncycastle.x509.X509V3CertificateGenerator();
+
+            v3CertGen.setSerialNumber(
+                    serialNumber != null ? serialNumber :
+                        // must be positive
+                        BigInteger.valueOf(srand.nextLong()).abs().add(BigInteger.ONE));  
+            v3CertGen.setIssuerDN(issuerPrincipal);  
+            v3CertGen.setNotBefore(validityStartDate);  
+            v3CertGen.setNotAfter(validityEndDate);
+            v3CertGen.setSignatureAlgorithm(signatureAlgorithm);   
+
+            v3CertGen.setSubjectDN(subject);  
+            v3CertGen.setPublicKey(keyToCertify);  
+
+            v3CertGen.addExtension(X509Extension.subjectKeyIdentifier, false,
+                    new org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure(keyToCertify));
+
+            if (authorityKeyIdentifier!=null)
+                v3CertGen.addExtension(X509Extension.authorityKeyIdentifier, false,
+                        authorityKeyIdentifier);
+
+            X509Certificate pkCertificate = v3CertGen.generate(issuerKey.getPrivate(), "BC");
+            return pkCertificate;
+            
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    public X509Certificate newCertificateFor(String commonName, PublicKey key) {
+//        SecureKeys.getX509PrincipalWithCommonName(commonName)
+        return newCertificateFor(
+                SecureKeys.getX500PrincipalWithCommonName(commonName)
+//                new X509Principal("CN=" + commonName + ", OU=None, O=None, L=None, C=None")
+                , key);
+    }
+
+    public X509Certificate newCertificateFor(String commonName, KeyPair key) {
+        return newCertificateFor(commonName, key.getPublic());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/crypto/SecureKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/crypto/SecureKeys.java b/core/src/main/java/org/apache/brooklyn/core/util/crypto/SecureKeys.java
new file mode 100644
index 0000000..5e630a8
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/crypto/SecureKeys.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Security;
+
+import org.apache.brooklyn.core.internal.BrooklynInitialization;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMDecryptorProvider;
+import org.bouncycastle.openssl.PEMEncryptedKeyPair;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.PEMWriter;
+import org.bouncycastle.openssl.PasswordFinder;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.crypto.AuthorizedKeysParser;
+import brooklyn.util.crypto.SecureKeysWithoutBouncyCastle;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.stream.Streams;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+
+/**
+ * Utility methods for generating and working with keys,
+ * extending the parent class with useful things provided by BouncyCastle crypto library.
+ * (Parent class is in a different project where BC is not included as a dependency.)
+ */
+public class SecureKeys extends SecureKeysWithoutBouncyCastle {
+
+    private static final Logger log = LoggerFactory.getLogger(SecureKeys.class);
+    
+    static { BrooklynInitialization.initSecureKeysBouncyCastleProvider(); }
+    
+    public static void initBouncyCastleProvider() {
+        Security.addProvider(new BouncyCastleProvider());
+    }
+    
+    public static class PassphraseProblem extends IllegalStateException {
+        private static final long serialVersionUID = -3382824813899223447L;
+        public PassphraseProblem(String message) { super("Passphrase problem with this key: "+message); }
+        public PassphraseProblem(String message, Exception cause) { super("Passphrase problem with this key: "+message, cause); }
+    }
+    
+    private SecureKeys() {}
+    
+    /** RFC1773 order, with None for other values. Normally prefer X500Principal. */
+    public static X509Principal getX509PrincipalWithCommonName(String commonName) {
+        return new X509Principal("" + "C=None," + "L=None," + "O=None," + "OU=None," + "CN=" + commonName);
+    }
+
+    /** reads RSA or DSA / pem style private key files (viz {@link #toPem(KeyPair)}), extracting also the public key if possible
+     * @throws IllegalStateException on errors, in particular {@link PassphraseProblem} if that is the problem */
+    public static KeyPair readPem(InputStream input, final String passphrase) {
+        // TODO cache is only for fallback "reader" strategy (2015-01); delete when Parser confirmed working
+        byte[] cache = Streams.readFully(input);
+        input = new ByteArrayInputStream(cache);
+
+        try {
+            PEMParser pemParser = new PEMParser(new InputStreamReader(input));
+
+            Object object = pemParser.readObject();
+            pemParser.close();
+
+            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
+            KeyPair kp = null;
+            if (object==null) {
+                throw new IllegalStateException("PEM parsing failed: missing or invalid data");
+            } else if (object instanceof PEMEncryptedKeyPair) {
+                if (passphrase==null) throw new PassphraseProblem("passphrase required");
+                try {
+                    PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(passphrase.toCharArray());
+                    kp = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv));
+                } catch (Exception e) {
+                    Exceptions.propagateIfFatal(e);
+                    throw new PassphraseProblem("wrong passphrase", e);
+                }
+            } else  if (object instanceof PEMKeyPair) {
+                kp = converter.getKeyPair((PEMKeyPair) object);
+            } else if (object instanceof PrivateKeyInfo) {
+                PrivateKey privKey = converter.getPrivateKey((PrivateKeyInfo) object);
+                kp = new KeyPair(null, privKey);
+            } else {
+                throw new IllegalStateException("PEM parser support missing for: "+object);
+            }
+
+            return kp;
+
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+
+            // older code relied on PEMReader, now deprecated
+            // replaced with above based on http://stackoverflow.com/questions/14919048/bouncy-castle-pemreader-pemparser
+            // passes the same tests (Jan 2015) but leaving the old code as a fallback for the time being 
+
+            input = new ByteArrayInputStream(cache);
+            try {
+                Security.addProvider(new BouncyCastleProvider());
+                @SuppressWarnings("deprecation")
+                org.bouncycastle.openssl.PEMReader pr = new org.bouncycastle.openssl.PEMReader(new InputStreamReader(input), new PasswordFinder() {
+                    public char[] getPassword() {
+                        return passphrase!=null ? passphrase.toCharArray() : new char[0];
+                    }
+                });
+                @SuppressWarnings("deprecation")
+                KeyPair result = (KeyPair) pr.readObject();
+                pr.close();
+                if (result==null)
+                    throw Exceptions.propagate(e);
+                
+                log.warn("PEMParser failed when deprecated PEMReader succeeded, with "+result+"; had: "+e);
+
+                return result;
+
+            } catch (Exception e2) {
+                Exceptions.propagateIfFatal(e2);
+                throw Exceptions.propagate(e);
+            }
+        }
+    }
+
+    /** because KeyPair.equals is not implemented :( */
+    public static boolean equal(KeyPair k1, KeyPair k2) {
+        return Objects.equal(k2.getPrivate(), k1.getPrivate()) && Objects.equal(k2.getPublic(), k1.getPublic());
+    }
+
+    /** returns the PEM (base64, ie for id_rsa) string for the private key / key pair;
+     * this starts -----BEGIN PRIVATE KEY----- and ends similarly, like id_rsa.
+     * also see {@link #readPem(InputStream, String)} */
+    public static String toPem(KeyPair key) {
+        return stringPem(key);
+    }
+
+    /** returns id_rsa.pub style file, of public key */
+    public static String toPub(KeyPair key) {
+        return AuthorizedKeysParser.encodePublicKey(key.getPublic());
+    }
+    
+    /** opposite of {@link #toPub(KeyPair)}, given text */
+    public static PublicKey fromPub(String pubText) {
+        return AuthorizedKeysParser.decodePublicKey(pubText);
+    }
+
+    /** @deprecated since 0.7.0, use {@link #toPem(KeyPair)} */ @Deprecated
+    public static String stringPem(KeyPair key) {
+        try {
+            StringWriter sw = new StringWriter();
+            PEMWriter w = new PEMWriter(sw);
+            w.writeObject(key);
+            w.close();
+            return sw.toString();
+        } catch (IOException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveBuilder.java b/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveBuilder.java
new file mode 100644
index 0000000..069f10b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveBuilder.java
@@ -0,0 +1,424 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.file;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.brooklyn.core.util.file.ArchiveUtils.ArchiveType;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.os.Os;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.io.Files;
+
+/**
+ * Build a Zip or Jar archive.
+ * <p>
+ * Supports creating temporary archives that will be deleted on exit, if no name is
+ * specified. The created file must be a Java archive type, with the extension {@code .zip},
+ * {@code .jar}, {@code .war} or {@code .ear}.
+ * <p>
+ * Example:
+ * <pre> File zip = ArchiveBuilder.archive("data/archive.zip")
+ *         .addAt(new File("./pom.xml"), "")
+ *         .addDirContentsAt(new File("./src"), "src/")
+ *         .addAt(new File("/tmp/Extra.java"), "src/main/java/")
+ *         .addDirContentsAt(new File("/tmp/overlay/"), "")
+ *         .create();
+ * </pre>
+ * <p>
+ */
+@Beta
+public class ArchiveBuilder {
+
+    /**
+     * Create an {@link ArchiveBuilder} for an archive with the given name.
+     */
+    public static ArchiveBuilder archive(String archive) {
+        return new ArchiveBuilder(archive);
+    }
+
+    /**
+     * Create an {@link ArchiveBuilder} for a {@link ArchiveType#ZIP Zip} format archive.
+     */
+    public static ArchiveBuilder zip() {
+        return new ArchiveBuilder(ArchiveType.ZIP);
+    }
+
+    /**
+     * Create an {@link ArchiveBuilder} for a {@link ArchiveType#JAR Jar} format archive.
+     */
+    public static ArchiveBuilder jar() {
+        return new ArchiveBuilder(ArchiveType.JAR);
+    }
+
+    // TODO would be nice to support TAR and TGZ
+    // e.g. using commons-compress
+    // TarArchiveOutputStream out = new TarArchiveOutputStream(new GZIPOutputStream(bytes));
+    // but I think the way entries are done is slightly different so we'd need a bit of refactoring
+    
+    private final ArchiveType type;
+    private File archive;
+    private Manifest manifest;
+    private Multimap<String, File> entries = LinkedHashMultimap.create();
+
+    private ArchiveBuilder() {
+        this(ArchiveType.ZIP);
+    }
+
+    private ArchiveBuilder(String filename) {
+        this(ArchiveType.of(filename));
+
+        named(filename);
+    }
+
+    private ArchiveBuilder(ArchiveType type) {
+        checkNotNull(type);
+        checkArgument(ArchiveType.ZIP_ARCHIVES.contains(type));
+
+        this.type = type;
+        this.manifest = new Manifest();
+    }
+
+    /**
+     * Set the location of the generated archive file.
+     */
+    public ArchiveBuilder named(String name) {
+        checkNotNull(name);
+        String ext = Files.getFileExtension(name);
+        if (ext.isEmpty()) {
+            name = name + "." + type.toString();
+        } else if (type != ArchiveType.of(name)) {
+            throw new IllegalArgumentException(String.format("Extension for '%s' did not match archive type of %s", ext, type));
+        }
+        this.archive = new File(Os.tidyPath(name));
+        return this;
+    }
+
+    /**
+     * @see #named(String)
+     */
+    public ArchiveBuilder named(File file) {
+        checkNotNull(file);
+        return named(file.getPath());
+    }
+
+    /**
+     * Add a manifest entry with the given {@code key} and {@code value}.
+     */
+    public ArchiveBuilder manifest(Object key, Object value) {
+        checkNotNull(key, "key");
+        checkNotNull(value, "value");
+        manifest.getMainAttributes().put(key, value);
+        return this;
+    }
+
+    /**
+     * Add the file located at the {@code filePath} to the archive, 
+     * with some complicated base-name strategies.
+     *
+     * @deprecated since 0.7.0 use one of the other add methods which makes the strategy explicit */ @Deprecated
+    public ArchiveBuilder add(String filePath) {
+        checkNotNull(filePath, "filePath");
+        return add(new File(Os.tidyPath(filePath)));
+    }
+
+    /**
+     * Add the {@code file} to the archive.
+     * <p>
+     * If the file path is absolute, or points to a file above the current directory,
+     * the file is added to the archive as a top-level entry, using the file name only.
+     * For relative {@code filePath}s below the current directory, the file is added
+     * using the path given and is assumed to be located relative to the current
+     * working directory.
+     * <p>
+     * No checks for file existence are made at this stage.
+     *
+     * @see #entry(String, File)
+     * @deprecated since 0.7.0 use one of the other add methods which makes the strategy explicit */ @Deprecated
+    public ArchiveBuilder add(File file) {
+        checkNotNull(file, "file");
+        String filePath = Os.tidyPath(file.getPath());
+        if (file.isAbsolute() || filePath.startsWith("../")) {
+            return entry(Os.mergePaths(".", file.getName()), file);
+        } else {
+            return entry(Os.mergePaths(".", filePath), file);
+        }
+    }
+
+    /**
+     * Add the file located at the {@code fileSubPath}, relative to the {@code baseDir} on the local system,
+     * to the archive.
+     * <p>
+     * Uses the {@code fileSubPath} as the name of the file in the archive. Note that the
+     * file is found by concatenating the two path components using {@link Os#mergePaths(String...)},
+     * thus {@code fileSubPath} should not be absolute or point to a location above the current directory.
+     * <p>
+     * Use {@link #entry(String, String)} directly or {@link #entries(Map)} for complete
+     * control over file locations and names in the archive.
+     *
+     * @see #entry(String, String)
+     */
+    public ArchiveBuilder addFromLocalBaseDir(File baseDir, String fileSubPath) {
+        checkNotNull(baseDir, "baseDir");
+        checkNotNull(fileSubPath, "filePath");
+        return entry(Os.mergePaths(".", fileSubPath), Os.mergePaths(baseDir.getPath(), fileSubPath));
+    }
+    /** @deprecated since 0.7.0 use {@link #addFromLocalBaseDir(File, String)}, or
+     * one of the other add methods if adding relative to baseDir was not intended */ @Deprecated
+    public ArchiveBuilder addFromLocalBaseDir(String baseDir, String fileSubPath) {
+        return addFromLocalBaseDir(new File(baseDir), fileSubPath);
+    }
+    /** @deprecated since 0.7.0 use {@link #addFromLocalBaseDir(File, String)}, or
+     * one of the other add methods if adding relative to baseDir was not intended */ @Deprecated
+    public ArchiveBuilder add(String baseDir, String fileSubPath) {
+        return addFromLocalBaseDir(baseDir, fileSubPath);
+    }
+     
+    /** adds the given file to the archive, preserving its name but putting under the given directory in the archive (may be <code>""</code> or <code>"./"</code>) */
+    public ArchiveBuilder addAt(File file, String archiveParentDir) {
+        checkNotNull(archiveParentDir, "archiveParentDir");
+        checkNotNull(file, "file");
+        return entry(Os.mergePaths(archiveParentDir, file.getName()), file);
+    }
+
+    /**
+     * Add the contents of the directory named {@code dirName} to the archive.
+     *
+     * @see #addDir(File)
+     * @deprecated since 0.7.0 use {@link #addDirContentsAt(File, String) */ @Deprecated
+    public ArchiveBuilder addDir(String dirName) {
+        checkNotNull(dirName, "dirName");
+        return addDir(new File(Os.tidyPath(dirName)));
+    }
+
+    /**
+     * Add the contents of the directory {@code dir} to the archive.
+     * The directory's name is not included; use {@link #addAtRoot(File)} if you want that behaviour. 
+     * <p>
+     * Uses {@literal .} as the parent directory name for the contents.
+     *
+     * @see #entry(String, File)
+     */
+    public ArchiveBuilder addDirContentsAt(File dir, String archiveParentDir) {
+        checkNotNull(dir, "dir");
+        if (!dir.isDirectory()) throw new IllegalArgumentException(dir+" is not a directory; cannot add contents to archive");
+        return entry(archiveParentDir, dir);
+    }
+    /**
+     * As {@link #addDirContentsAt(File, String)}, 
+     * using {@literal .} as the parent directory name for the contents.
+     * 
+     * @deprecated since 0.7.0 use {@link #addDirContentsAt(File, String)
+     * to clarify API, argument types, and be explicit about where it should be installed,
+     * because JARs seem to require <code>""<code> whereas ZIPs might want <code>"./"</code>. */ @Deprecated
+    public ArchiveBuilder addDir(File dir) {
+        return addDirContentsAt(dir, ".");
+    }
+
+    /**
+     * Add the collection of {@code files} to the archive.
+     *
+     * @see #add(String)
+     * @deprecated since 0.7.0 use one of the other add methods if keeping this file's path was not intended */ @Deprecated
+    public ArchiveBuilder add(Iterable<String> files) {
+        checkNotNull(files, "files");
+        for (String filePath : files) {
+            add(filePath);
+        }
+        return this;
+    }
+
+    /**
+     * Add the collection of {@code files}, relative to the {@code baseDir}, to
+     * the archive.
+     *
+     * @see #add(String, String)
+     * @deprecated since 0.7.0 use one of the other add methods if keeping this file's path was not intended */ @Deprecated
+    public ArchiveBuilder add(String baseDir, Iterable<String> files) {
+        checkNotNull(baseDir, "baseDir");
+        checkNotNull(files, "files");
+        for (String filePath : files) {
+            add(baseDir, filePath);
+        }
+        return this;
+    }
+
+    /**
+     * Add the {@code file} to the archive with the path {@code entryPath}.
+     *
+     * @see #entry(String, File)
+     */
+    public ArchiveBuilder entry(String entryPath, String filePath) {
+        checkNotNull(entryPath, "entryPath");
+        checkNotNull(filePath, "filePath");
+        return entry(entryPath, new File(filePath));
+    }
+
+    /**
+     * Add the {@code file} to the archive with the path {@code entryPath}.
+     */
+    public ArchiveBuilder entry(String entryPath, File file) {
+        checkNotNull(entryPath, "entryPath");
+        checkNotNull(file, "file");
+        this.entries.put(entryPath, file);
+        return this;
+    }
+
+    /**
+     * Add a {@link Map} of entries to the archive.
+     * <p>
+     * The keys should be the names of the file entries to be added to the archive and
+     * the value should point to the actual {@link File} to be added.
+     * <p>
+     * This allows complete control over the directory structure of the eventual archive,
+     * as the entry names do not need to bear any relationship to the name or location
+     * of the files on the filesystem.
+     */
+    public ArchiveBuilder entries(Map<String, File> entries) {
+        checkNotNull(entries, "entries");
+        for (Map.Entry<String, File> entry: entries.entrySet())
+            this.entries.put(entry.getKey(), entry.getValue());
+        return this;
+    }
+
+    /**
+     * Generates the archive and outputs it to the given stream, ignoring any file name.
+     * <p>
+     * This will add a manifest file if the type is a Jar archive.
+     */
+    public void stream(OutputStream output) {
+        try {
+            ZipOutputStream target;
+            if (type == ArchiveType.ZIP) {
+                target = new ZipOutputStream(output);
+            } else {
+                manifest(Attributes.Name.MANIFEST_VERSION, "1.0");
+                target = new JarOutputStream(output, manifest);
+            }
+            for (String entry : entries.keySet()) {
+                addToArchive(entry, entries.get(entry), target);
+            }
+            target.close();
+        } catch (IOException ioe) {
+            throw Exceptions.propagate(ioe);
+        }
+    }
+
+    /**
+     * Generates the archive, saving it with the given name.
+     */
+    public File create(String archiveFile) {
+        return named(archiveFile).create();
+    }
+
+    /**
+     * Generates the archive.
+     * <p>
+     * If no name has been specified, the archive will be created as a temporary file with
+     * a unique name, that is deleted on exit. Otherwise, the given name will be used.
+     */
+    public File create() {
+        if (archive == null) {
+            File temp = Os.newTempFile("brooklyn-archive", type.toString());
+            temp.deleteOnExit();
+            named(temp);
+        }
+        try {
+            OutputStream output = new FileOutputStream(archive);
+            stream(output);
+            output.close();
+        } catch (IOException ioe) {
+            throw Exceptions.propagate(ioe);
+        }
+        return archive;
+    }
+
+    /**
+     * Recursively add files to the archive.
+     * <p>
+     * Code adapted from this <a href="http://stackoverflow.com/questions/1281229/how-to-use-jaroutputstream-to-create-a-jar-file">example</a>
+     * <p>
+     * <strong>Note</strong> {@link File} provides no support for symbolic links, and as such there is
+     * no way to ensure that a symbolic link to a directory is not followed when traversing the
+     * tree. In this case, iterables created by this traverser could contain files that are
+     * outside of the given directory or even be infinite if there is a symbolic link loop.
+     */
+    private void addToArchive(String path, Iterable<File> sources, ZipOutputStream target) throws IOException {
+        int size = Iterables.size(sources);
+        if (size==0) return;
+        boolean isDirectory;
+        if (size>1) {
+            // it must be directories if we are putting multiple things here 
+            isDirectory = true;
+        } else {
+            isDirectory = Iterables.getOnlyElement(sources).isDirectory();
+        }
+        
+        String name = path.replace("\\", "/");
+        if (isDirectory) {
+            name += "/";
+            JarEntry entry = new JarEntry(name);
+            
+            long lastModified=-1;
+            for (File source: sources)
+                if (source.lastModified()>lastModified)
+                    lastModified = source.lastModified();
+            
+            entry.setTime(lastModified);
+            target.putNextEntry(entry);
+            target.closeEntry();
+
+            for (File source: sources) {
+                if (!source.isDirectory()) {
+                    throw new IllegalStateException("Cannot add multiple items at a path in archive unless they are directories: "+sources+" at "+path+" is not valid.");
+                }
+                Iterable<File> children = Files.fileTreeTraverser().children(source);
+                for (File child : children) {
+                    addToArchive(Os.mergePaths(path, child.getName()), Collections.singleton(child), target);
+                }
+            }
+            return;
+        }
+
+        File source = Iterables.getOnlyElement(sources);
+        JarEntry entry = new JarEntry(name);
+        entry.setTime(source.lastModified());
+        target.putNextEntry(entry);
+        Files.asByteSource(source).copyTo(target);
+        target.closeEntry();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveTasks.java b/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveTasks.java
new file mode 100644
index 0000000..b58359a
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveTasks.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.file;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.net.Urls;
+
+public class ArchiveTasks {
+
+    /** as {@link #deploy(ResourceUtils, Map, String, SshMachineLocation, String, String, String)} with the most common parameters */
+    public static TaskFactory<?> deploy(final ResourceUtils optionalResolver, final String archiveUrl, final SshMachineLocation machine, final String destDir) {
+        return deploy(optionalResolver, null, archiveUrl, machine, destDir, false, null, null);
+    }
+    
+    /** returns a task which installs and unpacks the given archive, as per {@link ArchiveUtils#deploy(ResourceUtils, Map, String, SshMachineLocation, String, String, String)};
+     * if allowNonarchivesOrKeepArchiveAfterDeploy is false, this task will fail if the item is not an archive;
+     * in cases where the download type is not clear in the URL but is known by the caller, supply a optionalDestFile including the appropriate file extension */
+    public static TaskFactory<?> deploy(final ResourceUtils resolver, final Map<String, ?> props, final String archiveUrl, final SshMachineLocation machine, final String destDir, final boolean allowNonarchivesOrKeepArchiveAfterDeploy, final String optionalTmpDir, final String optionalDestFile) {
+        return new TaskFactory<TaskAdaptable<?>>() {
+            @Override
+            public TaskAdaptable<?> newTask() {
+                return Tasks.<Void>builder().name("deploying "+Urls.getBasename(archiveUrl)).description("installing "+archiveUrl+" and unpacking to "+destDir).body(new Runnable() {
+                    @Override
+                    public void run() {
+                        boolean unpacked = ArchiveUtils.deploy(resolver, props, archiveUrl, machine, destDir, allowNonarchivesOrKeepArchiveAfterDeploy, optionalTmpDir, optionalDestFile);
+                        if (!unpacked && !allowNonarchivesOrKeepArchiveAfterDeploy) {
+                            throw new IllegalStateException("Unable to unpack archive from "+archiveUrl+"; not able to infer archive type");
+                        }
+                    }
+                }).build();
+            }
+        };
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveUtils.java b/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveUtils.java
new file mode 100644
index 0000000..8277a0d
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/file/ArchiveUtils.java
@@ -0,0 +1,351 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.file;
+
+import static java.lang.String.format;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.javalang.StackTraceSimplifier;
+import brooklyn.util.net.Urls;
+import brooklyn.util.os.Os;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
+import com.google.common.io.Files;
+
+public class ArchiveUtils {
+
+    private static final Logger log = LoggerFactory.getLogger(ArchiveUtils.class);
+
+    // TODO Make this a ConfigKey on the machine location
+    /** Number of attempts when copying a file to a remote server. */
+    public static final int NUM_RETRIES_FOR_COPYING = 5;
+
+    /**
+     * The types of archive that are supported by Brooklyn.
+     */
+    public static enum ArchiveType {
+        TAR,
+        TGZ,
+        TBZ,
+        ZIP,
+        JAR,
+        WAR,
+        EAR,
+        UNKNOWN;
+
+        /**
+         * Zip format archives used by Java.
+         */
+        public static Set<ArchiveType> ZIP_ARCHIVES = EnumSet.of(ArchiveType.ZIP, ArchiveType.JAR, ArchiveType.WAR, ArchiveType.EAR);
+
+        public static ArchiveUtils.ArchiveType of(String filename) {
+            if (filename == null) return null;
+            String ext = Files.getFileExtension(filename);
+            try {
+                return valueOf(ext.toUpperCase());
+            } catch (IllegalArgumentException iae) {
+                if (filename.toLowerCase().endsWith(".tar.gz")) {
+                    return TGZ;
+                } else if (filename.toLowerCase().endsWith(".tar.bz") ||
+                        filename.toLowerCase().endsWith(".tar.bz2") ||
+                        filename.toLowerCase().endsWith(".tar.xz")) {
+                    return TBZ;
+                } else {
+                    return UNKNOWN;
+                }
+            }
+        }
+
+        @Override
+        public String toString() {
+            if (UNKNOWN.equals(this)) {
+                return "";
+            } else {
+                return name().toLowerCase();
+            }
+        }
+    }
+
+    /**
+     * Returns the list of commands used to install support for an archive with the given name.
+     */
+    public static List<String> installCommands(String fileName) {
+        List<String> commands = new LinkedList<String>();
+        switch (ArchiveType.of(fileName)) {
+            case TAR:
+            case TGZ:
+            case TBZ:
+                commands.add(BashCommands.INSTALL_TAR);
+                break;
+            case ZIP:
+                commands.add(BashCommands.INSTALL_UNZIP);
+                break;
+            case JAR:
+            case WAR:
+            case EAR:
+            case UNKNOWN:
+                break;
+        }
+        return commands;
+    }
+
+    /**
+     * Returns the list of commands used to extract the contents of the archive with the given name.
+     * <p>
+     * Optionally, Java archives of type
+     *
+     * @see #extractCommands(String, String)
+     */
+    public static List<String> extractCommands(String fileName, String sourceDir, String targetDir, boolean extractJar) {
+        return extractCommands(fileName, sourceDir, targetDir, extractJar, true);
+    }
+    
+    /** as {@link #extractCommands(String, String, String, boolean)}, but also with option to keep the original */
+    public static List<String> extractCommands(String fileName, String sourceDir, String targetDir, boolean extractJar, boolean keepOriginal) {
+        List<String> commands = new LinkedList<String>();
+        commands.add("cd " + targetDir);
+        String sourcePath = Os.mergePathsUnix(sourceDir, fileName);
+        switch (ArchiveType.of(fileName)) {
+            case TAR:
+                commands.add("tar xvf " + sourcePath);
+                break;
+            case TGZ:
+                commands.add("tar xvfz " + sourcePath);
+                break;
+            case TBZ:
+                commands.add("tar xvfj " + sourcePath);
+                break;
+            case ZIP:
+                commands.add("unzip " + sourcePath);
+                break;
+            case JAR:
+            case WAR:
+            case EAR:
+                if (extractJar) {
+                    commands.add("jar -xvf " + sourcePath);
+                    break;
+                }
+            case UNKNOWN:
+                if (!sourcePath.equals(Urls.mergePaths(targetDir, fileName))) {
+                    commands.add("cp " + sourcePath + " " + targetDir);
+                } else {
+                    keepOriginal = true;
+                    // else we'd just end up deleting it!
+                    // this branch will often lead to errors in any case, see the allowNonarchivesOrKeepArchiveAfterDeploy parameter 
+                    // in ArchiveTasks which calls through to here and then fails in the case corresponding to this code branch
+                }
+                break;
+        }
+        if (!keepOriginal && !commands.isEmpty())
+            commands.add("rm "+sourcePath);
+        return commands;
+    }
+
+    /**
+     * Returns the list of commands used to extract the contents of the archive with the given name.
+     * <p>
+     * The archive will be extracted in its current directory unless it is a Java archive of type {@code .jar},
+     * {@code .war} or {@code .ear}, which will be left as is.
+     *
+     * @see #extractCommands(String, String, String, boolean)
+     */
+    public static List<String> extractCommands(String fileName, String sourceDir) {
+        return extractCommands(fileName, sourceDir, ".", false);
+    }
+
+    /**
+     * Deploys an archive file to a remote machine and extracts the contents.
+     */
+    public static void deploy(String archiveUrl, SshMachineLocation machine, String destDir) {
+        deploy(MutableMap.<String, Object>of(), archiveUrl, machine, destDir);
+    }
+
+    /**
+     * Deploys an archive file to a remote machine and extracts the contents.
+     * <p>
+     * Copies the archive file from the given URL to the destination directory and extracts
+     * the contents. If the URL is a local directory, the contents are packaged as a Zip archive first.
+     *
+     * @see #deploy(String, SshMachineLocation, String, String)
+     * @see #deploy(Map, String, SshMachineLocation, String, String, String)
+     */
+    public static void deploy(Map<String, ?> props, String archiveUrl, SshMachineLocation machine, String destDir) {
+        if (Urls.isDirectory(archiveUrl)) {
+            File zipFile = ArchiveBuilder.zip().entry(".", Urls.toFile(archiveUrl)).create();
+            archiveUrl = zipFile.getAbsolutePath();
+        }
+
+        // Determine filename
+        String destFile = archiveUrl.contains("?") ? archiveUrl.substring(0, archiveUrl.indexOf('?')) : archiveUrl;
+        destFile = destFile.substring(destFile.lastIndexOf('/') + 1);
+
+        deploy(props, archiveUrl, machine, destDir, destFile);
+    }
+
+    /**
+     * Deploys an archive file to a remote machine and extracts the contents.
+     * <p>
+     * Copies the archive file from the given URL to a file in the destination directory and extracts
+     * the contents.
+     *
+     * @see #deploy(String, SshMachineLocation, String)
+     * @see #deploy(Map, String, SshMachineLocation, String, String, String)
+     */
+    public static void deploy(String archiveUrl, SshMachineLocation machine, String destDir, String destFile) {
+        deploy(MutableMap.<String, Object>of(), archiveUrl, machine, destDir, destDir, destFile);
+    }
+    public static void deploy(Map<String, ?> props, String archiveUrl, SshMachineLocation machine, String destDir, String destFile) {
+        deploy(props, archiveUrl, machine, destDir, destDir, destFile);
+    }
+    public static void deploy(Map<String, ?> props, String archiveUrl, SshMachineLocation machine, String tmpDir, String destDir, String destFile) {
+        deploy(null, props, archiveUrl, machine, destDir, true, tmpDir, destFile);
+    }
+    
+    /**
+     * Deploys an archive file to a remote machine and extracts the contents.
+     * <p>
+     * Copies the archive file from the given URL to a file in a temporary directory and extracts
+     * the contents in the destination directory. For Java archives of type {@code .jar},
+     * {@code .war} or {@code .ear} the file is simply copied.
+     * 
+     * @return true if the archive is downloaded AND unpacked; false if it is downloaded but not unpacked; 
+     * throws if there was an error downloading or, for known archive types, unpacking.
+     *
+     * @see #deploy(String, SshMachineLocation, String)
+     * @see #deploy(Map, String, SshMachineLocation, String, String, String)
+     * @see #install(SshMachineLocation, String, String, int)
+     */
+    public static boolean deploy(ResourceUtils resolver, Map<String, ?> props, String archiveUrl, SshMachineLocation machine, String destDir, boolean keepArchiveAfterUnpacking, String optionalTmpDir, String optionalDestFile) {
+        String destFile = optionalDestFile;
+        if (destFile==null) destFile = Urls.getBasename(Preconditions.checkNotNull(archiveUrl, "archiveUrl"));
+        if (Strings.isBlank(destFile)) 
+            throw new IllegalStateException("Not given filename and cannot infer archive type from '"+archiveUrl+"'");
+        
+        String tmpDir = optionalTmpDir;
+        if (tmpDir==null) tmpDir=Preconditions.checkNotNull(destDir, "destDir");
+        if (props==null) props = MutableMap.of();
+        String destPath = Os.mergePaths(tmpDir, destFile);
+
+        // Use the location mutex to prevent package manager locking issues
+        machine.acquireMutex("installing", "installing archive");
+        try {
+            int result = install(resolver, props, machine, archiveUrl, destPath, NUM_RETRIES_FOR_COPYING);
+            if (result != 0) {
+                throw new IllegalStateException(format("Unable to install archive %s to %s", archiveUrl, machine));
+            }
+
+            // extract, now using task if available
+            MutableList<String> commands = MutableList.copyOf(installCommands(destFile))
+                    .appendAll(extractCommands(destFile, tmpDir, destDir, false, keepArchiveAfterUnpacking));
+            if (DynamicTasks.getTaskQueuingContext()!=null) {
+                result = DynamicTasks.queue(SshTasks.newSshExecTaskFactory(machine, commands.toArray(new String[0])).summary("extracting archive").requiringExitCodeZero()).get();
+            } else {
+                result = machine.execCommands(props, "extracting content", commands);
+            }
+            if (result != 0) {
+                throw new IllegalStateException(format("Failed to expand archive %s on %s", archiveUrl, machine));
+            }
+            return ArchiveType.of(destFile)!=ArchiveType.UNKNOWN;
+        } finally {
+            machine.releaseMutex("installing");
+        }
+    }
+
+    /**
+     * Installs a URL onto a remote machine.
+     *
+     * @see #install(Map, SshMachineLocation, String, String, int)
+     */
+    public static int install(SshMachineLocation machine, String urlToInstall, String target) {
+        return install(MutableMap.<String, Object>of(), machine, urlToInstall, target, NUM_RETRIES_FOR_COPYING);
+    }
+
+    /**
+     * Installs a URL onto a remote machine.
+     *
+     * @see #install(SshMachineLocation, String, String)
+     * @see SshMachineLocation#installTo(Map, String, String)
+     */
+    public static int install(Map<String, ?> props, SshMachineLocation machine, String urlToInstall, String target, int numAttempts) {
+        return install(null, props, machine, urlToInstall, target, numAttempts);
+    }
+    
+    public static int install(ResourceUtils resolver, Map<String, ?> props, SshMachineLocation machine, String urlToInstall, String target, int numAttempts) {
+        if (resolver==null) resolver = ResourceUtils.create(machine);
+        Exception lastError = null;
+        int retriesRemaining = numAttempts;
+        int attemptNum = 0;
+        do {
+            attemptNum++;
+            try {
+                Tasks.setBlockingDetails("Installing "+urlToInstall+" at "+machine);
+                // TODO would be nice to have this in a task (and the things within it!)
+                return machine.installTo(resolver, props, urlToInstall, target);
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                lastError = e;
+                String stack = StackTraceSimplifier.toString(e);
+                if (stack.contains("net.schmizz.sshj.sftp.RemoteFile.write")) {
+                    log.warn("Failed to transfer "+urlToInstall+" to "+machine+", retryable error, attempt "+attemptNum+"/"+numAttempts+": "+e);
+                    continue;
+                }
+                log.warn("Failed to transfer "+urlToInstall+" to "+machine+", not a retryable error so failing: "+e);
+                throw Exceptions.propagate(e);
+            } finally {
+                Tasks.resetBlockingDetails();
+            }
+        } while (retriesRemaining --> 0);
+        throw Exceptions.propagate(lastError);
+    }
+
+    /**
+     * Copies the entire contents of a file to a String.
+     *
+     * @see com.google.common.io.Files#toString(File, java.nio.charset.Charset)
+     */
+    public static String readFullyString(File sourceFile) {
+        try {
+            return Files.toString(sourceFile, Charsets.UTF_8);
+        } catch (IOException ioe) {
+            throw Exceptions.propagate(ioe);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/flags/ClassCoercionException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/flags/ClassCoercionException.java b/core/src/main/java/org/apache/brooklyn/core/util/flags/ClassCoercionException.java
new file mode 100644
index 0000000..72c8698
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/flags/ClassCoercionException.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.flags;
+
+/**
+ * Thrown to indicate that {@link TypeCoercions} could not cast an object from one
+ * class to another.
+ */
+public class ClassCoercionException extends ClassCastException {
+    public ClassCoercionException() {
+        super();
+    }
+
+    /**
+     * Constructs a <code>ClassCoercionException</code> with the specified
+     * detail message.
+     *
+     * @param s the detail message.
+     */
+    public ClassCoercionException(String s) {
+        super(s);
+    }
+}


[14/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/task/system/SystemTasksTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/system/SystemTasksTest.java b/core/src/test/java/brooklyn/util/task/system/SystemTasksTest.java
deleted file mode 100644
index e60310d..0000000
--- a/core/src/test/java/brooklyn/util/task/system/SystemTasksTest.java
+++ /dev/null
@@ -1,134 +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.system;
-
-import java.io.File;
-
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.core.management.internal.LocalManagementContext;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.basic.Entities;
-import brooklyn.util.os.Os;
-import brooklyn.util.task.ssh.SshTasks;
-
-/**
- * Some tests for {@link SystemTasks}. See {@link SshTasks}.
- */
-public class SystemTasksTest {
-
-    ManagementContext mgmt;
-    File tempDir;
-    
-    boolean failureExpected;
-
-    @BeforeMethod(alwaysRun=true)
-    public void setup() throws Exception {
-        mgmt = new LocalManagementContext();
-        
-        clearExpectedFailure();
-        tempDir = Os.newTempDir(getClass());
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (mgmt != null) Entities.destroyAll(mgmt);
-        mgmt = null;
-        tempDir = Os.deleteRecursively(tempDir).asNullOrThrowing();
-        checkExpectedFailure();
-    }
-
-    protected void checkExpectedFailure() {
-        if (failureExpected) {
-            clearExpectedFailure();
-            Assert.fail("Test should have thrown an exception but it did not.");
-        }
-    }
-    
-    protected void clearExpectedFailure() {
-        failureExpected = false;
-    }
-
-    protected void setExpectingFailure() {
-        failureExpected = true;
-    }
-
-
-    protected <T> ProcessTaskWrapper<T> submit(final ProcessTaskFactory<T> tf) {
-        ProcessTaskWrapper<T> t = tf.newTask();
-        mgmt.getExecutionManager().submit(t);
-        return t;
-    }
-
-    @Test(groups="Integration")
-    public void testExecEchoHello() {
-        ProcessTaskWrapper<Integer> t = submit(SystemTasks.exec("sleep 1 ; echo hello world"));
-        Assert.assertFalse(t.isDone());
-        Assert.assertEquals(t.get(), (Integer)0);
-        Assert.assertEquals(t.getTask().getUnchecked(), (Integer)0);
-        Assert.assertEquals(t.getStdout().trim(), "hello world");
-    }
-
-    // FIXME Behaviour of Bash shell changes from 3.x to 4.x so test is disabled
-    @Test(groups="Integration", enabled=false)
-    public void testSubshellExitScriptDoesNotExit() {
-        checkSubshellExitDoesNotExit(taskSubshellExit().runAsScript());
-    }
-
-    @Test(groups="Integration")
-    public void testSubshellExitCommandDoesNotExit() {
-        checkSubshellExitDoesNotExit(taskSubshellExit().runAsCommand());
-    }
-
-    public ProcessTaskFactory<Integer> taskSubshellExit() {
-        return SystemTasks.exec("echo hello", "( exit 1 )", "echo bye code $?");
-    }
-
-    public void checkSubshellExitDoesNotExit(ProcessTaskFactory<Integer> task) {
-        ProcessTaskWrapper<Integer> t = submit(task);
-        t.block();
-        Assert.assertEquals(t.get(), (Integer)0);
-        Assert.assertTrue(t.getStdout().contains("bye code 1"), "stdout is: "+t.getStdout());
-    }
-
-    @Test(groups="Integration")
-    public void testGroupExitScriptDoesNotExit() {
-        checkGroupExitDoesExit(taskGroupExit().runAsScript());
-    }
-
-    @Test(groups="Integration")
-    public void testGroupExitCommandDoesNotExit() {
-        checkGroupExitDoesExit(taskGroupExit().runAsCommand());
-    }
-
-    public ProcessTaskFactory<Integer> taskGroupExit() {
-        return SystemTasks.exec("echo hello", "{ exit 1 ; }", "echo bye code $?");
-    }
-
-    public void checkGroupExitDoesExit(ProcessTaskFactory<Integer> task) {
-        ProcessTaskWrapper<Integer> t = submit(task);
-        t.block();
-        Assert.assertEquals(t.get(), (Integer)1);
-        Assert.assertFalse(t.getStdout().contains("bye"), "stdout is: "+t.getStdout());
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/text/DataUriSchemeParserTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/text/DataUriSchemeParserTest.java b/core/src/test/java/brooklyn/util/text/DataUriSchemeParserTest.java
deleted file mode 100644
index ff2dc9b..0000000
--- a/core/src/test/java/brooklyn/util/text/DataUriSchemeParserTest.java
+++ /dev/null
@@ -1,52 +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.text;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-
-import org.bouncycastle.util.encoders.Base64;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-public class DataUriSchemeParserTest {
-
-    @Test
-    public void testSimple() {
-        Assert.assertEquals(new DataUriSchemeParser("data:,hello").parse().getDataAsString(), "hello");
-        Assert.assertEquals(DataUriSchemeParser.toString("data:,hello"), "hello");
-    }
-
-    @Test
-    public void testMimeType() throws UnsupportedEncodingException {
-        DataUriSchemeParser p = new DataUriSchemeParser("data:application/json,"+URLEncoder.encode("{ }", "US-ASCII")).parse();
-        Assert.assertEquals(p.getMimeType(), "application/json");
-        Assert.assertEquals(p.getData(), "{ }".getBytes());
-    }
-
-    @Test
-    public void testBase64() {
-        Assert.assertEquals(DataUriSchemeParser.toString(
-                "data:;base64,"+new String(Base64.encode("hello".getBytes()))), 
-            "hello");
-    }
-
-    // TODO test pictures, etc
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/text/TemplateProcessorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/text/TemplateProcessorTest.java b/core/src/test/java/brooklyn/util/text/TemplateProcessorTest.java
deleted file mode 100644
index bd8ef87..0000000
--- a/core/src/test/java/brooklyn/util/text/TemplateProcessorTest.java
+++ /dev/null
@@ -1,179 +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.text;
-
-import static org.testng.Assert.assertEquals;
-
-import org.apache.brooklyn.api.entity.proxying.EntitySpec;
-import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
-import org.apache.brooklyn.test.entity.TestApplication;
-import org.apache.brooklyn.test.entity.TestEntity;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.BrooklynAppUnitTestSupport;
-import brooklyn.event.basic.DependentConfiguration;
-import brooklyn.test.FixedLocaleTest;
-
-import com.google.common.collect.ImmutableMap;
-
-public class TemplateProcessorTest extends BrooklynAppUnitTestSupport {
-    private FixedLocaleTest localeFix = new FixedLocaleTest();
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        super.setUp();
-        localeFix.setUp();
-    }
-
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        super.tearDown();
-        localeFix.tearDown();
-    }
-
-    @Test
-    public void testAdditionalArgs() {
-        String templateContents = "${mykey}";
-        String result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.of("mykey", "myval"));
-        assertEquals(result, "myval");
-    }
-    
-    @Test
-    public void testEntityConfig() {
-        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
-                .configure(TestEntity.CONF_NAME, "myval"));
-        String templateContents = "${config['"+TestEntity.CONF_NAME.getName()+"']}";
-        String result = TemplateProcessor.processTemplateContents(templateContents, entity, ImmutableMap.<String,Object>of());
-        assertEquals(result, "myval");
-    }
-    
-    @Test
-    public void testEntityConfigNumber() {
-        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
-                .configure(TestEntity.CONF_OBJECT, 123456));
-        String templateContents = "${config['"+TestEntity.CONF_OBJECT.getName()+"']}";
-        String result = TemplateProcessor.processTemplateContents(templateContents, entity, ImmutableMap.<String,Object>of());
-        assertEquals(result, "123,456");
-    }
-    
-    @Test
-    public void testEntityConfigNumberUnadorned() {
-        // ?c is needed to avoid commas (i always forget this!)
-        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
-                .configure(TestEntity.CONF_OBJECT, 123456));
-        String templateContents = "${config['"+TestEntity.CONF_OBJECT.getName()+"']?c}";
-        String result = TemplateProcessor.processTemplateContents(templateContents, entity, ImmutableMap.<String,Object>of());
-        assertEquals(result, "123456");
-    }
-    
-    @Test
-    public void testGetSysProp() {
-        System.setProperty("testGetSysProp", "myval");
-        
-        String templateContents = "${javaSysProps['testGetSysProp']}";
-        String result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
-        assertEquals(result, "myval");
-    }
-    
-    @Test
-    public void testEntityGetterMethod() {
-        String templateContents = "${entity.id}";
-        String result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
-        assertEquals(result, app.getId());
-    }
-    
-    @Test
-    public void testManagementContextConfig() {
-        mgmt.getBrooklynProperties().put("globalmykey", "myval");
-        String templateContents = "${mgmt.globalmykey}";
-        String result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
-        assertEquals(result, "myval");
-    }
-    
-    @Test
-    public void testManagementContextDefaultValue() {
-        String templateContents = "${(missing)!\"defval\"}";
-        Object result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
-        assertEquals(result, "defval");
-    }
-    
-    @Test
-    public void testManagementContextDefaultValueInDotMissingValue() {
-        String templateContents = "${(mgmt.missing.more_missing)!\"defval\"}";
-        Object result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
-        assertEquals(result, "defval");
-    }
-    
-    @Test
-    public void testManagementContextConfigWithDot() {
-        mgmt.getBrooklynProperties().put("global.mykey", "myval");
-        String templateContents = "${mgmt['global.mykey']}";
-        String result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
-        assertEquals(result, "myval");
-    }
-    
-    @Test
-    public void testManagementContextErrors() {
-        try {
-            // NB: dot has special meaning so this should fail; must be accessed using bracket notation as above
-            mgmt.getBrooklynProperties().put("global.mykey", "myval");
-            String templateContents = "${mgmt.global.mykey}";
-            TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
-            Assert.fail("Should not have found value with intermediate dot");
-        } catch (Exception e) {
-            Assert.assertTrue(e.toString().contains("global"), "Should have mentioned missing key 'global' in error");
-        }
-    }
-    
-    @Test
-    public void testApplyTemplatedConfigWithAttributeWhenReady() {
-        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
-
-        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
-                .configure(TestEntity.CONF_NAME, DependentConfiguration.attributeWhenReady(app, TestApplication.MY_ATTRIBUTE)));
-        
-        String templateContents = "${config['"+TestEntity.CONF_NAME.getName()+"']}";
-        String result = TemplateProcessor.processTemplateContents(templateContents, entity, ImmutableMap.<String,Object>of());
-        assertEquals(result, "myval");
-    }
-    
-    @Test
-    public void testDotSeparatedKey() {
-        String templateContents = "${a.b}";
-        String result = TemplateProcessor.processTemplateContents(templateContents, (ManagementContextInternal)null, 
-            ImmutableMap.<String,Object>of("a.b", "myval"));
-        assertEquals(result, "myval");
-    }
-    
-    @Test
-    public void testDotSeparatedKeyCollisionFailure() {
-        String templateContents = "${aaa.bbb}";
-        try {
-            TemplateProcessor.processTemplateContents(templateContents, (ManagementContextInternal)null, 
-                ImmutableMap.<String,Object>of("aaa.bbb", "myval", "aaa", "blocker"));
-            Assert.fail("Should not have found value with intermediate dot where prefix is overridden");
-        } catch (Exception e) {
-            Assert.assertTrue(e.toString().contains("aaa"), "Should have mentioned missing key 'aaa' in error");
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/xstream/CompilerCompatibilityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/xstream/CompilerCompatibilityTest.java b/core/src/test/java/brooklyn/util/xstream/CompilerCompatibilityTest.java
deleted file mode 100644
index 5a1f844..0000000
--- a/core/src/test/java/brooklyn/util/xstream/CompilerCompatibilityTest.java
+++ /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.xstream;
-
-import static org.testng.Assert.assertTrue;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Field;
-
-import org.testng.annotations.Test;
-
-import brooklyn.util.xstream.CompilerCompatibilityTest.EnclosingClass.DynamicClass;
-import brooklyn.util.xstream.CompilerCompatibilityTest.EnclosingClass.DynamicExtendingClass;
-import brooklyn.util.xstream.CompilerCompatibilityTest.EnclosingClass.EnclosingDynamicClass;
-import brooklyn.util.xstream.CompilerCompatibilityTest.EnclosingClass.EnclosingDynamicClass.NestedDynamicClass;
-
-import com.thoughtworks.xstream.XStream;
-import com.thoughtworks.xstream.mapper.MapperWrapper;
-
-// To get the generated synthetic fields use the command:
-/*
-   find core/target/test-classes -name CompilerCompatibilityTest\$EnclosingClass\$* | \
-   sed s@core/target/test-classes/@@ | sed 's@.class$@@' | sed s@/@.@g | \
-   xargs javap -classpath core/target/test-classes/ | grep -B1 this
-*/
-@SuppressWarnings("unused")
-public class CompilerCompatibilityTest {
-    private EnclosingClass enclosingClass = new EnclosingClass();
-    private DynamicClass dynamicClass = enclosingClass.new DynamicClass();
-    private DynamicExtendingClass dynamicExtendingClass = enclosingClass.new DynamicExtendingClass();
-    private EnclosingDynamicClass enclosingDynamicClass = enclosingClass.new EnclosingDynamicClass();
-    private NestedDynamicClass nestedDynamicClass = enclosingDynamicClass.new NestedDynamicClass();
-//  NOT SUPPORTED
-//    private DynamicExtendingClassWithDifferentScope dynamicExtendingClassWithDifferentScope =
-//                enclosingClass.new DynamicExtendingClassWithDifferentScope(enclosingDynamicClass);
-
-    public static class EnclosingClass {
-        public class DynamicClass {
-            //Oracle/OpenJDK/IBM generates
-            //final EnclosingClass this$0;
-
-            //eclipse-[groovy-]compiler generates
-            //final EnclosingClass this$1;
-        }
-
-        public class DynamicExtendingClass extends DynamicClass {
-            //The field here masks the parent field
-
-            //Oracle/OpenJDK/IBM generates
-            //final EnclosingClass this$0;
-
-            //eclipse-[groovy-]compiler generates
-            //final EnclosingClass this$1;
-        }
-
-        public class EnclosingDynamicClass {
-            //Oracle/OpenJDK/IBM generates
-            //final EnclosingClass this$0;
-
-            //eclipse-[groovy-]compiler generates
-            //final EnclosingClass this$1;
-
-            public class NestedDynamicClass {
-                //Oracle/OpenJDK/IBM generates
-                //final EnclosingClass this$1;
-
-                //eclipse-[groovy-]compiler generates
-                //final EnclosingClass this$2;
-            }
-        }
-
-//        WARNING: Combination NOT SUPPORTED. Not enough information in XML to deserialize reliably,
-//        having in mind that different compilers could be used for parent/child classes.
-//        If we really need to, we can extend the heuristic to check for field types or assume that
-//        only one compiler was used for the whole class hierarchy covering some more cases.
-//
-//        The problem is that we have two fields with different names, without relation between the
-//        indexes in each one. Changing compilers (or combination of compilers) could change the 
-//        indexes independently in each field. This makes it impossible to infer which field in the xml
-//        maps to which field in the object.
-//        When having identical field names with parent classes XStream will put a defined-in attribute
-//        which makes it possible to deserialize, but it can't be forced to put it in each element.
-//
-        public class DynamicExtendingClassWithDifferentScope extends NestedDynamicClass {
-            //Oracle/OpenJDK/IBM generates
-            //final EnclosingClass this$0;
-
-            //eclipse-[groovy-]compiler generates
-            //final EnclosingClass this$1;
-
-            //constructor required to compile
-            public DynamicExtendingClassWithDifferentScope(EnclosingDynamicClass superEnclosingScope) {
-                superEnclosingScope.super();
-            }
-        }
-    }
-
-    @Test
-    public void testXStreamDeserialize() throws Exception {
-        deserialize("/brooklyn/entity/rebind/compiler_compatibility_eclipse.xml");
-        deserialize("/brooklyn/entity/rebind/compiler_compatibility_oracle.xml");
-    }
-
-    private void deserialize(String inputUrl) throws Exception {
-        XStream xstream = new XStream() {
-            @Override
-            protected MapperWrapper wrapMapper(MapperWrapper next) {
-                return new CompilerIndependentOuterClassFieldMapper(super.wrapMapper(next));
-            }
-        };
-
-        InputStream in = this.getClass().getResourceAsStream(inputUrl);
-        try {
-            Object obj = xstream.fromXML(in);
-            assertNonNullOuterFields(obj);
-        } finally {
-            in.close();
-        }
-    }
-
-    private void assertNonNullOuterFields(Object obj) throws Exception {
-        Field[] testInstances = obj.getClass().getDeclaredFields();
-        for (Field instanceField : testInstances) {
-            Object instance = instanceField.get(obj);
-            Class<?> type = instance.getClass();
-            do {
-                for (Field field : type.getDeclaredFields()) {
-                    if (field.getName().startsWith("this$")) {
-                        Object value = field.get(instance);
-                        assertTrue(value != null, field + " should not be null");
-                    }
-                }
-                type = type.getSuperclass();
-            } while (type != null);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/xstream/ConverterTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/xstream/ConverterTestFixture.java b/core/src/test/java/brooklyn/util/xstream/ConverterTestFixture.java
deleted file mode 100644
index bf566ba..0000000
--- a/core/src/test/java/brooklyn/util/xstream/ConverterTestFixture.java
+++ /dev/null
@@ -1,40 +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.xstream;
-
-import org.testng.Assert;
-
-import com.thoughtworks.xstream.XStream;
-
-public class ConverterTestFixture {
-
-    protected Object assertX(Object obj, String fmt) {
-        XStream xstream = new XStream();
-        registerConverters(xstream);
-        String s1 = xstream.toXML(obj);
-        Assert.assertEquals(s1, fmt);
-        Object out = xstream.fromXML(s1);
-        Assert.assertEquals(out, obj);
-        return out;
-    }
-
-    protected void registerConverters(XStream xstream) {
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/xstream/EnumCaseForgivingConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/xstream/EnumCaseForgivingConverterTest.java b/core/src/test/java/brooklyn/util/xstream/EnumCaseForgivingConverterTest.java
deleted file mode 100644
index f22f91b..0000000
--- a/core/src/test/java/brooklyn/util/xstream/EnumCaseForgivingConverterTest.java
+++ /dev/null
@@ -1,52 +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.xstream;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.fail;
-
-import org.testng.annotations.Test;
-
-public class EnumCaseForgivingConverterTest {
-
-    public enum MyEnum {
-        FOO,
-        BaR;
-    }
-    
-    @Test
-    public void testFindsCaseInsensitive() throws Exception {
-        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "FOO"), MyEnum.FOO);
-        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "foo"), MyEnum.FOO);
-        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "Foo"), MyEnum.FOO);
-        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "BAR"), MyEnum.BaR);
-        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "bar"), MyEnum.BaR);
-        assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "Bar"), MyEnum.BaR);
-    }
-    
-    @Test
-    public void testFailsIfNoMatch() throws Exception {
-        try {
-            assertEquals(EnumCaseForgivingConverter.resolve(MyEnum.class, "DoesNotExist"), MyEnum.BaR);
-            fail();
-        } catch (IllegalArgumentException e) {
-            if (!e.toString().matches(".*No enum.*MyEnum.DOESNOTEXIST")) throw e;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/xstream/ImmutableListConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/xstream/ImmutableListConverterTest.java b/core/src/test/java/brooklyn/util/xstream/ImmutableListConverterTest.java
deleted file mode 100644
index f8d8855..0000000
--- a/core/src/test/java/brooklyn/util/xstream/ImmutableListConverterTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.xstream;
-
-import java.net.UnknownHostException;
-
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.thoughtworks.xstream.XStream;
-
-@Test
-public class ImmutableListConverterTest extends ConverterTestFixture {
-
-    protected void registerConverters(XStream xstream) {
-        super.registerConverters(xstream);
-        xstream.aliasType("ImmutableList", ImmutableList.class);
-        xstream.registerConverter(new ImmutableListConverter(xstream.getMapper()));
-    }
-
-    @Test
-    public void testImmutableEmptyList() throws UnknownHostException {
-        assertX(ImmutableList.of(), "<ImmutableList/>");
-    }
-
-    @Test
-    public void testImmutableSingletonDoubleList() throws UnknownHostException {
-        assertX(ImmutableList.of(1.2d), "<ImmutableList>\n  <double>1.2</double>\n</ImmutableList>");
-    }
-
-    @Test
-    public void testImmutableTwoValStringList() throws UnknownHostException {
-        assertX(ImmutableList.of("a","b"), "<ImmutableList>\n  <string>a</string>\n  <string>b</string>\n</ImmutableList>");
-    }
-
-    @Test
-    public void testImmutableEmptyListStaysImmutable() throws UnknownHostException {
-        Object x = assertX(ImmutableList.of(), "<ImmutableList/>");
-        Assert.assertTrue(x instanceof ImmutableList);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/xstream/InetAddressConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/xstream/InetAddressConverterTest.java b/core/src/test/java/brooklyn/util/xstream/InetAddressConverterTest.java
deleted file mode 100644
index 237c670..0000000
--- a/core/src/test/java/brooklyn/util/xstream/InetAddressConverterTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.xstream;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-import org.testng.annotations.Test;
-
-import com.thoughtworks.xstream.XStream;
-
-@Test
-public class InetAddressConverterTest extends ConverterTestFixture {
-
-    protected void registerConverters(XStream xstream) {
-        super.registerConverters(xstream);
-        xstream.registerConverter(new Inet4AddressConverter());
-    }
-
-    public void testFoo1234() throws UnknownHostException {
-        assertX(InetAddress.getByAddress("foo", new byte[] { 1, 2, 3, 4 }), 
-                "<java.net.Inet4Address>foo/1.2.3.4</java.net.Inet4Address>");
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/xstream/StringKeyMapConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/xstream/StringKeyMapConverterTest.java b/core/src/test/java/brooklyn/util/xstream/StringKeyMapConverterTest.java
deleted file mode 100644
index 7d30bc2..0000000
--- a/core/src/test/java/brooklyn/util/xstream/StringKeyMapConverterTest.java
+++ /dev/null
@@ -1,77 +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.xstream;
-
-import java.net.UnknownHostException;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.testng.annotations.Test;
-
-import brooklyn.util.collections.MutableMap;
-
-import com.google.common.collect.Maps;
-import com.thoughtworks.xstream.XStream;
-
-@SuppressWarnings({ "rawtypes", "unchecked" })
-@Test
-public class StringKeyMapConverterTest extends ConverterTestFixture {
-
-    protected void registerConverters(XStream xstream) {
-        super.registerConverters(xstream);
-        xstream.alias("map", Map.class, LinkedHashMap.class);
-        xstream.alias("MutableMap", MutableMap.class);
-        xstream.registerConverter(new StringKeyMapConverter(xstream.getMapper()), /* priority */ 10);
-    }
-
-    @Test
-    public void testSimple() throws UnknownHostException {
-        Map m = Maps.newLinkedHashMap();
-        m.put("a", "v");
-        assertX(m, "<map>\n  <a>v</a>\n</map>");
-    }
-    
-    @Test
-    public void testDouble() throws UnknownHostException {
-        Map m = Maps.newLinkedHashMap();
-        m.put("a", "v");
-        m.put("x", 1.0d);
-        assertX(m, "<map>\n  <a>v</a>\n  <x type=\"double\">1.0</x>\n</map>");
-    }
-    
-    @Test
-    public void testEmpty() throws UnknownHostException {
-        Map m = Maps.newLinkedHashMap();
-        assertX(m, "<map/>");
-    }
-    
-    @Test
-    public void testBigSpacedKeyInMutableMap() throws UnknownHostException {
-        Map m = MutableMap.of("a b", "x");
-        assertX(m, "<MutableMap>\n  <entry key=\"a b\">x</entry>\n</MutableMap>");
-    }
-
-    @Test
-    public void testWithNumericKey() throws UnknownHostException {
-        Map m = Maps.newLinkedHashMap();
-        m.put("123", "v");
-        m.put("a", "v2");
-        assertX(m, "<map>\n  <entry key=\"123\">v</entry>\n  <a>v2</a>\n</map>");
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/xstream/XmlUtilTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/xstream/XmlUtilTest.java b/core/src/test/java/brooklyn/util/xstream/XmlUtilTest.java
deleted file mode 100644
index a81e299..0000000
--- a/core/src/test/java/brooklyn/util/xstream/XmlUtilTest.java
+++ /dev/null
@@ -1,33 +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.xstream;
-
-import static org.testng.Assert.assertEquals;
-
-import org.testng.annotations.Test;
-
-
-public class XmlUtilTest {
-
-    @Test
-    public void testXpath() throws Exception {
-        String xml = "<a><b>myb</b></a>";
-        assertEquals(XmlUtil.xpath(xml, "/a/b[text()]"), "myb");
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoTest.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoTest.java
index 90e039d..3229aac 100644
--- a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoTest.java
@@ -39,12 +39,12 @@ import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.catalog.internal.CatalogXmlSerializer;
 import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.BrooklynMavenArtifacts;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
 
 import brooklyn.entity.basic.Entities;
-import brooklyn.util.BrooklynMavenArtifacts;
 import brooklyn.util.maven.MavenRetriever;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogLoadTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogLoadTest.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogLoadTest.java
index eea4933..fb28780 100644
--- a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogLoadTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogLoadTest.java
@@ -28,8 +28,7 @@ import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
 import org.apache.brooklyn.core.catalog.internal.CatalogDto;
 import org.apache.brooklyn.core.catalog.internal.CatalogItemDtoAbstract;
 import org.apache.brooklyn.core.catalog.internal.CatalogXmlSerializer;
-
-import brooklyn.util.ResourceUtils;
+import org.apache.brooklyn.core.util.ResourceUtils;
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogScanTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogScanTest.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogScanTest.java
index 2d5abad..0ccb709 100644
--- a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogScanTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogScanTest.java
@@ -34,11 +34,11 @@ import org.apache.brooklyn.core.catalog.CatalogPredicates;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.catalog.internal.MyCatalogItems.MySillyAppTemplate;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.ResourceUtils;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.basic.Entities;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.net.Urls;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManagerTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManagerTestFixture.java b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManagerTestFixture.java
index 5689da2..24b1a1d 100644
--- a/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManagerTestFixture.java
+++ b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManagerTestFixture.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.core.management.entitlement.NotEntitledException;
 import org.apache.brooklyn.core.management.entitlement.WebEntitlementContext;
 import org.apache.brooklyn.core.management.entitlement.Entitlements.EntityAndItem;
 import org.apache.brooklyn.core.management.entitlement.Entitlements.StringAndArgument;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
@@ -42,7 +43,6 @@ import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.BasicApplication;
 import brooklyn.entity.basic.Entities;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 
 public abstract class AcmeEntitlementManagerTestFixture {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/management/entitlement/EntityEntitlementTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/entitlement/EntityEntitlementTest.java b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/EntityEntitlementTest.java
index bb25740..7181c99 100644
--- a/core/src/test/java/org/apache/brooklyn/core/management/entitlement/EntityEntitlementTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/EntityEntitlementTest.java
@@ -27,6 +27,7 @@ import org.apache.brooklyn.core.management.entitlement.NotEntitledException;
 import org.apache.brooklyn.core.management.entitlement.Entitlements.EntityAndItem;
 import org.apache.brooklyn.core.management.entitlement.Entitlements.StringAndArgument;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,7 +41,6 @@ import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.BasicApplication;
 import brooklyn.entity.basic.Entities;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 
 public class EntityEntitlementTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/management/internal/EntityExecutionManagerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/internal/EntityExecutionManagerTest.java b/core/src/test/java/org/apache/brooklyn/core/management/internal/EntityExecutionManagerTest.java
index 7f35bba..721057d 100644
--- a/core/src/test/java/org/apache/brooklyn/core/management/internal/EntityExecutionManagerTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/management/internal/EntityExecutionManagerTest.java
@@ -37,6 +37,10 @@ import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.core.management.internal.BrooklynGarbageCollector;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.ExecutionListener;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -58,10 +62,6 @@ import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.repeat.Repeater;
-import brooklyn.util.task.BasicExecutionManager;
-import brooklyn.util.task.ExecutionListener;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiStandaloneTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiStandaloneTest.java b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiStandaloneTest.java
index 7f1cc48..7180b1b 100644
--- a/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiStandaloneTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiStandaloneTest.java
@@ -27,6 +27,9 @@ import java.util.List;
 import java.util.jar.JarInputStream;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.osgi.Osgis;
+import org.apache.brooklyn.core.util.osgi.Osgis.ManifestHelper;
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 
 import brooklyn.util.exceptions.Exceptions;
@@ -43,14 +46,11 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.maven.MavenArtifact;
 import brooklyn.util.maven.MavenRetriever;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
-import brooklyn.util.osgi.Osgis;
-import brooklyn.util.osgi.Osgis.ManifestHelper;
 import brooklyn.util.stream.Streams;
 
 /** 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiVersionMoreEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiVersionMoreEntityTest.java b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiVersionMoreEntityTest.java
index 7220e47..edc73d6 100644
--- a/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiVersionMoreEntityTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiVersionMoreEntityTest.java
@@ -45,6 +45,7 @@ import org.apache.brooklyn.core.catalog.internal.CatalogTestUtils;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.osgi.Osgis;
 
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.effector.Effectors;
@@ -57,7 +58,6 @@ import org.apache.brooklyn.test.entity.TestApplication;
 
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.os.Os;
-import brooklyn.util.osgi.Osgis;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/BrooklynMavenArtifactsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/BrooklynMavenArtifactsTest.java b/core/src/test/java/org/apache/brooklyn/core/util/BrooklynMavenArtifactsTest.java
new file mode 100644
index 0000000..a86d1e0
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/BrooklynMavenArtifactsTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util;
+
+import org.apache.brooklyn.core.util.BrooklynMavenArtifacts;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.test.Asserts;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.maven.MavenArtifact;
+import brooklyn.util.maven.MavenRetriever;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+@Test
+public class BrooklynMavenArtifactsTest {
+
+    private static final Logger log = LoggerFactory.getLogger(BrooklynMavenArtifactsTest.class);
+    
+    @Test(groups="Integration")
+    public void testUtilsCommon() {
+        ResourceUtils.create(this).checkUrlExists(BrooklynMavenArtifacts.localUrlForJar("brooklyn-utils-common"));
+    }
+
+    @Test(groups="Integration")
+    public void testExampleWar() {
+        String url = BrooklynMavenArtifacts.localUrl("example", "brooklyn-example-hello-world-sql-webapp", "war");
+        ResourceUtils.create(this).checkUrlExists(url);
+        log.info("found example war at: "+url);
+    }
+
+    @Test(groups="Integration")
+    // runs without internet but doesn't assert what it should, and can take a long time, so integration
+    public void testBadExampleWar() {
+        String url = BrooklynMavenArtifacts.localUrl("example", "brooklyn-example-GOODBYE-world-sql-webapp", "war");
+        Assert.assertFalse(ResourceUtils.create(this).doesUrlExist(url), "should not exist: "+url);
+    }
+
+    public void testHostedIsHttp() {
+        String common = BrooklynMavenArtifacts.hostedUrlForJar("brooklyn-utils-common");
+        log.info("online should be at: "+common);
+        Assert.assertTrue(common.startsWith("http"));
+    }
+
+    @Test(groups="Integration")
+    public void testHistoricHosted() {
+        // NB: this should be a version known to be up at sonatype or maven central, NOT necessarily the current version!
+        String snapshot = MavenRetriever.hostedUrl(MavenArtifact.fromCoordinate("org.apache.brooklyn:brooklyn-utils-common:jar:0.7.0-SNAPSHOT"));
+        log.info("Sample snapshot URL is: "+snapshot);
+        checkValidArchive(snapshot);
+        ResourceUtils.create(this).checkUrlExists(snapshot);
+        
+        // NB: this should be a version known to be up at sonatype or maven central, NOT necessarily the current version!
+        String release = MavenRetriever.hostedUrl(MavenArtifact.fromCoordinate("io.brooklyn:brooklyn-utils-common:jar:0.6.0"));
+        log.info("Sample release URL is: "+release);
+        checkValidArchive(release);
+    }
+
+    private void checkValidArchive(final String url) {
+        // Note have seen response code 500 from repository.apache.org, for
+        //   https://repository.apache.org/service/local/artifact/maven/redirect?r=snapshots&v=0.7.0-SNAPSHOT&g=org.apache.brooklyn&a=brooklyn-utils-common&e=jar
+        // Therefore willing to retry, rather than failing immediately.
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                try {
+                    byte[] bytes = Streams.readFully(ResourceUtils.create(this).getResourceFromUrl(url));
+                    // confirm this follow redirects!
+                    Assert.assertTrue(bytes.length > 100*1000, "download of "+url+" is suspect ("+Strings.makeSizeString(bytes.length)+")");
+                    // (could also check it is a zip etc)
+                } catch (Exception e) {
+                    throw Exceptions.propagate(e);
+                }
+            }});
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/ResourceUtilsHttpTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/ResourceUtilsHttpTest.java b/core/src/test/java/org/apache/brooklyn/core/util/ResourceUtilsHttpTest.java
new file mode 100644
index 0000000..6ea434d
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/ResourceUtilsHttpTest.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.localserver.RequestBasicAuth;
+import org.apache.http.localserver.ResponseBasicUnauthorized;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.protocol.ResponseServer;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import brooklyn.test.TestHttpRequestHandler;
+import brooklyn.test.TestHttpServer;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Strings;
+
+public class ResourceUtilsHttpTest {
+    private ResourceUtils utils;
+    private TestHttpServer server;
+    private String baseUrl;
+
+    @BeforeClass(alwaysRun=true)
+    public void setUp() throws Exception {
+        utils = ResourceUtils.create(this, "mycontext");
+        server = new TestHttpServer()
+            .interceptor(new ResponseServer())
+            .interceptor(new ResponseBasicUnauthorized())
+            .interceptor(new RequestBasicAuth())
+            .handler("/simple", new TestHttpRequestHandler().response("OK"))
+            .handler("/empty", new TestHttpRequestHandler().code(HttpStatus.SC_NO_CONTENT))
+            .handler("/missing", new TestHttpRequestHandler().code(HttpStatus.SC_NOT_FOUND).response("Missing"))
+            .handler("/redirect", new TestHttpRequestHandler().code(HttpStatus.SC_MOVED_TEMPORARILY).response("Redirect").header("Location", "/simple"))
+            .handler("/cycle", new TestHttpRequestHandler().code(HttpStatus.SC_MOVED_TEMPORARILY).response("Redirect").header("Location", "/cycle"))
+            .handler("/secure", new TestHttpRequestHandler().code(HttpStatus.SC_MOVED_TEMPORARILY).response("Redirect").header("Location", "https://0.0.0.0/"))
+            .handler("/auth", new AuthHandler("test", "test", "OK"))
+            .handler("/auth_escape", new AuthHandler("test@me:/", "test", "OK"))
+            .handler("/auth_escape2", new AuthHandler("test@me:test", "", "OK"))
+            .handler("/no_credentials", new CheckNoCredentials())
+            .start();
+        baseUrl = server.getUrl();
+    }
+
+    @AfterClass(alwaysRun=true)
+    public void tearDown() throws Exception {
+        server.stop();
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        InputStream stream = utils.getResourceFromUrl(baseUrl + "/simple");
+        assertEquals(Streams.readFullyString(stream), "OK");
+    }
+
+    @Test
+    public void testGetEmpty() throws Exception {
+        InputStream stream = utils.getResourceFromUrl(baseUrl + "/empty");
+        assertEquals(Streams.readFullyString(stream), "");
+    }
+
+    @Test
+    public void testGetProtected() throws Exception {
+        String url = baseUrl.replace("http://", "http://test:test@") + "/auth";
+        InputStream stream = utils.getResourceFromUrl(url);
+        assertEquals(Streams.readFullyString(stream), "OK");
+    }
+
+    @Test
+    public void testGetProtectedEscape() throws Exception {
+        String url = baseUrl.replace("http://", "http://test%40me%3A%2F:test@") + "/auth_escape";
+        InputStream stream = utils.getResourceFromUrl(url);
+        assertEquals(Streams.readFullyString(stream), "OK");
+    }
+
+    @Test
+    public void testGetProtectedEscape2() throws Exception {
+        String url = baseUrl.replace("http://", "http://test%40me%3Atest@") + "/auth_escape2";
+        InputStream stream = utils.getResourceFromUrl(url);
+        assertEquals(Streams.readFullyString(stream), "OK");
+    }
+
+    @Test(expectedExceptions = RuntimeException.class)
+    public void testProtectedFailsWithoutCredentials() throws Exception {
+        utils.getResourceFromUrl(baseUrl + "/auth");
+    }
+
+    @Test
+    public void testInvalidCredentialsNotPassed() throws Exception {
+        String url = baseUrl + "/no_credentials?no:auth@needed";
+        InputStream stream = utils.getResourceFromUrl(url);
+        assertEquals(Streams.readFullyString(stream), "OK");
+    }
+
+    @Test
+    public void testRedirect() throws Exception {
+        InputStream stream = utils.getResourceFromUrl(baseUrl + "/redirect");
+        assertEquals(Streams.readFullyString(stream), "OK");
+    }
+
+    @Test(expectedExceptions = RuntimeException.class)
+    public void testCycleRedirect() throws Exception {
+        InputStream stream = utils.getResourceFromUrl(baseUrl + "/cycle");
+        assertEquals(Streams.readFullyString(stream), "OK");
+    }
+
+    @Test(expectedExceptions = RuntimeException.class)
+    public void testGetMissing() throws Exception {
+        utils.getResourceFromUrl(baseUrl + "/missing");
+    }
+
+    @Test(expectedExceptions = RuntimeException.class)
+    public void testFollowsProtoChange() throws Exception {
+        utils.getResourceFromUrl(baseUrl + "/secure");
+    }
+
+    // See https://github.com/brooklyncentral/brooklyn/issues/1338
+    @Test(groups={"Integration"})
+    public void testResourceFromUrlFollowsRedirect() throws Exception {
+        String contents = new ResourceUtils(this).getResourceAsString("http://bit.ly/brooklyn-visitors-creation-script");
+        assertFalse(contents.contains("bit.ly"), "contents="+contents);
+    }
+
+    private static class AuthHandler implements HttpRequestHandler {
+        private String username;
+        private String password;
+        private String responseBody;
+
+        public AuthHandler(String username, String password, String response) {
+            this.username = username;
+            this.password = password;
+            this.responseBody = response;
+        }
+
+        @Override
+        public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
+            String creds = (String) context.getAttribute("creds");
+            if (creds == null || !creds.equals(getExpectedCredentials())) {
+                response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
+            } else {
+                response.setEntity(new StringEntity(responseBody));
+            }
+        }
+
+        private String getExpectedCredentials() {
+            if (Strings.isEmpty(password)) {
+                return username;
+            } else {
+                return username + ":" + password;
+            }
+        }
+
+    }
+
+    private static class CheckNoCredentials implements HttpRequestHandler {
+
+        @Override
+        public void handle(HttpRequest request, HttpResponse response,
+                HttpContext context) throws HttpException, IOException {
+            String creds = (String) context.getAttribute("creds");
+            if (creds == null) {
+                response.setEntity(new StringEntity("OK"));
+            } else {
+                response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
+            }
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/ResourceUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/ResourceUtilsTest.java b/core/src/test/java/org/apache/brooklyn/core/util/ResourceUtilsTest.java
new file mode 100644
index 0000000..10cf455
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/ResourceUtilsTest.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import brooklyn.util.net.Urls;
+import brooklyn.util.os.Os;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Identifiers;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Files;
+
+public class ResourceUtilsTest {
+
+    private static final Logger log = LoggerFactory.getLogger(ResourceUtilsTest.class);
+    
+    private String tempFileContents = "abc";
+    private ResourceUtils utils;
+    private File tempFile;
+    
+    @BeforeClass(alwaysRun=true)
+    public void setUp() throws Exception {
+        utils = ResourceUtils.create(this, "mycontext");
+        tempFile = Os.writeToTempFile(new ByteArrayInputStream(tempFileContents.getBytes()), "resourceutils-test", ".txt");
+    }
+    
+    @AfterClass(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (tempFile != null) tempFile.delete();
+    }
+
+    @Test
+    public void testWriteStreamToTempFile() throws Exception {
+        File tempFileLocal = Os.writeToTempFile(new ByteArrayInputStream("mycontents".getBytes()), "resourceutils-test", ".txt");
+        try {
+            List<String> lines = Files.readLines(tempFileLocal, Charsets.UTF_8);
+            assertEquals(lines, ImmutableList.of("mycontents"));
+        } finally {
+            tempFileLocal.delete();
+        }
+    }
+
+    @Test
+    public void testPropertiesStreamToTempFile() throws Exception {
+        Properties props = new Properties();
+        props.setProperty("mykey", "myval");
+        File tempFileLocal = Os.writePropertiesToTempFile(props, "resourceutils-test", ".txt");
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(tempFileLocal);
+            Properties props2 = new Properties();
+            props2.load(fis);
+            assertEquals(props2.getProperty("mykey"), "myval");
+        } finally {
+            Streams.closeQuietly(fis);
+            tempFileLocal.delete();
+        }
+    }
+
+    @Test
+    public void testGetResourceViaClasspathWithPrefix() throws Exception {
+        InputStream stream = utils.getResourceFromUrl("classpath://brooklyn/config/sample.properties");
+        assertNotNull(stream);
+    }
+    
+    @Test
+    public void testGetResourceViaClasspathWithoutPrefix() throws Exception {
+        InputStream stream = utils.getResourceFromUrl("/brooklyn/config/sample.properties");
+        assertNotNull(stream);
+    }
+
+    @Test
+    public void testGetResourceViaFileWithPrefix() throws Exception {
+        // The correct format for file URLs is file:///<absolute path>.
+        // On UNIX file:///tmp.
+        // On Windows both file:/C:/temp and file:///C:/temp are supported by Java, 
+        // while Windows itself supports the latter only. 
+        // Note that file://C:/temp is *wrong*, because C: is interpreted as the host
+        InputStream stream = utils.getResourceFromUrl(tempFile.toURI().toURL().toString());
+        assertEquals(Streams.readFullyString(stream), tempFileContents);
+    }
+    
+    @Test
+    public void testGetResourceViaFileWithoutPrefix() throws Exception {
+        InputStream stream = utils.getResourceFromUrl(tempFile.getAbsolutePath());
+        assertEquals(Streams.readFullyString(stream), tempFileContents);
+    }
+
+    @Test
+    public void testClassLoaderDir() throws Exception {
+        String d = utils.getClassLoaderDir();
+        log.info("Found resource "+this+" in: "+d);
+        assertTrue(new File(d, "brooklyn/util/").exists());
+    }
+
+    @Test
+    public void testClassLoaderDirFromJar() throws Exception {
+        String d = utils.getClassLoaderDir("java/lang/Object.class");
+        log.info("Found Object in: "+d);
+        assertTrue(d.toLowerCase().endsWith(".jar"));
+    }
+
+    @Test
+    public void testClassLoaderDirFromJarWithSlash() throws Exception {
+        String d = utils.getClassLoaderDir("/java/lang/Object.class");
+        log.info("Found Object in: "+d);
+        assertTrue(d.toLowerCase().endsWith(".jar"));
+    }
+
+    @Test(expectedExceptions={NoSuchElementException.class})
+    public void testClassLoaderDirNotFound() throws Exception {
+        String d = utils.getClassLoaderDir("/somewhere/not/found/XXX.xxx");
+        // above should fail
+        log.warn("Uh oh found imaginary resource in: "+d);
+    }
+
+    @Test(groups="Integration")
+    public void testGetResourceViaSftp() throws Exception {
+        InputStream stream = utils.getResourceFromUrl("sftp://localhost:"+tempFile.getAbsolutePath());
+        assertEquals(Streams.readFullyString(stream), tempFileContents);
+    }
+    
+    @Test(groups="Integration")
+    public void testGetResourceViaSftpWithUsername() throws Exception {
+        String user = System.getProperty("user.name");
+        InputStream stream = utils.getResourceFromUrl("sftp://"+user+"@localhost:"+tempFile.getAbsolutePath());
+        assertEquals(Streams.readFullyString(stream), tempFileContents);
+    }
+
+    @Test
+    public void testDataUrl() throws Exception {
+        assertEquals(utils.getResourceAsString("data:,hello"), "hello");
+        assertEquals(utils.getResourceAsString("data:,hello%20world"), "hello world");
+        // above is correct. below are not valid ... but we accept them anyway
+        assertEquals(utils.getResourceAsString("data:hello"), "hello");
+        assertEquals(utils.getResourceAsString("data://hello"), "hello");
+        assertEquals(utils.getResourceAsString("data:hello world"), "hello world");
+        assertEquals(utils.getResourceAsString(Urls.asDataUrlBase64("hello world")), "hello world");
+        
+        String longString = Identifiers.makeRandomId(256);
+        for (int a=32; a<128; a++) longString += (char)a;
+        assertEquals(utils.getResourceAsString(Urls.asDataUrlBase64(longString)), longString);
+    }
+
+    @Test
+    public void testGetResources() {
+        Iterable<URL> manifests = ResourceUtils.create().getResources("META-INF/MANIFEST.MF");
+        assertFalse(Iterables.isEmpty(manifests));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/config/ConfigBagTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/config/ConfigBagTest.java b/core/src/test/java/org/apache/brooklyn/core/util/config/ConfigBagTest.java
new file mode 100644
index 0000000..f4db6c2
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/config/ConfigBagTest.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.config;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.config.ConfigBagTest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.time.Duration;
+
+public class ConfigBagTest {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(ConfigBagTest.class);
+    
+    private static final ConfigKey<String> K1 = ConfigKeys.newStringConfigKey("k1");
+    private static final ConfigKey<String> K2 = ConfigKeys.newStringConfigKey("k2");
+    private static final ConfigKey<String> K3 = ConfigKeys.newStringConfigKey("k3");
+    
+    @Test
+    public void testPutAndGet() {
+        ConfigBag bag = ConfigBag.newInstance();
+        bag.put(K1, "v1");
+        assertEquals(bag.get(K1), "v1");
+    }
+    
+    @Test
+    public void testPutStringAndGet() {
+        ConfigBag bag = ConfigBag.newInstance();
+        bag.putAsStringKey(K1.getName(), "v1");
+        assertEquals(bag.get(K1), "v1");
+    }
+    
+    @Test
+    public void testUnused() {
+        ConfigBag bag = ConfigBag.newInstance();
+        bag.put(K1, "v1");
+        bag.put(K2, "v2a");
+        assertEquals(bag.get(K1), "v1");
+        assertEquals(bag.getUnusedConfig().size(), 1);
+        assertEquals(bag.peek(K2), "v2a");
+        assertEquals(bag.getUnusedConfig().size(), 1);
+        assertEquals(bag.get(K2), "v2a");
+        Assert.assertTrue(bag.getUnusedConfig().isEmpty());
+    }
+
+    @Test
+    public void testOrder() {
+        ConfigBag bag = ConfigBag.newInstance();
+        bag.put(K1, "v1");
+        bag.put(K2, "v2");
+        bag.put(K3, "v3");
+        Assert.assertEquals(MutableList.copyOf(bag.getAllConfig().keySet()), MutableList.of(K1.getName(), K2.getName(), K3.getName()));
+        Assert.assertEquals(MutableList.copyOf(bag.getAllConfig().values()), MutableList.of("v1", "v2", "v3"));
+    }
+        
+    @Test
+    public void testCopyOverwriteAndGet() {
+        ConfigBag bag1 = ConfigBag.newInstance();
+        bag1.put(K1, "v1");
+        bag1.put(K2, "v2a");
+        bag1.put(K3, "v3");
+        assertEquals(bag1.get(K1), "v1");
+        
+        ConfigBag bag2 = ConfigBag.newInstanceCopying(bag1).putAll(MutableMap.of(K2, "v2b"));
+        assertEquals(bag1.getUnusedConfig().size(), 2);
+        assertEquals(bag2.getUnusedConfig().size(), 2);
+        
+        assertEquals(bag2.get(K1), "v1");
+        assertEquals(bag1.get(K2), "v2a");
+        assertEquals(bag1.getUnusedConfig().size(), 1);
+        assertEquals(bag2.getUnusedConfig().size(), 2);
+        
+        assertEquals(bag2.get(K2), "v2b");
+        assertEquals(bag2.getUnusedConfig().size(), 1);
+        
+        assertEquals(bag2.get(K3), "v3");
+        assertEquals(bag2.getUnusedConfig().size(), 0);
+        assertEquals(bag1.getUnusedConfig().size(), 1);
+    }
+    
+    @Test
+    public void testCopyExtendingAndGet() {
+        ConfigBag bag1 = ConfigBag.newInstance();
+        bag1.put(K1, "v1");
+        bag1.put(K2, "v2a");
+        bag1.put(K3, "v3");
+        assertEquals(bag1.get(K1), "v1");
+        
+        ConfigBag bag2 = ConfigBag.newInstanceExtending(bag1, null).putAll(MutableMap.of(K2, "v2b"));
+        assertEquals(bag1.getUnusedConfig().size(), 2);
+        assertEquals(bag2.getUnusedConfig().size(), 2, "unused are: "+bag2.getUnusedConfig());
+        
+        assertEquals(bag2.get(K1), "v1");
+        assertEquals(bag1.get(K2), "v2a");
+        assertEquals(bag1.getUnusedConfig().size(), 1);
+        assertEquals(bag2.getUnusedConfig().size(), 2);
+        
+        assertEquals(bag2.get(K2), "v2b");
+        assertEquals(bag2.getUnusedConfig().size(), 1);
+        
+        assertEquals(bag2.get(K3), "v3");
+        assertEquals(bag2.getUnusedConfig().size(), 0);
+        // when extended, the difference is that parent is also marked
+        assertEquals(bag1.getUnusedConfig().size(), 0);
+    }
+
+    @Test
+    public void testConcurrent() throws InterruptedException {
+        ConfigBag bag = ConfigBag.newInstance();
+        bag.put(K1, "v1");
+        bag.put(K2, "v2");
+        bag.put(K3, "v3");
+        runConcurrentTest(bag, 10, Duration.millis(50));
+    }
+    
+    @Test(groups="Integration")
+    public void testConcurrentBig() throws InterruptedException {
+        ConfigBag bag = ConfigBag.newInstance();
+        bag.put(K1, "v1");
+        bag.put(K2, "v2");
+        bag.put(K3, "v3");
+        runConcurrentTest(bag, 20, Duration.seconds(5));
+    }
+    
+    private void runConcurrentTest(final ConfigBag bag, int numThreads, Duration time) throws InterruptedException {
+        List<Thread> threads = MutableList.of();
+        final Map<Thread,Exception> exceptions = new ConcurrentHashMap<Thread,Exception>();
+        final AtomicInteger successes = new AtomicInteger();
+        for (int i=0; i<numThreads; i++) {
+            Thread t = new Thread() {
+                @Override
+                public void run() {
+                    try {
+                        while (!interrupted()) {
+                            if (Math.random()<0.9)
+                                bag.put(ConfigKeys.newStringConfigKey("k"+((int)(10*Math.random()))), "v"+((int)(10*Math.random())));
+                            if (Math.random()<0.8)
+                                bag.get(ConfigKeys.newStringConfigKey("k"+((int)(10*Math.random()))));
+                            if (Math.random()<0.2)
+                                bag.copy(bag);
+                            if (Math.random()<0.6)
+                                bag.remove(ConfigKeys.newStringConfigKey("k"+((int)(10*Math.random()))));
+                            successes.incrementAndGet();
+                        }
+                    } catch (Exception e) {
+                        exceptions.put(Thread.currentThread(), e);
+                        Exceptions.propagateIfFatal(e);
+                    }
+                }
+            };
+            t.setName("ConfigBagTest-concurrent-thread-"+i);
+            threads.add(t);
+        }
+        for (Thread t: threads) t.start();
+        time.countdownTimer().waitForExpiry();
+        for (Thread t: threads) t.interrupt();
+        for (Thread t: threads) t.join();
+        Assert.assertTrue(exceptions.isEmpty(), "Got "+exceptions.size()+"/"+numThreads+" exceptions ("+successes.get()+" successful): "+exceptions);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/crypto/SecureKeysAndSignerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/crypto/SecureKeysAndSignerTest.java b/core/src/test/java/org/apache/brooklyn/core/util/crypto/SecureKeysAndSignerTest.java
new file mode 100644
index 0000000..d681b0f
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/crypto/SecureKeysAndSignerTest.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 org.apache.brooklyn.core.util.crypto;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.charset.Charset;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.crypto.FluentKeySigner;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
+import org.apache.brooklyn.core.util.crypto.SecureKeys.PassphraseProblem;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.util.crypto.AuthorizedKeysParser;
+import brooklyn.util.os.Os;
+
+import com.google.common.io.Files;
+
+public class SecureKeysAndSignerTest {
+
+    // a bit slow, so marked as integration (but possibly due to leftover rebind-cleanup, benign failures writing to /tmp/xx)
+    @Test(groups="Integration")
+    public void testGenerateSignedKeys() throws Exception {
+        FluentKeySigner signer = new FluentKeySigner("the-root").
+            validForYears(2).
+            selfsign();
+        X509Certificate signerCert = signer.getAuthorityCertificate();
+
+        KeyPair aKey = SecureKeys.newKeyPair();
+        X509Certificate aCert = signer.newCertificateFor("A", aKey);
+        
+        KeyPair bKey = SecureKeys.newKeyPair();
+        X509Certificate bCert = signer.newCertificateFor("B", bKey);
+
+        FluentKeySigner selfSigner1 = new FluentKeySigner("self1").selfsign();
+        X509Certificate selfCert1 = selfSigner1.getAuthorityCertificate();
+
+        SecureKeys.getTrustManager(aCert).checkClientTrusted(new X509Certificate[] { aCert }, "RSA");
+        SecureKeys.getTrustManager(signerCert).checkClientTrusted(new X509Certificate[] { signerCert }, "RSA");
+        
+        try {
+            SecureKeys.getTrustManager(aCert).checkClientTrusted(new X509Certificate[] { bCert }, "RSA");
+            Assert.fail("Trust manager for A should not accept B");
+        } catch (CertificateException e) { /* expected */ }
+        
+//        SecureKeys.getTrustManager(signerCert).checkClientTrusted(new X509Certificate[] { aCert }, "RSA");
+        // NB, the above failes; we have to convert to a canonical implementation, handled by the following
+        
+        Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(signerCert, signerCert));
+        Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(aCert, signerCert));
+        Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(bCert, signerCert));
+        Assert.assertFalse(SecureKeys.isCertificateAuthorizedBy(signerCert, aCert));
+        Assert.assertFalse(SecureKeys.isCertificateAuthorizedBy(bCert, aCert));
+        
+        Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(selfCert1, selfCert1));
+        Assert.assertFalse(SecureKeys.isCertificateAuthorizedBy(selfCert1, signerCert));
+    }
+
+    @Test
+    public void testInjectCertificateAuthority() throws Exception {
+        KeyPair caKey = SecureKeys.newKeyPair();
+        X509Certificate caCert = new FluentKeySigner("the-root", caKey).selfsign().getAuthorityCertificate();
+
+        FluentKeySigner signer = new FluentKeySigner(caCert, caKey);
+        Assert.assertEquals("the-root", signer.getCommonName());
+        
+        KeyPair aKey = SecureKeys.newKeyPair();
+        X509Certificate aCert = signer.newCertificateFor("A", aKey);
+        
+        Assert.assertTrue(SecureKeys.isCertificateAuthorizedBy(aCert, caCert));
+    }
+
+    @Test
+    public void testReadRsaKey() throws Exception {
+        KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa.pem"), null);
+        checkNonTrivial(key);
+    }
+
+    @Test(expectedExceptions=IllegalStateException.class)
+    public void testReadRsaPublicKeyAsPemFails() throws Exception {
+        // should fail; see next test
+        SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa.pem.pub"), null);
+    }
+    
+    @Test
+    public void testReadRsaPublicKeyAsAuthKeysWorks() throws Exception {
+        PublicKey key = AuthorizedKeysParser.decodePublicKey(
+            ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/util/crypto/sample_rsa.pem.pub"));
+        KeyPair fromPem = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa.pem"), null);        
+        Assert.assertEquals(key, fromPem.getPublic());
+    }
+
+    @Test
+    public void testEncodeDecodeRsaPublicKey() throws Exception {
+        String data = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/util/crypto/sample_rsa.pem.pub");
+        PublicKey key = AuthorizedKeysParser.decodePublicKey(data);
+        String data2 = AuthorizedKeysParser.encodePublicKey(key);
+        Assert.assertTrue(data.contains(data2), "Expected to find '"+data2+"' in '"+data+"'");
+        PublicKey key2 = AuthorizedKeysParser.decodePublicKey(data2);
+        Assert.assertEquals(key2, key);
+    }
+
+    @Test
+    public void testEncodeDecodeDsaPublicKey() throws Exception {
+        String data = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/util/crypto/sample_dsa.pem.pub");
+        PublicKey key = AuthorizedKeysParser.decodePublicKey(data);
+        String data2 = AuthorizedKeysParser.encodePublicKey(key);
+        Assert.assertTrue(data.contains(data2), "Expected to find '"+data2+"' in '"+data+"'");
+        PublicKey key2 = AuthorizedKeysParser.decodePublicKey(data2);
+        Assert.assertEquals(key2, key);
+    }
+
+    @Test
+    public void testReadDsaKey() throws Exception {
+        KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_dsa.pem"), null);
+        checkNonTrivial(key);
+    }
+
+    @Test(expectedExceptions=Exception.class)
+    public void testCantReadRsaPassphraseKeyWithoutPassphrase() throws Exception {
+        KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa_passphrase.pem"), null);
+        checkNonTrivial(key);
+    }
+
+    @Test(expectedExceptions=PassphraseProblem.class)
+    public void testReadRsaPassphraseWithoutKeyFails() throws Exception {
+        SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa_passphrase.pem"), null);
+    }
+    
+    @Test
+    public void testReadRsaPassphraseKeyAndWriteWithoutPassphrase() throws Exception {
+        KeyPair key = SecureKeys.readPem(ResourceUtils.create(this).getResourceFromUrl("classpath://brooklyn/util/crypto/sample_rsa_passphrase.pem"), "passphrase");
+        checkNonTrivial(key);
+        File f = Os.newTempFile(getClass(), "brooklyn-sample_rsa_passphrase_without_passphrase.pem");
+        Files.write(SecureKeys.stringPem(key), f, Charset.defaultCharset());
+        KeyPair key2 = SecureKeys.readPem(new FileInputStream(f), null);
+        checkNonTrivial(key2);
+        Assert.assertEquals(key2.getPrivate().getEncoded(), key.getPrivate().getEncoded());
+        Assert.assertEquals(key2.getPublic().getEncoded(), key.getPublic().getEncoded());
+    }
+
+    private void checkNonTrivial(KeyPair key) {
+        Assert.assertNotEquals(key.getPrivate().getEncoded().length, 0);
+        Assert.assertNotEquals(key.getPublic().getEncoded().length, 0);
+    }
+
+}



[10/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/DynamicSequentialTaskTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/DynamicSequentialTaskTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/DynamicSequentialTaskTest.java
new file mode 100644
index 0000000..c4f8d4c
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/DynamicSequentialTaskTest.java
@@ -0,0 +1,371 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.brooklyn.api.management.HasTaskChildren;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.DynamicSequentialTask;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskTags;
+import org.apache.brooklyn.core.util.task.Tasks;
+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;
+
+import brooklyn.test.Asserts;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.time.CountdownTimer;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+public class DynamicSequentialTaskTest {
+
+    private static final Logger log = LoggerFactory.getLogger(DynamicSequentialTaskTest.class);
+    
+    public static final Duration TIMEOUT = Duration.TEN_SECONDS;
+    public static final Duration TINY_TIME = Duration.millis(20);
+    
+    BasicExecutionManager em;
+    BasicExecutionContext ec;
+    List<String> messages;
+    Semaphore cancellations;
+    Stopwatch stopwatch;
+    Map<String,Semaphore> monitorableJobSemaphoreMap;
+    Map<String,Task<String>> monitorableTasksMap;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() {
+        em = new BasicExecutionManager("mycontext");
+        ec = new BasicExecutionContext(em);
+        cancellations = new Semaphore(0);
+        messages = new ArrayList<String>();
+        monitorableJobSemaphoreMap = MutableMap.of();
+        monitorableTasksMap = MutableMap.of();
+        monitorableTasksMap.clear();
+        stopwatch = Stopwatch.createStarted();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (em != null) em.shutdownNow();
+    }
+
+    @Test
+    public void testSimple() throws InterruptedException, ExecutionException {
+        Callable<String> mainJob = new Callable<String>() {
+            public String call() {
+                log.info("main job - "+Tasks.current());
+                messages.add("main");
+                DynamicTasks.queue( sayTask("world") );
+                return "bye";
+            }            
+        };
+        DynamicSequentialTask<String> t = new DynamicSequentialTask<String>(mainJob);
+        // this should be added before anything added when the task is invoked
+        t.queue(sayTask("hello"));
+        
+        Assert.assertEquals(messages, Lists.newArrayList());
+        Assert.assertEquals(t.isBegun(), false);
+        Assert.assertEquals(Iterables.size(t.getChildren()), 1);
+        
+        ec.submit(t);
+        Assert.assertEquals(t.isSubmitted(), true);
+        Assert.assertEquals(t.getUnchecked(Duration.ONE_SECOND), "bye");
+        long elapsed = t.getEndTimeUtc() - t.getSubmitTimeUtc();
+        Assert.assertTrue(elapsed < 1000, "elapsed time should have been less than 1s but was "+
+                Time.makeTimeString(elapsed, true));
+        Assert.assertEquals(Iterables.size(t.getChildren()), 2);
+        Assert.assertEquals(messages.size(), 3, "expected 3 entries, but had "+messages);
+        // either main or hello can be first, but world should be last 
+        Assert.assertEquals(messages.get(2), "world");
+    }
+    
+    public Callable<String> sayCallable(final String message, final Duration duration, final String message2) {
+        return new Callable<String>() {
+            public String call() {
+                try {
+                    if (message != null) {
+                        log.info("saying: "+message+ " - "+Tasks.current());
+                        synchronized (messages) {
+                            messages.add(message);
+                            messages.notifyAll();
+                        }
+                    }
+                    if (message2 != null) {
+                        log.info("will say "+message2+" after "+duration);
+                    }
+                    if (duration != null && duration.toMilliseconds() > 0) {
+                        Thread.sleep(duration.toMillisecondsRoundingUp());
+                    }
+                } catch (InterruptedException e) {
+                    cancellations.release();
+                    throw Exceptions.propagate(e);
+                }
+                if (message2 != null) {
+                    log.info("saying: "+message2+ " - "+Tasks.current());
+                    synchronized (messages) {
+                        messages.add(message2);
+                        messages.notifyAll();
+                    }
+                }
+                return message;
+            }            
+        };
+    }
+    
+    public Task<String> sayTask(String message) {
+        return sayTask(message, null, null);
+    }
+    
+    public Task<String> sayTask(String message, Duration duration, String message2) {
+        return Tasks.<String>builder().body(sayCallable(message, duration, message2)).build();
+    }
+    
+    @Test
+    public void testComplex() throws InterruptedException, ExecutionException {
+        Task<List<?>> t = Tasks.sequential(
+                sayTask("1"),
+                sayTask("2"),
+                Tasks.parallel(sayTask("4"), sayTask("3")),
+                sayTask("5")
+            );
+        ec.submit(t);
+        Assert.assertEquals(t.get().size(), 4); 
+        Asserts.assertEqualsIgnoringOrder((List<?>)t.get().get(2), ImmutableSet.of("3", "4"));
+        Assert.assertTrue(messages.equals(Arrays.asList("1", "2", "3", "4", "5")) || messages.equals(Arrays.asList("1", "2", "4", "3", "5")), "messages="+messages);
+    }
+    
+    @Test
+    public void testCancelled() throws InterruptedException, ExecutionException {
+        Task<List<?>> t = Tasks.sequential(
+                sayTask("1"),
+                sayTask("2a", Duration.THIRTY_SECONDS, "2b"),
+                sayTask("3"));
+        ec.submit(t);
+        synchronized (messages) {
+            while (messages.size() <= 1)
+                messages.wait();
+        }
+        Assert.assertEquals(messages, Arrays.asList("1", "2a"));
+        Time.sleep(Duration.millis(50));
+        t.cancel(true);
+        Assert.assertTrue(t.isDone());
+        // 2 should get cancelled, and invoke the cancellation semaphore
+        // 3 should get cancelled and not run at all
+        Assert.assertEquals(messages, Arrays.asList("1", "2a"));
+        
+        // Need to ensure that 2 has been started; race where we might cancel it before its run method
+        // is even begun. Hence doing "2a; pause; 2b" where nothing is interruptable before pause.
+        Assert.assertTrue(cancellations.tryAcquire(10, TimeUnit.SECONDS));
+        
+        Iterator<Task<?>> ci = ((HasTaskChildren)t).getChildren().iterator();
+        Assert.assertEquals(ci.next().get(), "1");
+        Task<?> task2 = ci.next();
+        Assert.assertTrue(task2.isBegun());
+        Assert.assertTrue(task2.isDone());
+        Assert.assertTrue(task2.isCancelled());
+        
+        Task<?> task3 = ci.next();
+        Assert.assertFalse(task3.isBegun());
+        Assert.assertTrue(task2.isDone());
+        Assert.assertTrue(task2.isCancelled());
+        
+        // but we do _not_ get a mutex from task3 as it does not run (is not interrupted)
+        Assert.assertEquals(cancellations.availablePermits(), 0);
+    }
+
+    protected Task<String> monitorableTask(final String id) {
+        return monitorableTask(null, id, null);
+    }
+    protected Task<String> monitorableTask(final Runnable pre, final String id, final Callable<String> post) {
+        Task<String> t = Tasks.<String>builder().body(monitorableJob(pre, id, post)).build();
+        monitorableTasksMap.put(id, t);
+        return t;
+    }
+    protected Callable<String> monitorableJob(final String id) {
+        return monitorableJob(null, id, null);
+    }
+    protected Callable<String> monitorableJob(final Runnable pre, final String id, final Callable<String> post) {
+        monitorableJobSemaphoreMap.put(id, new Semaphore(0));
+        return new Callable<String>() {
+            @Override
+            public String call() throws Exception {
+                if (pre!=null) pre.run();
+                // wait for semaphore
+                if (!monitorableJobSemaphoreMap.get(id).tryAcquire(1, TIMEOUT.toMilliseconds(), TimeUnit.MILLISECONDS))
+                    throw new IllegalStateException("timeout for "+id);
+                synchronized (messages) {
+                    messages.add(id);
+                    messages.notifyAll();
+                }
+                if (post!=null) return post.call();
+                return id;
+            }
+        };
+    }
+    protected void releaseMonitorableJob(final String id) {
+        monitorableJobSemaphoreMap.get(id).release();
+    }
+    protected void waitForMessage(final String id) {
+        CountdownTimer timer = CountdownTimer.newInstanceStarted(TIMEOUT);
+        synchronized (messages) {
+            while (!timer.isExpired()) {
+                if (messages.contains(id)) return;
+                timer.waitOnForExpiryUnchecked(messages);
+            }
+        }
+        Assert.fail("Did not see message "+id);
+    }
+    protected void releaseAndWaitForMonitorableJob(final String id) {
+        releaseMonitorableJob(id);
+        waitForMessage(id);
+    }
+    
+    @Test
+    public void testChildrenRunConcurrentlyWithPrimary() {
+        Task<String> t = Tasks.<String>builder().dynamic(true)
+            .body(monitorableJob("main"))
+            .add(monitorableTask("1")).add(monitorableTask("2")).build();
+        ec.submit(t);
+        releaseAndWaitForMonitorableJob("1");
+        releaseAndWaitForMonitorableJob("main");
+        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
+        releaseMonitorableJob("2");
+        
+        Assert.assertTrue(t.blockUntilEnded(TIMEOUT));
+        Assert.assertEquals(messages, MutableList.of("1", "main", "2"));
+        Assert.assertTrue(stopwatch.elapsed(TimeUnit.MILLISECONDS) < TIMEOUT.toMilliseconds(), "took too long: "+stopwatch);
+        Assert.assertFalse(t.isError());
+    }
+    
+    protected static class FailRunnable implements Runnable {
+        @Override public void run() { throw new RuntimeException("Planned exception for test"); }
+    }
+    protected static class FailCallable implements Callable<String> {
+        @Override public String call() { throw new RuntimeException("Planned exception for test"); }
+    }
+    
+    @Test
+    public void testByDefaultChildrenFailureAbortsSecondaryFailsPrimaryButNotAbortsPrimary() {
+        Task<String> t1 = monitorableTask(null, "1", new FailCallable());
+        Task<String> t = Tasks.<String>builder().dynamic(true)
+            .body(monitorableJob("main"))
+            .add(t1).add(monitorableTask("2")).build();
+        ec.submit(t);
+        releaseAndWaitForMonitorableJob("1");
+        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
+        releaseMonitorableJob("main");
+        
+        Assert.assertTrue(t.blockUntilEnded(TIMEOUT));
+        Assert.assertEquals(messages, MutableList.of("1", "main"));
+        Assert.assertTrue(stopwatch.elapsed(TimeUnit.MILLISECONDS) < TIMEOUT.toMilliseconds(), "took too long: "+stopwatch);
+        Assert.assertTrue(t.isError());
+        Assert.assertTrue(t1.isError());
+    }
+
+    @Test
+    public void testWhenSwallowingChildrenFailureDoesNotAbortSecondaryOrFailPrimary() {
+        Task<String> t1 = monitorableTask(null, "1", new FailCallable());
+        Task<String> t = Tasks.<String>builder().dynamic(true)
+            .body(monitorableJob("main"))
+            .add(t1).add(monitorableTask("2")).swallowChildrenFailures(true).build();
+        ec.submit(t);
+        releaseAndWaitForMonitorableJob("1");
+        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
+        releaseAndWaitForMonitorableJob("2");
+        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
+        releaseMonitorableJob("main");
+        Assert.assertTrue(t.blockUntilEnded(TIMEOUT));
+        Assert.assertEquals(messages, MutableList.of("1", "2", "main"));
+        Assert.assertTrue(stopwatch.elapsed(TimeUnit.MILLISECONDS) < TIMEOUT.toMilliseconds(), "took too long: "+stopwatch);
+        Assert.assertFalse(t.isError());
+        Assert.assertTrue(t1.isError());
+    }
+
+    @Test
+    public void testInessentialChildrenFailureDoesNotAbortSecondaryOrFailPrimary() {
+        Task<String> t1 = monitorableTask(null, "1", new FailCallable());
+        TaskTags.markInessential(t1);
+        Task<String> t = Tasks.<String>builder().dynamic(true)
+            .body(monitorableJob("main"))
+            .add(t1).add(monitorableTask("2")).build();
+        ec.submit(t);
+        releaseAndWaitForMonitorableJob("1");
+        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
+        releaseAndWaitForMonitorableJob("2");
+        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
+        releaseMonitorableJob("main");
+        Assert.assertTrue(t.blockUntilEnded(TIMEOUT));
+        Assert.assertEquals(messages, MutableList.of("1", "2", "main"));
+        Assert.assertTrue(stopwatch.elapsed(TimeUnit.MILLISECONDS) < TIMEOUT.toMilliseconds(), "took too long: "+stopwatch);
+        Assert.assertFalse(t.isError());
+        Assert.assertTrue(t1.isError());
+    }
+
+    @Test
+    public void testTaskBuilderUsingAddVarargChildren() {
+        Task<String> t = Tasks.<String>builder().dynamic(true)
+            .body(monitorableJob("main"))
+            .add(monitorableTask("1"), monitorableTask("2"))
+            .build();
+        ec.submit(t);
+        releaseAndWaitForMonitorableJob("1");
+        releaseAndWaitForMonitorableJob("2");
+        releaseAndWaitForMonitorableJob("main");
+        
+        Assert.assertEquals(messages, MutableList.of("1", "2", "main"));
+    }
+    
+    @Test
+    public void testTaskBuilderUsingAddAllChildren() {
+        Task<String> t = Tasks.<String>builder().dynamic(true)
+            .body(monitorableJob("main"))
+            .addAll(ImmutableList.of(monitorableTask("1"), monitorableTask("2")))
+            .build();
+        ec.submit(t);
+        releaseAndWaitForMonitorableJob("1");
+        releaseAndWaitForMonitorableJob("2");
+        releaseAndWaitForMonitorableJob("main");
+        
+        Assert.assertEquals(messages, MutableList.of("1", "2", "main"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/NonBasicTaskExecutionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/NonBasicTaskExecutionTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/NonBasicTaskExecutionTest.java
new file mode 100644
index 0000000..980a701
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/NonBasicTaskExecutionTest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.BasicTask;
+import org.apache.brooklyn.core.util.task.ForwardingTask;
+import org.apache.brooklyn.core.util.task.TaskInternal;
+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;
+
+/**
+ * Test the operation of the {@link BasicTask} class.
+ *
+ * TODO clarify test purpose
+ */
+public class NonBasicTaskExecutionTest {
+    private static final Logger log = LoggerFactory.getLogger(NonBasicTaskExecutionTest.class);
+ 
+    private static final int TIMEOUT_MS = 10*1000;
+    
+    public static class ConcreteForwardingTask<T> extends ForwardingTask<T> {
+        private final TaskInternal<T> delegate;
+
+        ConcreteForwardingTask(TaskInternal<T> delegate) {
+            this.delegate = delegate;
+        }
+        
+        @Override
+        protected TaskInternal<T> delegate() {
+            return delegate;
+        }
+    }
+    
+    private BasicExecutionManager em;
+    private Map<Integer,String> data;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        em = new BasicExecutionManager("mycontext");
+        data = Collections.synchronizedMap(new HashMap<Integer,String>());
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (em != null) em.shutdownNow();
+    }
+    
+    @Test
+    public void runSimpleTask() throws Exception {
+        TaskInternal<Object> t = new ConcreteForwardingTask<Object>(new BasicTask<Object>(new Callable<Object>() {
+            @Override public Object call() {
+                return data.put(1, "b");
+            }}));
+        data.put(1, "a");
+        Task<?> t2 = em.submit(MutableMap.of("tag", "A"), t);
+        assertEquals("a", t.get());
+        assertEquals("a", t2.get());
+        assertSame(t, t2, "t="+t+"; t2="+t2);
+        assertEquals("b", data.get(1));
+    }
+    
+    @Test
+    public void runBasicTaskWithWaits() throws Exception {
+        final CountDownLatch signalStarted = new CountDownLatch(1);
+        final CountDownLatch allowCompletion = new CountDownLatch(1);
+        final TaskInternal<Object> t = new ConcreteForwardingTask<Object>(new BasicTask<Object>(new Callable<Object>() {
+            @Override 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() {
+            @Override public void run() {
+                t.getStatusDetail(false).toLowerCase().contains("waiting");
+            }});
+        // "details="+t.getStatusDetail(false))
+        
+        allowCompletion.countDown();
+        assertEquals("a", t.get());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/ScheduledExecutionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/ScheduledExecutionTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/ScheduledExecutionTest.java
new file mode 100644
index 0000000..3b338da
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/ScheduledExecutionTest.java
@@ -0,0 +1,291 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.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.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.BasicTask;
+import org.apache.brooklyn.core.util.task.ScheduledTask;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.test.Asserts;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.RuntimeInterruptedException;
+import brooklyn.util.javalang.JavaClassNames;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.Lists;
+
+@SuppressWarnings({"unchecked","rawtypes"})
+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()).getSubmittedByTask();
+                        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 {
+        Duration PERIOD = Duration.millis(20);
+        BasicExecutionManager m = new BasicExecutionManager("mycontextid");
+        final AtomicInteger i = new AtomicInteger();
+        ScheduledTask t = new ScheduledTask(MutableMap.of("delay", PERIOD.times(2), "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()).getSubmittedByTask();
+                        i.incrementAndGet();
+                        if (i.get() >= 5) submitter.cancel();
+                        return i.get();
+                    }});
+            }});
+    
+        log.info(JavaClassNames.niceClassAndMethod()+" - 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 testScheduledTaskCancelOuter() throws Exception {
+        final Duration PERIOD = Duration.millis(20);
+        final Duration CYCLE_DELAY = Duration.ONE_SECOND;
+        // this should be enough to start the next cycle, but not so much that the cycle ends;
+        // and enough that when a task is interrupted it terminates within this period
+        final Duration SMALL_FRACTION_OF_CYCLE_DELAY = PERIOD.add(CYCLE_DELAY.multiply(0.1));
+        
+        BasicExecutionManager m = new BasicExecutionManager("mycontextid");
+        final AtomicInteger i = new AtomicInteger();
+        ScheduledTask t = new ScheduledTask(MutableMap.of("delay", PERIOD.times(2), "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));
+                        Time.sleep(CYCLE_DELAY);
+                        i.incrementAndGet();
+                        return i.get();
+                    }});
+            }});
+    
+        log.info(JavaClassNames.niceClassAndMethod()+" - 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)});
+        assertEquals(i.get(), 1);
+        
+        Time.sleep(SMALL_FRACTION_OF_CYCLE_DELAY);
+        assertEquals(t.get(), 2);
+        
+        Time.sleep(SMALL_FRACTION_OF_CYCLE_DELAY);
+        Stopwatch timer = Stopwatch.createUnstarted();
+        t.cancel(true);
+        t.blockUntilEnded();
+//      int finalResult = t.get()
+        log.info("blocked until ended ({}) {} {}, in {}", new Object[] {i, t, t.getStatusDetail(false), Duration.of(timer)});
+        try {
+            t.get();
+            Assert.fail("Should have failed getting result of cancelled "+t);
+        } catch (Exception e) {
+            /* expected */
+        }
+        assertEquals(i.get(), 2);
+        log.info("ended ({}) {} {}, in {}", new Object[] {i, t, t.getStatusDetail(false), Duration.of(timer)});
+        Assert.assertTrue(Duration.of(timer).isShorterThan(SMALL_FRACTION_OF_CYCLE_DELAY));
+    }
+
+    @Test(groups="Integration")
+    public void testScheduledTaskCancelInterrupts() throws Exception {
+        final Duration PERIOD = Duration.millis(20);
+        final Duration CYCLE_DELAY = Duration.ONE_SECOND;
+        // this should be enough to start the next cycle, but not so much that the cycle ends;
+        // and enough that when a task is interrupted it terminates within this period
+        final Duration SMALL_FRACTION_OF_CYCLE_DELAY = PERIOD.add(CYCLE_DELAY.multiply(0.1));
+        
+        BasicExecutionManager m = new BasicExecutionManager("mycontextid");
+        final Semaphore interruptedSemaphore = new Semaphore(0);
+        final AtomicInteger i = new AtomicInteger();
+        ScheduledTask t = new ScheduledTask(MutableMap.of("delay", PERIOD.times(2), "period", PERIOD), new Callable<Task<?>>() {
+            public Task<?> call() throws Exception {
+                return new BasicTask<Integer>(new Callable<Integer>() {
+                    public Integer call() {
+                        try {
+                            log.info("task running ("+i+"): "+Tasks.current()+" "+Tasks.current().getStatusDetail(false));
+                            Time.sleep(CYCLE_DELAY);
+                            i.incrementAndGet();
+                            return i.get();
+                        } catch (RuntimeInterruptedException e) {
+                            interruptedSemaphore.release();
+                            throw Exceptions.propagate(e);
+                        }
+                    }});
+            }});
+    
+        log.info(JavaClassNames.niceClassAndMethod()+" - 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)});
+        assertEquals(i.get(), 1);
+        
+        Time.sleep(SMALL_FRACTION_OF_CYCLE_DELAY);
+        assertEquals(t.get(), 2);
+        
+        Time.sleep(SMALL_FRACTION_OF_CYCLE_DELAY);
+        Stopwatch timer = Stopwatch.createUnstarted();
+        t.cancel(true);
+        t.blockUntilEnded();
+//      int finalResult = t.get()
+        log.info("blocked until ended ({}) {} {}, in {}", new Object[] {i, t, t.getStatusDetail(false), Duration.of(timer)});
+        try {
+            t.get();
+            Assert.fail("Should have failed getting result of cancelled "+t);
+        } catch (Exception e) {
+            /* expected */
+        }
+        assertEquals(i.get(), 2);
+        Assert.assertTrue(interruptedSemaphore.tryAcquire(1, SMALL_FRACTION_OF_CYCLE_DELAY.toMilliseconds(), TimeUnit.MILLISECONDS), "child thread was not interrupted");
+        log.info("ended ({}) {} {}, in {}", new Object[] {i, t, t.getStatusDetail(false), Duration.of(timer)});
+        Assert.assertTrue(Duration.of(timer).isShorterThan(SMALL_FRACTION_OF_CYCLE_DELAY));
+    }
+
+    @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/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/SingleThreadedSchedulerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/SingleThreadedSchedulerTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/SingleThreadedSchedulerTest.java
new file mode 100644
index 0000000..e3420c8
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/SingleThreadedSchedulerTest.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.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.apache.brooklyn.core.util.task.BasicExecutionManager;
+import org.apache.brooklyn.core.util.task.BasicTask;
+import org.apache.brooklyn.core.util.task.SingleThreadedScheduler;
+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;
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/TaskFinalizationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/TaskFinalizationTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/TaskFinalizationTest.java
new file mode 100644
index 0000000..1ff181b
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/TaskFinalizationTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.BasicTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Stopwatch;
+
+public class TaskFinalizationTest {
+
+    private static final Logger log = LoggerFactory.getLogger(TaskFinalizationTest.class);
+    
+    // integration because it can take a while (and finalizers aren't even guaranteed)
+    @Test(groups="Integration")
+    public void testFinalizerInvoked() throws InterruptedException {
+        BasicTask<?> t = new BasicTask<Void>(new Runnable() { public void run() { /* no op */ }});
+        final Semaphore x = new Semaphore(0);
+        t.setFinalizer(new BasicTask.TaskFinalizer() {
+            public void onTaskFinalization(Task<?> t) {
+                synchronized (x) { 
+                    x.release();
+                }
+            }
+        });
+        t = null;
+        Stopwatch watch = Stopwatch.createStarted();
+        for (int i=0; i<30; i++) {
+            System.gc(); System.gc();
+            if (x.tryAcquire(1, TimeUnit.SECONDS)) {
+                log.info("finalizer ran after "+Time.makeTimeStringRounded(watch));
+                return;
+            }
+        }
+        Assert.fail("finalizer did not run in time");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/TasksTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/TasksTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/TasksTest.java
new file mode 100644
index 0000000..0800984
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/TasksTest.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import static brooklyn.event.basic.DependentConfiguration.attributeWhenReady;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.TaskInternal;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ValueResolver;
+import org.apache.brooklyn.test.entity.TestApplication;
+import org.apache.brooklyn.test.entity.TestEntity;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.BrooklynAppUnitTestSupport;
+import brooklyn.entity.basic.EntityFunctions;
+import brooklyn.util.guava.Functionals;
+import brooklyn.util.repeat.Repeater;
+import brooklyn.util.time.Duration;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.Callables;
+
+
+public class TasksTest extends BrooklynAppUnitTestSupport {
+
+    private ExecutionContext executionContext;
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        executionContext = app.getExecutionContext();
+    }
+    
+    @Test
+    public void testResolveNull() throws Exception {
+        assertResolvesValue(null, String.class, null);
+    }
+    
+    @Test
+    public void testResolveValueCastsToType() throws Exception {
+        assertResolvesValue(123, String.class, "123");
+    }
+    
+    @Test
+    public void testResolvesAttributeWhenReady() throws Exception {
+        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
+        assertResolvesValue(attributeWhenReady(app, TestApplication.MY_ATTRIBUTE), String.class, "myval");
+    }
+    
+    @Test
+    public void testResolvesMapWithAttributeWhenReady() throws Exception {
+        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
+        Map<?,?> orig = ImmutableMap.of("mykey", attributeWhenReady(app, TestApplication.MY_ATTRIBUTE));
+        Map<?,?> expected = ImmutableMap.of("mykey", "myval");
+        assertResolvesValue(orig, String.class, expected);
+    }
+    
+    @Test
+    public void testResolvesSetWithAttributeWhenReady() throws Exception {
+        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
+        Set<?> orig = ImmutableSet.of(attributeWhenReady(app, TestApplication.MY_ATTRIBUTE));
+        Set<?> expected = ImmutableSet.of("myval");
+        assertResolvesValue(orig, String.class, expected);
+    }
+    
+    @Test
+    public void testResolvesMapOfMapsWithAttributeWhenReady() throws Exception {
+        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
+        Map<?,?> orig = ImmutableMap.of("mykey", ImmutableMap.of("mysubkey", attributeWhenReady(app, TestApplication.MY_ATTRIBUTE)));
+        Map<?,?> expected = ImmutableMap.of("mykey", ImmutableMap.of("mysubkey", "myval"));
+        assertResolvesValue(orig, String.class, expected);
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testResolvesIterableOfMapsWithAttributeWhenReady() throws Exception {
+        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
+        // using Iterables.concat so that orig is of type FluentIterable rather than List etc
+        Iterable<?> orig = Iterables.concat(ImmutableList.of(ImmutableMap.of("mykey", attributeWhenReady(app, TestApplication.MY_ATTRIBUTE))));
+        Iterable<Map<?,?>> expected = ImmutableList.<Map<?,?>>of(ImmutableMap.of("mykey", "myval"));
+        assertResolvesValue(orig, String.class, expected);
+    }
+    
+    private void assertResolvesValue(Object actual, Class<?> type, Object expected) throws Exception {
+        Object result = Tasks.resolveValue(actual, type, executionContext);
+        assertEquals(result, expected);
+    }
+    
+    @Test
+    public void testErrorsResolvingPropagatesOrSwallowedAllCorrectly() throws Exception {
+        app.setConfig(TestEntity.CONF_OBJECT, ValueResolverTest.newThrowTask(Duration.ZERO));
+        Task<Object> t = Tasks.builder().body(Functionals.callable(EntityFunctions.config(TestEntity.CONF_OBJECT), app)).build();
+        ValueResolver<Object> v = Tasks.resolving(t).as(Object.class).context(app.getExecutionContext());
+        
+        ValueResolverTest.assertThrowsOnMaybe(v);
+        ValueResolverTest.assertThrowsOnGet(v);
+        
+        v.swallowExceptions();
+        ValueResolverTest.assertMaybeIsAbsent(v);
+        ValueResolverTest.assertThrowsOnGet(v);
+        
+        v.defaultValue("foo");
+        ValueResolverTest.assertMaybeIsAbsent(v);
+        assertEquals(v.clone().get(), "foo");
+        assertResolvesValue(v, Object.class, "foo");
+    }
+
+    @Test
+    public void testRepeater() throws Exception {
+        Task<?> t;
+        
+        t = Tasks.requiring(Repeater.create().until(Callables.returning(true)).every(Duration.millis(1))).build();
+        app.getExecutionContext().submit(t);
+        t.get(Duration.TEN_SECONDS);
+        
+        t = Tasks.testing(Repeater.create().until(Callables.returning(true)).every(Duration.millis(1))).build();
+        app.getExecutionContext().submit(t);
+        Assert.assertEquals(t.get(Duration.TEN_SECONDS), true);
+        
+        t = Tasks.requiring(Repeater.create().until(Callables.returning(false)).limitIterationsTo(2).every(Duration.millis(1))).build();
+        app.getExecutionContext().submit(t);
+        try {
+            t.get(Duration.TEN_SECONDS);
+            Assert.fail("Should have failed");
+        } catch (Exception e) {
+            // expected
+        }
+
+        t = Tasks.testing(Repeater.create().until(Callables.returning(false)).limitIterationsTo(2).every(Duration.millis(1))).build();
+        app.getExecutionContext().submit(t);
+        Assert.assertEquals(t.get(Duration.TEN_SECONDS), false);
+    }
+
+    @Test
+    public void testRepeaterDescription() throws Exception{
+        final String description = "task description";
+        Repeater repeater = Repeater.create(description)
+            .repeat(Callables.returning(null))
+            .every(Duration.ONE_MILLISECOND)
+            .limitIterationsTo(1)
+            .until(new Callable<Boolean>() {
+                @Override
+                public Boolean call() {
+                    TaskInternal<?> current = (TaskInternal<?>)Tasks.current();
+                    assertEquals(current.getBlockingDetails(), description);
+                    return true;
+                }
+            });
+        Task<Boolean> t = Tasks.testing(repeater).build();
+        app.getExecutionContext().submit(t);
+        assertTrue(t.get(Duration.TEN_SECONDS));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/ValueResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/ValueResolverTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/ValueResolverTest.java
new file mode 100644
index 0000000..9f65bc4
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/ValueResolverTest.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ValueResolver;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.BrooklynAppUnitTestSupport;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+/**
+ * see also {@link TasksTest} for more tests
+ */
+@Test
+public class ValueResolverTest extends BrooklynAppUnitTestSupport {
+
+    private ExecutionContext executionContext;
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        executionContext = app.getExecutionContext();
+    }
+    
+    public static final Task<String> newSleepTask(final Duration timeout, final String result) {
+        return Tasks.<String>builder().body(new Callable<String>() { 
+            public String call() { 
+                Time.sleep(timeout); 
+                return result; 
+            }}
+        ).build();
+    }
+    
+    public static final Task<String> newThrowTask(final Duration timeout) {
+        return Tasks.<String>builder().body(new Callable<String>() { 
+            public String call() {
+                Time.sleep(timeout); 
+                throw new IllegalStateException("intended, during tests");
+            }}
+        ).build();
+    }
+    
+    public void testTimeoutZero() {
+        Maybe<String> result = Tasks.resolving(newSleepTask(Duration.TEN_SECONDS, "foo")).as(String.class).context(executionContext).timeout(Duration.ZERO).getMaybe();
+        Assert.assertFalse(result.isPresent());
+    }
+    
+    public void testTimeoutBig() {
+        Maybe<String> result = Tasks.resolving(newSleepTask(Duration.ZERO, "foo")).as(String.class).context(executionContext).timeout(Duration.TEN_SECONDS).getMaybe();
+        Assert.assertEquals(result.get(), "foo");
+    }
+
+    public void testNoExecutionContextOnCompleted() {
+        Task<String> t = newSleepTask(Duration.ZERO, "foo");
+        executionContext.submit(t).getUnchecked();
+        Maybe<String> result = Tasks.resolving(t).as(String.class).timeout(Duration.ZERO).getMaybe();
+        Assert.assertEquals(result.get(), "foo");
+    }
+
+    public static Throwable assertThrowsOnMaybe(ValueResolver<?> result) {
+        try {
+            result = result.clone();
+            result.getMaybe();
+            Assert.fail("should have thrown");
+            return null;
+        } catch (Exception e) { return e; }
+    }
+    public static Throwable assertThrowsOnGet(ValueResolver<?> result) {
+        result = result.clone();
+        try {
+            result.get();
+            Assert.fail("should have thrown");
+            return null;
+        } catch (Exception e) { return e; }
+    }
+    public static <T> Maybe<T> assertMaybeIsAbsent(ValueResolver<T> result) {
+        result = result.clone();
+        Maybe<T> maybe = result.getMaybe();
+        Assert.assertFalse(maybe.isPresent());
+        return maybe;
+    }
+    
+    public void testSwallowError() {
+        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(executionContext).swallowExceptions();
+        assertMaybeIsAbsent(result);
+        assertThrowsOnGet(result);
+    }
+
+
+    public void testDontSwallowError() {
+        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(executionContext);
+        assertThrowsOnMaybe(result);
+        assertThrowsOnGet(result);
+    }
+
+    public void testDefaultWhenSwallowError() {
+        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(executionContext).swallowExceptions().defaultValue("foo");
+        assertMaybeIsAbsent(result);
+        Assert.assertEquals(result.get(), "foo");
+    }
+
+    public void testDefaultBeforeDelayAndError() {
+        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.TEN_SECONDS)).as(String.class).context(executionContext).timeout(Duration.ZERO).defaultValue("foo");
+        assertMaybeIsAbsent(result);
+        Assert.assertEquals(result.get(), "foo");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/ssh/SshTasksTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/ssh/SshTasksTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/ssh/SshTasksTest.java
new file mode 100644
index 0000000..94fe3e6
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/ssh/SshTasksTest.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.ssh;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.ssh.BashCommandsIntegrationTest;
+import org.apache.brooklyn.core.util.task.ssh.SshFetchTaskFactory;
+import org.apache.brooklyn.core.util.task.ssh.SshFetchTaskWrapper;
+import org.apache.brooklyn.core.util.task.ssh.SshPutTaskFactory;
+import org.apache.brooklyn.core.util.task.ssh.SshPutTaskWrapper;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasksTest;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.commons.io.FileUtils;
+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;
+
+import brooklyn.entity.basic.BrooklynConfigKeys;
+import brooklyn.entity.basic.Entities;
+
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.net.Urls;
+import brooklyn.util.os.Os;
+
+/**
+ * Some tests for {@link SshTasks}. Note more tests in {@link BashCommandsIntegrationTest}, 
+ * {@link SshEffectorTasksTest}, and {@link SoftwareEffectorTest}.
+ */
+public class SshTasksTest {
+
+    private static final Logger log = LoggerFactory.getLogger(SshTasksTest.class);
+    
+    ManagementContext mgmt;
+    SshMachineLocation host;
+    File tempDir;
+    
+    boolean failureExpected;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setup() throws Exception {
+        mgmt = new LocalManagementContext();
+        
+        LocalhostMachineProvisioningLocation lhc = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
+        host = lhc.obtain();
+        clearExpectedFailure();
+        tempDir = Os.newTempDir(getClass());
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (mgmt != null) Entities.destroyAll(mgmt);
+        mgmt = null;
+        tempDir = Os.deleteRecursively(tempDir).asNullOrThrowing();
+        checkExpectedFailure();
+    }
+
+    protected void checkExpectedFailure() {
+        if (failureExpected) {
+            clearExpectedFailure();
+            Assert.fail("Test should have thrown an exception but it did not.");
+        }
+    }
+    
+    protected void clearExpectedFailure() {
+        failureExpected = false;
+    }
+
+    protected void setExpectingFailure() {
+        failureExpected = true;
+    }
+
+
+    protected <T> ProcessTaskWrapper<T> submit(final ProcessTaskFactory<T> tf) {
+        tf.machine(host);
+        ProcessTaskWrapper<T> t = tf.newTask();
+        mgmt.getExecutionManager().submit(t);
+        return t;
+    }
+
+    protected SshPutTaskWrapper submit(final SshPutTaskFactory tf) {
+        SshPutTaskWrapper t = tf.newTask();
+        mgmt.getExecutionManager().submit(t);
+        return t;
+    }
+
+    @Test(groups="Integration")
+    public void testSshEchoHello() {
+        ProcessTaskWrapper<Integer> t = submit(SshTasks.newSshExecTaskFactory(host, "sleep 1 ; echo hello world"));
+        Assert.assertFalse(t.isDone());
+        Assert.assertEquals(t.get(), (Integer)0);
+        Assert.assertEquals(t.getTask().getUnchecked(), (Integer)0);
+        Assert.assertEquals(t.getStdout().trim(), "hello world");
+    }
+
+    @Test(groups="Integration")
+    public void testCopyTo() throws IOException {
+        String fn = Urls.mergePaths(tempDir.getPath(), "f1");
+        SshPutTaskWrapper t = submit(SshTasks.newSshPutTaskFactory(host, fn).contents("hello world"));
+        t.block();
+        Assert.assertEquals(FileUtils.readFileToString(new File(fn)), "hello world");
+        // and make sure this doesn't throw
+        Assert.assertTrue(t.isDone());
+        Assert.assertTrue(t.isSuccessful());
+        Assert.assertEquals(t.get(), null);
+        Assert.assertEquals(t.getExitCode(), (Integer)0);
+    }
+    
+    @Test(groups="Integration")
+    public void testCopyToFailBadSubdir() throws IOException {
+        String fn = Urls.mergePaths(tempDir.getPath(), "non-existent-subdir/file");
+        SshPutTaskWrapper t = submit(SshTasks.newSshPutTaskFactory(host, fn).contents("hello world"));
+        //this doesn't fail
+        t.block();        
+        Assert.assertTrue(t.isDone());
+        setExpectingFailure();
+        try {
+            // but this does
+            t.get();
+        } catch (Exception e) {
+            log.info("The error if file cannot be written is: "+e);
+            clearExpectedFailure();
+        }
+        checkExpectedFailure();
+        // and the results indicate failure
+        Assert.assertFalse(t.isSuccessful());
+        Assert.assertNotNull(t.getException());
+        Assert.assertNotEquals(t.getExitCode(), (Integer)0);
+    }
+
+    @Test(groups="Integration")
+    public void testCopyToFailBadSubdirAllow() throws IOException {
+        String fn = Urls.mergePaths(tempDir.getPath(), "non-existent-subdir/file");
+        SshPutTaskWrapper t = submit(SshTasks.newSshPutTaskFactory(host, fn).contents("hello world").allowFailure());
+        //this doesn't fail
+        t.block();        
+        Assert.assertTrue(t.isDone());
+        // and this doesn't fail either
+        Assert.assertEquals(t.get(), null);
+        // but it's not successful
+        Assert.assertNotNull(t.getException());
+        Assert.assertFalse(t.isSuccessful());
+        // exit code probably null, but won't be zero
+        Assert.assertNotEquals(t.getExitCode(), (Integer)0);
+    }
+
+    @Test(groups="Integration")
+    public void testCopyToFailBadSubdirCreate() throws IOException {
+        String fn = Urls.mergePaths(tempDir.getPath(), "non-existent-subdir-to-create/file");
+        SshPutTaskWrapper t = submit(SshTasks.newSshPutTaskFactory(host, fn).contents("hello world").createDirectory());
+        t.block();
+        // directory should be created, and file readable now
+        Assert.assertEquals(FileUtils.readFileToString(new File(fn)), "hello world");
+        Assert.assertEquals(t.getExitCode(), (Integer)0);
+    }
+
+    @Test(groups="Integration")
+    public void testSshFetch() throws IOException {
+        String fn = Urls.mergePaths(tempDir.getPath(), "f2");
+        FileUtils.write(new File(fn), "hello fetched world");
+        
+        SshFetchTaskFactory tf = SshTasks.newSshFetchTaskFactory(host, fn);
+        SshFetchTaskWrapper t = tf.newTask();
+        mgmt.getExecutionManager().submit(t);
+
+        t.block();
+        Assert.assertTrue(t.isDone());
+        Assert.assertEquals(t.get(), "hello fetched world");
+        Assert.assertEquals(t.getBytes(), "hello fetched world".getBytes());
+    }
+
+    @Test(groups="Integration")
+    public void testSshWithHeaderProperty() {
+        host.setConfig(BrooklynConfigKeys.SSH_CONFIG_SCRIPT_HEADER, "#!/bin/bash -e\necho foo\n");
+        ProcessTaskWrapper<Integer> t = submit(SshTasks.newSshExecTaskFactory(host, "echo bar"));
+        Assert.assertTrue(t.block().getStdout().trim().matches("foo\\s+bar"), "mismatched output was: "+t.getStdout());
+    }
+
+    @Test(groups="Integration")
+    public void testSshIgnoringHeaderProperty() {
+        host.setConfig(BrooklynConfigKeys.SSH_CONFIG_SCRIPT_HEADER, "#!/bin/bash -e\necho foo\n");
+        ProcessTaskWrapper<Integer> t = submit(SshTasks.newSshExecTaskFactory(host, false, "echo bar"));
+        Assert.assertTrue(t.block().getStdout().trim().matches("bar"), "mismatched output was: "+t.getStdout());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/task/system/SystemTasksTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/task/system/SystemTasksTest.java b/core/src/test/java/org/apache/brooklyn/core/util/task/system/SystemTasksTest.java
new file mode 100644
index 0000000..b673056
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/task/system/SystemTasksTest.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.system;
+
+import java.io.File;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.core.util.task.system.SystemTasks;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.basic.Entities;
+import brooklyn.util.os.Os;
+
+/**
+ * Some tests for {@link SystemTasks}. See {@link SshTasks}.
+ */
+public class SystemTasksTest {
+
+    ManagementContext mgmt;
+    File tempDir;
+    
+    boolean failureExpected;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setup() throws Exception {
+        mgmt = new LocalManagementContext();
+        
+        clearExpectedFailure();
+        tempDir = Os.newTempDir(getClass());
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (mgmt != null) Entities.destroyAll(mgmt);
+        mgmt = null;
+        tempDir = Os.deleteRecursively(tempDir).asNullOrThrowing();
+        checkExpectedFailure();
+    }
+
+    protected void checkExpectedFailure() {
+        if (failureExpected) {
+            clearExpectedFailure();
+            Assert.fail("Test should have thrown an exception but it did not.");
+        }
+    }
+    
+    protected void clearExpectedFailure() {
+        failureExpected = false;
+    }
+
+    protected void setExpectingFailure() {
+        failureExpected = true;
+    }
+
+
+    protected <T> ProcessTaskWrapper<T> submit(final ProcessTaskFactory<T> tf) {
+        ProcessTaskWrapper<T> t = tf.newTask();
+        mgmt.getExecutionManager().submit(t);
+        return t;
+    }
+
+    @Test(groups="Integration")
+    public void testExecEchoHello() {
+        ProcessTaskWrapper<Integer> t = submit(SystemTasks.exec("sleep 1 ; echo hello world"));
+        Assert.assertFalse(t.isDone());
+        Assert.assertEquals(t.get(), (Integer)0);
+        Assert.assertEquals(t.getTask().getUnchecked(), (Integer)0);
+        Assert.assertEquals(t.getStdout().trim(), "hello world");
+    }
+
+    // FIXME Behaviour of Bash shell changes from 3.x to 4.x so test is disabled
+    @Test(groups="Integration", enabled=false)
+    public void testSubshellExitScriptDoesNotExit() {
+        checkSubshellExitDoesNotExit(taskSubshellExit().runAsScript());
+    }
+
+    @Test(groups="Integration")
+    public void testSubshellExitCommandDoesNotExit() {
+        checkSubshellExitDoesNotExit(taskSubshellExit().runAsCommand());
+    }
+
+    public ProcessTaskFactory<Integer> taskSubshellExit() {
+        return SystemTasks.exec("echo hello", "( exit 1 )", "echo bye code $?");
+    }
+
+    public void checkSubshellExitDoesNotExit(ProcessTaskFactory<Integer> task) {
+        ProcessTaskWrapper<Integer> t = submit(task);
+        t.block();
+        Assert.assertEquals(t.get(), (Integer)0);
+        Assert.assertTrue(t.getStdout().contains("bye code 1"), "stdout is: "+t.getStdout());
+    }
+
+    @Test(groups="Integration")
+    public void testGroupExitScriptDoesNotExit() {
+        checkGroupExitDoesExit(taskGroupExit().runAsScript());
+    }
+
+    @Test(groups="Integration")
+    public void testGroupExitCommandDoesNotExit() {
+        checkGroupExitDoesExit(taskGroupExit().runAsCommand());
+    }
+
+    public ProcessTaskFactory<Integer> taskGroupExit() {
+        return SystemTasks.exec("echo hello", "{ exit 1 ; }", "echo bye code $?");
+    }
+
+    public void checkGroupExitDoesExit(ProcessTaskFactory<Integer> task) {
+        ProcessTaskWrapper<Integer> t = submit(task);
+        t.block();
+        Assert.assertEquals(t.get(), (Integer)1);
+        Assert.assertFalse(t.getStdout().contains("bye"), "stdout is: "+t.getStdout());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/text/DataUriSchemeParserTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/text/DataUriSchemeParserTest.java b/core/src/test/java/org/apache/brooklyn/core/util/text/DataUriSchemeParserTest.java
new file mode 100644
index 0000000..73794a3
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/text/DataUriSchemeParserTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.text;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.apache.brooklyn.core.util.text.DataUriSchemeParser;
+import org.bouncycastle.util.encoders.Base64;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class DataUriSchemeParserTest {
+
+    @Test
+    public void testSimple() {
+        Assert.assertEquals(new DataUriSchemeParser("data:,hello").parse().getDataAsString(), "hello");
+        Assert.assertEquals(DataUriSchemeParser.toString("data:,hello"), "hello");
+    }
+
+    @Test
+    public void testMimeType() throws UnsupportedEncodingException {
+        DataUriSchemeParser p = new DataUriSchemeParser("data:application/json,"+URLEncoder.encode("{ }", "US-ASCII")).parse();
+        Assert.assertEquals(p.getMimeType(), "application/json");
+        Assert.assertEquals(p.getData(), "{ }".getBytes());
+    }
+
+    @Test
+    public void testBase64() {
+        Assert.assertEquals(DataUriSchemeParser.toString(
+                "data:;base64,"+new String(Base64.encode("hello".getBytes()))), 
+            "hello");
+    }
+
+    // TODO test pictures, etc
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/org/apache/brooklyn/core/util/text/TemplateProcessorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/util/text/TemplateProcessorTest.java b/core/src/test/java/org/apache/brooklyn/core/util/text/TemplateProcessorTest.java
new file mode 100644
index 0000000..05f4fde
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/util/text/TemplateProcessorTest.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.text;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
+import org.apache.brooklyn.test.entity.TestApplication;
+import org.apache.brooklyn.test.entity.TestEntity;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.BrooklynAppUnitTestSupport;
+import brooklyn.event.basic.DependentConfiguration;
+import brooklyn.test.FixedLocaleTest;
+
+import com.google.common.collect.ImmutableMap;
+
+public class TemplateProcessorTest extends BrooklynAppUnitTestSupport {
+    private FixedLocaleTest localeFix = new FixedLocaleTest();
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        super.setUp();
+        localeFix.setUp();
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        super.tearDown();
+        localeFix.tearDown();
+    }
+
+    @Test
+    public void testAdditionalArgs() {
+        String templateContents = "${mykey}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.of("mykey", "myval"));
+        assertEquals(result, "myval");
+    }
+    
+    @Test
+    public void testEntityConfig() {
+        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .configure(TestEntity.CONF_NAME, "myval"));
+        String templateContents = "${config['"+TestEntity.CONF_NAME.getName()+"']}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, entity, ImmutableMap.<String,Object>of());
+        assertEquals(result, "myval");
+    }
+    
+    @Test
+    public void testEntityConfigNumber() {
+        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .configure(TestEntity.CONF_OBJECT, 123456));
+        String templateContents = "${config['"+TestEntity.CONF_OBJECT.getName()+"']}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, entity, ImmutableMap.<String,Object>of());
+        assertEquals(result, "123,456");
+    }
+    
+    @Test
+    public void testEntityConfigNumberUnadorned() {
+        // ?c is needed to avoid commas (i always forget this!)
+        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .configure(TestEntity.CONF_OBJECT, 123456));
+        String templateContents = "${config['"+TestEntity.CONF_OBJECT.getName()+"']?c}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, entity, ImmutableMap.<String,Object>of());
+        assertEquals(result, "123456");
+    }
+    
+    @Test
+    public void testGetSysProp() {
+        System.setProperty("testGetSysProp", "myval");
+        
+        String templateContents = "${javaSysProps['testGetSysProp']}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
+        assertEquals(result, "myval");
+    }
+    
+    @Test
+    public void testEntityGetterMethod() {
+        String templateContents = "${entity.id}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
+        assertEquals(result, app.getId());
+    }
+    
+    @Test
+    public void testManagementContextConfig() {
+        mgmt.getBrooklynProperties().put("globalmykey", "myval");
+        String templateContents = "${mgmt.globalmykey}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
+        assertEquals(result, "myval");
+    }
+    
+    @Test
+    public void testManagementContextDefaultValue() {
+        String templateContents = "${(missing)!\"defval\"}";
+        Object result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
+        assertEquals(result, "defval");
+    }
+    
+    @Test
+    public void testManagementContextDefaultValueInDotMissingValue() {
+        String templateContents = "${(mgmt.missing.more_missing)!\"defval\"}";
+        Object result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
+        assertEquals(result, "defval");
+    }
+    
+    @Test
+    public void testManagementContextConfigWithDot() {
+        mgmt.getBrooklynProperties().put("global.mykey", "myval");
+        String templateContents = "${mgmt['global.mykey']}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
+        assertEquals(result, "myval");
+    }
+    
+    @Test
+    public void testManagementContextErrors() {
+        try {
+            // NB: dot has special meaning so this should fail; must be accessed using bracket notation as above
+            mgmt.getBrooklynProperties().put("global.mykey", "myval");
+            String templateContents = "${mgmt.global.mykey}";
+            TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
+            Assert.fail("Should not have found value with intermediate dot");
+        } catch (Exception e) {
+            Assert.assertTrue(e.toString().contains("global"), "Should have mentioned missing key 'global' in error");
+        }
+    }
+    
+    @Test
+    public void testApplyTemplatedConfigWithAttributeWhenReady() {
+        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
+
+        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .configure(TestEntity.CONF_NAME, DependentConfiguration.attributeWhenReady(app, TestApplication.MY_ATTRIBUTE)));
+        
+        String templateContents = "${config['"+TestEntity.CONF_NAME.getName()+"']}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, entity, ImmutableMap.<String,Object>of());
+        assertEquals(result, "myval");
+    }
+    
+    @Test
+    public void testDotSeparatedKey() {
+        String templateContents = "${a.b}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, (ManagementContextInternal)null, 
+            ImmutableMap.<String,Object>of("a.b", "myval"));
+        assertEquals(result, "myval");
+    }
+    
+    @Test
+    public void testDotSeparatedKeyCollisionFailure() {
+        String templateContents = "${aaa.bbb}";
+        try {
+            TemplateProcessor.processTemplateContents(templateContents, (ManagementContextInternal)null, 
+                ImmutableMap.<String,Object>of("aaa.bbb", "myval", "aaa", "blocker"));
+            Assert.fail("Should not have found value with intermediate dot where prefix is overridden");
+        } catch (Exception e) {
+            Assert.assertTrue(e.toString().contains("aaa"), "Should have mentioned missing key 'aaa' in error");
+        }
+    }
+
+}


[08/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/enricher/TimeFractionDeltaEnricher.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/enricher/TimeFractionDeltaEnricher.java b/policy/src/main/java/brooklyn/enricher/TimeFractionDeltaEnricher.java
index e1fce23..d86aec0 100644
--- a/policy/src/main/java/brooklyn/enricher/TimeFractionDeltaEnricher.java
+++ b/policy/src/main/java/brooklyn/enricher/TimeFractionDeltaEnricher.java
@@ -27,9 +27,9 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.enricher.basic.AbstractTypeTransformingEnricher;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/enricher/TimeWeightedDeltaEnricher.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/enricher/TimeWeightedDeltaEnricher.java b/policy/src/main/java/brooklyn/enricher/TimeWeightedDeltaEnricher.java
index 3f7357d..0d5e066 100644
--- a/policy/src/main/java/brooklyn/enricher/TimeWeightedDeltaEnricher.java
+++ b/policy/src/main/java/brooklyn/enricher/TimeWeightedDeltaEnricher.java
@@ -24,13 +24,13 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.enricher.basic.AbstractTypeTransformingEnricher;
 import brooklyn.enricher.basic.YamlTimeWeightedDeltaEnricher;
 import brooklyn.util.GroovyJavaMethods;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetrics.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetrics.java b/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetrics.java
index e927c1e..f717812 100644
--- a/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetrics.java
+++ b/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetrics.java
@@ -21,10 +21,10 @@ package brooklyn.entity.brooklyn;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 @ImplementedBy(BrooklynMetricsImpl.class)
 public interface BrooklynMetrics extends Entity {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetricsImpl.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetricsImpl.java b/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetricsImpl.java
index 93ece4a..caf1120 100644
--- a/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetricsImpl.java
+++ b/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetricsImpl.java
@@ -26,9 +26,9 @@ import java.util.concurrent.TimeUnit;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.management.internal.LocalSubscriptionManager;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.task.BasicExecutionManager;
 
 import brooklyn.entity.basic.AbstractEntity;
-import brooklyn.util.task.BasicExecutionManager;
 
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/autoscaling/AutoScalerPolicy.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/autoscaling/AutoScalerPolicy.java b/policy/src/main/java/brooklyn/policy/autoscaling/AutoScalerPolicy.java
index 28c2123..9da5e12 100644
--- a/policy/src/main/java/brooklyn/policy/autoscaling/AutoScalerPolicy.java
+++ b/policy/src/main/java/brooklyn/policy/autoscaling/AutoScalerPolicy.java
@@ -40,6 +40,9 @@ import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.task.Tasks;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynTaskTags;
@@ -52,9 +55,6 @@ import brooklyn.policy.autoscaling.SizeHistory.WindowSummary;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.loadbalancing.LoadBalancingPolicy;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java b/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
index 1747424..dde1609 100644
--- a/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
+++ b/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
@@ -38,6 +38,7 @@ import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -46,7 +47,6 @@ import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.followthesun.FollowTheSunPool.ContainerItemPair;
 import brooklyn.policy.loadbalancing.Movable;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/ha/AbstractFailureDetector.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/ha/AbstractFailureDetector.java b/policy/src/main/java/brooklyn/policy/ha/AbstractFailureDetector.java
index f88dc60..0c2f4d2 100644
--- a/policy/src/main/java/brooklyn/policy/ha/AbstractFailureDetector.java
+++ b/policy/src/main/java/brooklyn/policy/ha/AbstractFailureDetector.java
@@ -28,6 +28,9 @@ import java.util.concurrent.atomic.AtomicReference;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.task.BasicTask;
+import org.apache.brooklyn.core.util.task.ScheduledTask;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,9 +42,6 @@ import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.ha.HASensors.FailureDescriptor;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.task.BasicTask;
-import brooklyn.util.task.ScheduledTask;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/ha/ConditionalSuspendPolicy.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/ha/ConditionalSuspendPolicy.java b/policy/src/main/java/brooklyn/policy/ha/ConditionalSuspendPolicy.java
index 9be8256..9a869ae 100644
--- a/policy/src/main/java/brooklyn/policy/ha/ConditionalSuspendPolicy.java
+++ b/policy/src/main/java/brooklyn/policy/ha/ConditionalSuspendPolicy.java
@@ -23,13 +23,13 @@ import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.policy.basic.AbstractPolicy;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.javalang.JavaClassNames;
 
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/ha/ConnectionFailureDetector.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/ha/ConnectionFailureDetector.java b/policy/src/main/java/brooklyn/policy/ha/ConnectionFailureDetector.java
index a1a59a4..ff5d60e 100644
--- a/policy/src/main/java/brooklyn/policy/ha/ConnectionFailureDetector.java
+++ b/policy/src/main/java/brooklyn/policy/ha/ConnectionFailureDetector.java
@@ -20,13 +20,13 @@ package brooklyn.policy.ha;
 
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.event.Sensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.BasicNotificationSensor;
 import brooklyn.policy.ha.HASensors.FailureDescriptor;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.net.Networking;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/ha/ServiceFailureDetector.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/ha/ServiceFailureDetector.java b/policy/src/main/java/brooklyn/policy/ha/ServiceFailureDetector.java
index 7ae6f33..2e4b719 100644
--- a/policy/src/main/java/brooklyn/policy/ha/ServiceFailureDetector.java
+++ b/policy/src/main/java/brooklyn/policy/ha/ServiceFailureDetector.java
@@ -24,6 +24,10 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.brooklyn.api.event.SensorEvent;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.task.BasicTask;
+import org.apache.brooklyn.core.util.task.ScheduledTask;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -38,11 +42,7 @@ import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.BasicNotificationSensor;
 import brooklyn.policy.ha.HASensors.FailureDescriptor;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.task.BasicTask;
-import brooklyn.util.task.ScheduledTask;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/ha/ServiceReplacer.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/ha/ServiceReplacer.java b/policy/src/main/java/brooklyn/policy/ha/ServiceReplacer.java
index 459c13e..884f2df 100644
--- a/policy/src/main/java/brooklyn/policy/ha/ServiceReplacer.java
+++ b/policy/src/main/java/brooklyn/policy/ha/ServiceReplacer.java
@@ -35,6 +35,8 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -48,9 +50,7 @@ import brooklyn.event.basic.BasicNotificationSensor;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.ha.HASensors.FailureDescriptor;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.base.Ticker;
 import com.google.common.collect.Lists;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/ha/ServiceRestarter.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/ha/ServiceRestarter.java b/policy/src/main/java/brooklyn/policy/ha/ServiceRestarter.java
index 3a3f0b4..d53434e 100644
--- a/policy/src/main/java/brooklyn/policy/ha/ServiceRestarter.java
+++ b/policy/src/main/java/brooklyn/policy/ha/ServiceRestarter.java
@@ -28,6 +28,8 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -40,8 +42,6 @@ import brooklyn.event.basic.BasicNotificationSensor;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.ha.HASensors.FailureDescriptor;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/ha/SshMachineFailureDetector.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/ha/SshMachineFailureDetector.java b/policy/src/main/java/brooklyn/policy/ha/SshMachineFailureDetector.java
index 7d31cf7..1fa9982 100644
--- a/policy/src/main/java/brooklyn/policy/ha/SshMachineFailureDetector.java
+++ b/policy/src/main/java/brooklyn/policy/ha/SshMachineFailureDetector.java
@@ -23,16 +23,18 @@ import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.api.catalog.Catalog;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicNotificationSensor;
+
 import org.apache.brooklyn.location.basic.Machines;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.policy.ha.HASensors.FailureDescriptor;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.time.Duration;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/loadbalancing/ItemsInContainersGroup.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/loadbalancing/ItemsInContainersGroup.java b/policy/src/main/java/brooklyn/policy/loadbalancing/ItemsInContainersGroup.java
index ac93639..efd41d8 100644
--- a/policy/src/main/java/brooklyn/policy/loadbalancing/ItemsInContainersGroup.java
+++ b/policy/src/main/java/brooklyn/policy/loadbalancing/ItemsInContainersGroup.java
@@ -21,11 +21,11 @@ package brooklyn.policy.loadbalancing;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.Group;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.DynamicGroup;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java b/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
index f5ae09c..2c50883 100644
--- a/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
+++ b/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
@@ -35,6 +35,7 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,7 +45,6 @@ import brooklyn.policy.autoscaling.AutoScalerPolicy;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.policy.loadbalancing.BalanceableWorkerPool.ContainerItemPair;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/main/java/brooklyn/policy/loadbalancing/Movable.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/loadbalancing/Movable.java b/policy/src/main/java/brooklyn/policy/loadbalancing/Movable.java
index f95ba4f..b0f1658 100644
--- a/policy/src/main/java/brooklyn/policy/loadbalancing/Movable.java
+++ b/policy/src/main/java/brooklyn/policy/loadbalancing/Movable.java
@@ -19,6 +19,7 @@
 package brooklyn.policy.loadbalancing;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.annotation.Effector;
@@ -26,7 +27,6 @@ import brooklyn.entity.annotation.EffectorParam;
 import brooklyn.entity.basic.MethodEffector;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/test/java/brooklyn/enricher/HttpLatencyDetectorTest.java
----------------------------------------------------------------------
diff --git a/policy/src/test/java/brooklyn/enricher/HttpLatencyDetectorTest.java b/policy/src/test/java/brooklyn/enricher/HttpLatencyDetectorTest.java
index 9777e03..7c7a9f3 100644
--- a/policy/src/test/java/brooklyn/enricher/HttpLatencyDetectorTest.java
+++ b/policy/src/test/java/brooklyn/enricher/HttpLatencyDetectorTest.java
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.http.BetterMockWebServer;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -35,9 +36,10 @@ import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Entities;
 import brooklyn.event.basic.Sensors;
+
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.http.BetterMockWebServer;
 
 import com.google.common.collect.ImmutableList;
 import com.google.mockwebserver.MockResponse;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/test/java/brooklyn/enricher/RebindEnricherTest.java
----------------------------------------------------------------------
diff --git a/policy/src/test/java/brooklyn/enricher/RebindEnricherTest.java b/policy/src/test/java/brooklyn/enricher/RebindEnricherTest.java
index ac1b5c1..a01fd17 100644
--- a/policy/src/test/java/brooklyn/enricher/RebindEnricherTest.java
+++ b/policy/src/test/java/brooklyn/enricher/RebindEnricherTest.java
@@ -24,6 +24,7 @@ import java.net.URL;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.http.BetterMockWebServer;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.annotations.AfterMethod;
@@ -33,7 +34,6 @@ import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.rebind.RebindTestFixtureWithApp;
 import brooklyn.event.basic.Sensors;
 import brooklyn.test.Asserts;
-import brooklyn.util.http.BetterMockWebServer;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/test/java/brooklyn/policy/ha/ServiceReplacerTest.java
----------------------------------------------------------------------
diff --git a/policy/src/test/java/brooklyn/policy/ha/ServiceReplacerTest.java b/policy/src/test/java/brooklyn/policy/ha/ServiceReplacerTest.java
index 5a88649..c679224 100644
--- a/policy/src/test/java/brooklyn/policy/ha/ServiceReplacerTest.java
+++ b/policy/src/test/java/brooklyn/policy/ha/ServiceReplacerTest.java
@@ -35,6 +35,7 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
@@ -60,7 +61,6 @@ import org.apache.brooklyn.location.basic.SimulatedLocation;
 
 import brooklyn.policy.ha.HASensors.FailureDescriptor;
 import brooklyn.test.Asserts;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.javalang.JavaClassNames;
 
 import com.google.common.base.Predicate;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/test/java/brooklyn/policy/ha/ServiceRestarterTest.java
----------------------------------------------------------------------
diff --git a/policy/src/test/java/brooklyn/policy/ha/ServiceRestarterTest.java b/policy/src/test/java/brooklyn/policy/ha/ServiceRestarterTest.java
index ce0fbf1..81cff78 100644
--- a/policy/src/test/java/brooklyn/policy/ha/ServiceRestarterTest.java
+++ b/policy/src/test/java/brooklyn/policy/ha/ServiceRestarterTest.java
@@ -31,6 +31,7 @@ import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -45,7 +46,6 @@ import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.trait.FailingEntity;
 import brooklyn.policy.ha.HASensors.FailureDescriptor;
 import brooklyn.test.Asserts;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/policy/src/test/java/brooklyn/policy/loadbalancing/MockContainerEntity.java
----------------------------------------------------------------------
diff --git a/policy/src/test/java/brooklyn/policy/loadbalancing/MockContainerEntity.java b/policy/src/test/java/brooklyn/policy/loadbalancing/MockContainerEntity.java
index da4472e..70d4b57 100644
--- a/policy/src/test/java/brooklyn/policy/loadbalancing/MockContainerEntity.java
+++ b/policy/src/test/java/brooklyn/policy/loadbalancing/MockContainerEntity.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.annotation.EffectorParam;
@@ -30,7 +31,6 @@ import brooklyn.entity.basic.AbstractGroup;
 import brooklyn.entity.basic.MethodEffector;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 @ImplementedBy(MockContainerEntityImpl.class)
 public interface MockContainerEntity extends AbstractGroup, BalanceableContainer<Movable>, Startable {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java
index bf7454d..a1b4ea8 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java
@@ -29,19 +29,19 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.os.Os;
 import brooklyn.util.stream.ReaderInputStream;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Optional;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
index c8efbb9..31ee4be 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
@@ -29,6 +29,11 @@ import java.util.Set;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolver;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.internal.ssh.sshj.SshjTool;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,18 +50,15 @@ import brooklyn.entity.basic.lifecycle.ScriptHelper;
 import brooklyn.entity.effector.EffectorTasks;
 import brooklyn.entity.software.SshEffectorTasks;
 import brooklyn.event.feed.ConfigToAttributes;
+
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.internal.ssh.sshj.SshjTool;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;
 import brooklyn.util.stream.KnownSizeInputStream;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.StringPredicates;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/basic/SameServerDriverLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/SameServerDriverLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/basic/SameServerDriverLifecycleEffectorTasks.java
index 1a926a5..c7d19c1 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/SameServerDriverLifecycleEffectorTasks.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/SameServerDriverLifecycleEffectorTasks.java
@@ -27,6 +27,8 @@ import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.PortRange;
 import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,9 +45,7 @@ import brooklyn.entity.trait.StartableMethods;
 import org.apache.brooklyn.location.basic.LocationConfigKeys;
 
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.DynamicTasks;
 
 public class SameServerDriverLifecycleEffectorTasks extends MachineLifecycleEffectorTasks {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/basic/SameServerEntity.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/SameServerEntity.java b/software/base/src/main/java/brooklyn/entity/basic/SameServerEntity.java
index 080cb87..374402a 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/SameServerEntity.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/SameServerEntity.java
@@ -24,6 +24,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers;
@@ -31,7 +32,6 @@ import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.QuorumCheck;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.reflect.TypeToken;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/basic/SameServerEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/SameServerEntityImpl.java b/software/base/src/main/java/brooklyn/entity/basic/SameServerEntityImpl.java
index 32050fd..ff58d89 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/SameServerEntityImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/SameServerEntityImpl.java
@@ -24,13 +24,13 @@ import java.util.Collection;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
 
 import brooklyn.entity.basic.ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers;
 import brooklyn.entity.software.MachineLifecycleEffectorTasks;
 import brooklyn.util.collections.QuorumCheck;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
 
 public class SameServerEntityImpl extends AbstractEntity implements SameServerEntity {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java
index cf0dd6b..e80013f 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java
@@ -24,6 +24,7 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Lifecycle.Transition;
@@ -33,7 +34,6 @@ import brooklyn.event.basic.AttributeSensorAndConfigKey;
 import brooklyn.event.basic.MapConfigKey;
 import brooklyn.event.basic.Sensors;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.time.Duration;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java
index 1067333..457bb12 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java
@@ -23,6 +23,8 @@ import java.util.Map;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,8 +33,6 @@ import brooklyn.entity.basic.SoftwareProcess.RestartSoftwareParameters;
 import brooklyn.entity.basic.SoftwareProcess.RestartSoftwareParameters.RestartMachineMode;
 import brooklyn.entity.software.MachineLifecycleEffectorTasks;
 import brooklyn.entity.trait.StartableMethods;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.task.DynamicTasks;
 import brooklyn.util.text.Strings;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessImpl.java b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessImpl.java
index dffe783..0ae6339 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessImpl.java
@@ -18,7 +18,6 @@
  */
 package brooklyn.entity.basic;
 
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import groovy.time.TimeDuration;
 
@@ -42,6 +41,10 @@ import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.PortRange;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.policy.EnricherSpec;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -58,10 +61,7 @@ import org.apache.brooklyn.location.cloud.CloudLocationConfig;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.CountdownTimer;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcessSshDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcessSshDriver.java b/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcessSshDriver.java
index b66bd62..29ef57f 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcessSshDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcessSshDriver.java
@@ -24,11 +24,13 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolver;
+import org.apache.brooklyn.core.util.file.ArchiveUtils;
 
 import brooklyn.entity.basic.lifecycle.ScriptHelper;
+
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.file.ArchiveUtils;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/basic/lifecycle/NaiveScriptRunner.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/lifecycle/NaiveScriptRunner.java b/software/base/src/main/java/brooklyn/entity/basic/lifecycle/NaiveScriptRunner.java
index 670b1df..7d51e22 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/lifecycle/NaiveScriptRunner.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/lifecycle/NaiveScriptRunner.java
@@ -21,7 +21,7 @@ package brooklyn.entity.basic.lifecycle;
 import java.util.List;
 import java.util.Map;
 
-import brooklyn.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
 
 /** Marks something which can run scripts. Called "Naive" because it hides too much of the complexity,
  * about script execution and other ssh-related tasks (put, etc). The {@link SshTasks} approach seems better.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/basic/lifecycle/ScriptHelper.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/lifecycle/ScriptHelper.java b/software/base/src/main/java/brooklyn/entity/basic/lifecycle/ScriptHelper.java
index 07532f8..da9e0a1 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/lifecycle/ScriptHelper.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/lifecycle/ScriptHelper.java
@@ -19,7 +19,6 @@
 package brooklyn.entity.basic.lifecycle;
 
 import static java.lang.String.format;
-import brooklyn.util.internal.ssh.ShellTool;
 import groovy.lang.Closure;
 
 import java.io.ByteArrayOutputStream;
@@ -35,20 +34,23 @@ import javax.annotation.Nullable;
 import org.apache.brooklyn.api.management.ExecutionContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.TaskQueueingContext;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.mutex.WithMutexes;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynTaskTags;
+
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.GroovyJavaMethods;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.RuntimeInterruptedException;
-import brooklyn.util.mutex.WithMutexes;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/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
index 7cbae99..22dae74 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirrorImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynEntityMirrorImpl.java
@@ -25,6 +25,10 @@ import java.util.concurrent.Callable;
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.entity.Effector;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.http.HttpStatus;
 
 import brooklyn.entity.basic.AbstractEntity;
@@ -39,11 +43,7 @@ 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.http.HttpToolResponse;
 import brooklyn.util.net.Urls;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/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 da6016b..83314db 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java
@@ -31,6 +31,7 @@ import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynConfigKeys;
@@ -45,7 +46,6 @@ import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.StringAttributeSens
 import brooklyn.event.basic.MapConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.net.Networking;
 import brooklyn.util.ssh.BashCommands;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/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 4250b74..4f1c2d5 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
@@ -31,6 +31,11 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.TaskAdaptable;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskTags;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.http.HttpStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -59,21 +64,18 @@ import brooklyn.event.feed.http.HttpFeed;
 import brooklyn.event.feed.http.HttpPollConfig;
 import brooklyn.event.feed.http.HttpValueFunctions;
 import brooklyn.event.feed.http.JsonFunctions;
+
 import org.apache.brooklyn.location.access.BrooklynAccessUtils;
 import org.apache.brooklyn.location.basic.Locations;
+
 import brooklyn.util.collections.Jsonya;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.PropagatedRuntimeException;
 import brooklyn.util.guava.Functionals;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.javalang.Enums;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.repeat.Repeater;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskTags;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeSshDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeSshDriver.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeSshDriver.java
index ab4ffb2..55c544e 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeSshDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeSshDriver.java
@@ -34,16 +34,18 @@ import brooklyn.entity.brooklynnode.BrooklynNode.ExistingFileBehaviour;
 import brooklyn.entity.drivers.downloads.DownloadSubstituters;
 import brooklyn.entity.java.JavaSoftwareProcessSshDriver;
 import brooklyn.entity.software.SshEffectorTasks;
+
+import org.apache.brooklyn.core.util.file.ArchiveBuilder;
+import org.apache.brooklyn.core.util.file.ArchiveUtils;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.file.ArchiveBuilder;
-import brooklyn.util.file.ArchiveUtils;
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.net.Networking;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClient.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClient.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClient.java
index 25df319..58d1229 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClient.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClient.java
@@ -20,8 +20,8 @@ package brooklyn.entity.brooklynnode;
 
 import java.util.Map;
 
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClientImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClientImpl.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClientImpl.java
index 2403380..59449a2 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClientImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/EntityHttpClientImpl.java
@@ -25,6 +25,9 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.http.auth.UsernamePasswordCredentials;
 import org.apache.http.client.HttpClient;
 import org.slf4j.Logger;
@@ -37,11 +40,8 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.net.Urls;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.Tasks;
 
 public class EntityHttpClientImpl implements EntityHttpClient {
     private static final Logger LOG = LoggerFactory.getLogger(EntityHttpClientImpl.class);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/brooklynnode/RemoteEffectorBuilder.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/RemoteEffectorBuilder.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/RemoteEffectorBuilder.java
index 0cb944e..05b28eb 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/RemoteEffectorBuilder.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/RemoteEffectorBuilder.java
@@ -23,11 +23,11 @@ import java.util.Collection;
 import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Effector;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 
 import brooklyn.entity.brooklynnode.BrooklynEntityMirrorImpl.RemoteEffector;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.effector.Effectors.EffectorBuilder;
-import brooklyn.util.http.HttpToolResponse;
 
 import com.google.common.base.Function;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynClusterUpgradeEffectorBody.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynClusterUpgradeEffectorBody.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynClusterUpgradeEffectorBody.java
index 798f9b5..df902b1 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynClusterUpgradeEffectorBody.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynClusterUpgradeEffectorBody.java
@@ -31,6 +31,9 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.TaskAdaptable;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -47,10 +50,7 @@ import brooklyn.entity.effector.EffectorBody;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.net.Urls;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynNodeUpgradeEffectorBody.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynNodeUpgradeEffectorBody.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynNodeUpgradeEffectorBody.java
index 9c3a17b..1706a93 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynNodeUpgradeEffectorBody.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/BrooklynNodeUpgradeEffectorBody.java
@@ -26,6 +26,9 @@ import org.apache.brooklyn.api.entity.drivers.DriverDependentEntity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,10 +47,7 @@ import brooklyn.entity.effector.EffectorBody;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.software.SshEffectorTasks;
 import brooklyn.event.basic.MapConfigKey;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.net.Urls;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SelectMasterEffectorBody.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SelectMasterEffectorBody.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SelectMasterEffectorBody.java
index 3817b6b..d5a5555 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SelectMasterEffectorBody.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SelectMasterEffectorBody.java
@@ -27,6 +27,8 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.Group;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,9 +41,7 @@ import brooklyn.entity.brooklynnode.BrooklynNode.SetHighAvailabilityPriorityEffe
 import brooklyn.entity.effector.EffectorBody;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.repeat.Repeater;
-import brooklyn.util.task.DynamicTasks;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityModeEffectorBody.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityModeEffectorBody.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityModeEffectorBody.java
index 2edf67d..89387d5 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityModeEffectorBody.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityModeEffectorBody.java
@@ -21,6 +21,8 @@ package brooklyn.entity.brooklynnode.effector;
 import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.http.HttpStatus;
 
 import brooklyn.entity.brooklynnode.BrooklynNode;
@@ -30,9 +32,7 @@ import brooklyn.entity.effector.EffectorBody;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.event.feed.http.HttpValueFunctions;
 import brooklyn.event.feed.http.JsonFunctions;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Functionals;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.javalang.Enums;
 
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityPriorityEffectorBody.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityPriorityEffectorBody.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityPriorityEffectorBody.java
index 884d7ba..b9496d5 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityPriorityEffectorBody.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/effector/SetHighAvailabilityPriorityEffectorBody.java
@@ -19,6 +19,8 @@
 package brooklyn.entity.brooklynnode.effector;
 
 import org.apache.brooklyn.api.entity.Effector;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.http.HttpStatus;
 
 import brooklyn.entity.brooklynnode.BrooklynNode;
@@ -26,8 +28,6 @@ import brooklyn.entity.brooklynnode.BrooklynNode.SetHighAvailabilityPriorityEffe
 import brooklyn.entity.brooklynnode.EntityHttpClient;
 import brooklyn.entity.effector.EffectorBody;
 import brooklyn.entity.effector.Effectors;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.http.HttpToolResponse;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java b/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java
index fde4f51..8d3a1ef 100644
--- a/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java
+++ b/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java
@@ -31,6 +31,8 @@ import java.util.concurrent.TimeUnit;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -41,8 +43,6 @@ import brooklyn.event.feed.AbstractFeed;
 import brooklyn.event.feed.PollHandler;
 import brooklyn.event.feed.Poller;
 import brooklyn.event.feed.ssh.SshPollValue;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Joiner;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/chef/ChefConfig.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefConfig.java b/software/base/src/main/java/brooklyn/entity/chef/ChefConfig.java
index 769d8d0..e768280 100644
--- a/software/base/src/main/java/brooklyn/entity/chef/ChefConfig.java
+++ b/software/base/src/main/java/brooklyn/entity/chef/ChefConfig.java
@@ -18,11 +18,12 @@
  */
 package brooklyn.entity.chef;
 
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.MapConfigKey;
 import brooklyn.event.basic.SetConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.annotations.Beta;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/chef/ChefLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/chef/ChefLifecycleEffectorTasks.java
index 9f6bf82..402d8d3 100644
--- a/software/base/src/main/java/brooklyn/entity/chef/ChefLifecycleEffectorTasks.java
+++ b/software/base/src/main/java/brooklyn/entity/chef/ChefLifecycleEffectorTasks.java
@@ -23,6 +23,11 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskTags;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,14 +42,9 @@ import org.apache.brooklyn.location.basic.Machines;
 import brooklyn.util.collections.Jsonya;
 import brooklyn.util.collections.Jsonya.Navigator;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.net.Urls;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskTags;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/chef/ChefServerTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefServerTasks.java b/software/base/src/main/java/brooklyn/entity/chef/ChefServerTasks.java
index 743e335..b192f0b 100644
--- a/software/base/src/main/java/brooklyn/entity/chef/ChefServerTasks.java
+++ b/software/base/src/main/java/brooklyn/entity/chef/ChefServerTasks.java
@@ -24,8 +24,8 @@ import java.io.IOException;
 import java.nio.charset.Charset;
 import java.security.KeyPair;
 
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.crypto.SecureKeys;
 
 import com.google.common.base.Throwables;
 import com.google.common.io.Files;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/chef/ChefSoloDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefSoloDriver.java b/software/base/src/main/java/brooklyn/entity/chef/ChefSoloDriver.java
index 0d9935c..ce727af 100644
--- a/software/base/src/main/java/brooklyn/entity/chef/ChefSoloDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/chef/ChefSoloDriver.java
@@ -21,12 +21,13 @@ package brooklyn.entity.chef;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.management.TaskAdaptable;
 import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver;
 import brooklyn.entity.basic.ConfigKeys;
+
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.task.DynamicTasks;
 
 import com.google.common.annotations.Beta;
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/chef/ChefTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefTasks.java b/software/base/src/main/java/brooklyn/entity/chef/ChefTasks.java
index 369df99..31f99a5 100644
--- a/software/base/src/main/java/brooklyn/entity/chef/ChefTasks.java
+++ b/software/base/src/main/java/brooklyn/entity/chef/ChefTasks.java
@@ -23,19 +23,19 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.TaskAdaptable;
 import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.core.util.file.ArchiveTasks;
+import org.apache.brooklyn.core.util.file.ArchiveUtils.ArchiveType;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.effector.EffectorTasks;
 import brooklyn.entity.software.SshEffectorTasks;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.file.ArchiveTasks;
-import brooklyn.util.file.ArchiveUtils.ArchiveType;
 import brooklyn.util.net.Urls;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/chef/KnifeConvergeTaskFactory.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/chef/KnifeConvergeTaskFactory.java b/software/base/src/main/java/brooklyn/entity/chef/KnifeConvergeTaskFactory.java
index f9ec730..b32cad7 100644
--- a/software/base/src/main/java/brooklyn/entity/chef/KnifeConvergeTaskFactory.java
+++ b/software/base/src/main/java/brooklyn/entity/chef/KnifeConvergeTaskFactory.java
@@ -29,16 +29,18 @@ import com.google.common.base.Strings;
 import com.google.common.net.HostAndPort;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.effector.EffectorTasks;
+
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.collections.Jsonya;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 
 import com.google.common.base.Function;
 import com.google.common.base.Functions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/chef/KnifeTaskFactory.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/chef/KnifeTaskFactory.java b/software/base/src/main/java/brooklyn/entity/chef/KnifeTaskFactory.java
index f3b748f..52abcc2 100644
--- a/software/base/src/main/java/brooklyn/entity/chef/KnifeTaskFactory.java
+++ b/software/base/src/main/java/brooklyn/entity/chef/KnifeTaskFactory.java
@@ -24,15 +24,15 @@ import java.util.List;
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.internal.ssh.process.ProcessTool;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.core.util.task.system.internal.SystemProcessTaskFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.util.collections.MutableList;
-import brooklyn.util.internal.ssh.process.ProcessTool;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.system.ProcessTaskFactory;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-import brooklyn.util.task.system.internal.SystemProcessTaskFactory;
 import brooklyn.util.text.StringEscapes.BashStringEscapes;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/java/JavaSoftwareProcessSshDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/java/JavaSoftwareProcessSshDriver.java b/software/base/src/main/java/brooklyn/entity/java/JavaSoftwareProcessSshDriver.java
index 1853259..e6b3245 100644
--- a/software/base/src/main/java/brooklyn/entity/java/JavaSoftwareProcessSshDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/java/JavaSoftwareProcessSshDriver.java
@@ -28,6 +28,13 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -48,18 +55,13 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.effector.EffectorTasks;
 import brooklyn.entity.software.SshEffectorTasks;
+
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.internal.ssh.ShellTool;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ssh.SshTasks;
-import brooklyn.util.task.system.ProcessTaskFactory;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.StringEscapes.BashStringEscapes;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/java/JmxSupport.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/java/JmxSupport.java b/software/base/src/main/java/brooklyn/entity/java/JmxSupport.java
index 2f88ff8..7e7c4c6 100644
--- a/software/base/src/main/java/brooklyn/entity/java/JmxSupport.java
+++ b/software/base/src/main/java/brooklyn/entity/java/JmxSupport.java
@@ -26,6 +26,9 @@ import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.core.util.BrooklynMavenArtifacts;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,11 +36,11 @@ import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.event.feed.jmx.JmxHelper;
+
 import org.apache.brooklyn.location.access.BrooklynAccessUtils;
 import org.apache.brooklyn.location.basic.Locations;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.BrooklynMavenArtifacts;
-import brooklyn.util.ResourceUtils;
+
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.guava.Maybe;
@@ -46,7 +49,6 @@ import brooklyn.util.jmx.jmxrmi.JmxRmiAgent;
 import brooklyn.util.maven.MavenArtifact;
 import brooklyn.util.maven.MavenRetriever;
 import brooklyn.util.net.Urls;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/java/JmxmpSslSupport.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/java/JmxmpSslSupport.java b/software/base/src/main/java/brooklyn/entity/java/JmxmpSslSupport.java
index 2c91999..dc57a3d 100644
--- a/software/base/src/main/java/brooklyn/entity/java/JmxmpSslSupport.java
+++ b/software/base/src/main/java/brooklyn/entity/java/JmxmpSslSupport.java
@@ -26,13 +26,14 @@ import java.security.PrivateKey;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 
+import org.apache.brooklyn.core.util.crypto.FluentKeySigner;
+import org.apache.brooklyn.core.util.crypto.SecureKeys;
+import org.apache.brooklyn.core.util.task.Tasks;
+
 import brooklyn.util.collections.MutableMap.Builder;
-import brooklyn.util.crypto.FluentKeySigner;
-import brooklyn.util.crypto.SecureKeys;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.jmx.jmxmp.JmxmpAgent;
 import brooklyn.util.net.Urls;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.base.Preconditions;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/java/UsesJava.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/java/UsesJava.java b/software/base/src/main/java/brooklyn/entity/java/UsesJava.java
index 1f824a1..a1afb97 100644
--- a/software/base/src/main/java/brooklyn/entity/java/UsesJava.java
+++ b/software/base/src/main/java/brooklyn/entity/java/UsesJava.java
@@ -18,6 +18,8 @@
  */
 package brooklyn.entity.java;
 
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 
@@ -25,7 +27,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.MapConfigKey;
 import brooklyn.event.basic.SetConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 public interface UsesJava {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/java/UsesJavaMXBeans.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/java/UsesJavaMXBeans.java b/software/base/src/main/java/brooklyn/entity/java/UsesJavaMXBeans.java
index f15e217..a556811 100644
--- a/software/base/src/main/java/brooklyn/entity/java/UsesJavaMXBeans.java
+++ b/software/base/src/main/java/brooklyn/entity/java/UsesJavaMXBeans.java
@@ -21,12 +21,12 @@ package brooklyn.entity.java;
 import java.util.Map;
 
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 public interface UsesJavaMXBeans {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/java/UsesJmx.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/java/UsesJmx.java b/software/base/src/main/java/brooklyn/entity/java/UsesJmx.java
index 141c5e5..2bb5bf0 100644
--- a/software/base/src/main/java/brooklyn/entity/java/UsesJmx.java
+++ b/software/base/src/main/java/brooklyn/entity/java/UsesJmx.java
@@ -24,6 +24,7 @@ import java.security.cert.Certificate;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.PortRange;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -34,8 +35,6 @@ import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 
 import org.apache.brooklyn.location.basic.PortRanges;
 
-import brooklyn.util.flags.SetFromFlag;
-
 public interface UsesJmx extends UsesJava {
 
     public static final int DEFAULT_JMX_PORT = 1099; // RMI port?

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/java/VanillaJavaApp.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/java/VanillaJavaApp.java b/software/base/src/main/java/brooklyn/entity/java/VanillaJavaApp.java
index 42e3090..6ae48e8 100644
--- a/software/base/src/main/java/brooklyn/entity/java/VanillaJavaApp.java
+++ b/software/base/src/main/java/brooklyn/entity/java/VanillaJavaApp.java
@@ -23,13 +23,13 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.event.basic.Sensors;
 import brooklyn.util.collections.MutableList;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/java/VanillaJavaAppImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/java/VanillaJavaAppImpl.java b/software/base/src/main/java/brooklyn/entity/java/VanillaJavaAppImpl.java
index 719a0ba..bbee991 100644
--- a/software/base/src/main/java/brooklyn/entity/java/VanillaJavaAppImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/java/VanillaJavaAppImpl.java
@@ -24,12 +24,12 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.SoftwareProcessImpl;
 import brooklyn.event.feed.jmx.JmxFeed;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.annotations.VisibleForTesting;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/java/VanillaJavaAppSshDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/java/VanillaJavaAppSshDriver.java b/software/base/src/main/java/brooklyn/entity/java/VanillaJavaAppSshDriver.java
index 104dd7d..db1bb63 100644
--- a/software/base/src/main/java/brooklyn/entity/java/VanillaJavaAppSshDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/java/VanillaJavaAppSshDriver.java
@@ -28,16 +28,18 @@ import java.util.Map;
 import javax.annotation.Nullable;
 
 import brooklyn.entity.basic.lifecycle.ScriptHelper;
+
+import org.apache.brooklyn.core.util.file.ArchiveBuilder;
+import org.apache.brooklyn.core.util.file.ArchiveUtils;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.file.ArchiveBuilder;
-import brooklyn.util.file.ArchiveUtils;
-import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.StringEscapes.BashStringEscapes;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/machine/MachineEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/machine/MachineEntityImpl.java b/software/base/src/main/java/brooklyn/entity/machine/MachineEntityImpl.java
index f101e66..7459c19 100644
--- a/software/base/src/main/java/brooklyn/entity/machine/MachineEntityImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/machine/MachineEntityImpl.java
@@ -31,11 +31,13 @@ import brooklyn.entity.software.SshEffectorTasks;
 import brooklyn.event.feed.ssh.SshFeed;
 import brooklyn.event.feed.ssh.SshPollConfig;
 import brooklyn.event.feed.ssh.SshPollValue;
+
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
 import org.apache.brooklyn.location.basic.Machines;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/pool/ServerPoolImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/pool/ServerPoolImpl.java b/software/base/src/main/java/brooklyn/entity/pool/ServerPoolImpl.java
index c35e287..e5066f2 100644
--- a/software/base/src/main/java/brooklyn/entity/pool/ServerPoolImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/pool/ServerPoolImpl.java
@@ -31,6 +31,7 @@ import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.management.LocationManager;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -51,7 +52,6 @@ import org.apache.brooklyn.location.dynamic.DynamicLocation;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.DynamicTasks;
 
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/pool/ServerPoolLocation.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/pool/ServerPoolLocation.java b/software/base/src/main/java/brooklyn/entity/pool/ServerPoolLocation.java
index 704459b..df0a76c 100644
--- a/software/base/src/main/java/brooklyn/entity/pool/ServerPoolLocation.java
+++ b/software/base/src/main/java/brooklyn/entity/pool/ServerPoolLocation.java
@@ -34,11 +34,10 @@ import brooklyn.entity.basic.ConfigKeys;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.location.basic.AbstractLocation;
 import org.apache.brooklyn.location.dynamic.DynamicLocation;
 
-import brooklyn.util.flags.SetFromFlag;
-
 public class ServerPoolLocation extends AbstractLocation implements MachineProvisioningLocation<MachineLocation>,
         DynamicLocation<ServerPool, ServerPoolLocation> {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/base/src/main/java/brooklyn/entity/service/EntityLaunchListener.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/service/EntityLaunchListener.java b/software/base/src/main/java/brooklyn/entity/service/EntityLaunchListener.java
index 354653d..e263d0d 100644
--- a/software/base/src/main/java/brooklyn/entity/service/EntityLaunchListener.java
+++ b/software/base/src/main/java/brooklyn/entity/service/EntityLaunchListener.java
@@ -28,11 +28,11 @@ import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.management.ExecutionManager;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.Tasks;
 
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.BrooklynTaskTags.EffectorCallTag;
 import brooklyn.entity.basic.Lifecycle;
-import brooklyn.util.task.Tasks;
 
 public class EntityLaunchListener implements Runnable, SensorEventListener<Lifecycle> {
     private static final String SSH_LAUNCH_TASK_PREFIX = "ssh: launching";


[26/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshTool.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshTool.java
new file mode 100644
index 0000000..4361f46
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/SshTool.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh;
+
+import static brooklyn.entity.basic.ConfigKeys.newConfigKey;
+import static brooklyn.entity.basic.ConfigKeys.newStringConfigKey;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.util.stream.KnownSizeInputStream;
+import brooklyn.util.time.Duration;
+
+/**
+ * Defines the methods available on the various different implementations of SSH,
+ * and configuration options which are also generally available.
+ * <p>
+ * The config keys in this class can be supplied (or their string equivalents, where the flags/props take {@code Map<String,?>})
+ * to influence configuration, either for the tool/session itself or for individual commands.
+ * <p>
+ * To specify some of these properties on a global basis, use the variants of the keys here
+ * contained in {@link ConfigKeys}
+ * (which are generally {@value #BROOKLYN_CONFIG_KEY_PREFIX} prefixed to the names of keys here).
+ */
+public interface SshTool extends ShellTool {
+    
+    /** Public-facing global config keys for Brooklyn are defined in ConfigKeys, 
+     * and have this prefix pre-prended to the config keys in this class. 
+     * These keys are detected from entity/global config and automatically applied to ssh executions. */
+    public static final String BROOKLYN_CONFIG_KEY_PREFIX = "brooklyn.ssh.config.";
+    
+    public static final ConfigKey<String> PROP_TOOL_CLASS = newStringConfigKey("tool.class", "SshTool implementation to use", null);
+    
+    public static final ConfigKey<String> PROP_HOST = newStringConfigKey("host", "Host to connect to (required)", null);
+    public static final ConfigKey<Integer> PROP_PORT = newConfigKey("port", "Port on host to connect to", 22);
+    public static final ConfigKey<String> PROP_USER = newConfigKey("user", "User to connect as", System.getProperty("user.name"));
+    public static final ConfigKey<String> PROP_PASSWORD = newStringConfigKey("password", "Password to use to connect", null);
+    
+    public static final ConfigKey<String> PROP_PRIVATE_KEY_FILE = newStringConfigKey("privateKeyFile", "the path of an ssh private key file; leave blank to use defaults (i.e. ~/.ssh/id_rsa and id_dsa)", null);
+    public static final ConfigKey<String> PROP_PRIVATE_KEY_DATA = newStringConfigKey("privateKeyData", "the private ssh key (e.g. contents of an id_rsa or id_dsa file)", null);
+    public static final ConfigKey<String> PROP_PRIVATE_KEY_PASSPHRASE = newStringConfigKey("privateKeyPassphrase", "the passphrase for the ssh private key", null);
+    public static final ConfigKey<Boolean> PROP_STRICT_HOST_KEY_CHECKING = newConfigKey("strictHostKeyChecking", "whether to check the remote host's identification; defaults to false", false);
+    public static final ConfigKey<Boolean> PROP_ALLOCATE_PTY = newConfigKey("allocatePTY", "whether to allocate PTY (vt100); if true then stderr is sent to stdout, but sometimes required for sudo'ing due to requiretty", false);
+
+    public static final ConfigKey<Long> PROP_CONNECT_TIMEOUT = newConfigKey("connectTimeout", "Timeout in millis when establishing an SSH connection; if 0 then uses default (usually 30s)", 0L);
+    public static final ConfigKey<Long> PROP_SESSION_TIMEOUT = newConfigKey("sessionTimeout", "Timeout in millis for an ssh session; if 0 then uses default", 0L);
+    public static final ConfigKey<Integer> PROP_SSH_TRIES = newConfigKey("sshTries", "Max number of times to attempt ssh operations", 4);
+    public static final ConfigKey<Long> PROP_SSH_TRIES_TIMEOUT = newConfigKey("sshTriesTimeout", "Time limit for attempting retries; will not interrupt tasks, but stops retrying after a total amount of elapsed time", Duration.TWO_MINUTES.toMilliseconds());
+    public static final ConfigKey<Long> PROP_SSH_RETRY_DELAY = newConfigKey("sshRetryDelay", "Time (in milliseconds) before first ssh-retry, after which it will do exponential backoff", 50L);
+
+    // NB -- items above apply for _session_ (a tool), below apply for a _call_
+    // TODO would be nice to track which arguments are used, so we can indicate whether extras are supplied
+
+    public static final ConfigKey<String> PROP_PERMISSIONS = newConfigKey("permissions", "Default permissions for files copied/created on remote machine; must be four-digit octal string, default '0644'", "0644");
+    public static final ConfigKey<Long> PROP_LAST_MODIFICATION_DATE = newConfigKey("lastModificationDate", "Last-modification-date to be set on files copied/created (should be UTC/1000, ie seconds since 1970; default 0 usually means current)", 0L);
+    public static final ConfigKey<Long> PROP_LAST_ACCESS_DATE = newConfigKey("lastAccessDate", "Last-access-date to be set on files copied/created (should be UTC/1000, ie seconds since 1970; default 0 usually means lastModificationDate)", 0L);
+    public static final ConfigKey<Integer> PROP_OWNER_UID = newConfigKey("ownerUid", "Default owner UID (not username) for files created on remote machine; default is unset", -1);
+    
+    // TODO remove unnecessary "public static final" modifiers
+    
+    // TODO Could define the following in SshMachineLocation, or some such?
+    //public static ConfigKey<String> PROP_LOG_PREFIX = newStringKey("logPrefix", "???", ???);
+    //public static ConfigKey<Boolean> PROP_NO_STDOUT_LOGGING = newStringKey("noStdoutLogging", "???", ???);
+    //public static ConfigKey<Boolean> PROP_NO_STDOUT_LOGGING = newStringKey("noStdoutLogging", "???", ???);
+
+    /**
+     * @throws SshException
+     */
+    public void connect();
+
+    /**
+     * @deprecated since 0.7.0; (since much earlier) this ignores the argument in favour of {@link #PROP_SSH_TRIES}
+     * 
+     * @param maxAttempts
+     * @throws SshException
+     */
+    public void connect(int maxAttempts);
+
+    public void disconnect();
+
+    public boolean isConnected();
+
+    /**
+     * @see super{@link #execScript(Map, List, Map)}
+     * @throws SshException If failed to connect
+     */
+    @Override
+    public int execScript(Map<String,?> props, List<String> commands, Map<String,?> env);
+
+    /**
+     * @see #execScript(Map, List, Map)
+     */
+    @Override
+    public int execScript(Map<String,?> props, List<String> commands);
+
+    /**
+     * @see super{@link #execCommands(Map, List, Map)}
+     * @throws SshException If failed to connect
+     */
+    @Override
+    public int execCommands(Map<String,?> properties, List<String> commands, Map<String,?> env);
+
+    /**
+     * @see #execCommands(Map, List, Map)
+     */
+    @Override
+    public int execCommands(Map<String,?> properties, List<String> commands);
+
+    /**
+     * Copies the file to the server at the given path.
+     * If path is null, empty, '.', '..', or ends with '/' then file name is used.
+     * <p>
+     * The file will not preserve the permission of last _access_ date.
+     * 
+     * Optional properties are:
+     * <ul>
+     *   <li>'permissions' (e.g. "0644") - see {@link #PROP_PERMISSIONS}
+     *   <li>'lastModificationDate' see {@link #PROP_LAST_MODIFICATION_DATE}; not supported by all SshTool implementations
+     *   <li>'lastAccessDate' see {@link #PROP_LAST_ACCESS_DATE}; not supported by all SshTool implementations
+     * </ul>
+     * 
+     * @return exit code (not supported by all SshTool implementations, usually throwing on error;
+     * sometimes possibly returning 0 even on error (?) )
+     */
+    public int copyToServer(Map<String,?> props, File localFile, String pathAndFileOnRemoteServer);
+
+    /**
+     * Closes the given input stream before returning.
+     * Consider using {@link KnownSizeInputStream} for efficiency when the size of the stream is known.
+     * 
+     * @see #copyToServer(Map, File, String)
+     */
+    public int copyToServer(Map<String,?> props, InputStream contents, String pathAndFileOnRemoteServer);
+
+    /**
+     * @see #copyToServer(Map, File, String)
+     */
+    public int copyToServer(Map<String,?> props, byte[] contents, String pathAndFileOnRemoteServer);
+
+    /**
+     * Copies the file from the server at the given path.
+     *
+     * @return exit code (not supported by all SshTool implementations, usually throwing on error;
+     * sometimes possibly returning 0 even on error (?) )
+     */
+    public int copyFromServer(Map<String,?> props, String pathAndFileOnRemoteServer, File local);
+
+    // TODO might be more efficicent than copyFrom by way of temp file
+//    /**
+//     * Reads from the file at the given path on the remote server.
+//     */
+//    public InputStream streamFromServer(Map<String,?> props, String pathAndFileOnRemoteServer);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliTool.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliTool.java
new file mode 100644
index 0000000..5fe0408
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/cli/SshCliTool.java
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh.cli;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.internal.ssh.SshAbstractTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
+import org.apache.brooklyn.core.util.internal.ssh.cli.SshCliTool;
+import org.apache.brooklyn.core.util.internal.ssh.process.ProcessTool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.text.StringEscapes.BashStringEscapes;
+import brooklyn.util.text.Strings;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+
+/**
+ * For ssh and scp commands, delegating to system calls.
+ */
+public class SshCliTool extends SshAbstractTool implements SshTool {
+
+    // TODO No retry support, with backoffLimitedRetryHandler
+    
+    private static final Logger LOG = LoggerFactory.getLogger(SshCliTool.class);
+
+    public static final ConfigKey<String> PROP_SSH_EXECUTABLE = ConfigKeys.newStringConfigKey("sshExecutable", "command to execute for ssh (defaults to \"ssh\", but could be overridden to sshg3 for Tectia for example)", "ssh");
+    public static final ConfigKey<String> PROP_SSH_FLAGS = ConfigKeys.newStringConfigKey("sshFlags", "flags to pass to ssh, as a space separated list", "");
+    public static final ConfigKey<String> PROP_SCP_EXECUTABLE = ConfigKeys.newStringConfigKey("scpExecutable", "command to execute for scp (defaults to \"scp\", but could be overridden to scpg3 for Tectia for example)", "scp");
+
+    public static Builder<SshCliTool,?> builder() {
+        return new ConcreteBuilder();
+    }
+    
+    private static class ConcreteBuilder extends Builder<SshCliTool, ConcreteBuilder> {
+    }
+    
+    public static class Builder<T extends SshCliTool, B extends Builder<T,B>> extends AbstractSshToolBuilder<T,B> {
+        private String sshExecutable;
+        private String sshFlags;
+        private String scpExecutable;
+
+        @Override
+        public B from(Map<String,?> props) {
+            super.from(props);
+            sshExecutable = getOptionalVal(props, PROP_SSH_EXECUTABLE);
+            sshFlags = getOptionalVal(props, PROP_SSH_FLAGS);
+            scpExecutable = getOptionalVal(props, PROP_SCP_EXECUTABLE);
+            return self();
+        }
+        public B sshExecutable(String val) {
+            this.sshExecutable = val; return self();
+        }
+        public B scpExecutable(String val) {
+            this.scpExecutable = val; return self();
+        }
+        @SuppressWarnings("unchecked")
+        public T build() {
+            return (T) new SshCliTool(this);
+        }
+    }
+
+    private final String sshExecutable;
+    private final String sshFlags;
+    private final String scpExecutable;
+
+    public SshCliTool(Map<String,?> map) {
+        this(builder().from(map));
+    }
+    
+    private SshCliTool(Builder<?,?> builder) {
+        super(builder);
+        sshExecutable = checkNotNull(builder.sshExecutable);
+        sshFlags = checkNotNull(builder.sshFlags);
+        scpExecutable = checkNotNull(builder.scpExecutable);
+        if (LOG.isTraceEnabled()) LOG.trace("Created SshCliTool {} ({})", this, System.identityHashCode(this));
+    }
+    
+    @Override
+    public void connect() {
+        // no-op
+    }
+
+    @Override
+    public void connect(int maxAttempts) {
+        // no-op
+    }
+
+    @Override
+    public void disconnect() {
+        if (LOG.isTraceEnabled()) LOG.trace("Disconnecting SshCliTool {} ({}) - no-op", this, System.identityHashCode(this));
+        // no-op
+    }
+
+    @Override
+    public boolean isConnected() {
+        // TODO Always pretends to be connected
+        return true;
+    }
+
+    @Override
+    public int copyToServer(java.util.Map<String,?> props, byte[] contents, String pathAndFileOnRemoteServer) {
+        return copyTempFileToServer(props, writeTempFile(contents), pathAndFileOnRemoteServer);
+    }
+    
+    @Override
+    public int copyToServer(java.util.Map<String,?> props, InputStream contents, String pathAndFileOnRemoteServer) {
+        return copyTempFileToServer(props, writeTempFile(contents), pathAndFileOnRemoteServer);
+    }
+    
+    @Override
+    public int copyToServer(Map<String,?> props, File f, String pathAndFileOnRemoteServer) {
+        if (hasVal(props, PROP_LAST_MODIFICATION_DATE)) {
+            LOG.warn("Unsupported ssh feature, setting lastModificationDate for {}:{}", this, pathAndFileOnRemoteServer);
+        }
+        if (hasVal(props, PROP_LAST_ACCESS_DATE)) {
+            LOG.warn("Unsupported ssh feature, setting lastAccessDate for {}:{}", this, pathAndFileOnRemoteServer);
+        }
+        String permissions = getOptionalVal(props, PROP_PERMISSIONS);
+        
+        int uid = getOptionalVal(props, PROP_OWNER_UID);
+        
+        int result = scpToServer(props, f, pathAndFileOnRemoteServer);
+        if (result == 0) {
+            result = chmodOnServer(props, permissions, pathAndFileOnRemoteServer);
+            if (result == 0) {
+                if (uid != -1) {
+                    result = chownOnServer(props, uid, pathAndFileOnRemoteServer);
+                    if (result != 0) {
+                        LOG.warn("Error setting file owner to {}, after copying file {} to {}:{}; exit code {}", new Object[] { uid, pathAndFileOnRemoteServer, this, f, result });
+                    }
+                }
+            } else {
+                LOG.warn("Error setting file permissions to {}, after copying file {} to {}:{}; exit code {}", new Object[] { permissions, pathAndFileOnRemoteServer, this, f, result });
+            }
+        } else {
+            LOG.warn("Error copying file {} to {}:{}; exit code {}", new Object[] {pathAndFileOnRemoteServer, this, f, result});
+        }
+        return result;
+    }
+
+    private int chownOnServer(Map<String,?> props, int uid, String remote) {
+        return sshExec(props, "chown "+uid+" "+remote);
+    }
+    
+    private int copyTempFileToServer(Map<String,?> props, File f, String pathAndFileOnRemoteServer) {
+        try {
+            return copyToServer(props, f, pathAndFileOnRemoteServer);
+        } finally {
+            f.delete();
+        }
+    }
+
+    @Override
+    public int copyFromServer(Map<String,?> props, String pathAndFileOnRemoteServer, File localFile) {
+        return scpFromServer(props, pathAndFileOnRemoteServer, localFile);
+    }
+
+    @Override
+    public int execScript(final Map<String,?> props, final List<String> commands, final Map<String,?> env) {
+        return new ToolAbstractExecScript(props) {
+            public int run() {
+                String scriptContents = toScript(props, commands, env);
+                if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} as script: {}", host, scriptContents);
+                copyTempFileToServer(ImmutableMap.of("permissions", "0700"), writeTempFile(scriptContents), scriptPath);
+
+                String cmd = Strings.join(buildRunScriptCommand(), separator);
+                return asInt(sshExec(props, cmd), -1);
+            }
+        }.run();
+    }
+
+    @Override
+    public int execCommands(Map<String,?> props, List<String> commands, Map<String,?> env) {
+        Map<String,Object> props2 = new MutableMap<String,Object>();
+        if (props!=null) props2.putAll(props);
+        props2.put(SshTool.PROP_NO_EXTRA_OUTPUT.getName(), true);
+        return execScript(props2, commands, env);
+    }
+    
+    private int scpToServer(Map<String,?> props, File local, String remote) {
+        String to = (Strings.isEmpty(getUsername()) ? "" : getUsername()+"@")+getHostAddress()+":"+remote;
+        return scpExec(props, local.getAbsolutePath(), to);
+    }
+
+    private int scpFromServer(Map<String,?> props, String remote, File local) {
+        String from = (Strings.isEmpty(getUsername()) ? "" : getUsername()+"@")+getHostAddress()+":"+remote;
+        return scpExec(props, from, local.getAbsolutePath());
+    }
+    
+    private int chmodOnServer(Map<String,?> props, String permissions, String remote) {
+        return sshExec(props, "chmod "+permissions+" "+remote);
+    }
+
+    private int scpExec(Map<String,?> props, String from, String to) {
+        File tempFile = null;
+        try {
+            List<String> cmd = Lists.newArrayList();
+            cmd.add(getOptionalVal(props, PROP_SCP_EXECUTABLE, scpExecutable));
+            if (privateKeyFile != null) {
+                cmd.add("-i");
+                cmd.add(privateKeyFile.getAbsolutePath());
+            } else if (privateKeyData != null) {
+                tempFile = writeTempFile(privateKeyData);
+                cmd.add("-i");
+                cmd.add(tempFile.getAbsolutePath());
+            }
+            if (!strictHostKeyChecking) {
+                cmd.add("-o");
+                cmd.add("StrictHostKeyChecking=no");
+            }
+            if (port != 22) {
+                cmd.add("-P");
+                cmd.add(""+port);
+            }
+            cmd.add(from);
+            cmd.add(to);
+            
+            if (LOG.isTraceEnabled()) LOG.trace("Executing with command: {}", cmd);
+            int result = execProcess(props, cmd);
+            
+            if (LOG.isTraceEnabled()) LOG.trace("Executed command: {}; exit code {}", cmd, result);
+            return result;
+
+        } finally {
+            if (tempFile != null) tempFile.delete();
+        }
+    }
+    
+    private int sshExec(Map<String,?> props, String command) {
+        File tempKeyFile = null;
+        try {
+            List<String> cmd = Lists.newArrayList();
+            cmd.add(getOptionalVal(props, PROP_SSH_EXECUTABLE, sshExecutable));
+            String propsFlags = getOptionalVal(props, PROP_SSH_FLAGS, sshFlags);
+            if (propsFlags!=null && propsFlags.trim().length()>0)
+                cmd.addAll(Arrays.asList(propsFlags.trim().split(" ")));
+            if (privateKeyFile != null) {
+                cmd.add("-i");
+                cmd.add(privateKeyFile.getAbsolutePath());
+            } else if (privateKeyData != null) {
+                tempKeyFile = writeTempFile(privateKeyData);
+                cmd.add("-i");
+                cmd.add(tempKeyFile.getAbsolutePath());
+            }
+            if (!strictHostKeyChecking) {
+                cmd.add("-o");
+                cmd.add("StrictHostKeyChecking=no");
+            }
+            if (port != 22) {
+                cmd.add("-P");
+                cmd.add(""+port);
+            }
+            if (allocatePTY) {
+                // have to be careful with double -tt as it can leave a shell session active
+                // when done from bash (ie  ssh -tt localhost < /tmp/myscript.sh);
+                // hover that doesn't seem to be a problem the way we use it from brooklyn
+                // (and note single -t doesn't work _programmatically_ since the input isn't a terminal)
+                cmd.add("-tt");
+            }
+            cmd.add((Strings.isEmpty(getUsername()) ? "" : getUsername()+"@")+getHostAddress());
+            
+            cmd.add("bash -c "+BashStringEscapes.wrapBash(command));
+            // previously we tried these approaches:
+            //cmd.add("$(<"+tempCmdFile.getAbsolutePath()+")");
+            // only pays attention to the first word; the "; echo Executing ..." get treated as arguments
+            // to the script in the first word, when invoked from java (when invoked from prompt the behaviour is as desired)
+            //cmd.add("\""+command+"\"");
+            // only works if command is a single word
+            //cmd.add(tempCmdFile.getAbsolutePath());
+            // above of course only works if the metafile is copied across
+            
+            if (LOG.isTraceEnabled()) LOG.trace("Executing ssh with command: {} (with {})", command, cmd);
+            int result = execProcess(props, cmd);
+            
+            if (LOG.isTraceEnabled()) LOG.trace("Executed command: {}; exit code {}", cmd, result);
+            return result;
+            
+        } finally {
+            if (tempKeyFile != null) tempKeyFile.delete();
+        }
+    }
+
+    private int execProcess(Map<String,?> props, List<String> cmdWords) {
+        OutputStream out = getOptionalVal(props, PROP_OUT_STREAM);
+        OutputStream err = getOptionalVal(props, PROP_ERR_STREAM);
+        return ProcessTool.execSingleProcess(cmdWords, null, (File)null, out, err, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessTool.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessTool.java
new file mode 100644
index 0000000..8f33143
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/process/ProcessTool.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh.process;
+
+import static brooklyn.entity.basic.ConfigKeys.newConfigKey;
+import static brooklyn.entity.basic.ConfigKeys.newStringConfigKey;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.core.util.internal.ssh.ShellAbstractTool;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.internal.ssh.SshException;
+import org.apache.brooklyn.core.util.internal.ssh.process.ProcessTool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.os.Os;
+import brooklyn.util.stream.StreamGobbler;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.io.ByteSource;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Files;
+
+/** Implementation of {@link ShellTool} which runs locally. */
+public class ProcessTool extends ShellAbstractTool implements ShellTool {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ProcessTool.class);
+
+    // applies to calls
+    
+    public static final ConfigKey<Boolean> PROP_LOGIN_SHELL = newConfigKey("loginShell", "Causes the commands to be invoked with bash arguments to forcea  login shell", Boolean.FALSE);
+
+    public static final ConfigKey<String> PROP_DIRECTORY = newStringConfigKey("directory", "the working directory, for executing commands", null);
+    
+    public ProcessTool() {
+        this(null);
+    }
+    
+    public ProcessTool(Map<String,?> flags) {
+        super(getOptionalVal(flags, PROP_LOCAL_TEMP_DIR));
+        if (flags!=null) {
+            MutableMap<String, Object> flags2 = MutableMap.copyOf(flags);
+            // TODO should remember other flags here?  (e.g. NO_EXTRA_OUTPUT, RUN_AS_ROOT, etc)
+            flags2.remove(PROP_LOCAL_TEMP_DIR.getName());
+            if (!flags2.isEmpty())
+                LOG.warn(""+this+" ignoring unsupported constructor flags: "+flags);
+        }
+    }
+
+    @Override
+    public int execScript(final Map<String,?> props, final List<String> commands, final Map<String,?> env) {
+        return new ToolAbstractExecScript(props) {
+            public int run() {
+                try {
+                    String directory = getOptionalVal(props, PROP_DIRECTORY);
+                    File directoryDir = (directory != null) ? new File(Os.tidyPath(directory)) : null;
+                    
+                    String scriptContents = toScript(props, commands, env);
+
+                    if (LOG.isTraceEnabled()) LOG.trace("Running shell process (process) as script:\n{}", scriptContents);
+                    File to = new File(scriptPath);
+                    Files.createParentDirs(to);
+                    ByteSource.wrap(scriptContents.getBytes()).copyTo(Files.asByteSink(to));
+
+                    List<String> cmds = buildRunScriptCommand();
+                    cmds.add(0, "chmod +x "+scriptPath);
+                    return asInt(execProcesses(cmds, null, directoryDir, out, err, separator, getOptionalVal(props, PROP_LOGIN_SHELL), this), -1);
+                } catch (IOException e) {
+                    throw Throwables.propagate(e);
+                }
+            }
+        }.run();
+    }
+
+    @Override
+    public int execCommands(Map<String,?> props, List<String> commands, Map<String,?> env) {
+        if (Boolean.FALSE.equals(props.get("blocks"))) {
+            throw new IllegalArgumentException("Cannot exec non-blocking: command="+commands);
+        }
+        OutputStream out = getOptionalVal(props, PROP_OUT_STREAM);
+        OutputStream err = getOptionalVal(props, PROP_ERR_STREAM);
+        String separator = getOptionalVal(props, PROP_SEPARATOR);
+        String directory = getOptionalVal(props, PROP_DIRECTORY);
+        File directoryDir = (directory != null) ? new File(Os.tidyPath(directory)) : null;
+
+        List<String> allcmds = toCommandSequence(commands, null);
+
+        String singlecmd = Joiner.on(separator).join(allcmds);
+        if (Boolean.TRUE.equals(getOptionalVal(props, PROP_RUN_AS_ROOT))) {
+            LOG.warn("Cannot run as root when executing as command; run as a script instead (will run as normal user): "+singlecmd);
+        }
+        if (LOG.isTraceEnabled()) LOG.trace("Running shell command (process): {}", singlecmd);
+        
+        return asInt(execProcesses(allcmds, env, directoryDir, out, err, separator, getOptionalVal(props, PROP_LOGIN_SHELL), this), -1);
+    }
+
+    /**
+     * as {@link #execProcesses(List, Map, OutputStream, OutputStream, String, boolean, Object)} but not using a login shell
+     * @deprecated since 0.7; use {@link #execProcesses(List, Map, File, OutputStream, OutputStream, String, boolean, Object)}
+     */
+    @Deprecated
+    public static int execProcesses(List<String> cmds, Map<String,?> env, OutputStream out, OutputStream err, String separator, Object contextForLogging) {
+        return execProcesses(cmds, env, (File)null, out, err, separator, false, contextForLogging);
+    }
+
+    /**
+     * @deprecated since 0.7; use {@link #execProcesses(List, Map, File, OutputStream, OutputStream, String, boolean, Object)}
+     */
+    @Deprecated
+    public static int execProcesses(List<String> cmds, Map<String,?> env, OutputStream out, OutputStream err, String separator, boolean asLoginShell, Object contextForLogging) {
+        return execProcesses(cmds, env, (File)null, out, err, separator, asLoginShell, contextForLogging);
+    }
+    
+    /** executes a set of commands by sending them as a single process to `bash -c` 
+     * (single command argument of all the commands, joined with separator)
+     * <p>
+     * consequence of this is that you should not normally need to escape things oddly in your commands, 
+     * type them just as you would into a bash shell (if you find exceptions please note them here!)
+     */
+    public static int execProcesses(List<String> cmds, Map<String,?> env, File directory, OutputStream out, OutputStream err, String separator, boolean asLoginShell, Object contextForLogging) {
+        MutableList<String> commands = new MutableList<String>().append("bash");
+        if (asLoginShell) commands.append("-l");
+        commands.append("-c", Strings.join(cmds, Preconditions.checkNotNull(separator, "separator")));
+        return execSingleProcess(commands, env, directory, out, err, contextForLogging);
+    }
+    
+    /**
+     * @deprecated since 0.7; use {@link #execSingleProcess(List, Map, File, OutputStream, OutputStream, Object)}
+     */
+    @Deprecated
+    public static int execSingleProcess(List<String> cmdWords, Map<String,?> env, OutputStream out, OutputStream err, Object contextForLogging) {
+        return execSingleProcess(cmdWords, env, (File)null, out, err, contextForLogging);
+    }
+    
+    /** executes a single process made up of the given command words (*not* bash escaped);
+     * should be portable across OS's */
+    public static int execSingleProcess(List<String> cmdWords, Map<String,?> env, File directory, OutputStream out, OutputStream err, Object contextForLogging) {
+        StreamGobbler errgobbler = null;
+        StreamGobbler outgobbler = null;
+        
+        ProcessBuilder pb = new ProcessBuilder(cmdWords);
+        if (env!=null) {
+            for (Map.Entry<String,?> kv: env.entrySet()) pb.environment().put(kv.getKey(), String.valueOf(kv.getValue())); 
+        }
+        if (directory != null) {
+            pb.directory(directory);
+        }
+        
+        try {
+            Process p = pb.start();
+            
+            if (out != null) {
+                InputStream outstream = p.getInputStream();
+                outgobbler = new StreamGobbler(outstream, out, (Logger) null);
+                outgobbler.start();
+            }
+            if (err != null) {
+                InputStream errstream = p.getErrorStream();
+                errgobbler = new StreamGobbler(errstream, err, (Logger) null);
+                errgobbler.start();
+            }
+            
+            int result = p.waitFor();
+            
+            if (outgobbler != null) outgobbler.blockUntilFinished();
+            if (errgobbler != null) errgobbler.blockUntilFinished();
+            
+            if (result==255)
+                // this is not definitive, but tests (and code?) expects throw exception if can't connect;
+                // only return exit code when it is exit code from underlying process;
+                // we have no way to distinguish 255 from ssh failure from 255 from the command run through ssh ...
+                // but probably 255 is from CLI ssh
+                throw new SshException("exit code 255 from CLI ssh; probably failed to connect");
+            
+            return result;
+        } catch (InterruptedException e) {
+            throw Exceptions.propagate(e);
+        } catch (IOException e) {
+            throw Exceptions.propagate(e);
+        } finally {
+            closeWhispering(outgobbler, contextForLogging, "execProcess");
+            closeWhispering(errgobbler, contextForLogging, "execProcess");
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjClientConnection.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjClientConnection.java b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjClientConnection.java
new file mode 100644
index 0000000..c042415
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/internal/ssh/sshj/SshjClientConnection.java
@@ -0,0 +1,282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.internal.ssh.sshj;
+
+import static com.google.common.base.Objects.equal;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.File;
+import java.io.IOException;
+
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
+import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
+import net.schmizz.sshj.userauth.password.PasswordUtils;
+
+import org.apache.brooklyn.core.util.internal.ssh.SshAbstractTool.SshAction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.GroovyJavaMethods;
+
+import com.google.common.base.Objects;
+import com.google.common.net.HostAndPort;
+
+/** based on code from jclouds */
+public class SshjClientConnection implements SshAction<SSHClient> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SshjClientConnection.class);
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static class Builder {
+
+        protected HostAndPort hostAndPort;
+        protected String username;
+        protected String password;
+        protected String privateKeyPassphrase;
+        protected String privateKeyData;
+        protected File privateKeyFile;
+        protected long connectTimeout;
+        protected long sessionTimeout;
+        protected boolean strictHostKeyChecking;
+
+        public Builder hostAndPort(HostAndPort hostAndPort) {
+            this.hostAndPort = hostAndPort;
+            return this;
+        }
+
+        public Builder username(String username) {
+            this.username = username;
+            return this;
+        }
+
+        public Builder password(String val) {
+            this.password = val;
+            return this;
+        }
+
+        /** @deprecated use privateKeyData */
+        public Builder privateKey(String val) {
+            this.privateKeyData = val;
+            return this;
+        }
+
+        public Builder privateKeyPassphrase(String val) {
+            this.privateKeyPassphrase = val;
+            return this;
+        }
+        
+        public Builder privateKeyData(String val) {
+            this.privateKeyData = val;
+            return this;
+        }
+        
+        public Builder privateKeyFile(File val) {
+            this.privateKeyFile = val;
+            return this;
+        }
+        
+        public Builder strictHostKeyChecking(boolean val) {
+            this.strictHostKeyChecking = val;
+            return this;
+        }
+
+        public Builder connectTimeout(long connectTimeout) {
+            this.connectTimeout = connectTimeout;
+            return this;
+        }
+
+        public Builder sessionTimeout(long sessionTimeout) {
+            this.sessionTimeout = sessionTimeout;
+            return this;
+        }
+
+        public SshjClientConnection build() {
+            return new SshjClientConnection(this);
+        }
+
+        protected static Builder fromSSHClientConnection(SshjClientConnection in) {
+            return new Builder().hostAndPort(in.getHostAndPort()).connectTimeout(in.getConnectTimeout()).sessionTimeout(
+                    in.getSessionTimeout()).username(in.username).password(in.password).privateKey(in.privateKeyData).privateKeyFile(in.privateKeyFile);
+        }
+    }
+
+    private final HostAndPort hostAndPort;
+    private final String username;
+    private final String password;
+    private final String privateKeyPassphrase;
+    private final String privateKeyData;
+    private final File privateKeyFile;
+    private final boolean strictHostKeyChecking;
+    private final int connectTimeout;
+    private final int sessionTimeout;
+    
+    SSHClient ssh;
+
+    private SshjClientConnection(Builder builder) {
+        this.hostAndPort = checkNotNull(builder.hostAndPort);
+        this.username = builder.username;
+        this.password = builder.password;
+        this.privateKeyPassphrase = builder.privateKeyPassphrase;
+        this.privateKeyData = builder.privateKeyData;
+        this.privateKeyFile = builder.privateKeyFile;
+        this.strictHostKeyChecking = builder.strictHostKeyChecking;
+        this.connectTimeout = checkInt("connectTimeout", builder.connectTimeout, Integer.MAX_VALUE);
+        this.sessionTimeout = checkInt("sessionTimeout", builder.sessionTimeout, Integer.MAX_VALUE);
+    }
+
+    static Integer checkInt(String context, long value, Integer ifTooLarge) {
+        if (value > Integer.MAX_VALUE) {
+            LOG.warn("Value '"+value+"' for "+context+" too large in SshjClientConnection; using "+value);
+            return ifTooLarge;
+        }
+        return (int)value;
+    }
+
+    public boolean isConnected() {
+        return ssh != null && ssh.isConnected();
+    }
+
+    public boolean isAuthenticated() {
+        return ssh != null && ssh.isAuthenticated();
+    }
+
+    @Override
+    public void clear() {
+        if (ssh != null && ssh.isConnected()) {
+            try {
+                if (LOG.isTraceEnabled()) LOG.trace("Disconnecting SshjClientConnection {} ({})", this, System.identityHashCode(this));
+                ssh.disconnect();
+            } catch (IOException e) {
+                if (LOG.isDebugEnabled()) LOG.debug("<< exception disconnecting from {}: {}", e, e.getMessage());
+            }
+        }
+        ssh = null;
+    }
+
+    @Override
+    public SSHClient create() throws Exception {
+        if (LOG.isTraceEnabled()) LOG.trace("Connecting SshjClientConnection {} ({})", this, System.identityHashCode(this));
+        ssh = new net.schmizz.sshj.SSHClient();
+        if (!strictHostKeyChecking) {
+            ssh.addHostKeyVerifier(new PromiscuousVerifier());
+        }
+        if (connectTimeout != 0) {
+            ssh.setConnectTimeout(connectTimeout);
+        }
+        if (sessionTimeout != 0) {
+            ssh.setTimeout(sessionTimeout);
+        }
+        ssh.connect(hostAndPort.getHostText(), hostAndPort.getPortOrDefault(22));
+        
+        if (password != null) {
+            ssh.authPassword(username, password);
+        } else if (privateKeyData != null) {
+            OpenSSHKeyFile key = new OpenSSHKeyFile();
+            key.init(privateKeyData, null, 
+                    GroovyJavaMethods.truth(privateKeyPassphrase) ? 
+                            PasswordUtils.createOneOff(privateKeyPassphrase.toCharArray())
+                            : null);
+            ssh.authPublickey(username, key);
+        } else if (privateKeyFile != null) {
+            OpenSSHKeyFile key = new OpenSSHKeyFile();
+            key.init(privateKeyFile, 
+                    GroovyJavaMethods.truth(privateKeyPassphrase) ? 
+                            PasswordUtils.createOneOff(privateKeyPassphrase.toCharArray())
+                            : null);
+            ssh.authPublickey(username, key);
+        } else {
+            // Accept defaults (in ~/.ssh)
+            ssh.authPublickey(username);
+        }
+        
+        return ssh;
+    }
+
+    /**
+     * @return host and port, where port if not present defaults to {@code 22}
+     */
+    public HostAndPort getHostAndPort() {
+        return hostAndPort;
+    }
+
+    /**
+     * @return username used in this ssh
+     */
+    public String getUsername() {
+        return username;
+    }
+
+    /**
+     * 
+     * @return how long to wait for the initial connection to be made
+     */
+    public int getConnectTimeout() {
+        return connectTimeout;
+    }
+
+    /**
+     * 
+     * @return how long to keep the ssh open, or {@code 0} for indefinitely
+     */
+    public int getSessionTimeout() {
+        return sessionTimeout;
+    }
+
+    /**
+     * 
+     * @return the current ssh or {@code null} if not connected
+     */
+    public SSHClient getSSHClient() {
+        return ssh;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        SshjClientConnection that = SshjClientConnection.class.cast(o);
+        return equal(this.hostAndPort, that.hostAndPort) && equal(this.username, that.username) 
+                && equal(this.password, that.password) && equal(this.privateKeyData, that.privateKeyData)
+                && equal(this.privateKeyFile, that.privateKeyFile) && equal(this.ssh, that.ssh);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(hostAndPort, username, password, privateKeyData, ssh);
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper("")
+                .add("hostAndPort", hostAndPort)
+                .add("user", username)
+                .add("ssh", ssh != null ? ssh.hashCode() : null)
+                .add("password", (password != null ? "xxxxxx" : null))
+                .add("privateKeyFile", privateKeyFile)
+                .add("privateKey", (privateKeyData != null ? "xxxxxx" : null))
+                .add("connectTimeout", connectTimeout)
+                .add("sessionTimeout", sessionTimeout).toString();
+    }
+}



[32/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ValueResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ValueResolver.java b/core/src/main/java/brooklyn/util/task/ValueResolver.java
deleted file mode 100644
index 37f4269..0000000
--- a/core/src/main/java/brooklyn/util/task/ValueResolver.java
+++ /dev/null
@@ -1,426 +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 java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Future;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskAdaptable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.javalang.JavaClassNames;
-import brooklyn.util.repeat.Repeater;
-import brooklyn.util.time.CountdownTimer;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Durations;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.reflect.TypeToken;
-
-/** 
- * Resolves a given object, as follows:
- * <li> If it is a {@link Tasks} or a {@link DeferredSupplier} then get its contents
- * <li> If it's a map and {@link #deep(boolean)} is requested, it applies resolution to contents
- * <li> It applies coercion
- * <p>
- * Fluent-style API exposes a number of other options.
- */
-public class ValueResolver<T> implements DeferredSupplier<T> {
-
-    /** 
-     * Period to wait if we're expected to return real quick 
-     * but we want fast things to have time to finish.
-     * <p>
-     * Timings are always somewhat arbitrary but this at least
-     * allows some intention to be captured in code rather than arbitrary values. */
-    public static Duration REAL_QUICK_WAIT = Duration.millis(50);
-    /** 
-     * Period to wait if we're expected to return quickly 
-     * but we want to be a bit more generous for things to finish,
-     * without letting a caller get annoyed. 
-     * <p>
-     * See {@link #REAL_QUICK_WAIT}. */
-    public static Duration PRETTY_QUICK_WAIT = Duration.millis(200);
-    
-    /** Period to wait when we have to poll but want to give the illusion of no wait.
-     * See {@link Repeater#DEFAULT_REAL_QUICK_PERIOD} */ 
-    public static Duration REAL_QUICK_PERIOD = Repeater.DEFAULT_REAL_QUICK_PERIOD;
-    
-    private static final Logger log = LoggerFactory.getLogger(ValueResolver.class);
-    
-    final Object value;
-    final Class<T> type;
-    ExecutionContext exec;
-    String description;
-    boolean forceDeep;
-    /** null means do it if you can; true means always, false means never */
-    Boolean embedResolutionInTask;
-    /** timeout on execution, if possible, or if embedResolutionInTask is true */
-    Duration timeout;
-    boolean isTransientTask = true;
-    
-    T defaultValue = null;
-    boolean returnDefaultOnGet = false;
-    boolean swallowExceptions = false;
-    
-    // internal fields
-    final Object parentOriginalValue;
-    final CountdownTimer parentTimer;
-    AtomicBoolean started = new AtomicBoolean(false);
-    boolean expired;
-    
-    ValueResolver(Object v, Class<T> type) {
-        this.value = v;
-        this.type = type;
-        checkTypeNotNull();
-        parentOriginalValue = null;
-        parentTimer = null;
-    }
-    
-    ValueResolver(Object v, Class<T> type, ValueResolver<?> parent) {
-        this.value = v;
-        this.type = type;
-        checkTypeNotNull();
-        
-        exec = parent.exec;
-        description = parent.description;
-        forceDeep = parent.forceDeep;
-        embedResolutionInTask = parent.embedResolutionInTask;
-
-        parentOriginalValue = parent.getOriginalValue();
-
-        timeout = parent.timeout;
-        parentTimer = parent.parentTimer;
-        if (parentTimer!=null && parentTimer.isExpired())
-            expired = true;
-        
-        // default value and swallow exceptions do not need to be nested
-    }
-
-    public static class ResolverBuilderPretype {
-        final Object v;
-        public ResolverBuilderPretype(Object v) {
-            this.v = v;
-        }
-        public <T> ValueResolver<T> as(Class<T> type) {
-            return new ValueResolver<T>(v, type);
-        }
-    }
-
-    /** returns a copy of this resolver which can be queried, even if the original (single-use instance) has already been copied */
-    public ValueResolver<T> clone() {
-        ValueResolver<T> result = new ValueResolver<T>(value, type)
-            .context(exec).description(description)
-            .embedResolutionInTask(embedResolutionInTask)
-            .deep(forceDeep)
-            .timeout(timeout);
-        if (returnDefaultOnGet) result.defaultValue(defaultValue);
-        if (swallowExceptions) result.swallowExceptions();
-        return result;
-    }
-    
-    /** execution context to use when resolving; required if resolving unsubmitted tasks or running with a time limit */
-    public ValueResolver<T> context(ExecutionContext exec) {
-        this.exec = exec;
-        return this;
-    }
-    /** as {@link #context(ExecutionContext)} for use from an entity */
-    public ValueResolver<T> context(Entity entity) {
-        return context(entity!=null ? ((EntityInternal)entity).getExecutionContext() : null);
-    }
-    
-    /** sets a message which will be displayed in status reports while it waits (e.g. the name of the config key being looked up) */
-    public ValueResolver<T> description(String description) {
-        this.description = description;
-        return this;
-    }
-    
-    /** sets a default value which will be returned on a call to {@link #get()} if the task does not complete
-     * or completes with an error
-     * <p>
-     * note that {@link #getMaybe()} returns an absent object even in the presence of
-     * a default, so that any error can still be accessed */
-    public ValueResolver<T> defaultValue(T defaultValue) {
-        this.defaultValue = defaultValue;
-        this.returnDefaultOnGet = true;
-        return this;
-    }
-
-    /** indicates that no default value should be returned on a call to {@link #get()}, and instead it should throw
-     * (this is the default; this method is provided to undo a call to {@link #defaultValue(Object)}) */
-    public ValueResolver<T> noDefaultValue() {
-        this.returnDefaultOnGet = false;
-        this.defaultValue = null;
-        return this;
-    }
-    
-    /** indicates that exceptions in resolution should not be thrown on a call to {@link #getMaybe()}, 
-     * but rather used as part of the {@link Maybe#get()} if it's absent, 
-     * and swallowed altogether on a call to {@link #get()} in the presence of a {@link #defaultValue(Object)} */
-    public ValueResolver<T> swallowExceptions() {
-        this.swallowExceptions = true;
-        return this;
-    }
-    
-    /** whether the task should be marked as transient; defaults true */
-    public ValueResolver<T> transientTask(boolean isTransientTask) {
-        this.isTransientTask = isTransientTask;
-        return this;
-    }
-    
-    public Maybe<T> getDefault() {
-        if (returnDefaultOnGet) return Maybe.of(defaultValue);
-        else return Maybe.absent("No default value set");
-    }
-    
-    /** causes nested structures (maps, lists) to be descended and nested unresolved values resolved */
-    public ValueResolver<T> deep(boolean forceDeep) {
-        this.forceDeep = forceDeep;
-        return this;
-    }
-
-    /** if true, forces execution of a deferred supplier to be run in a task;
-     * if false, it prevents it (meaning time limits may not be applied);
-     * if null, the default, it runs in a task if a time limit is applied.
-     * <p>
-     * running inside a task is required for some {@link DeferredSupplier}
-     * instances which look up a task {@link ExecutionContext}. */
-    public ValueResolver<T> embedResolutionInTask(Boolean embedResolutionInTask) {
-        this.embedResolutionInTask = embedResolutionInTask;
-        return this;
-    }
-    
-    /** sets a time limit on executions
-     * <p>
-     * used for {@link Task} and {@link DeferredSupplier} instances.
-     * may require an execution context at runtime. */
-    public ValueResolver<T> timeout(Duration timeout) {
-        this.timeout = timeout;
-        return this;
-    }
-    
-    protected void checkTypeNotNull() {
-        if (type==null) 
-            throw new NullPointerException("type must be set to resolve, for '"+value+"'"+(description!=null ? ", "+description : ""));
-    }
-
-    public T get() {
-        Maybe<T> m = getMaybe();
-        if (m.isPresent()) return m.get();
-        if (returnDefaultOnGet) return defaultValue;
-        return m.get();
-    }
-    
-    public Maybe<T> getMaybe() {
-        Maybe<T> result = getMaybeInternal();
-        if (log.isTraceEnabled()) {
-            log.trace(this+" evaluated as "+result);
-        }
-        return result;
-    }
-    
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    protected Maybe<T> getMaybeInternal() {
-        if (started.getAndSet(true))
-            throw new IllegalStateException("ValueResolver can only be used once");
-        
-        if (expired) return Maybe.absent("Nested resolution of "+getOriginalValue()+" did not complete within "+timeout);
-        
-        ExecutionContext exec = this.exec;
-        if (exec==null) {
-            // if execution context not specified, take it from the current task if present
-            exec = BasicExecutionContext.getCurrentExecutionContext();
-        }
-        
-        CountdownTimer timerU = parentTimer;
-        if (timerU==null && timeout!=null)
-            timerU = timeout.countdownTimer();
-        final CountdownTimer timer = timerU;
-        if (timer!=null && !timer.isRunning())
-            timer.start();
-        
-        checkTypeNotNull();
-        Object v = this.value;
-        
-        //if the expected type is a closure or map and that's what we have, we're done (or if it's null);
-        //but not allowed to return a future or DeferredSupplier as the resolved value
-        if (v==null || (!forceDeep && type.isInstance(v) && !Future.class.isInstance(v) && !DeferredSupplier.class.isInstance(v)))
-            return Maybe.of((T) v);
-        
-        try {
-            //if it's a task or a future, we wait for the task to complete
-            if (v instanceof TaskAdaptable<?>) {
-                //if it's a task, we make sure it is submitted
-                if (!((TaskAdaptable<?>) v).asTask().isSubmitted() ) {
-                    if (exec==null)
-                        return Maybe.absent("Value for unsubmitted task '"+getDescription()+"' requested but no execution context available");
-                    exec.submit(((TaskAdaptable<?>) v).asTask());
-                }
-            }
-
-            if (v instanceof Future) {
-                final Future<?> vfuture = (Future<?>) v;
-
-                //including tasks, above
-                if (!vfuture.isDone()) {
-                    Callable<Maybe> callable = new Callable<Maybe>() {
-                        public Maybe call() throws Exception {
-                            return Durations.get(vfuture, timer);
-                        } };
-
-                    String description = getDescription();
-                    Maybe vm = Tasks.withBlockingDetails("Waiting for "+description, callable);
-                    if (vm.isAbsent()) return vm;
-                    v = vm.get();
-
-                } else {
-                    v = vfuture.get();
-                    
-                }
-
-            } else if (v instanceof DeferredSupplier<?>) {
-                final Object vf = v;
-
-                if ((!Boolean.FALSE.equals(embedResolutionInTask) && (exec!=null || timeout!=null)) || Boolean.TRUE.equals(embedResolutionInTask)) {
-                    if (exec==null)
-                        return Maybe.absent("Embedding in task needed for '"+getDescription()+"' but no execution context available");
-                        
-                    Callable<Object> callable = new Callable<Object>() {
-                        public Object call() throws Exception {
-                            try {
-                                Tasks.setBlockingDetails("Retrieving "+vf);
-                                return ((DeferredSupplier<?>) vf).get();
-                            } finally {
-                                Tasks.resetBlockingDetails();
-                            }
-                        } };
-                    String description = getDescription();
-                    TaskBuilder<Object> vb = Tasks.<Object>builder().body(callable).name("Resolving dependent value").description(description);
-                    if (isTransientTask) vb.tag(BrooklynTaskTags.TRANSIENT_TASK_TAG);
-                    Task<Object> vt = exec.submit(vb.build());
-                    // TODO to handle immediate resolution, it would be nice to be able to submit 
-                    // so it executes in the current thread,
-                    // or put a marker in the target thread or task while it is running that the task 
-                    // should never wait on anything other than another value being resolved 
-                    // (though either could recurse infinitely) 
-                    Maybe<Object> vm = Durations.get(vt, timer);
-                    vt.cancel(true);
-                    if (vm.isAbsent()) return (Maybe<T>)vm;
-                    v = vm.get();
-                    
-                } else {
-                    try {
-                        Tasks.setBlockingDetails("Retrieving (non-task) "+vf);
-                        v = ((DeferredSupplier<?>) vf).get();
-                    } finally {
-                        Tasks.resetBlockingDetails();
-                    }
-                }
-
-            } else if (v instanceof Map) {
-                //and if a map or list we look inside
-                Map result = Maps.newLinkedHashMap();
-                for (Map.Entry<?,?> entry : ((Map<?,?>)v).entrySet()) {
-                    Maybe<?> kk = new ValueResolver(entry.getKey(), type, this)
-                        .description( (description!=null ? description+", " : "") + "map key "+entry.getKey() )
-                        .getMaybe();
-                    if (kk.isAbsent()) return (Maybe<T>)kk;
-                    Maybe<?> vv = new ValueResolver(entry.getValue(), type, this)
-                        .description( (description!=null ? description+", " : "") + "map value for key "+kk.get() )
-                        .getMaybe();
-                    if (vv.isAbsent()) return (Maybe<T>)vv;
-                    result.put(kk.get(), vv.get());
-                }
-                return Maybe.of((T) result);
-
-            } else if (v instanceof Set) {
-                Set result = Sets.newLinkedHashSet();
-                int count = 0;
-                for (Object it : (Set)v) {
-                    Maybe<?> vv = new ValueResolver(it, type, this)
-                        .description( (description!=null ? description+", " : "") + "entry "+count )
-                        .getMaybe();
-                    if (vv.isAbsent()) return (Maybe<T>)vv;
-                    result.add(vv.get());
-                    count++;
-                }
-                return Maybe.of((T) result);
-
-            } else if (v instanceof Iterable) {
-                List result = Lists.newArrayList();
-                int count = 0;
-                for (Object it : (Iterable)v) {
-                    Maybe<?> vv = new ValueResolver(it, type, this)
-                        .description( (description!=null ? description+", " : "") + "entry "+count )
-                        .getMaybe();
-                    if (vv.isAbsent()) return (Maybe<T>)vv;
-                    result.add(vv.get());
-                    count++;
-                }
-                return Maybe.of((T) result);
-
-            } else {
-                return TypeCoercions.tryCoerce(v, TypeToken.of(type));
-            }
-
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            
-            IllegalArgumentException problem = new IllegalArgumentException("Error resolving "+(description!=null ? description+", " : "")+v+", in "+exec+": "+e, e);
-            if (swallowExceptions) {
-                if (log.isDebugEnabled())
-                    log.debug("Resolution of "+this+" failed, swallowing and returning: "+e);
-                return Maybe.absent(problem);
-            }
-            if (log.isDebugEnabled())
-                log.debug("Resolution of "+this+" failed, throwing: "+e);
-            throw problem;
-        }
-        
-        return new ValueResolver(v, type, this).getMaybe();
-    }
-
-    protected String getDescription() {
-        return description!=null ? description : ""+value;
-    }
-    protected Object getOriginalValue() {
-        if (parentOriginalValue!=null) return parentOriginalValue;
-        return value;
-    }
-    
-    @Override
-    public String toString() {
-        return JavaClassNames.cleanSimpleClassName(this)+"["+JavaClassNames.cleanSimpleClassName(type)+" "+value+"]";
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ssh/SshFetchTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ssh/SshFetchTaskFactory.java b/core/src/main/java/brooklyn/util/task/ssh/SshFetchTaskFactory.java
deleted file mode 100644
index bd9e96e..0000000
--- a/core/src/main/java/brooklyn/util/task/ssh/SshFetchTaskFactory.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task.ssh;
-
-import org.apache.brooklyn.api.management.TaskFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.config.ConfigBag;
-
-// cannot be (cleanly) instantiated due to nested generic self-referential type; however trivial subclasses do allow it 
-public class SshFetchTaskFactory implements TaskFactory<SshFetchTaskWrapper> {
-    
-    private static final Logger log = LoggerFactory.getLogger(SshFetchTaskFactory.class);
-    
-    private boolean dirty = false;
-    
-    protected SshMachineLocation machine;
-    protected String remoteFile;
-    protected final ConfigBag config = ConfigBag.newInstance();
-
-    /** constructor where machine will be added later */
-    public SshFetchTaskFactory(String remoteFile) {
-        remoteFile(remoteFile);
-    }
-
-    /** convenience constructor to supply machine immediately */
-    public SshFetchTaskFactory(SshMachineLocation machine, String remoteFile) {
-        machine(machine);
-        remoteFile(remoteFile);
-    }
-
-    protected SshFetchTaskFactory self() { return this; }
-
-    protected void markDirty() {
-        dirty = true;
-    }
-    
-    public SshFetchTaskFactory machine(SshMachineLocation machine) {
-        markDirty();
-        this.machine = machine;
-        return self();
-    }
-        
-    public SshMachineLocation getMachine() {
-        return machine;
-    }
-    
-    public SshFetchTaskFactory remoteFile(String remoteFile) {
-        this.remoteFile = remoteFile;
-        return self();
-    }
-
-    public ConfigBag getConfig() {
-        return config;
-    }
-    
-    @Override
-    public SshFetchTaskWrapper newTask() {
-        dirty = false;
-        return new SshFetchTaskWrapper(this);
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        // help let people know of API usage error
-        if (dirty)
-            log.warn("Task "+this+" was modified but modification was never used");
-        super.finalize();
-    }
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ssh/SshFetchTaskWrapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ssh/SshFetchTaskWrapper.java b/core/src/main/java/brooklyn/util/task/ssh/SshFetchTaskWrapper.java
deleted file mode 100644
index 9553b4f..0000000
--- a/core/src/main/java/brooklyn/util/task/ssh/SshFetchTaskWrapper.java
+++ /dev/null
@@ -1,135 +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.ssh;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.Callable;
-
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskWrapper;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.FilenameUtils;
-
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.os.Os;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Preconditions;
-
-/**
- * As {@link ProcessTaskWrapper}, but putting a file on the remote machine
- * 
- * @since 0.6.0
- */
-@Beta
-public class SshFetchTaskWrapper implements TaskWrapper<String> {
-
-    private final Task<String> task;
-
-    private final String remoteFile;
-    private final SshMachineLocation machine;
-    private File backingFile;
-    private final ConfigBag config;
-    
-    
-    // package private as only AbstractSshTaskFactory should invoke
-    SshFetchTaskWrapper(SshFetchTaskFactory factory) {
-        this.remoteFile = Preconditions.checkNotNull(factory.remoteFile, "remoteFile");
-        this.machine = Preconditions.checkNotNull(factory.machine, "machine");
-        TaskBuilder<String> tb = TaskBuilder.<String>builder().dynamic(false).name("ssh fetch "+factory.remoteFile);
-        task = tb.body(new SshFetchJob()).build();
-        config = factory.getConfig();
-    }
-    
-    @Override
-    public Task<String> asTask() {
-        return getTask();
-    }
-    
-    @Override
-    public Task<String> getTask() {
-        return task;
-    }
-    
-    public String getRemoteFile() {
-        return remoteFile;
-    }
-    
-    public SshMachineLocation getMachine() {
-        return machine;
-    }
-        
-    private class SshFetchJob implements Callable<String> {
-        @Override
-        public String call() throws Exception {
-            int result = -1;
-            try {
-                Preconditions.checkNotNull(getMachine(), "machine");
-                backingFile = Os.newTempFile("brooklyn-ssh-fetch-", FilenameUtils.getName(remoteFile));
-                backingFile.deleteOnExit();
-                
-                result = getMachine().copyFrom(config.getAllConfig(), remoteFile, backingFile.getPath());
-            } catch (Exception e) {
-                throw new IllegalStateException("SSH fetch "+getRemoteFile()+" from "+getMachine()+" returned threw exception, in "+Tasks.current()+": "+e, e);
-            }
-            if (result!=0) {
-                throw new IllegalStateException("SSH fetch "+getRemoteFile()+" from "+getMachine()+" returned non-zero exit code  "+result+", in "+Tasks.current());
-            }
-            return FileUtils.readFileToString(backingFile);
-        }
-    }
-    
-    @Override
-    public String toString() {
-        return super.toString()+"["+task+"]";
-    }
-
-    /** blocks, returns the fetched file as a string, throwing if there was an exception */
-    public String get() {
-        return getTask().getUnchecked();
-    }
-    
-    /** blocks, returns the fetched file as bytes, throwing if there was an exception */
-    public byte[] getBytes() {
-        block();
-        try {
-            return FileUtils.readFileToByteArray(backingFile);
-        } catch (IOException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-    
-    /** blocks until the task completes; does not throw */
-    public SshFetchTaskWrapper block() {
-        getTask().blockUntilEnded();
-        return this;
-    }
- 
-    /** true iff the ssh job has completed (with or without failure) */
-    public boolean isDone() {
-        return getTask().isDone();
-    }   
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskFactory.java b/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskFactory.java
deleted file mode 100644
index e2c5502..0000000
--- a/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskFactory.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task.ssh;
-
-import java.io.InputStream;
-import java.io.Reader;
-
-import org.apache.brooklyn.api.management.TaskFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.stream.KnownSizeInputStream;
-import brooklyn.util.stream.ReaderInputStream;
-
-import com.google.common.base.Suppliers;
-
-// cannot be (cleanly) instantiated due to nested generic self-referential type; however trivial subclasses do allow it 
-public class SshPutTaskFactory extends SshPutTaskStub implements TaskFactory<SshPutTaskWrapper> {
-    
-    private static final Logger log = LoggerFactory.getLogger(SshPutTaskFactory.class);
-    
-    private boolean dirty = false;
-
-    /** constructor where machine will be added later */
-    public SshPutTaskFactory(String remoteFile) {
-        remoteFile(remoteFile);
-    }
-
-    /** convenience constructor to supply machine immediately */
-    public SshPutTaskFactory(SshMachineLocation machine, String remoteFile) {
-        machine(machine);
-        remoteFile(remoteFile);
-    }
-
-    protected SshPutTaskFactory self() { return this; }
-
-    protected void markDirty() {
-        dirty = true;
-    }
-    
-    public SshPutTaskFactory machine(SshMachineLocation machine) {
-        markDirty();
-        this.machine = machine;
-        return self();
-    }
-        
-    public SshPutTaskFactory remoteFile(String remoteFile) {
-        this.remoteFile = remoteFile;
-        return self();
-    }
-
-    public SshPutTaskFactory summary(String summary) {
-        markDirty();
-        this.summary = summary;
-        return self();
-    }
-
-    public SshPutTaskFactory contents(String contents) {
-        markDirty();
-        this.contents = Suppliers.ofInstance(KnownSizeInputStream.of(contents));  
-        return self();
-    }
-
-    public SshPutTaskFactory contents(byte[] contents) {
-        markDirty();
-        this.contents = Suppliers.ofInstance(KnownSizeInputStream.of(contents));  
-        return self();
-    }
-
-    public SshPutTaskFactory contents(InputStream stream) {
-        markDirty();
-        this.contents = Suppliers.ofInstance(stream);  
-        return self();
-    }
-
-    public SshPutTaskFactory contents(Reader reader) {
-        markDirty();
-        this.contents = Suppliers.ofInstance(new ReaderInputStream(reader));  
-        return self();
-    }
-
-    public SshPutTaskFactory allowFailure() {
-        markDirty();
-        allowFailure = true;
-        return self();
-    }
-    
-    public SshPutTaskFactory createDirectory() {
-        markDirty();
-        createDirectory = true;
-        return self();
-    }
-    
-    public SshPutTaskWrapper newTask() {
-        dirty = false;
-        return new SshPutTaskWrapper(this);
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        // help let people know of API usage error
-        if (dirty)
-            log.warn("Task "+this+" was modified but modification was never used");
-        super.finalize();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskStub.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskStub.java b/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskStub.java
deleted file mode 100644
index 185e819..0000000
--- a/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskStub.java
+++ /dev/null
@@ -1,69 +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.ssh;
-
-import java.io.InputStream;
-
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.config.ConfigBag;
-
-import com.google.common.base.Supplier;
-
-public class SshPutTaskStub {
-
-    protected String remoteFile;
-    protected SshMachineLocation machine;
-    protected Supplier<? extends InputStream> contents;
-    protected String summary;
-    protected String permissions;
-    protected boolean allowFailure = false;
-    protected boolean createDirectory = false;
-    protected final ConfigBag config = ConfigBag.newInstance();
-
-    protected SshPutTaskStub() {
-    }
-    
-    protected SshPutTaskStub(SshPutTaskStub constructor) {
-        this.remoteFile = constructor.remoteFile;
-        this.machine = constructor.machine;
-        this.contents = constructor.contents;
-        this.summary = constructor.summary;
-        this.allowFailure = constructor.allowFailure;
-        this.createDirectory = constructor.createDirectory;
-        this.permissions = constructor.permissions;
-        this.config.copy(constructor.config);
-    }
-
-    public String getRemoteFile() {
-        return remoteFile;
-    }
-    
-    public String getSummary() {
-        if (summary!=null) return summary;
-        return "scp put: "+remoteFile;
-    }
-
-    public SshMachineLocation getMachine() {
-        return machine;
-    }
-    
-    protected ConfigBag getConfig() {
-        return config;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskWrapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskWrapper.java b/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskWrapper.java
deleted file mode 100644
index 4f0cd76..0000000
--- a/core/src/main/java/brooklyn/util/task/ssh/SshPutTaskWrapper.java
+++ /dev/null
@@ -1,190 +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.ssh;
-
-import java.util.Arrays;
-import java.util.concurrent.Callable;
-
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskWrapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Preconditions;
-
-/** As {@link ProcessTaskWrapper}, but putting a file on the remote machine */
-@Beta
-public class SshPutTaskWrapper extends SshPutTaskStub implements TaskWrapper<Void> {
-
-    private static final Logger log = LoggerFactory.getLogger(SshPutTaskWrapper.class);
-    
-    private final Task<Void> task;
-
-    protected Integer exitCodeOfCopy = null;
-    protected Exception exception = null;
-    protected boolean successful = false;
-    
-    // package private as only AbstractSshTaskFactory should invoke
-    SshPutTaskWrapper(SshPutTaskFactory constructor) {
-        super(constructor);
-        TaskBuilder<Void> tb = TaskBuilder.<Void>builder().dynamic(false).name(getSummary());
-        task = tb.body(new SshPutJob()).build();
-    }
-    
-    @Override
-    public Task<Void> asTask() {
-        return getTask();
-    }
-    
-    @Override
-    public Task<Void> getTask() {
-        return task;
-    }
-        
-    // TODO:
-    //   verify
-    //   copyAsRoot
-    //   owner
-    //   lastModificationDate - see {@link #PROP_LAST_MODIFICATION_DATE}; not supported by all SshTool implementations
-    //   lastAccessDate - see {@link #PROP_LAST_ACCESS_DATE}; not supported by all SshTool implementations
-
-    private class SshPutJob implements Callable<Void> {
-        @Override
-        public Void call() throws Exception {
-            try {
-                Preconditions.checkNotNull(getMachine(), "machine");
-                
-                String remoteFile = getRemoteFile();
-
-                if (createDirectory) {
-                    String remoteDir = remoteFile;
-                    int exitCodeOfCreate = -1;
-                    try {
-                        int li = remoteDir.lastIndexOf("/");
-                        if (li>=0) {
-                            remoteDir = remoteDir.substring(0, li+1);
-                            exitCodeOfCreate = getMachine().execCommands("creating directory for "+getSummary(), 
-                                    Arrays.asList("mkdir -p "+remoteDir));
-                        } else {
-                            // nothing to create
-                            exitCodeOfCreate = 0;
-                        }
-                    } catch (Exception e) {
-                        if (log.isDebugEnabled())
-                            log.debug("SSH put "+getRemoteFile()+" (create dir, in task "+getSummary()+") to "+getMachine()+" threw exception: "+e);
-                        exception = e;
-                    }
-                    if (exception!=null || !((Integer)0).equals(exitCodeOfCreate)) {
-                        if (!allowFailure) {
-                            if (exception != null) {
-                                throw new IllegalStateException(getSummary()+" (creating dir "+remoteDir+" for SSH put task) ended with exception, in "+Tasks.current()+": "+exception, exception);
-                            }
-                            if (exitCodeOfCreate!=0) {
-                                exception = new IllegalStateException(getSummary()+" (creating dir "+remoteDir+" SSH put task) ended with exit code "+exitCodeOfCreate+", in "+Tasks.current());
-                                throw exception;
-                            }
-                        }
-                        // not successful, but allowed
-                        return null;
-                    }
-                }
-                
-                ConfigBag config = ConfigBag.newInstanceCopying(getConfig());
-                if (permissions!=null) config.put(SshTool.PROP_PERMISSIONS, permissions);
-                
-                exitCodeOfCopy = getMachine().copyTo(config.getAllConfig(), contents.get(), remoteFile);
-
-                if (log.isDebugEnabled())
-                    log.debug("SSH put "+getRemoteFile()+" (task "+getSummary()+") to "+getMachine()+" completed with exit code "+exitCodeOfCopy);
-            } catch (Exception e) {
-                if (log.isDebugEnabled())
-                    log.debug("SSH put "+getRemoteFile()+" (task "+getSummary()+") to "+getMachine()+" threw exception: "+e);
-                exception = e;
-            }
-            
-            if (exception!=null || !((Integer)0).equals(exitCodeOfCopy)) {
-                if (!allowFailure) {
-                    if (exception != null) {
-                        throw new IllegalStateException(getSummary()+" (SSH put task) ended with exception, in "+Tasks.current()+": "+exception, exception);
-                    }
-                    if (exitCodeOfCopy!=0) {
-                        exception = new IllegalStateException(getSummary()+" (SSH put task) ended with exit code "+exitCodeOfCopy+", in "+Tasks.current());
-                        throw exception;
-                    }
-                }
-                // not successful, but allowed
-                return null;
-            }
-            
-            // TODO verify
-
-            successful = (exception==null && ((Integer)0).equals(exitCodeOfCopy));
-            return null;
-        }
-    }
-    
-    @Override
-    public String toString() {
-        return super.toString()+"["+task+"]";
-    }
-
-    /** blocks, throwing if there was an exception */
-    public Void get() {
-        return getTask().getUnchecked();
-    }
-    
-    /** returns the exit code from the copy, 0 on success; 
-     * null if it has not completed or threw exception
-     * (not sure if this is ever a non-zero integer or if it is meaningful)
-     * <p>
-     * most callers will want the simpler {@link #isSuccessful()} */
-    public Integer getExitCode() {
-        return exitCodeOfCopy;
-    }
-    
-    /** returns any exception encountered in the operation */
-    public Exception getException() {
-        return exception;
-    }
-    
-    /** blocks until the task completes; does not throw */
-    public SshPutTaskWrapper block() {
-        getTask().blockUntilEnded();
-        return this;
-    }
- 
-    /** true iff the ssh job has completed (with or without failure) */
-    public boolean isDone() {
-        return getTask().isDone();
-    }
-
-    /** true iff the scp has completed successfully; guaranteed to be set before {@link #isDone()} or {@link #block()} are satisfied */
-    public boolean isSuccessful() {
-        return successful;
-    }
-    
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ssh/SshTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ssh/SshTasks.java b/core/src/main/java/brooklyn/util/task/ssh/SshTasks.java
deleted file mode 100644
index 9f6fbb9..0000000
--- a/core/src/main/java/brooklyn/util/task/ssh/SshTasks.java
+++ /dev/null
@@ -1,236 +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.ssh;
-
-import java.util.Map;
-
-import javax.annotation.Nullable;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskAdaptable;
-import org.apache.brooklyn.api.management.TaskFactory;
-import org.apache.brooklyn.api.management.TaskQueueingContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.config.ConfigUtils;
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.basic.ConfigKeys;
-
-import org.apache.brooklyn.location.basic.AbstractLocation;
-import org.apache.brooklyn.location.basic.LocationInternal;
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.net.Urls;
-import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ssh.internal.PlainSshExecTaskFactory;
-import brooklyn.util.task.system.ProcessTaskFactory;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-import brooklyn.util.text.Identifiers;
-import brooklyn.util.text.Strings;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-
-/**
- * Conveniences for generating {@link Task} instances to perform SSH activities on an {@link SshMachineLocation}.
- * <p>
- * To infer the {@link SshMachineLocation} and take properties from entities and global management context the
- * {@link SshEffectorTasks} should be preferred over this class.
- *  
- * @see SshEffectorTasks
- * @since 0.6.0
- */
-@Beta
-public class SshTasks {
-
-    private static final Logger log = LoggerFactory.getLogger(SshTasks.class);
-        
-    public static ProcessTaskFactory<Integer> newSshExecTaskFactory(SshMachineLocation machine, String ...commands) {
-        return newSshExecTaskFactory(machine, true, commands);
-    }
-    
-    public static ProcessTaskFactory<Integer> newSshExecTaskFactory(SshMachineLocation machine, final boolean useMachineConfig, String ...commands) {
-        return new PlainSshExecTaskFactory<Integer>(machine, commands) {
-            {
-                if (useMachineConfig)
-                    config.putIfAbsent(getSshFlags(machine));
-            }
-        };
-    }
-
-    public static SshPutTaskFactory newSshPutTaskFactory(SshMachineLocation machine, String remoteFile) {
-        return newSshPutTaskFactory(machine, true, remoteFile);
-    }
-    
-    public static SshPutTaskFactory newSshPutTaskFactory(SshMachineLocation machine, final boolean useMachineConfig, String remoteFile) {
-        return new SshPutTaskFactory(machine, remoteFile) {
-            {
-                if (useMachineConfig)
-                    config.putIfAbsent(getSshFlags(machine));
-            }
-        };
-    }
-
-    public static SshFetchTaskFactory newSshFetchTaskFactory(SshMachineLocation machine, String remoteFile) {
-        return newSshFetchTaskFactory(machine, true, remoteFile);
-    }
-    
-    public static SshFetchTaskFactory newSshFetchTaskFactory(SshMachineLocation machine, final boolean useMachineConfig, String remoteFile) {
-        return new SshFetchTaskFactory(machine, remoteFile) {
-            {
-                if (useMachineConfig)
-                    config.putIfAbsent(getSshFlags(machine));
-            }
-        };
-    }
-
-    private static Map<String, Object> getSshFlags(Location location) {
-        ConfigBag allConfig = ConfigBag.newInstance();
-        
-        if (location instanceof AbstractLocation) {
-            ManagementContext mgmt = ((AbstractLocation)location).getManagementContext();
-            if (mgmt!=null)
-                allConfig.putAll(mgmt.getConfig().getAllConfig());
-        }
-        
-        allConfig.putAll(((LocationInternal)location).config().getBag());
-        
-        Map<String, Object> result = Maps.newLinkedHashMap();
-        for (String keyS : allConfig.getAllConfig().keySet()) {
-            ConfigKey<?> key = ConfigKeys.newConfigKey(Object.class, keyS);
-            if (key.getName().startsWith(SshTool.BROOKLYN_CONFIG_KEY_PREFIX)) {
-                result.put(ConfigUtils.unprefixedKey(SshTool.BROOKLYN_CONFIG_KEY_PREFIX, key).getName(), allConfig.get(key));
-            }
-        }
-        return result;
-    }
-
-    @Beta
-    public static enum OnFailingTask { 
-        FAIL,
-        /** issues a warning, sometimes implemented as marking the task inessential and failing it if it appears
-         * we are in a dynamic {@link TaskQueueingContext};
-         * useful because this way the warning appears to the user;
-         * but note that the check is done against the calling thread so use with some care
-         * (and thus this enum is currently here rather then elsewhere) */
-        WARN_OR_IF_DYNAMIC_FAIL_MARKING_INESSENTIAL,
-        /** issues a warning in the log if the task fails, otherwise swallows it */
-        WARN_IN_LOG_ONLY, 
-        /** not even a warning if the task fails (the caller is expected to handle it as appropriate) */
-        IGNORE }
-    
-    public static ProcessTaskFactory<Boolean> dontRequireTtyForSudo(SshMachineLocation machine, final boolean failIfCantSudo) {
-        return dontRequireTtyForSudo(machine, failIfCantSudo ? OnFailingTask.FAIL : OnFailingTask.WARN_IN_LOG_ONLY);
-    }
-    /** creates a task which returns modifies sudoers to ensure non-tty access is permitted;
-     * also gives nice warnings if sudo is not permitted */
-    public static ProcessTaskFactory<Boolean> dontRequireTtyForSudo(SshMachineLocation machine, OnFailingTask onFailingTaskRequested) {
-        final OnFailingTask onFailingTask;
-        if (onFailingTaskRequested==OnFailingTask.WARN_OR_IF_DYNAMIC_FAIL_MARKING_INESSENTIAL) {
-            if (DynamicTasks.getTaskQueuingContext()!=null)
-                onFailingTask = onFailingTaskRequested;
-            else
-                onFailingTask = OnFailingTask.WARN_IN_LOG_ONLY;
-        } else {
-            onFailingTask = onFailingTaskRequested;
-        }
-        
-        final String id = Identifiers.makeRandomId(6);
-        return newSshExecTaskFactory(machine, 
-                BashCommands.dontRequireTtyForSudo(),
-                // strange quotes are to ensure we don't match against echoed stdin
-                BashCommands.sudo("echo \"sudo\"-is-working-"+id))
-            .summary("setting up sudo")
-            .configure(SshTool.PROP_ALLOCATE_PTY, true)
-            .allowingNonZeroExitCode()
-            .returning(new Function<ProcessTaskWrapper<?>,Boolean>() { public Boolean apply(ProcessTaskWrapper<?> task) {
-                if (task.getExitCode()==0 && task.getStdout().contains("sudo-is-working-"+id)) return true;
-                Entity entity = BrooklynTaskTags.getTargetOrContextEntity(Tasks.current());
-                
-                
-                if (onFailingTask!=OnFailingTask.IGNORE) {
-                    // TODO if in a queueing context can we mark this task inessential and throw?
-                    // that way user sees the message...
-                    String message = "Error setting up sudo for "+task.getMachine().getUser()+"@"+task.getMachine().getAddress().getHostName()+" "+
-                        " (exit code "+task.getExitCode()+(entity!=null ? ", entity "+entity : "")+")";
-                    DynamicTasks.queueIfPossible(Tasks.warning(message, null));
-                }
-                Streams.logStreamTail(log, "STDERR of sudo setup problem", Streams.byteArrayOfString(task.getStderr()), 1024);
-                
-                if (onFailingTask==OnFailingTask.WARN_OR_IF_DYNAMIC_FAIL_MARKING_INESSENTIAL) {
-                    Tasks.markInessential();
-                }
-                if (onFailingTask==OnFailingTask.FAIL || onFailingTask==OnFailingTask.WARN_OR_IF_DYNAMIC_FAIL_MARKING_INESSENTIAL) {
-                    throw new IllegalStateException("Passwordless sudo is required for "+task.getMachine().getUser()+"@"+task.getMachine().getAddress().getHostName()+
-                            (entity!=null ? " ("+entity+")" : ""));
-                }
-                return false; 
-            } });
-    }
-
-    /** Function for use in {@link ProcessTaskFactory#returning(Function)} which logs all information, optionally requires zero exit code, 
-     * and then returns stdout */
-    public static Function<ProcessTaskWrapper<?>, String> returningStdoutLoggingInfo(final Logger logger, final boolean requireZero) {
-        return new Function<ProcessTaskWrapper<?>, String>() {
-          public String apply(@Nullable ProcessTaskWrapper<?> input) {
-            if (logger!=null) logger.info(input+" COMMANDS:\n"+Strings.join(input.getCommands(),"\n"));
-            if (logger!=null) logger.info(input+" STDOUT:\n"+input.getStdout());
-            if (logger!=null) logger.info(input+" STDERR:\n"+input.getStderr());
-            if (requireZero && input.getExitCode()!=0) 
-                throw new IllegalStateException("non-zero exit code in "+input.getSummary()+": see log for more details!");
-            return input.getStdout();
-          }
-        };
-    }
-
-    /** task to install a file given a url, where the url is resolved remotely first then locally */
-    public static TaskFactory<?> installFromUrl(final SshMachineLocation location, final String url, final String destPath) {
-        return installFromUrl(ResourceUtils.create(SshTasks.class), ImmutableMap.<String,Object>of(), location, url, destPath);
-    }
-    /** task to install a file given a url, where the url is resolved remotely first then locally */
-    public static TaskFactory<?> installFromUrl(final ResourceUtils utils, final Map<String, ?> props, final SshMachineLocation location, final String url, final String destPath) {
-        return new TaskFactory<TaskAdaptable<?>>() {
-            @Override
-            public TaskAdaptable<?> newTask() {
-                return Tasks.<Void>builder().name("installing "+Urls.getBasename(url)).description("installing "+url+" to "+destPath).body(new Runnable() {
-                    @Override
-                    public void run() {
-                        int result = location.installTo(utils, props, url, destPath);
-                        if (result!=0) 
-                            throw new IllegalStateException("Failed to install '"+url+"' to '"+destPath+"' at "+location+": exit code "+result);
-                    }
-                }).build();
-            }
-        };
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ssh/internal/AbstractSshExecTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ssh/internal/AbstractSshExecTaskFactory.java b/core/src/main/java/brooklyn/util/task/ssh/internal/AbstractSshExecTaskFactory.java
deleted file mode 100644
index 86764f3..0000000
--- a/core/src/main/java/brooklyn/util/task/ssh/internal/AbstractSshExecTaskFactory.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task.ssh.internal;
-
-import com.google.common.base.Preconditions;
-
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.task.system.ProcessTaskFactory;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-import brooklyn.util.task.system.internal.AbstractProcessTaskFactory;
-
-// cannot be (cleanly) instantiated due to nested generic self-referential type; however trivial subclasses do allow it 
-public abstract class AbstractSshExecTaskFactory<T extends AbstractProcessTaskFactory<T,RET>,RET> extends AbstractProcessTaskFactory<T,RET> implements ProcessTaskFactory<RET> {
-    
-    /** constructor where machine will be added later */
-    public AbstractSshExecTaskFactory(String ...commands) {
-        super(commands);
-    }
-
-    /** convenience constructor to supply machine immediately */
-    public AbstractSshExecTaskFactory(SshMachineLocation machine, String ...commands) {
-        this(commands);
-        machine(machine);
-    }
-    
-    @Override
-    public ProcessTaskWrapper<RET> newTask() {
-        dirty = false;
-        return new ProcessTaskWrapper<RET>(this) {
-            protected void run(ConfigBag config) {
-                Preconditions.checkNotNull(getMachine(), "machine");
-                if (Boolean.FALSE.equals(this.runAsScript)) {
-                    this.exitCode = getMachine().execCommands(config.getAllConfig(), getSummary(), commands, shellEnvironment);
-                } else { // runScript = null or TRUE
-                    this.exitCode = getMachine().execScript(config.getAllConfig(), getSummary(), commands, shellEnvironment);
-                }
-            }
-            protected String taskTypeShortName() { return "SSH"; }
-        };
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/ssh/internal/PlainSshExecTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ssh/internal/PlainSshExecTaskFactory.java b/core/src/main/java/brooklyn/util/task/ssh/internal/PlainSshExecTaskFactory.java
deleted file mode 100644
index efc14db..0000000
--- a/core/src/main/java/brooklyn/util/task/ssh/internal/PlainSshExecTaskFactory.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task.ssh.internal;
-
-import java.util.List;
-
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-
-import com.google.common.base.Function;
-
-/** the "Plain" class exists purely so we can massage return types for callers' convenience */
-public class PlainSshExecTaskFactory<RET> extends AbstractSshExecTaskFactory<PlainSshExecTaskFactory<RET>,RET> {
-    /** constructor where machine will be added later */
-    public PlainSshExecTaskFactory(String ...commands) {
-        super(commands);
-    }
-
-    /** convenience constructor to supply machine immediately */
-    public PlainSshExecTaskFactory(SshMachineLocation machine, String ...commands) {
-        this(commands);
-        machine(machine);
-    }
-
-    /** Constructor where machine will be added later */
-    public PlainSshExecTaskFactory(List<String> commands) {
-        this(commands.toArray(new String[commands.size()]));
-    }
-
-    /** Convenience constructor to supply machine immediately */
-    public PlainSshExecTaskFactory(SshMachineLocation machine, List<String> commands) {
-        this(machine, commands.toArray(new String[commands.size()]));
-    }
-
-    @Override
-    public <T2> PlainSshExecTaskFactory<T2> returning(ScriptReturnType type) {
-        return (PlainSshExecTaskFactory<T2>) super.<T2>returning(type);
-    }
-
-    @Override
-    public <RET2> PlainSshExecTaskFactory<RET2> returning(Function<ProcessTaskWrapper<?>, RET2> resultTransformation) {
-        return (PlainSshExecTaskFactory<RET2>) super.returning(resultTransformation);
-    }
-    
-    @Override
-    public PlainSshExecTaskFactory<Boolean> returningIsExitCodeZero() {
-        return (PlainSshExecTaskFactory<Boolean>) super.returningIsExitCodeZero();
-    }
-    
-    @Override
-    public PlainSshExecTaskFactory<String> requiringZeroAndReturningStdout() {
-        return (PlainSshExecTaskFactory<String>) super.requiringZeroAndReturningStdout();
-    }
-    
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/system/ProcessTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/system/ProcessTaskFactory.java b/core/src/main/java/brooklyn/util/task/system/ProcessTaskFactory.java
deleted file mode 100644
index 407111c..0000000
--- a/core/src/main/java/brooklyn/util/task/system/ProcessTaskFactory.java
+++ /dev/null
@@ -1,65 +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.system;
-
-import java.util.Map;
-
-import org.apache.brooklyn.api.management.TaskFactory;
-
-import brooklyn.config.ConfigKey;
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.internal.ssh.SshTool;
-import brooklyn.util.task.system.ProcessTaskStub.ScriptReturnType;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Function;
-
-public interface ProcessTaskFactory<T> extends TaskFactory<ProcessTaskWrapper<T>> {
-    public ProcessTaskFactory<T> machine(SshMachineLocation machine);
-    public ProcessTaskFactory<T> add(String ...commandsToAdd);
-    public ProcessTaskFactory<T> add(Iterable<String> commandsToAdd);
-    public ProcessTaskFactory<T> requiringExitCodeZero();
-    public ProcessTaskFactory<T> requiringExitCodeZero(String extraErrorMessage);
-    public ProcessTaskFactory<T> allowingNonZeroExitCode();
-    public ProcessTaskFactory<String> requiringZeroAndReturningStdout();
-    public ProcessTaskFactory<Boolean> returningIsExitCodeZero();
-    public <RET2> ProcessTaskFactory<RET2> returning(ScriptReturnType type);
-    public <RET2> ProcessTaskFactory<RET2> returning(Function<ProcessTaskWrapper<?>, RET2> resultTransformation);
-    public ProcessTaskFactory<T> runAsCommand();
-    public ProcessTaskFactory<T> runAsScript();
-    public ProcessTaskFactory<T> runAsRoot();
-    public ProcessTaskFactory<T> environmentVariable(String key, String val);
-    public ProcessTaskFactory<T> environmentVariables(Map<String,String> vars);
-    public ProcessTaskFactory<T> summary(String summary);
-    
-    /** allows setting config-key based properties for specific underlying tools */
-    @Beta
-    public <V> ProcessTaskFactory<T> configure(ConfigKey<V> key, V value);
-
-    /** allows setting config-key/flag based properties for specific underlying tools;
-     * but note that if any are prefixed with {@link SshTool#BROOKLYN_CONFIG_KEY_PREFIX}
-     * these should normally be filtered out */
-    @Beta
-    public ProcessTaskFactory<T> configure(Map<?,?> flags);
-
-    /** adds a listener which will be notified of (otherwise) successful completion,
-     * typically used to invalidate the result (ie throw exception, to promote a string in the output to an exception);
-     * invoked even if return code is zero, so a better error can be thrown */
-    public ProcessTaskFactory<T> addCompletionListener(Function<ProcessTaskWrapper<?>, Void> function);
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/system/ProcessTaskStub.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/system/ProcessTaskStub.java b/core/src/main/java/brooklyn/util/task/system/ProcessTaskStub.java
deleted file mode 100644
index df37691..0000000
--- a/core/src/main/java/brooklyn/util/task/system/ProcessTaskStub.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util.task.system;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class ProcessTaskStub {
-    
-    protected final List<String> commands = new ArrayList<String>();
-    /** null for localhost */
-    protected SshMachineLocation machine;
-    
-    // config data
-    protected String summary;
-    protected final ConfigBag config = ConfigBag.newInstance();
-    
-    public static enum ScriptReturnType { CUSTOM, EXIT_CODE, STDOUT_STRING, STDOUT_BYTES, STDERR_STRING, STDERR_BYTES }
-    protected Function<ProcessTaskWrapper<?>, ?> returnResultTransformation = null;
-    protected ScriptReturnType returnType = ScriptReturnType.EXIT_CODE;
-    
-    protected Boolean runAsScript = null;
-    protected boolean runAsRoot = false;
-    protected Boolean requireExitCodeZero = null;
-    protected String extraErrorMessage = null;
-    protected Map<String,String> shellEnvironment = new MutableMap<String, String>();
-    protected final List<Function<ProcessTaskWrapper<?>, Void>> completionListeners = new ArrayList<Function<ProcessTaskWrapper<?>,Void>>();
-
-    public ProcessTaskStub() {}
-    
-    protected ProcessTaskStub(ProcessTaskStub source) {
-        commands.addAll(source.getCommands());
-        machine = source.getMachine();
-        summary = source.getSummary();
-        config.copy(source.getConfig());
-        returnResultTransformation = source.returnResultTransformation;
-        returnType = source.returnType;
-        runAsScript = source.runAsScript;
-        runAsRoot = source.runAsRoot;
-        requireExitCodeZero = source.requireExitCodeZero;
-        extraErrorMessage = source.extraErrorMessage;
-        shellEnvironment.putAll(source.getShellEnvironment());
-        completionListeners.addAll(source.getCompletionListeners());
-    }
-
-    public String getSummary() {
-        if (summary!=null) return summary;
-        return Strings.maxlen(Strings.join(commands, " ; "), 160);
-    }
-    
-    /** null for localhost */
-    public SshMachineLocation getMachine() {
-        return machine;
-    }
-    
-    public Map<String, String> getShellEnvironment() {
-        return ImmutableMap.copyOf(shellEnvironment);
-    }
- 
-    @Override
-    public String toString() {
-        return super.toString()+"["+getSummary()+"]";
-    }
-
-    public List<String> getCommands() {
-        return ImmutableList.copyOf(commands);
-    }
- 
-    public List<Function<ProcessTaskWrapper<?>, Void>> getCompletionListeners() {
-        return completionListeners;
-    }
-
-    protected ConfigBag getConfig() { return config; }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/system/ProcessTaskWrapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/system/ProcessTaskWrapper.java b/core/src/main/java/brooklyn/util/task/system/ProcessTaskWrapper.java
deleted file mode 100644
index 5c18fdd..0000000
--- a/core/src/main/java/brooklyn/util/task/system/ProcessTaskWrapper.java
+++ /dev/null
@@ -1,187 +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.system;
-
-import java.io.ByteArrayOutputStream;
-import java.util.concurrent.Callable;
-
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskWrapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.internal.ssh.ShellTool;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.system.internal.AbstractProcessTaskFactory;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Function;
-
-/** Wraps a fully constructed process task, and allows callers to inspect status. 
- * Note that methods in here such as {@link #getStdout()} will return partially completed streams while the task is ongoing
- * (and exit code will be null). You can {@link #block()} or {@link #get()} as conveniences on the underlying {@link #getTask()}. */ 
-public abstract class ProcessTaskWrapper<RET> extends ProcessTaskStub implements TaskWrapper<RET> {
-
-    private static final Logger log = LoggerFactory.getLogger(ProcessTaskWrapper.class);
-    
-    private final Task<RET> task;
-
-    // execution details
-    protected ByteArrayOutputStream stdout = new ByteArrayOutputStream();
-    protected ByteArrayOutputStream stderr = new ByteArrayOutputStream();
-    protected Integer exitCode = null;
-    
-    @SuppressWarnings("unchecked")
-    protected ProcessTaskWrapper(AbstractProcessTaskFactory<?,RET> constructor) {
-        super(constructor);
-        TaskBuilder<Object> tb = constructor.constructCustomizedTaskBuilder();
-        if (stdout!=null) tb.tag(BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDOUT, stdout));
-        if (stderr!=null) tb.tag(BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDERR, stderr));
-        task = (Task<RET>) tb.body(new ProcessTaskInternalJob()).build();
-    }
-    
-    @Override
-    public Task<RET> asTask() {
-        return getTask();
-    }
-    
-    @Override
-    public Task<RET> getTask() {
-        return task;
-    }
-    
-    public Integer getExitCode() {
-        return exitCode;
-    }
-    
-    public byte[] getStdoutBytes() {
-        if (stdout==null) return null;
-        return stdout.toByteArray();
-    }
-    
-    public byte[] getStderrBytes() {
-        if (stderr==null) return null;
-        return stderr.toByteArray();
-    }
-    
-    public String getStdout() {
-        if (stdout==null) return null;
-        return stdout.toString();
-    }
-    
-    public String getStderr() {
-        if (stderr==null) return null;
-        return stderr.toString();
-    }
-
-    protected class ProcessTaskInternalJob implements Callable<Object> {
-        @Override
-        public Object call() throws Exception {
-            run( getConfigForRunning() );
-            
-            for (Function<ProcessTaskWrapper<?>, Void> listener: completionListeners) {
-                try {
-                    listener.apply(ProcessTaskWrapper.this);
-                } catch (Exception e) {
-                    logWithDetailsAndThrow("Error in "+taskTypeShortName()+" task "+getSummary()+": "+e, e);                    
-                }
-            }
-            
-            if (exitCode!=0 && !Boolean.FALSE.equals(requireExitCodeZero)) {
-                if (Boolean.TRUE.equals(requireExitCodeZero)) {
-                    logWithDetailsAndThrow(taskTypeShortName()+" task ended with exit code "+exitCode+" when 0 was required, in "+Tasks.current()+": "+getSummary(), null);
-                } else {
-                    // warn, but allow, on non-zero not explicitly allowed
-                    log.warn(taskTypeShortName()+" task ended with exit code "+exitCode+" when non-zero was not explicitly allowed (error may be thrown in future), in "
-                            +Tasks.current()+": "+getSummary());
-                }
-            }
-            switch (returnType) {
-            case CUSTOM: return returnResultTransformation.apply(ProcessTaskWrapper.this);
-            case STDOUT_STRING: return stdout.toString();
-            case STDOUT_BYTES: return stdout.toByteArray();
-            case STDERR_STRING: return stderr.toString();
-            case STDERR_BYTES: return stderr.toByteArray();
-            case EXIT_CODE: return exitCode;
-            }
-
-            throw new IllegalStateException("Unknown return type for "+taskTypeShortName()+" job "+getSummary()+": "+returnType);
-        }
-
-        protected void logWithDetailsAndThrow(String message, Throwable optionalCause) {
-            message = (extraErrorMessage!=null ? extraErrorMessage+": " : "") + message;
-            log.warn(message+" (throwing)");
-            logProblemDetails("STDERR", stderr, 1024);
-            logProblemDetails("STDOUT", stdout, 1024);
-            logProblemDetails("STDIN", Streams.byteArrayOfString(Strings.join(commands,"\n")), 4096);
-            if (optionalCause!=null) throw new IllegalStateException(message, optionalCause);
-            throw new IllegalStateException(message);
-        }
-        
-        protected void logProblemDetails(String streamName, ByteArrayOutputStream stream, int max) {
-            Streams.logStreamTail(log, streamName+" for problem in "+Tasks.current(), stream, max);
-        }
-
-    }
-    
-    @Override
-    public String toString() {
-        return super.toString()+"["+task+"]";
-    }
-
-    /** blocks and gets the result, throwing if there was an exception */
-    public RET get() {
-        return getTask().getUnchecked();
-    }
-    
-    /** blocks until the task completes; does not throw */
-    public ProcessTaskWrapper<RET> block() {
-        getTask().blockUntilEnded();
-        return this;
-    }
- 
-    /** true iff the process has completed (with or without failure) */
-    public boolean isDone() {
-        return getTask().isDone();
-    }
-
-    /** for overriding */
-    protected ConfigBag getConfigForRunning() {
-        ConfigBag config = ConfigBag.newInstanceCopying(ProcessTaskWrapper.this.config);
-        if (stdout!=null) config.put(ShellTool.PROP_OUT_STREAM, stdout);
-        if (stderr!=null) config.put(ShellTool.PROP_ERR_STREAM, stderr);
-        
-        if (!config.containsKey(ShellTool.PROP_NO_EXTRA_OUTPUT))
-            // by default no extra output (so things like cat, etc work as expected)
-            config.put(ShellTool.PROP_NO_EXTRA_OUTPUT, true);
-
-        if (runAsRoot)
-            config.put(ShellTool.PROP_RUN_AS_ROOT, true);
-        return config;
-    }
-
-    protected abstract void run(ConfigBag config);
-    
-    protected abstract String taskTypeShortName();
-    
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/system/SystemTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/system/SystemTasks.java b/core/src/main/java/brooklyn/util/task/system/SystemTasks.java
deleted file mode 100644
index 8553935..0000000
--- a/core/src/main/java/brooklyn/util/task/system/SystemTasks.java
+++ /dev/null
@@ -1,29 +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.system;
-
-import brooklyn.util.task.system.internal.SystemProcessTaskFactory.ConcreteSystemProcessTaskFactory;
-
-public class SystemTasks {
-
-    public static ProcessTaskFactory<Integer> exec(String ...commands) {
-        return new ConcreteSystemProcessTaskFactory<Integer>(commands);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/task/system/internal/AbstractProcessTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/system/internal/AbstractProcessTaskFactory.java b/core/src/main/java/brooklyn/util/task/system/internal/AbstractProcessTaskFactory.java
deleted file mode 100644
index e41a9a9..0000000
--- a/core/src/main/java/brooklyn/util/task/system/internal/AbstractProcessTaskFactory.java
+++ /dev/null
@@ -1,214 +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.system.internal;
-
-import java.util.Arrays;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.BrooklynTaskTags;
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.system.ProcessTaskFactory;
-import brooklyn.util.task.system.ProcessTaskStub;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
-
-public abstract class AbstractProcessTaskFactory<T extends AbstractProcessTaskFactory<T,RET>,RET> extends ProcessTaskStub implements ProcessTaskFactory<RET> {
-    
-    private static final Logger log = LoggerFactory.getLogger(AbstractProcessTaskFactory.class);
-    
-    protected boolean dirty = false;
-    
-    public AbstractProcessTaskFactory(String ...commands) {
-        this.commands.addAll(Arrays.asList(commands));
-    }
-
-    @SuppressWarnings("unchecked")
-    protected T self() { return (T)this; }
-    
-    protected void markDirty() {
-        dirty = true;
-    }
-    
-    @Override
-    public T add(String ...commandsToAdd) {
-        markDirty();
-        for (String commandToAdd: commandsToAdd) this.commands.add(commandToAdd);
-        return self();
-    }
-
-    @Override
-    public T add(Iterable<String> commandsToAdd) {
-        Iterables.addAll(this.commands, commandsToAdd);
-        return self();
-    }
-    
-    @Override
-    public T machine(SshMachineLocation machine) {
-        markDirty();
-        this.machine = machine;
-        return self();
-    }
-
-    @Override
-    public T requiringExitCodeZero() {
-        markDirty();
-        requireExitCodeZero = true;
-        return self();
-    }
-    
-    @Override
-    public T requiringExitCodeZero(String extraErrorMessage) {
-        markDirty();
-        requireExitCodeZero = true;
-        this.extraErrorMessage = extraErrorMessage;
-        return self();
-    }
-    
-    @Override
-    public T allowingNonZeroExitCode() {
-        markDirty();
-        requireExitCodeZero = false;
-        return self();
-    }
-
-    @Override
-    public ProcessTaskFactory<Boolean> returningIsExitCodeZero() {
-        if (requireExitCodeZero==null) allowingNonZeroExitCode();
-        return returning(new Function<ProcessTaskWrapper<?>,Boolean>() {
-            public Boolean apply(ProcessTaskWrapper<?> input) {
-                return input.getExitCode()==0;
-            }
-        });
-    }
-
-    @Override
-    public ProcessTaskFactory<String> requiringZeroAndReturningStdout() {
-        requiringExitCodeZero();
-        return this.<String>returning(ScriptReturnType.STDOUT_STRING);
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <RET2> ProcessTaskFactory<RET2> returning(ScriptReturnType type) {
-        markDirty();
-        returnType = Preconditions.checkNotNull(type);
-        return (ProcessTaskFactory<RET2>) self();
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <RET2> ProcessTaskFactory<RET2> returning(Function<ProcessTaskWrapper<?>, RET2> resultTransformation) {
-        markDirty();
-        returnType = ScriptReturnType.CUSTOM;
-        this.returnResultTransformation = resultTransformation;
-        return (ProcessTaskFactory<RET2>) self();
-    }
-    
-    @Override
-    public T runAsCommand() {
-        markDirty();
-        runAsScript = false;
-        return self();
-    }
-
-    @Override
-    public T runAsScript() {
-        markDirty();
-        runAsScript = true;
-        return self();
-    }
-
-    @Override
-    public T runAsRoot() {
-        markDirty();
-        runAsRoot = true;
-        return self();
-    }
-    
-    @Override
-    public T environmentVariable(String key, String val) {
-        markDirty();
-        shellEnvironment.put(key, val);
-        return self();
-    }
-
-    @Override
-    public T environmentVariables(Map<String,String> vars) {
-        if (vars!=null) {
-            markDirty();
-            shellEnvironment.putAll(vars);
-        }
-        return self();
-    }
-
-    /** creates the TaskBuilder which can be further customized; typically invoked by the initial {@link #newTask()} */
-    public TaskBuilder<Object> constructCustomizedTaskBuilder() {
-        TaskBuilder<Object> tb = TaskBuilder.builder().dynamic(false).name("ssh: "+getSummary());
-        
-        tb.tag(BrooklynTaskTags.tagForStream(BrooklynTaskTags.STREAM_STDIN, 
-                Streams.byteArrayOfString(Strings.join(commands, "\n"))));
-        tb.tag(BrooklynTaskTags.tagForEnvStream(BrooklynTaskTags.STREAM_ENV, shellEnvironment));
-        
-        return tb;
-    }
-    
-    @Override
-    public T summary(String summary) {
-        markDirty();
-        this.summary = summary;
-        return self();
-    }
-
-    @Override
-    public <V> T configure(ConfigKey<V> key, V value) {
-        config.configure(key, value);
-        return self();
-    }
-    
-    @Override
-    public T configure(Map<?, ?> flags) {
-        if (flags!=null)
-            config.putAll(flags);
-        return self();
-    }
- 
-    @Override
-    public T addCompletionListener(Function<ProcessTaskWrapper<?>, Void> listener) {
-        completionListeners.add(listener);
-        return self();
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        // help let people know of API usage error
-        if (dirty)
-            log.warn("Task "+this+" was modified but modification was never used");
-        super.finalize();
-    }
-}


[20/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/ExecWithLoggingHelpers.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/ExecWithLoggingHelpers.java b/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/ExecWithLoggingHelpers.java
new file mode 100644
index 0000000..9e96674
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/ExecWithLoggingHelpers.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.system.internal;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+
+import brooklyn.config.ConfigKey;
+
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.internal.ssh.ShellAbstractTool;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.stream.StreamGobbler;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Function;
+import com.google.common.base.Throwables;
+
+public abstract class ExecWithLoggingHelpers {
+
+    public static final ConfigKey<OutputStream> STDOUT = SshMachineLocation.STDOUT;
+    public static final ConfigKey<OutputStream> STDERR = SshMachineLocation.STDERR;
+    public static final ConfigKey<Boolean> NO_STDOUT_LOGGING = SshMachineLocation.NO_STDOUT_LOGGING;
+    public static final ConfigKey<Boolean> NO_STDERR_LOGGING = SshMachineLocation.NO_STDERR_LOGGING;
+    public static final ConfigKey<String> LOG_PREFIX = SshMachineLocation.LOG_PREFIX;
+
+    protected final String shortName;
+    protected Logger commandLogger = null;
+    
+    public interface ExecRunner {
+        public int exec(ShellTool ssh, Map<String,?> flags, List<String> cmds, Map<String,?> env);
+    }
+
+    protected abstract <T> T execWithTool(MutableMap<String, Object> toolCreationAndConnectionProperties, Function<ShellTool, T> runMethodOnTool);
+    protected abstract void preExecChecks();
+    protected abstract String getTargetName();
+    protected abstract String constructDefaultLoggingPrefix(ConfigBag execFlags);
+
+    /** takes a very short name for use in blocking details, e.g. SSH or Process */
+    public ExecWithLoggingHelpers(String shortName) {
+        this.shortName = shortName;
+    }
+
+    public ExecWithLoggingHelpers logger(Logger commandLogger) {
+        this.commandLogger = commandLogger;
+        return this;
+    }
+    
+    public int execScript(Map<String,?> props, String summaryForLogging, List<String> commands, Map<String,?> env) {
+        // TODO scriptHeader are the extra commands we expect the SshTool/ShellTool to add.
+        // Would be better if could get this from the ssh-tool, rather than assuming it will behave as
+        // we expect.
+        String scriptHeader = ShellAbstractTool.getOptionalVal(props, ShellTool.PROP_SCRIPT_HEADER);
+        
+        return execWithLogging(props, summaryForLogging, commands, env, scriptHeader, new ExecRunner() {
+                @Override public int exec(ShellTool ssh, Map<String, ?> flags, List<String> cmds, Map<String, ?> env) {
+                    return ssh.execScript(flags, cmds, env);
+                }});
+    }
+
+    protected static <T> T getOptionalVal(Map<String,?> map, ConfigKey<T> keyC) {
+        if (keyC==null) return null;
+        String key = keyC.getName();
+        if (map!=null && map.containsKey(key)) {
+            return TypeCoercions.coerce(map.get(key), keyC.getTypeToken());
+        } else {
+            return keyC.getDefaultValue();
+        }
+    }
+
+    public int execCommands(Map<String,?> props, String summaryForLogging, List<String> commands, Map<String,?> env) {
+        return execWithLogging(props, summaryForLogging, commands, env, new ExecRunner() {
+                @Override public int exec(ShellTool tool, Map<String,?> flags, List<String> cmds, Map<String,?> env) {
+                    return tool.execCommands(flags, cmds, env);
+                }});
+    }
+
+    public int execWithLogging(Map<String,?> props, final String summaryForLogging, final List<String> commands,
+            final Map<String,?> env, final ExecRunner execCommand) {
+        return execWithLogging(props, summaryForLogging, commands, env, null, execCommand);
+    }
+    
+    @SuppressWarnings("resource")
+    public int execWithLogging(Map<String,?> props, final String summaryForLogging, final List<String> commands,
+            final Map<String,?> env, String expectedCommandHeaders, final ExecRunner execCommand) {
+        if (commandLogger!=null && commandLogger.isDebugEnabled()) {
+            String allcmds = (Strings.isBlank(expectedCommandHeaders) ? "" : expectedCommandHeaders + " ; ") + Strings.join(commands, " ; ");
+            commandLogger.debug("{}, initiating "+shortName.toLowerCase()+" on machine {}{}: {}",
+                    new Object[] {summaryForLogging, getTargetName(),
+                    env!=null && !env.isEmpty() ? " (env "+env+")": "", allcmds});
+        }
+
+        if (commands.isEmpty()) {
+            if (commandLogger!=null && commandLogger.isDebugEnabled())
+                commandLogger.debug("{}, on machine {}, ending: no commands to run", summaryForLogging, getTargetName());
+            return 0;
+        }
+
+        final ConfigBag execFlags = new ConfigBag().putAll(props);
+        // some props get overridden in execFlags, so remove them from the tool flags
+        final ConfigBag toolFlags = new ConfigBag().putAll(props).removeAll(
+                LOG_PREFIX, STDOUT, STDERR, ShellTool.PROP_NO_EXTRA_OUTPUT);
+
+        execFlags.configure(ShellTool.PROP_SUMMARY, summaryForLogging);
+        
+        PipedOutputStream outO = null;
+        PipedOutputStream outE = null;
+        StreamGobbler gO=null, gE=null;
+        try {
+            preExecChecks();
+            
+            String logPrefix = execFlags.get(LOG_PREFIX);
+            if (logPrefix==null) logPrefix = constructDefaultLoggingPrefix(execFlags);
+
+            if (!execFlags.get(NO_STDOUT_LOGGING)) {
+                PipedInputStream insO = new PipedInputStream();
+                outO = new PipedOutputStream(insO);
+
+                String stdoutLogPrefix = "["+(logPrefix != null ? logPrefix+":stdout" : "stdout")+"] ";
+                gO = new StreamGobbler(insO, execFlags.get(STDOUT), commandLogger).setLogPrefix(stdoutLogPrefix);
+                gO.start();
+
+                execFlags.put(STDOUT, outO);
+            }
+
+            if (!execFlags.get(NO_STDERR_LOGGING)) {
+                PipedInputStream insE = new PipedInputStream();
+                outE = new PipedOutputStream(insE);
+
+                String stderrLogPrefix = "["+(logPrefix != null ? logPrefix+":stderr" : "stderr")+"] ";
+                gE = new StreamGobbler(insE, execFlags.get(STDERR), commandLogger).setLogPrefix(stderrLogPrefix);
+                gE.start();
+
+                execFlags.put(STDERR, outE);
+            }
+
+            Tasks.setBlockingDetails(shortName+" executing, "+summaryForLogging);
+            try {
+                return execWithTool(MutableMap.copyOf(toolFlags.getAllConfig()), new Function<ShellTool, Integer>() {
+                    public Integer apply(ShellTool tool) {
+                        int result = execCommand.exec(tool, MutableMap.copyOf(execFlags.getAllConfig()), commands, env);
+                        if (commandLogger!=null && commandLogger.isDebugEnabled()) 
+                            commandLogger.debug("{}, on machine {}, completed: return status {}",
+                                    new Object[] {summaryForLogging, getTargetName(), result});
+                        return result;
+                    }});
+
+            } finally {
+                Tasks.setBlockingDetails(null);
+            }
+
+        } catch (IOException e) {
+            if (commandLogger!=null && commandLogger.isDebugEnabled()) 
+                commandLogger.debug("{}, on machine {}, failed: {}", new Object[] {summaryForLogging, getTargetName(), e});
+            throw Throwables.propagate(e);
+        } finally {
+            // Must close the pipedOutStreams, otherwise input will never read -1 so StreamGobbler thread would never die
+            if (outO!=null) try { outO.flush(); } catch (IOException e) {}
+            if (outE!=null) try { outE.flush(); } catch (IOException e) {}
+            Streams.closeQuietly(outO);
+            Streams.closeQuietly(outE);
+
+            try {
+                if (gE!=null) { gE.join(); }
+                if (gO!=null) { gO.join(); }
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                Throwables.propagate(e);
+            }
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/SystemProcessTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/SystemProcessTaskFactory.java b/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/SystemProcessTaskFactory.java
new file mode 100644
index 0000000..4a6dacb
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/system/internal/SystemProcessTaskFactory.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task.system.internal;
+
+import java.io.File;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.internal.ssh.ShellTool;
+import org.apache.brooklyn.core.util.internal.ssh.process.ProcessTool;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.base.Function;
+
+public class SystemProcessTaskFactory<T extends SystemProcessTaskFactory<T,RET>,RET> extends AbstractProcessTaskFactory<T, RET> {
+
+    private static final Logger log = LoggerFactory.getLogger(SystemProcessTaskFactory.class);
+    
+    // FIXME Plum this through?!
+    private File directory;
+    private Boolean loginShell;
+
+    public SystemProcessTaskFactory(String ...commands) {
+        super(commands);
+    }
+    
+    public T directory(File directory) {
+        markDirty();
+        this.directory = directory;
+        return self();
+    }
+    
+    public T loginShell(boolean loginShell) {
+        markDirty();
+        this.loginShell = loginShell;
+        return self();
+    }
+    
+    @Override
+    public T machine(SshMachineLocation machine) {
+        log.warn("Not permitted to set machines on "+this+" (ignoring - "+machine+")");
+        if (log.isDebugEnabled())
+            log.debug("Source of attempt to set machines on "+this+" ("+machine+")",
+                    new Throwable("Source of attempt to set machines on "+this+" ("+machine+")"));
+        return self();
+    }
+
+    @Override
+    public ProcessTaskWrapper<RET> newTask() {
+        return new SystemProcessTaskWrapper();
+    }
+
+    protected class SystemProcessTaskWrapper extends ProcessTaskWrapper<RET> {
+        protected final String taskTypeShortName;
+        
+        public SystemProcessTaskWrapper() {
+            this("Process");
+        }
+        public SystemProcessTaskWrapper(String taskTypeShortName) {
+            super(SystemProcessTaskFactory.this);
+            this.taskTypeShortName = taskTypeShortName;
+        }
+        @Override
+        protected ConfigBag getConfigForRunning() {
+            ConfigBag result = super.getConfigForRunning();
+            if (directory != null) config.put(ProcessTool.PROP_DIRECTORY, directory.getAbsolutePath());
+            if (loginShell != null) config.put(ProcessTool.PROP_LOGIN_SHELL, loginShell);
+            return result;
+        }
+        @Override
+        protected void run(ConfigBag config) {
+            if (Boolean.FALSE.equals(this.runAsScript)) {
+                this.exitCode = newExecWithLoggingHelpers().execCommands(config.getAllConfig(), getSummary(), getCommands(), getShellEnvironment());
+            } else { // runScript = null or TRUE
+                this.exitCode = newExecWithLoggingHelpers().execScript(config.getAllConfig(), getSummary(), getCommands(), getShellEnvironment());
+            }
+        }
+        @Override
+        protected String taskTypeShortName() { return taskTypeShortName; }
+    }
+    
+    protected ExecWithLoggingHelpers newExecWithLoggingHelpers() {
+        return new ExecWithLoggingHelpers("Process") {
+            @Override
+            protected <U> U execWithTool(MutableMap<String, Object> props, Function<ShellTool, U> task) {
+                // properties typically passed to both
+                if (log.isDebugEnabled() && props!=null && !props.isEmpty())
+                    log.debug("Ignoring flags "+props+" when running "+this);
+                return task.apply(new ProcessTool());
+            }
+            @Override
+            protected void preExecChecks() {}
+            @Override
+            protected String constructDefaultLoggingPrefix(ConfigBag execFlags) {
+                return "system.exec";
+            }
+            @Override
+            protected String getTargetName() {
+                return "local host";
+            }
+        }.logger(log);
+    }
+
+    /** concrete instance (for generics) */
+    public static class ConcreteSystemProcessTaskFactory<RET> extends SystemProcessTaskFactory<ConcreteSystemProcessTaskFactory<RET>, RET> {
+        public ConcreteSystemProcessTaskFactory(String ...commands) {
+            super(commands);
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/text/DataUriSchemeParser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/text/DataUriSchemeParser.java b/core/src/main/java/org/apache/brooklyn/core/util/text/DataUriSchemeParser.java
new file mode 100644
index 0000000..d4ed26c
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/text/DataUriSchemeParser.java
@@ -0,0 +1,267 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.text;
+
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import brooklyn.util.exceptions.Exceptions;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.BaseEncoding;
+//import com.sun.jersey.core.util.Base64;
+
+/** implementation (currently hokey) of RFC-2397 data: URI scheme.
+ * see: http://stackoverflow.com/questions/12353552/any-rfc-2397-data-uri-parser-for-java */
+public class DataUriSchemeParser {
+
+    public static final String PROTOCOL_PREFIX = "data:";
+    public static final String DEFAULT_MIME_TYPE = "text/plain";
+    public static final String DEFAULT_CHARSET = "US-ASCII";
+    
+    private final String url;
+    private int parseIndex = 0;
+    private boolean isParsed = false;
+    private boolean allowMissingComma = false;
+    private boolean allowSlashesAfterColon = false;
+    private boolean allowOtherLaxities = false;
+    
+    private String mimeType;
+    private byte[] data;
+    private Map<String,String> parameters = new LinkedHashMap<String,String>();
+
+    public DataUriSchemeParser(String url) {
+        this.url = Preconditions.checkNotNull(url, "url");
+    }
+
+    // ---- static conveniences -----
+    
+    public static String toString(String url) {
+        return new DataUriSchemeParser(url).lax().parse().getDataAsString();
+    }
+
+    public static byte[] toBytes(String url) {
+        return new DataUriSchemeParser(url).lax().parse().getData();
+    }
+
+    // ---- accessors (once it is parsed) -----------
+    
+    public String getCharset() {
+        String charset = parameters.get("charset");
+        if (charset!=null) return charset;
+        return DEFAULT_CHARSET;
+    }
+
+    public String getMimeType() {
+        assertParsed();
+        if (mimeType!=null) return mimeType;
+        return DEFAULT_MIME_TYPE;
+    }
+    
+    public Map<String, String> getParameters() {
+        return ImmutableMap.<String, String>copyOf(parameters);
+    }
+
+    public byte[] getData() {
+        assertParsed();
+        return data;
+    }
+    
+    public ByteArrayInputStream getDataAsInputStream() {
+        return new ByteArrayInputStream(getData());
+    }
+
+    public String getDataAsString() {
+        return new String(getData(), Charset.forName(getCharset()));
+    }
+
+    // ---- config ------------------
+    
+    public synchronized DataUriSchemeParser lax() {
+        return allowMissingComma(true).allowSlashesAfterColon(true).allowOtherLaxities(true);
+    }
+        
+    public synchronized DataUriSchemeParser allowMissingComma(boolean allowMissingComma) {
+        assertNotParsed();
+        this.allowMissingComma = allowMissingComma;
+        return this;
+    }
+    
+    public synchronized DataUriSchemeParser allowSlashesAfterColon(boolean allowSlashesAfterColon) {
+        assertNotParsed();
+        this.allowSlashesAfterColon = allowSlashesAfterColon;
+        return this;
+    }
+    
+    private synchronized DataUriSchemeParser allowOtherLaxities(boolean allowOtherLaxities) {
+        assertNotParsed();
+        this.allowOtherLaxities = allowOtherLaxities;
+        return this;
+    }
+    
+    private void assertNotParsed() {
+        if (isParsed) throw new IllegalStateException("Operation not permitted after parsing");
+    }
+
+    private void assertParsed() {
+        if (!isParsed) throw new IllegalStateException("Operation not permitted before parsing");
+    }
+
+    public synchronized DataUriSchemeParser parse() {
+        try {
+            return parseChecked();
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+    public synchronized DataUriSchemeParser parseChecked() throws UnsupportedEncodingException, MalformedURLException {
+        if (isParsed) return this;
+        
+        skipOptional(PROTOCOL_PREFIX);
+        if (allowSlashesAfterColon)
+            while (skipOptional("/")) ;
+        
+        if (allowMissingComma && remainder().indexOf(',')==-1) {
+            mimeType = DEFAULT_MIME_TYPE;
+            parameters.put("charset", DEFAULT_CHARSET);
+        } else {        
+            parseMediaType();
+            parseParameterOrParameterValues();
+            skipRequired(",");
+        }
+        
+        parseData();
+        
+        isParsed = true;
+        return this;
+    }
+
+    private void parseMediaType() throws MalformedURLException {
+        if (remainder().startsWith(";") || remainder().startsWith(","))
+            return;
+        int slash = remainder().indexOf("/");
+        if (slash==-1) throw new MalformedURLException("Missing required '/' in MIME type of data: URL");
+        String type = read(slash);
+        skipRequired("/");
+        int next = nextSemiOrComma();
+        String subtype = read(next);
+        mimeType = type+"/"+subtype;
+    }
+
+    private String read(int next) {
+        String result = remainder().substring(0, next);
+        parseIndex += next;
+        return result;
+    }
+
+    private int nextSemiOrComma() throws MalformedURLException {
+        int semi = remainder().indexOf(';');
+        int comma = remainder().indexOf(',');
+        if (semi<0 && comma<0) throw new MalformedURLException("Missing required ',' in data: URL");
+        if (semi<0) return comma;
+        if (comma<0) return semi;
+        return Math.min(semi, comma);
+    }
+
+    private void parseParameterOrParameterValues() throws MalformedURLException {
+        while (true) {
+            if (!remainder().startsWith(";")) return;
+            parseIndex++;
+            int eq = remainder().indexOf('=');
+            String word, value;
+            int nextSemiOrComma = nextSemiOrComma();
+            if (eq==-1 || eq>nextSemiOrComma) {
+                word = read(nextSemiOrComma);
+                value = null;
+            } else {
+                word = read(eq);
+                if (remainder().startsWith("\"")) {
+                    // is quoted
+                    parseIndex++;
+                    int nextUnescapedQuote = nextUnescapedQuote();
+                    value = "\"" + read(nextUnescapedQuote);
+                } else {
+                    value = read(nextSemiOrComma());
+                }
+            }
+            parameters.put(word, value);
+        }
+    }
+
+    private int nextUnescapedQuote() throws MalformedURLException {
+        int i=0;
+        String r = remainder();
+        boolean escaped = false;
+        while (i<r.length()) {
+            if (escaped) {
+                escaped = false;
+            } else {
+                if (r.charAt(i)=='"') return i;
+                if (r.charAt(i)=='\\') escaped = true;
+            }
+            i++;
+        }
+        throw new MalformedURLException("Unclosed double-quote in data: URL");
+    }
+
+    private void parseData() throws UnsupportedEncodingException, MalformedURLException {
+        if (parameters.containsKey("base64")) {
+            checkNoParamValue("base64");
+            data = BaseEncoding.base64().decode(remainder());
+        } else if (parameters.containsKey("base64url")) {
+            checkNoParamValue("base64url");
+            data = BaseEncoding.base64Url().decode(remainder());
+        } else {
+            data = URLDecoder.decode(remainder(), getCharset()).getBytes(Charset.forName(getCharset()));
+        }
+    }
+
+    private void checkNoParamValue(String param) throws MalformedURLException {
+        if (allowOtherLaxities) return; 
+        String value = parameters.get(param);
+        if (value!=null)
+            throw new MalformedURLException(param+" parameter must not take a value ("+value+") in data: URL");
+    }
+
+    private String remainder() {
+        return url.substring(parseIndex);
+    }
+
+    private boolean skipOptional(String word) {
+        if (remainder().startsWith(word)) {
+            parseIndex += word.length();
+            return true;
+        }
+        return false;
+    }
+
+    private void skipRequired(String word) throws MalformedURLException {
+        if (!remainder().startsWith(word))
+            throw new MalformedURLException("Missing required '"+word+"' at position "+parseIndex+" of data: URL");
+        parseIndex += word.length();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/text/TemplateProcessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/text/TemplateProcessor.java b/core/src/main/java/org/apache/brooklyn/core/util/text/TemplateProcessor.java
new file mode 100644
index 0000000..6fb3c15
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/text/TemplateProcessor.java
@@ -0,0 +1,398 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.text;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.drivers.EntityDriver;
+import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.event.basic.DependentConfiguration;
+import brooklyn.event.basic.Sensors;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Files;
+
+import freemarker.cache.StringTemplateLoader;
+import freemarker.template.Configuration;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateHashModel;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+/** A variety of methods to assist in Freemarker template processing,
+ * including passing in maps with keys flattened (dot-separated namespace),
+ * and accessing {@link ManagementContext} brooklyn.properties 
+ * and {@link Entity}, {@link EntityDriver}, and {@link Location} methods and config.
+ * <p>
+ * See {@link #processTemplateContents(String, ManagementContextInternal, Map)} for
+ * a description of how management access is done.
+ */
+public class TemplateProcessor {
+
+    private static final Logger log = LoggerFactory.getLogger(TemplateProcessor.class);
+
+    protected static TemplateModel wrapAsTemplateModel(Object o) throws TemplateModelException {
+        if (o instanceof Map) return new DotSplittingTemplateModel((Map<?,?>)o);
+        return ObjectWrapper.DEFAULT_WRAPPER.wrap(o);
+    }
+    
+    /** @deprecated since 0.7.0 use {@link #processTemplateFile(String, Map)} */ @Deprecated
+    public static String processTemplate(String templateFileName, Map<String, ? extends Object> substitutions) {
+        return processTemplateFile(templateFileName, substitutions);
+    }
+    
+    /** As per {@link #processTemplateContents(String, Map)}, but taking a file. */
+    public static String processTemplateFile(String templateFileName, Map<String, ? extends Object> substitutions) {
+        String templateContents;
+        try {
+            templateContents = Files.toString(new File(templateFileName), Charsets.UTF_8);
+        } catch (IOException e) {
+            log.warn("Error loading file " + templateFileName, e);
+            throw Exceptions.propagate(e);
+        }
+        return processTemplateContents(templateContents, substitutions);
+    }
+
+    /** @deprecated since 0.7.0 use {@link #processTemplateFile(String, EntityDriver, Map)} */ @Deprecated
+    public static String processTemplate(String templateFileName, EntityDriver driver, Map<String, ? extends Object> extraSubstitutions) {
+        return processTemplateFile(templateFileName, driver, extraSubstitutions);
+    }
+    
+    /** Processes template contents according to {@link EntityAndMapTemplateModel}. */
+    public static String processTemplateFile(String templateFileName, EntityDriver driver, Map<String, ? extends Object> extraSubstitutions) {
+        String templateContents;
+        try {
+            templateContents = Files.toString(new File(templateFileName), Charsets.UTF_8);
+        } catch (IOException e) {
+            log.warn("Error loading file " + templateFileName, e);
+            throw Exceptions.propagate(e);
+        }
+        return processTemplateContents(templateContents, driver, extraSubstitutions);
+    }
+
+    /** Processes template contents according to {@link EntityAndMapTemplateModel}. */
+    public static String processTemplateContents(String templateContents, EntityDriver driver, Map<String,? extends Object> extraSubstitutions) {
+        return processTemplateContents(templateContents, new EntityAndMapTemplateModel(driver, extraSubstitutions));
+    }
+
+    /** Processes template contents according to {@link EntityAndMapTemplateModel}. */
+    public static String processTemplateContents(String templateContents, ManagementContext managementContext, Map<String,? extends Object> extraSubstitutions) {
+        return processTemplateContents(templateContents, new EntityAndMapTemplateModel(managementContext, extraSubstitutions));
+    }
+
+    /**
+     * A Freemarker {@link TemplateHashModel} which will correctly handle entries of the form "a.b" in this map,
+     * matching against template requests for "${a.b}".
+     * <p>
+     * Freemarker requests "a" in a map when given such a request, and expects that to point to a map
+     * with a key "b". This model provides such maps even for "a.b" in a map.
+     * <p>
+     * However if "a" <b>and</b> "a.b" are in the map, this will <b>not</b> currently do the deep mapping.
+     * (It does not have enough contextual information from Freemarker to handle this case.) */
+    public static final class DotSplittingTemplateModel implements TemplateHashModel {
+        protected final Map<?,?> map;
+
+        protected DotSplittingTemplateModel(Map<?,?> map) {
+            this.map = map;
+        }
+
+        @Override
+        public boolean isEmpty() { return map!=null && map.isEmpty(); }
+
+        public boolean contains(String key) {
+            if (map==null) return false;
+            if (map.containsKey(key)) return true;
+            for (Map.Entry<?,?> entry: map.entrySet()) {
+                String k = Strings.toString(entry.getKey());
+                if (k.startsWith(key+".")) {
+                    // contains this prefix
+                    return true;
+                }
+            }
+            return false;
+        }
+        
+        @Override
+        public TemplateModel get(String key) throws TemplateModelException {
+            if (map==null) return null;
+            try {
+                if (map.containsKey(key)) 
+                    return wrapAsTemplateModel( map.get(key) );
+                
+                Map<String,Object> result = MutableMap.of();
+                for (Map.Entry<?,?> entry: map.entrySet()) {
+                    String k = Strings.toString(entry.getKey());
+                    if (k.startsWith(key+".")) {
+                        String k2 = Strings.removeFromStart(k, key+".");
+                        result.put(k2, entry.getValue());
+                    }
+                }
+                if (!result.isEmpty()) 
+                        return wrapAsTemplateModel( result );
+                
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                throw new IllegalStateException("Error accessing config '"+key+"'"+": "+e, e);
+            }
+            
+            return null;
+        }
+        
+        @Override
+        public String toString() {
+            return getClass().getName()+"["+map+"]";
+        }
+    }
+    
+    /** FreeMarker {@link TemplateHashModel} which resolves keys inside the given entity or management context.
+     * Callers are required to include dots for dot-separated keys.
+     * Freemarker will only due this when in inside bracket notation in an outer map, as in <code>${outer['a.b.']}</code>; 
+     * as a result this is intended only for use by {@link EntityAndMapTemplateModel} where 
+     * a caller has used bracked notation, as in <code>${mgmt['key.subkey']}</code>. */
+    protected static final class EntityConfigTemplateModel implements TemplateHashModel {
+        protected final EntityInternal entity;
+        protected final ManagementContext mgmt;
+
+        protected EntityConfigTemplateModel(EntityInternal entity) {
+            this.entity = entity;
+            this.mgmt = entity.getManagementContext();
+        }
+
+        protected EntityConfigTemplateModel(ManagementContext mgmt) {
+            this.entity = null;
+            this.mgmt = mgmt;
+        }
+
+        @Override
+        public boolean isEmpty() { return false; }
+
+        @Override
+        public TemplateModel get(String key) throws TemplateModelException {
+            try {
+                Object result = null;
+                
+                if (entity!=null)
+                    result = entity.getConfig(ConfigKeys.builder(Object.class).name(key).build());
+                if (result==null && mgmt!=null)
+                    result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());
+                
+                if (result!=null)
+                    return wrapAsTemplateModel( result );
+                
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                throw new IllegalStateException("Error accessing config '"+key+"'"
+                    + (entity!=null ? " on "+entity : "")+": "+e, e);
+            }
+            
+            return null;
+        }
+        
+        @Override
+        public String toString() {
+            return getClass().getName()+"["+entity+"]";
+        }
+    }
+
+    protected final static class EntityAttributeTemplateModel implements TemplateHashModel {
+        protected final EntityInternal entity;
+
+        protected EntityAttributeTemplateModel(EntityInternal entity) {
+            this.entity = entity;
+        }
+
+        @Override
+        public boolean isEmpty() throws TemplateModelException {
+            return false;
+        }
+
+        @Override
+        public TemplateModel get(String key) throws TemplateModelException {
+            Object result;
+            try {
+                result = Entities.submit(entity, DependentConfiguration.attributeWhenReady(entity,
+                        Sensors.builder(Object.class, key).persistence(AttributeSensor.SensorPersistenceMode.NONE).build())).get();
+            } catch (Exception e) {
+                throw Exceptions.propagate(e);
+            }
+            if (result == null) {
+                return null;
+            } else {
+                return wrapAsTemplateModel(result);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return getClass().getName()+"["+entity+"]";
+        }
+    }
+
+    /**
+     * Provides access to config on an entity or management context, using
+     * <code>${config['entity.config.key']}</code> or <code>${mgmt['brooklyn.properties.key']}</code> notation,
+     * and also allowing access to <code>getX()</code> methods on entity (interface) or driver
+     * using <code>${entity.x}</code> or <code><${driver.x}</code>.
+     * Optional extra properties can be supplied, treated as per {@link DotSplittingTemplateModel}.
+     */
+    protected static final class EntityAndMapTemplateModel implements TemplateHashModel {
+        protected final EntityInternal entity;
+        protected final EntityDriver driver;
+        protected final ManagementContext mgmt;
+        protected final DotSplittingTemplateModel extraSubstitutionsModel;
+
+        protected EntityAndMapTemplateModel(ManagementContext mgmt, Map<String,? extends Object> extraSubstitutions) {
+            this.entity = null;
+            this.driver = null;
+            this.mgmt = mgmt;
+            this.extraSubstitutionsModel = new DotSplittingTemplateModel(extraSubstitutions);
+        }
+
+        protected EntityAndMapTemplateModel(EntityDriver driver, Map<String,? extends Object> extraSubstitutions) {
+            this.driver = driver;
+            this.entity = (EntityInternal) driver.getEntity();
+            this.mgmt = entity.getManagementContext();
+            this.extraSubstitutionsModel = new DotSplittingTemplateModel(extraSubstitutions);
+        }
+
+        protected EntityAndMapTemplateModel(EntityInternal entity, Map<String,? extends Object> extraSubstitutions) {
+            this.entity = entity;
+            this.driver = null;
+            this.mgmt = entity.getManagementContext();
+            this.extraSubstitutionsModel = new DotSplittingTemplateModel(extraSubstitutions);
+        }
+
+        @Override
+        public boolean isEmpty() { return false; }
+
+        @Override
+        public TemplateModel get(String key) throws TemplateModelException {
+            if (extraSubstitutionsModel.contains(key))
+                return wrapAsTemplateModel( extraSubstitutionsModel.get(key) );
+
+            if ("entity".equals(key) && entity!=null)
+                return wrapAsTemplateModel( entity );
+            if ("config".equals(key)) {
+                if (entity!=null)
+                    return new EntityConfigTemplateModel(entity);
+                else
+                    return new EntityConfigTemplateModel(mgmt);
+            }
+            if ("mgmt".equals(key)) {
+                return new EntityConfigTemplateModel(mgmt);
+            }
+
+            if ("driver".equals(key) && driver!=null)
+                return wrapAsTemplateModel( driver );
+            if ("location".equals(key)) {
+                if (driver!=null && driver.getLocation()!=null)
+                    return wrapAsTemplateModel( driver.getLocation() );
+                if (entity!=null)
+                    return wrapAsTemplateModel( Iterables.getOnlyElement( entity.getLocations() ) );
+            }
+            if ("attribute".equals(key)) {
+                return new EntityAttributeTemplateModel(entity);
+            }
+            
+            if (mgmt!=null) {
+                // TODO deprecated in 0.7.0, remove after next version
+                // ie not supported to access global props without qualification
+                Object result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());
+                if (result!=null) { 
+                    log.warn("Deprecated access of global brooklyn.properties value for "+key+"; should be qualified with 'mgmt.'");
+                    return wrapAsTemplateModel( result );
+                }
+            }
+            
+            if ("javaSysProps".equals(key))
+                return wrapAsTemplateModel( System.getProperties() );
+
+            return null;
+        }
+        
+        @Override
+        public String toString() {
+            return getClass().getName()+"["+(entity!=null ? entity : mgmt)+"]";
+        }
+    }
+
+    /** Processes template contents with the given items in scope as per {@link EntityAndMapTemplateModel}. */
+    public static String processTemplateContents(String templateContents, final EntityInternal entity, Map<String,? extends Object> extraSubstitutions) {
+        return processTemplateContents(templateContents, new EntityAndMapTemplateModel(entity, extraSubstitutions));
+    }
+    
+    /** Processes template contents using the given map, passed to freemarker,
+     * with dot handling as per {@link DotSplittingTemplateModel}. */
+    public static String processTemplateContents(String templateContents, final Map<String, ? extends Object> substitutions) {
+        TemplateHashModel root;
+        try {
+            root = substitutions != null
+                ? (TemplateHashModel)wrapAsTemplateModel(substitutions)
+                : null;
+        } catch (TemplateModelException e) {
+            throw new IllegalStateException("Unable to set up TemplateHashModel to parse template, given "+substitutions+": "+e, e);
+        }
+        
+        return processTemplateContents(templateContents, root);
+    }
+    
+    /** Processes template contents against the given {@link TemplateHashModel}. */
+    public static String processTemplateContents(String templateContents, final TemplateHashModel substitutions) {
+        try {
+            Configuration cfg = new Configuration();
+            StringTemplateLoader templateLoader = new StringTemplateLoader();
+            templateLoader.putTemplate("config", templateContents);
+            cfg.setTemplateLoader(templateLoader);
+            Template template = cfg.getTemplate("config");
+
+            // TODO could expose CAMP '$brooklyn:' style dsl, based on template.createProcessingEnvironment
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            Writer out = new OutputStreamWriter(baos);
+            template.process(substitutions, out);
+            out.flush();
+
+            return new String(baos.toByteArray());
+        } catch (Exception e) {
+            log.warn("Error processing template (propagating): "+e, e);
+            log.debug("Template which could not be parsed (causing "+e+") is:"
+                + (Strings.isMultiLine(templateContents) ? "\n"+templateContents : templateContents));
+            throw Exceptions.propagate(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/CompilerIndependentOuterClassFieldMapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/CompilerIndependentOuterClassFieldMapper.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/CompilerIndependentOuterClassFieldMapper.java
new file mode 100644
index 0000000..68c7382
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/CompilerIndependentOuterClassFieldMapper.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.thoughtworks.xstream.core.Caching;
+import com.thoughtworks.xstream.mapper.Mapper;
+import com.thoughtworks.xstream.mapper.MapperWrapper;
+
+/**
+ * <p>Compiler independent outer class field mapper.</p>
+ * <p>Different compilers generate different indexes for the names of outer class reference
+ *    field (this$N) leading to deserialization errors.</p>
+ * <ul>
+ *   <li> eclipse-[groovy-]compiler counts all outer static classes
+ *   <li> OpenJDK/Oracle/IBM compiler starts at 0, regardless of the nesting level
+ * </ul>
+ * <p>The mapper will be able to update field names for instances with a single this$N
+ *    field only (including those from parent classes).</p>
+ * <p>For difference between generated field names compare
+ *    {@code src/test/java/brooklyn/util/xstream/compiler_compatibility_eclipse.xml} and
+ *    {@code src/test/java/brooklyn/util/xstream/compiler_compatibility_oracle.xml},
+ *    generated from {@code org.apache.brooklyn.core.util.xstream.CompilerCompatibilityTest}</p>
+ * <p>JLS 1.1 relevant section, copied verbatim for a lack of reliable URL:</p>
+ * <blockquote>
+ *  <p>Java 1.1 compilers are strongly encouraged, though not required, to use the
+ *     following naming conventions when implementing inner classes. Compilers may
+ *     not use synthetic names of the forms defined here for any other purposes.</p>
+ *  <p>A synthetic field pointing to the outermost enclosing instance is named this$0.
+ *     The next-outermost enclosing instance is this$1, and so forth. (At most one such
+ *     field is necessary in any given inner class.) A synthetic field containing a copy
+ *     of a constant v is named val$v. These fields are final.</p>
+ * </blockquote>
+ * <p>Currently available at
+ *    http://web.archive.org/web/20000830111107/http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html</p>
+ */
+public class CompilerIndependentOuterClassFieldMapper extends MapperWrapper implements Caching {
+    public static final Logger LOG = LoggerFactory.getLogger(CompilerIndependentOuterClassFieldMapper.class);
+
+    private static final String OUTER_CLASS_FIELD_PREFIX = "this$";
+
+    private final Map<String, Collection<String>> classOuterFields = new ConcurrentHashMap<String, Collection<String>>();
+
+    public CompilerIndependentOuterClassFieldMapper(Mapper wrapped) {
+        super(wrapped);
+        classOuterFields.put(Object.class.getName(), Collections.<String>emptyList());
+    }
+
+    @Override
+    public String realMember(@SuppressWarnings("rawtypes") Class type, String serialized) {
+        // Let com.thoughtworks.xstream.mapper.OuterClassMapper also run on the input.
+        String serializedFieldName = super.realMember(type, serialized);
+
+        if (serializedFieldName.startsWith(OUTER_CLASS_FIELD_PREFIX)) {
+            Collection<String> compiledFieldNames = findOuterClassFieldNames(type);
+            if (compiledFieldNames.size() == 0) {
+                throw new IllegalStateException("Unable to find any outer class fields in " + type + ", searching specifically for " + serializedFieldName);
+            }
+
+            Set<String> uniqueFieldNames = new HashSet<String>(compiledFieldNames);
+            String deserializeFieldName;
+            if (!compiledFieldNames.contains(serializedFieldName)) {
+                String msg =
+                        "Unable to find outer class field " + serializedFieldName + " in class " + type + ". " +
+                        "This could be caused by " +
+                        "1) changing the class (or one of its parents) to a static or " +
+                        "2) moving the class to a different lexical level (enclosing classes) or " +
+                        "3) using a different compiler (i.e eclipse vs oracle) at the time the object was serialized. ";
+                if (uniqueFieldNames.size() == 1) {
+                    // Try to fix the field naming only for the case with a single field or 
+                    // multiple fields with the same name, in which case XStream puts defined-in
+                    // for the field declared in super.
+                    //
+                    // We don't have access to the XML elements from here to check for same name
+                    // so we check the target class instead. This should work most of the time, but
+                    // if code is recompiled in such a way that the new instance has fields with
+                    // different names, where only the field of the extending class is renamed and
+                    // the super field is not, then the instance will be deserialized incorrectly -
+                    // the super field will be assigned both times. If the field type is incompatible
+                    // then a casting exception will be thrown, if it's the same then only the warning
+                    // below will indicate of a possible problem down the line - most probably NPE on
+                    // the this$N field.
+                    deserializeFieldName = compiledFieldNames.iterator().next();
+                    LOG.warn(msg + "Will use the field " + deserializeFieldName + " instead.");
+                } else {
+                    // Multiple fields with differing names case - don't try to fix it.
+                    // Better fail with an explicit error, and have someone fix it manually,
+                    // than try to fix it here non-reliably and have it fail down the line
+                    // with some unrelated error.
+                    // XStream will fail later with a field not found exception.
+                    LOG.error(msg + "Will fail with a field not found exception. " +
+                            "Edit the persistence state manually and update the field names. "+
+                            "Existing field names are " + uniqueFieldNames);
+                    deserializeFieldName = serializedFieldName;
+                }
+            } else {
+                if (uniqueFieldNames.size() > 1) {
+                    // Log at debug level as the actual problem would occur in very specific cases. Only
+                    // useful when the compiler is changed, otherwise leads to false positives.
+                    LOG.debug("Deserializing the non-static class " + type + " with multiple outer class fields " + uniqueFieldNames + ". " +
+                            "When changing compilers it's possible that the instance won't be able to be deserialized due to changed outer class field names. " +
+                            "In those cases deserialization could fail with field not found exception or class cast exception following this log line.");
+                }
+                deserializeFieldName = serializedFieldName;
+            }
+
+            return deserializeFieldName;
+        } else {
+            return serializedFieldName;
+        }
+    }
+    
+    private Collection<String> findOuterClassFieldNames(Class<?> type) {
+        Collection<String> fields = classOuterFields.get(type.getName());
+        if (fields == null) {
+            fields = new ArrayList<String>();
+            addOuterClassFields(type, fields);
+            classOuterFields.put(type.getName(), fields);
+        }
+        return fields;
+    }
+    
+    private void addOuterClassFields(Class<?> type, Collection<String> fields) {
+        for (Field field : type.getDeclaredFields()) {
+            if (field.isSynthetic()) {
+                fields.add(field.getName());
+            }
+        }
+        if (type.getSuperclass() != null) {
+            addOuterClassFields(type.getSuperclass(), fields);
+        }
+    }
+
+    @Override
+    public void flushCache() {
+        classOuterFields.keySet().retainAll(Collections.singletonList(Object.class.getName()));
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingConverter.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingConverter.java
new file mode 100644
index 0000000..7d95568
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingConverter.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import brooklyn.util.exceptions.Exceptions;
+
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.converters.enums.EnumConverter;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+/** ... except this doesn't seem to get applied when we think it should
+ * (normal xstream.resgisterConverter doesn't apply to enums) */
+public class EnumCaseForgivingConverter extends EnumConverter {
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        Class type = context.getRequiredType();
+        if (type.getSuperclass() != Enum.class) {
+            type = type.getSuperclass(); // polymorphic enums
+        }
+        String token = reader.getValue();
+        // this is the new bit (overriding superclass to accept case-insensitive)
+        return resolve(type, token);
+    }
+
+    public static <T extends Enum<T>> T resolve(Class<T> type, String token) {
+        try {
+            return Enum.valueOf(type, token.toUpperCase());
+        } catch (Exception e) {
+            
+            // new stuff here:  try reading case insensitive
+            
+            Exceptions.propagateIfFatal(e);
+            try {
+                for (T v: type.getEnumConstants())
+                    if (v.name().equalsIgnoreCase(token)) return v;
+                throw e;
+            } catch (Exception e2) {
+                throw Exceptions.propagate(e2);
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingSingleValueConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingSingleValueConverter.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingSingleValueConverter.java
new file mode 100644
index 0000000..4bc507c
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/EnumCaseForgivingSingleValueConverter.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import com.thoughtworks.xstream.converters.enums.EnumSingleValueConverter;
+
+public class EnumCaseForgivingSingleValueConverter extends EnumSingleValueConverter {
+
+    private final Class enumType;
+
+    public EnumCaseForgivingSingleValueConverter(Class type) {
+        super(type);
+        enumType = type;
+    }
+
+    public Object fromString(String str) {
+        return EnumCaseForgivingConverter.resolve(enumType, str);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableListConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableListConverter.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableListConverter.java
new file mode 100644
index 0000000..a59cfa8
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableListConverter.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.util.Collection;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.converters.collections.CollectionConverter;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.mapper.Mapper;
+
+public class ImmutableListConverter extends CollectionConverter {
+
+    public ImmutableListConverter(Mapper mapper) {
+        super(mapper);
+    }
+
+    @Override
+    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
+        return ImmutableList.class.isAssignableFrom(type);
+    }
+
+    // marshalling is the same
+    // so is unmarshalling the entries
+
+    // only differences are creating the overarching collection, which we do after the fact
+    // (optimizing format on disk as opposed to in-memory), and we discard null values 
+    // to avoid failing entirely.
+    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        Collection<?> collection = Lists.newArrayList();
+        populateCollection(reader, context, collection);
+        return ImmutableList.copyOf(Iterables.filter(collection, Predicates.notNull()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableMapConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableMapConverter.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableMapConverter.java
new file mode 100644
index 0000000..00e44dd
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableMapConverter.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.mapper.Mapper;
+
+public class ImmutableMapConverter extends MapConverter {
+
+    public ImmutableMapConverter(Mapper mapper) {
+        super(mapper);
+    }
+
+    @Override
+    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
+        return ImmutableMap.class.isAssignableFrom(type);
+    }
+
+    // marshalling is the same
+    // so is unmarshalling the entries
+
+    // only differences are creating the overarching collection, which we do after the fact
+    // (optimizing format on disk as opposed to in-memory), and we discard null key/values 
+    // to avoid failing entirely.
+    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        Map<?, ?> map = Maps.newLinkedHashMap();
+        populateMap(reader, context, map);
+        return ImmutableMap.copyOf(Maps.filterEntries(map, new Predicate<Map.Entry<?,?>>() {
+                @Override public boolean apply(Entry<?, ?> input) {
+                    return input != null && input.getKey() != null && input.getValue() != null;
+                }}));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableSetConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableSetConverter.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableSetConverter.java
new file mode 100644
index 0000000..76975ff
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/ImmutableSetConverter.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.util.Collection;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.converters.collections.CollectionConverter;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.mapper.Mapper;
+
+public class ImmutableSetConverter extends CollectionConverter {
+
+    public ImmutableSetConverter(Mapper mapper) {
+        super(mapper);
+    }
+
+    @Override
+    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
+        return ImmutableSet.class.isAssignableFrom(type);
+    }
+
+    // marshalling is the same
+    // so is unmarshalling the entries
+
+    // only differences are creating the overarching collection, which we do after the fact
+    // (optimizing format on disk as opposed to in-memory), and we discard null values 
+    // to avoid failing entirely.
+    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        Collection<?> collection = Lists.newArrayList();
+        populateCollection(reader, context, collection);
+        return ImmutableSet.copyOf(Iterables.filter(collection, Predicates.notNull()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/Inet4AddressConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/Inet4AddressConverter.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/Inet4AddressConverter.java
new file mode 100644
index 0000000..53133c1
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/Inet4AddressConverter.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
+
+import brooklyn.util.exceptions.Exceptions;
+
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+public class Inet4AddressConverter implements Converter {
+
+    @Override
+    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
+        return type.equals(Inet4Address.class);
+    }
+
+    @Override
+    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
+        Inet4Address addr = (Inet4Address) source;
+        writer.setValue(addr.getHostName()+"/"+addr.getHostAddress());
+    }
+
+    @Override
+    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        String hostSlashAddress = reader.getValue();
+        int i = hostSlashAddress.indexOf('/');
+        try {
+            if (i==-1) {
+                return Inet4Address.getByName(hostSlashAddress);
+            } else {
+                String host = hostSlashAddress.substring(0, i);
+                String addrS = hostSlashAddress.substring(i+1);
+                byte[] addr = new byte[4];
+                String[] addrSI = addrS.split("\\.");
+                for (int k=0; k<4; k++) addr[k] = (byte)(int)Integer.valueOf(addrSI[k]);
+                return Inet4Address.getByAddress(host, addr);
+            }
+        } catch (UnknownHostException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/MapConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/MapConverter.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/MapConverter.java
new file mode 100644
index 0000000..752ef70
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/MapConverter.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.core.ReferencingMarshallingContext;
+import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.mapper.Mapper;
+
+/** equivalent to super, but cleaner methods, overridable, logging, and some retries */
+public class MapConverter extends com.thoughtworks.xstream.converters.collections.MapConverter {
+
+    private static final Logger log = LoggerFactory.getLogger(MapConverter.class);
+    
+    public MapConverter(Mapper mapper) {
+        super(mapper);
+    }
+
+    @SuppressWarnings({ "rawtypes" })
+    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
+        Map map = (Map) source;
+        try {
+            for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                marshalEntry(writer, context, entry);
+            }
+        } catch (ConcurrentModificationException e) {
+            log.debug("Map "
+                // seems there is no non-deprecated way to get the path...
+                + (context instanceof ReferencingMarshallingContext ? "at "+((ReferencingMarshallingContext)context).currentPath() : "")
+                + "["+source+"] modified while serializing; will fail, and retry may be attempted");
+            throw e;
+            // would be nice to attempt to re-serialize being slightly more defensive, as code below;
+            // but the code above may have written partial data so that is dangerous, we could have corrupted output. 
+            // if we could mark and restore in the output stream then we could do this below (but we don't have that in our stream),
+            // or we could try this copying code in the first instance (but that's slow);
+            // error is rare most of the time (e.g. attribute being updated) so we bail on this whole attempt
+            // and simply try serializing the map-owner (e.g. an entity) again.
+//            ImmutableList entries = ImmutableList.copyOf(map.entrySet());
+//            for (Iterator iterator = entries.iterator(); iterator.hasNext();) {
+//                Map.Entry entry = (Map.Entry) iterator.next();
+//                marshalEntry(writer, context, entry);                
+//            }
+        }
+    }
+
+    protected String getEntryNodeName() { return mapper().serializedClass(Map.Entry.class); }
+    
+    protected void marshalEntry(HierarchicalStreamWriter writer, MarshallingContext context, Map.Entry entry) {
+        ExtendedHierarchicalStreamWriterHelper.startNode(writer, getEntryNodeName(), Map.Entry.class);
+
+        writeItem(entry.getKey(), context, writer);
+        writeItem(entry.getValue(), context, writer);
+
+        writer.endNode();
+    }
+
+    protected void populateMap(HierarchicalStreamReader reader, UnmarshallingContext context, Map map) {
+        while (reader.hasMoreChildren()) {
+            reader.moveDown();
+            unmarshalEntry(reader, context, map);
+            reader.moveUp();
+        }
+    }
+
+    protected void unmarshalEntry(HierarchicalStreamReader reader, UnmarshallingContext context, Map map) {
+        reader.moveDown();
+        Object key = readItem(reader, context, map);
+        reader.moveUp();
+
+        reader.moveDown();
+        Object value = readItem(reader, context, map);
+        reader.moveUp();
+
+        map.put(key, value);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/MutableSetConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/MutableSetConverter.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/MutableSetConverter.java
new file mode 100644
index 0000000..4b7bcbc
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/MutableSetConverter.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import brooklyn.util.collections.MutableSet;
+
+import com.thoughtworks.xstream.converters.collections.CollectionConverter;
+import com.thoughtworks.xstream.mapper.Mapper;
+
+public class MutableSetConverter extends CollectionConverter {
+
+    // Although this class seems pointless (!), without registering an explicit converter for MutableSet then the
+    // declaration for Set interferes, causing MutableSet.map field to be null on deserialization.
+    
+    public MutableSetConverter(Mapper mapper) {
+        super(mapper);
+    }
+
+    @Override
+    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
+        return MutableSet.class.isAssignableFrom(type);
+    }
+
+    @Override
+    protected Object createCollection(Class type) {
+        return new MutableSet<Object>();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/StringKeyMapConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/StringKeyMapConverter.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/StringKeyMapConverter.java
new file mode 100644
index 0000000..86a4cd0
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/StringKeyMapConverter.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.text.Identifiers;
+
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.mapper.Mapper;
+
+/** converter which simplifies representation of a map for string-based keys,
+ * to <key>value</key>, or <entry key="key" type="string">value</entry> 
+ * @author alex
+ *
+ */
+public class StringKeyMapConverter extends MapConverter {
+
+    private static final Logger log = LoggerFactory.getLogger(StringKeyMapConverter.class);
+    
+    // full stop is technically allowed ... goes against "best practice" ... 
+    // but simplifies property maps, and is used elsewhere in xstream's repn
+    final static String VALID_XML_NODE_NAME_CHARS = Identifiers.JAVA_GOOD_NONSTART_CHARS + ".";
+
+    final static String VALID_XML_NODE_NAME_START_CHARS = Identifiers.JAVA_GOOD_START_CHARS + ".";
+
+    public StringKeyMapConverter(Mapper mapper) {
+        super(mapper);
+    }
+    
+    protected boolean isKeyValidForNodeName(String key) {
+        // return false to always write as <entry key="key" ...; otherwise only use that when key is not valid xml
+        return Identifiers.isValidToken(key, VALID_XML_NODE_NAME_START_CHARS, VALID_XML_NODE_NAME_CHARS);
+    }
+    
+    public boolean canConvert(Class type) {
+        return super.canConvert(type) || type.getName().equals(MutableMap.class.getName());
+    }
+    
+    @Override
+    protected void marshalEntry(HierarchicalStreamWriter writer, MarshallingContext context, Entry entry) {
+        if (entry.getKey() instanceof String) {
+            marshalStringKey(writer, context, entry);
+        } else {
+            super.marshalEntry(writer, context, entry);
+        }
+    }
+    
+    protected void marshalStringKey(HierarchicalStreamWriter writer, MarshallingContext context, Entry entry) {
+        String key = (String)entry.getKey();
+        String entryNodeName = getEntryNodeName();
+        boolean useKeyAsNodeName = (!key.equals(entryNodeName) && isKeyValidForNodeName(key));
+        if (useKeyAsNodeName) entryNodeName = key;
+        ExtendedHierarchicalStreamWriterHelper.startNode(writer, entryNodeName, Map.Entry.class);
+        if (!useKeyAsNodeName)
+            writer.addAttribute("key", key);
+        
+        Object value = entry.getValue();
+        if (entry.getValue()!=null && isInlineableType(value.getClass())) {
+            if (!(value instanceof String))
+                writer.addAttribute("type", mapper().serializedClass(entry.getValue().getClass()));
+            if (entry.getValue().getClass().isEnum())
+                writer.setValue(((Enum)entry.getValue()).name());
+            else
+                writer.setValue(""+entry.getValue());
+        } else {
+            writeItem(entry.getValue(), context, writer);
+        }
+        
+        writer.endNode();
+    }
+
+    protected boolean isInlineableType(Class<?> type) {
+        return TypeCoercions.isPrimitiveOrBoxer(type) || String.class.equals(type) || type.isEnum();
+    }
+    
+    @Override
+    protected void unmarshalEntry(HierarchicalStreamReader reader, UnmarshallingContext context, Map map) {
+        String key = reader.getNodeName(); 
+        if (key.equals(getEntryNodeName())) key = reader.getAttribute("key");
+        if (key==null) {
+            super.unmarshalEntry(reader, context, map);
+        } else {
+            unmarshalStringKey(reader, context, map, key);
+        }
+    }
+
+    protected void unmarshalStringKey(HierarchicalStreamReader reader, UnmarshallingContext context, Map map, String key) {
+        String type = reader.getAttribute("type");
+        Object value;
+        if (type==null && reader.hasMoreChildren()) {
+            reader.moveDown();
+            value = readItem(reader, context, map);
+            reader.moveUp();
+        } else {
+            Class typeC = type!=null ? mapper().realClass(type) : String.class;
+            try {
+                value = TypeCoercions.coerce(reader.getValue(), typeC);
+            } catch (Exception e) {
+                log.warn("FAILED to coerce "+reader.getValue()+" to "+typeC+": "+e);
+                throw Exceptions.propagate(e);
+            }
+        }
+        map.put(key, value);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/xstream/XmlSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/xstream/XmlSerializer.java b/core/src/main/java/org/apache/brooklyn/core/util/xstream/XmlSerializer.java
new file mode 100644
index 0000000..3b3b582
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/xstream/XmlSerializer.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.xstream;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.collections.MutableSet;
+
+import com.google.common.collect.ImmutableList;
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.mapper.MapperWrapper;
+
+public class XmlSerializer<T> {
+
+    protected final XStream xstream;
+    
+    public XmlSerializer() {
+        xstream = new XStream() {
+            @Override
+            protected MapperWrapper wrapMapper(MapperWrapper next) {
+                MapperWrapper result = super.wrapMapper(next);
+                return XmlSerializer.this.wrapMapper(result);
+            }
+        };
+        
+        // list as array list is default
+        xstream.alias("map", Map.class, LinkedHashMap.class);
+        xstream.alias("set", Set.class, LinkedHashSet.class);
+        
+        xstream.registerConverter(new StringKeyMapConverter(xstream.getMapper()), /* priority */ 10);
+        xstream.alias("MutableMap", MutableMap.class);
+        xstream.alias("MutableSet", MutableSet.class);
+        xstream.alias("MutableList", MutableList.class);
+        
+        // Needs an explicit MutableSet converter!
+        // Without it, the alias for "set" seems to interfere with the MutableSet.map field, so it gets
+        // a null field on deserialization.
+        xstream.registerConverter(new MutableSetConverter(xstream.getMapper()));
+        
+        xstream.aliasType("ImmutableList", ImmutableList.class);
+        xstream.registerConverter(new ImmutableListConverter(xstream.getMapper()));
+        xstream.registerConverter(new ImmutableSetConverter(xstream.getMapper()));
+        xstream.registerConverter(new ImmutableMapConverter(xstream.getMapper()));
+
+        xstream.registerConverter(new EnumCaseForgivingConverter());
+        xstream.registerConverter(new Inet4AddressConverter());
+    }
+    
+    protected MapperWrapper wrapMapper(MapperWrapper next) {
+        return new CompilerIndependentOuterClassFieldMapper(next);
+    }
+
+    public void serialize(Object object, Writer writer) {
+        xstream.toXML(object, writer);
+    }
+
+    @SuppressWarnings("unchecked")
+    public T deserialize(Reader xml) {
+        return (T) xstream.fromXML(xml);
+    }
+
+    public String toString(T memento) {
+        Writer writer = new StringWriter();
+        serialize(memento, writer);
+        return writer.toString();
+    }
+
+    public T fromString(String xml) {
+        return deserialize(new StringReader(xml));
+    }
+
+}



[22/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ForwardingTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ForwardingTask.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ForwardingTask.java
new file mode 100644
index 0000000..794dea9
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ForwardingTask.java
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.brooklyn.api.management.Task;
+
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.ExecutionList;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public abstract class ForwardingTask<T> extends ForwardingObject implements TaskInternal<T> {
+
+    /** Constructor for use by subclasses. */
+    protected ForwardingTask() {}
+
+    @Override
+    protected abstract TaskInternal<T> delegate();
+
+    @Override
+    public void addListener(Runnable listener, Executor executor) {
+        delegate().addListener(listener, executor);
+    }
+
+    @Override
+    public boolean cancel(boolean arg0) {
+        return delegate().cancel(arg0);
+    }
+
+    @Override
+    public T get() throws InterruptedException, ExecutionException {
+        return delegate().get();
+    }
+
+    @Override
+    public T get(long arg0, TimeUnit arg1) throws InterruptedException, ExecutionException, TimeoutException {
+        return delegate().get(arg0, arg1);
+    }
+
+    @Override
+    public boolean isCancelled() {
+        return delegate().isCancelled();
+    }
+
+    @Override
+    public boolean isDone() {
+        return delegate().isDone();
+    }
+
+    @Override
+    public Task<T> asTask() {
+        return delegate().asTask();
+    }
+
+    @Override
+    public String getId() {
+        return delegate().getId();
+    }
+
+    @Override
+    public Set<Object> getTags() {
+        return delegate().getTags();
+    }
+
+    @Override
+    public long getSubmitTimeUtc() {
+        return delegate().getSubmitTimeUtc();
+    }
+
+    @Override
+    public long getStartTimeUtc() {
+        return delegate().getStartTimeUtc();
+    }
+
+    @Override
+    public long getEndTimeUtc() {
+        return delegate().getEndTimeUtc();
+    }
+
+    @Override
+    public String getDisplayName() {
+        return delegate().getDisplayName();
+    }
+
+    @Override
+    public String getDescription() {
+        return delegate().getDescription();
+    }
+
+    @Override
+    public Task<?> getSubmittedByTask() {
+        return delegate().getSubmittedByTask();
+    }
+
+    @Override
+    public Thread getThread() {
+        return delegate().getThread();
+    }
+
+    @Override
+    public boolean isSubmitted() {
+        return delegate().isSubmitted();
+    }
+
+    @Override
+    public boolean isBegun() {
+        return delegate().isBegun();
+    }
+
+    @Override
+    public boolean isError() {
+        return delegate().isError();
+    }
+
+    @Override
+    public void blockUntilStarted() {
+        delegate().blockUntilStarted();
+    }
+
+    @Override
+    public void blockUntilEnded() {
+        delegate().blockUntilEnded();
+    }
+
+    @Override
+    public boolean blockUntilEnded(Duration timeout) {
+        return delegate().blockUntilEnded(timeout);
+    }
+
+    @Override
+    public String getStatusSummary() {
+        return delegate().getStatusSummary();
+    }
+
+    @Override
+    public String getStatusDetail(boolean multiline) {
+        return delegate().getStatusDetail(multiline);
+    }
+
+    @Override
+    public T get(Duration duration) throws InterruptedException, ExecutionException, TimeoutException {
+        return delegate().get(duration);
+    }
+
+    @Override
+    public T getUnchecked() {
+        return delegate().getUnchecked();
+    }
+
+    @Override
+    public T getUnchecked(Duration duration) {
+        return delegate().getUnchecked(duration);
+    }
+
+    @Override
+    public void initInternalFuture(ListenableFuture<T> result) {
+        delegate().initInternalFuture(result);
+    }
+
+    @Override
+    public long getQueuedTimeUtc() {
+        return delegate().getQueuedTimeUtc();
+    }
+
+    @Override
+    public Future<T> getInternalFuture() {
+        return delegate().getInternalFuture();
+    }
+
+    @Override
+    public boolean isQueued() {
+        return delegate().isQueued();
+    }
+
+    @Override
+    public boolean isQueuedOrSubmitted() {
+        return delegate().isQueuedOrSubmitted();
+    }
+
+    @Override
+    public boolean isQueuedAndNotSubmitted() {
+        return delegate().isQueuedAndNotSubmitted();
+    }
+
+    @Override
+    public void markQueued() {
+        delegate().markQueued();
+    }
+
+    @Override
+    public boolean cancel() {
+        return delegate().cancel();
+    }
+
+    @Override
+    public boolean blockUntilStarted(Duration timeout) {
+        return delegate().blockUntilStarted(timeout);
+    }
+
+    @Override
+    public String setBlockingDetails(String blockingDetails) {
+        return delegate().setBlockingDetails(blockingDetails);
+    }
+
+    @Override
+    public Task<?> setBlockingTask(Task<?> blockingTask) {
+        return delegate().setBlockingTask(blockingTask);
+    }
+
+    @Override
+    public void resetBlockingDetails() {
+        delegate().resetBlockingDetails();
+    }
+
+    @Override
+    public void resetBlockingTask() {
+        delegate().resetBlockingTask();
+    }
+
+    @Override
+    public String getBlockingDetails() {
+        return delegate().getBlockingDetails();
+    }
+
+    @Override
+    public Task<?> getBlockingTask() {
+        return delegate().getBlockingTask();
+    }
+
+    @Override
+    public void setExtraStatusText(Object extraStatus) {
+        delegate().setExtraStatusText(extraStatus);
+    }
+
+    @Override
+    public Object getExtraStatusText() {
+        return delegate().getExtraStatusText();
+    }
+
+    @Override
+    public void runListeners() {
+        delegate().runListeners();
+    }
+
+    @Override
+    public void setEndTimeUtc(long val) {
+        delegate().setEndTimeUtc(val);
+    }
+
+    @Override
+    public void setThread(Thread thread) {
+        delegate().setThread(thread);
+    }
+
+    @Override
+    public Callable<T> getJob() {
+        return delegate().getJob();
+    }
+
+    @Override
+    public void setJob(Callable<T> job) {
+        delegate().setJob(job);
+    }
+
+    @Override
+    public ExecutionList getListeners() {
+        return delegate().getListeners();
+    }
+
+    @Override
+    public void setSubmitTimeUtc(long currentTimeMillis) {
+        delegate().setSubmitTimeUtc(currentTimeMillis);
+    }
+
+    @Override
+    public void setSubmittedByTask(Task<?> task) {
+        delegate().setSubmittedByTask(task);
+    }
+
+    @Override
+    public Set<Object> getMutableTags() {
+        return delegate().getMutableTags();
+    }
+
+    @Override
+    public void setStartTimeUtc(long currentTimeMillis) {
+        delegate().setStartTimeUtc(currentTimeMillis);
+    }
+
+    @Override
+    public void applyTagModifier(Function<Set<Object>, Void> modifier) {
+        delegate().applyTagModifier(modifier);
+    }
+    
+    @Override
+    public Task<?> getProxyTarget() {
+        return delegate().getProxyTarget();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ListenableForwardingFuture.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ListenableForwardingFuture.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ListenableForwardingFuture.java
new file mode 100644
index 0000000..bfe88b0
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ListenableForwardingFuture.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+
+import com.google.common.util.concurrent.ExecutionList;
+import com.google.common.util.concurrent.ForwardingFuture.SimpleForwardingFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/** Wraps a Future, making it a ListenableForwardingFuture, but with the caller having the resposibility to:
+ * <li> invoke the listeners on job completion (success or error)
+ * <li> invoke the listeners on cancel */
+public abstract class ListenableForwardingFuture<T> extends SimpleForwardingFuture<T> implements ListenableFuture<T> {
+
+    final ExecutionList listeners;
+    
+    protected ListenableForwardingFuture(Future<T> delegate) {
+        super(delegate);
+        this.listeners = new ExecutionList();
+    }
+
+    protected ListenableForwardingFuture(Future<T> delegate, ExecutionList list) {
+        super(delegate);
+        this.listeners = list;
+    }
+
+    @Override
+    public void addListener(Runnable listener, Executor executor) {
+        listeners.add(listener, executor);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ParallelTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ParallelTask.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ParallelTask.java
new file mode 100644
index 0000000..10da414
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ParallelTask.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+import org.apache.brooklyn.api.management.Task;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.text.Strings;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+/**
+ * Runs {@link Task}s in parallel.
+ *
+ * No guarantees of order of starting the tasks, but the return value is a
+ * {@link List} of the return values of supplied tasks in the same
+ * order they were passed as arguments.
+ */
+public class ParallelTask<T> extends CompoundTask<T> {
+    public ParallelTask(Object... tasks) { super(tasks); }
+    
+    public ParallelTask(Map<String,?> flags, Collection<? extends Object> tasks) { super(flags, tasks); }
+    public ParallelTask(Collection<? extends Object> tasks) { super(tasks); }
+    
+    public ParallelTask(Map<String,?> flags, Iterable<? extends Object> tasks) { super(flags, ImmutableList.copyOf(tasks)); }
+    public ParallelTask(Iterable<? extends Object> tasks) { super(ImmutableList.copyOf(tasks)); }
+
+    @Override
+    protected List<T> runJobs() throws InterruptedException, ExecutionException {
+        setBlockingDetails("Executing "+
+                (children.size()==1 ? "1 child task" :
+                children.size()+" children tasks in parallel") );
+        for (Task<? extends T> task : children) {
+            submitIfNecessary(task);
+        }
+
+        List<T> result = Lists.newArrayList();
+        List<Exception> exceptions = Lists.newArrayList();
+        for (Task<? extends T> task : children) {
+            T x;
+            try {
+                x = task.get();
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                if (TaskTags.isInessential(task)) {
+                    // ignore exception as it's inessential
+                } else {
+                    exceptions.add(e);
+                }
+                x = null;
+            }
+            result.add(x);
+        }
+        
+        if (exceptions.isEmpty()) {
+            return result;
+        } else {
+            if (result.size()==1 && exceptions.size()==1)
+                throw Exceptions.propagate( exceptions.get(0) );
+            throw Exceptions.propagate(exceptions.size()+" of "+result.size()+" parallel child task"+Strings.s(result.size())+" failed", exceptions);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/ScheduledTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/ScheduledTask.java b/core/src/main/java/org/apache/brooklyn/core/util/task/ScheduledTask.java
new file mode 100644
index 0000000..5c4b208
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/ScheduledTask.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import static brooklyn.util.GroovyJavaMethods.elvis;
+import static brooklyn.util.GroovyJavaMethods.truth;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.brooklyn.api.management.Task;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.time.Duration;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Throwables;
+
+/**
+ * A task which runs with a fixed period.
+ * <p>
+ * Note that some termination logic, including {@link #addListener(Runnable, java.util.concurrent.Executor)},
+ * is not precisely defined. 
+ */
+// TODO ScheduledTask is a very pragmatic implementation; would be nice to tighten, 
+// reduce external assumptions about internal structure, and clarify "done" semantics
+public class ScheduledTask extends BasicTask {
+    
+    final Callable<Task<?>> taskFactory;
+    /** initial delay before running, set as flag in constructor; defaults to 0 */
+    protected Duration delay;
+    /** time to wait between executions, or null if not to repeat (default), set as flag to constructor;
+     * this may be modified for subsequent submissions by a running task generated by the factory 
+     * using getSubmittedByTask().setPeriod(Duration) */
+    protected Duration period = null;
+    /** optional, set as flag in constructor; defaults to null meaning no limit */
+    protected Integer maxIterations = null;
+    
+    protected int runCount=0;
+    protected Task<?> recentRun, nextRun;
+
+    public int getRunCount() { return runCount; }
+    public ScheduledFuture<?> getNextScheduled() { return (ScheduledFuture<?>)internalFuture; }
+
+    public ScheduledTask(Callable<Task<?>> taskFactory) {
+        this(MutableMap.of(), taskFactory);
+    }
+
+    public ScheduledTask(final Task<?> task) {
+        this(MutableMap.of(), task);
+    }
+
+    public ScheduledTask(Map flags, final Task<?> task){
+        this(flags, new Callable<Task<?>>(){
+            @Override
+            public Task<?> call() throws Exception {
+                return task;
+            }});
+    }
+
+    public ScheduledTask(Map flags, Callable<Task<?>> taskFactory) {
+        super(flags);
+        this.taskFactory = taskFactory;
+        
+        delay = Duration.of(elvis(flags.remove("delay"), 0));
+        period = Duration.of(elvis(flags.remove("period"), null));
+        maxIterations = elvis(flags.remove("maxIterations"), null);
+    }
+    
+    public ScheduledTask delay(Duration d) {
+        this.delay = d;
+        return this;
+    }
+    public ScheduledTask delay(long val) {
+        return delay(Duration.millis(val));
+    }
+
+    public ScheduledTask period(Duration d) {
+        this.period = d;
+        return this;
+    }
+    public ScheduledTask period(long val) {
+        return period(Duration.millis(val));
+    }
+
+    public ScheduledTask maxIterations(int val) {
+        this.maxIterations = val;
+        return this;
+    }
+
+    public Callable<Task<?>> getTaskFactory() {
+        return taskFactory;
+    }
+
+    public Task<?> newTask() {
+        try {
+            return taskFactory.call();
+        } catch (Exception e) {
+            throw Throwables.propagate(e);
+        }
+    }
+    
+    protected String getActiveTaskStatusString(int verbosity) {
+        StringBuilder rv = new StringBuilder("Scheduler");
+        if (runCount>0) rv.append(", iteration "+(runCount+1));
+        if (recentRun!=null) rv.append(", last run "+
+            Duration.sinceUtc(recentRun.getStartTimeUtc())+" ms ago");
+        if (truth(getNextScheduled())) {
+            Duration untilNext = Duration.millis(getNextScheduled().getDelay(TimeUnit.MILLISECONDS));
+            if (untilNext.isPositive())
+                rv.append(", next in "+untilNext);
+            else 
+                rv.append(", next imminent");
+        }
+        return rv.toString();
+    }
+    
+    @Override
+    public boolean isDone() {
+        return isCancelled() || (maxIterations!=null && maxIterations <= runCount) || (period==null && nextRun!=null && nextRun.isDone());
+    }
+    
+    public synchronized void blockUntilFirstScheduleStarted() {
+        // TODO Assumes that maxIterations is not negative!
+        while (true) {
+            if (isCancelled()) throw new CancellationException();
+            if (recentRun==null)
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    Throwables.propagate(e);
+                }
+            if (recentRun!=null) return;
+        }
+    }
+    
+    public void blockUntilEnded() {
+        while (!isDone()) super.blockUntilEnded();
+    }
+
+    /** gets the value of the most recently run task */
+    public Object get() throws InterruptedException, ExecutionException {
+        blockUntilStarted();
+        blockUntilFirstScheduleStarted();
+        return (truth(recentRun)) ? recentRun.get() : internalFuture.get();
+    }
+    
+    @Override
+    public synchronized boolean cancel(boolean mayInterrupt) {
+        boolean result = super.cancel(mayInterrupt);
+        if (nextRun!=null) {
+            nextRun.cancel(mayInterrupt);
+            notifyAll();
+        }
+        return result;
+    }
+    
+    /** internal method used to allow callers to wait for underlying tasks to finished in the case of cancellation 
+     * @param duration */ 
+    @Beta
+    public boolean blockUntilNextRunFinished(Duration timeout) {
+        return Tasks.blockUntilInternalTasksEnded(nextRun, timeout);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/SequentialTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/SequentialTask.java b/core/src/main/java/org/apache/brooklyn/core/util/task/SequentialTask.java
new file mode 100644
index 0000000..93ecdf6
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/SequentialTask.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+import org.apache.brooklyn.api.management.Task;
+
+import com.google.common.collect.ImmutableList;
+
+
+/** runs tasks in order, waiting for one to finish before starting the next; return value here is TBD;
+ * (currently is all the return values of individual tasks, but we
+ * might want some pipeline support and eventually only to return final value...) */
+public class SequentialTask<T> extends CompoundTask<T> {
+
+    public SequentialTask(Object... tasks) { super(tasks); }
+    
+    public SequentialTask(Map<String,?> flags, Collection<? extends Object> tasks) { super(flags, tasks); }
+    public SequentialTask(Collection<? extends Object> tasks) { super(tasks); }
+    
+    public SequentialTask(Map<String,?> flags, Iterable<? extends Object> tasks) { super(flags, ImmutableList.copyOf(tasks)); }
+    public SequentialTask(Iterable<? extends Object> tasks) { super(ImmutableList.copyOf(tasks)); }
+    
+    protected List<T> runJobs() throws InterruptedException, ExecutionException {
+        setBlockingDetails("Executing "+
+                (children.size()==1 ? "1 child task" :
+                children.size()+" children tasks sequentially") );
+
+        List<T> result = new ArrayList<T>();
+        for (Task<? extends T> task : children) {
+            submitIfNecessary(task);
+            // throw exception (and cancel subsequent tasks) on error
+            result.add(task.get());
+        }
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/SingleThreadedScheduler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/SingleThreadedScheduler.java b/core/src/main/java/org/apache/brooklyn/core/util/task/SingleThreadedScheduler.java
new file mode 100644
index 0000000..2a5b51d
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/SingleThreadedScheduler.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.Queue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.brooklyn.api.management.Task;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Instances of this class ensures that {@link Task}s execute with in-order
+ * single-threaded semantics.
+ *
+ * Tasks can be presented through {@link #submit(Callable)}. The order of execution is the
+ * sumbission order.
+ * <p>
+ * This implementation does so by blocking on a {@link ConcurrentLinkedQueue}, <em>after</em>
+ * the task is started in a thread (and {@link Task#isBegun()} returns true), but (of course)
+ * <em>before</em> the {@link TaskInternal#getJob()} actually gets invoked.
+ */
+public class SingleThreadedScheduler implements TaskScheduler, CanSetName {
+    private static final Logger LOG = LoggerFactory.getLogger(SingleThreadedScheduler.class);
+    
+    private final Queue<QueuedSubmission<?>> order = new ConcurrentLinkedQueue<QueuedSubmission<?>>();
+    private int queueSize = 0;
+    private final AtomicBoolean running = new AtomicBoolean(false);
+    
+    private ExecutorService executor;
+
+    private String name;
+    
+    @Override
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return name!=null ? "SingleThreadedScheduler["+name+"]" : super.toString();
+    }
+    
+    @Override
+    public void injectExecutor(ExecutorService executor) {
+        this.executor = executor;
+    }
+
+    @Override
+    public synchronized <T> Future<T> submit(Callable<T> c) {
+        if (running.compareAndSet(false, true)) {
+            return executeNow(c);
+        } else {
+            WrappingFuture<T> f = new WrappingFuture<T>();
+            order.add(new QueuedSubmission<T>(c, f));
+            queueSize++;
+            if (queueSize>0 && (queueSize == 50 || (queueSize<=500 && (queueSize%100)==0) || (queueSize%1000)==0) && queueSize!=lastSizeWarn) {
+                LOG.warn("{} is backing up, {} tasks queued", this, queueSize);
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Task queue backing up detail, queue "+this+"; task context is "+Tasks.current()+"; latest task is "+c+"; first task is "+order.peek());
+                }
+                lastSizeWarn = queueSize;
+            }
+            return f;
+        }
+    }
+    int lastSizeWarn = 0;
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private synchronized void onEnd() {
+        boolean done = false;
+        while (!done) {
+            if (order.isEmpty()) {
+                running.set(false);
+                done = true;
+            } else {
+                QueuedSubmission<?> qs = order.remove();
+                queueSize--;
+                if (!qs.f.isCancelled()) {
+                    Future future = executeNow(qs.c);
+                    qs.f.setDelegate(future);
+                    done = true;
+                }
+            }
+        }
+    }
+
+    private synchronized <T> Future<T> executeNow(final Callable<T> c) {
+        return executor.submit(new Callable<T>() {
+            @Override public T call() throws Exception {
+                try {
+                    return c.call();
+                } finally {
+                    onEnd();
+                }
+            }});
+    }
+    
+    
+    private static class QueuedSubmission<T> {
+        final Callable<T> c;
+        final WrappingFuture<T> f;
+        
+        QueuedSubmission(Callable<T> c, WrappingFuture<T> f) {
+            this.c = c;
+            this.f = f;
+        }
+        
+        @Override
+        public String toString() {
+            return "QueuedSubmission["+c+"]@"+Integer.toHexString(System.identityHashCode(this));
+        }
+    }
+    
+    /**
+     * A future, where the task may not yet have been submitted to the real executor.
+     * It delegates to the real future if present, and otherwise waits for that to appear
+     */
+    private static class WrappingFuture<T> implements Future<T> {
+        private volatile Future<T> delegate;
+        private boolean cancelled;
+        
+        void setDelegate(Future<T> delegate) {
+            synchronized (this) {
+                this.delegate = delegate;
+                notifyAll();
+            }
+        }
+        
+        @Override public boolean cancel(boolean mayInterruptIfRunning) {
+            if (delegate != null) {
+                return delegate.cancel(mayInterruptIfRunning);
+            } else {
+                cancelled = true;
+                synchronized (this) {
+                    notifyAll();
+                }
+                return true;
+            }
+        }
+        
+        @Override public boolean isCancelled() {
+            if (delegate != null) {
+                return delegate.isCancelled();
+            } else {
+                return cancelled;
+            }
+        }
+        
+        @Override public boolean isDone() {
+            return (delegate != null) ? delegate.isDone() : cancelled;
+        }
+        
+        @Override public T get() throws CancellationException, ExecutionException, InterruptedException {
+            if (cancelled) {
+                throw new CancellationException();
+            } else if (delegate != null) {
+                return delegate.get();
+            } else {
+                synchronized (this) {
+                    while (delegate == null && !cancelled) {
+                        wait();
+                    }
+                }
+                return get();
+            }
+        }
+        
+        @Override public T get(long timeout, TimeUnit unit) throws CancellationException, ExecutionException, InterruptedException, TimeoutException {
+            long endtime = System.currentTimeMillis()+unit.toMillis(timeout);
+            
+            if (cancelled) {
+                throw new CancellationException();
+            } else if (delegate != null) {
+                return delegate.get(timeout, unit);
+            } else if (System.currentTimeMillis() >= endtime) {
+                throw new TimeoutException();
+            } else {
+                synchronized (this) {
+                    while (delegate == null && !cancelled && System.currentTimeMillis() < endtime) {
+                        long remaining = endtime - System.currentTimeMillis();
+                        if (remaining > 0) {
+                            wait(remaining);
+                        }
+                    }
+                }
+                long remaining = endtime - System.currentTimeMillis();
+                return get(remaining, TimeUnit.MILLISECONDS);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/TaskBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/TaskBuilder.java b/core/src/main/java/org/apache/brooklyn/core/util/task/TaskBuilder.java
new file mode 100644
index 0000000..b105a00
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/TaskBuilder.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.api.management.TaskQueueingContext;
+
+import brooklyn.util.JavaGroovyEquivalents;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.collections.MutableSet;
+
+import com.google.common.collect.Iterables;
+
+/** Convenience for creating tasks; note that DynamicSequentialTask is the default */
+public class TaskBuilder<T> {
+
+    String name = null;
+    String description = null;
+    Callable<T> body = null;
+    Boolean swallowChildrenFailures = null;
+    List<TaskAdaptable<?>> children = MutableList.of();
+    Set<Object> tags = MutableSet.of();
+    Map<String,Object> flags = MutableMap.of();
+    Boolean dynamic = null;
+    boolean parallel = false;
+    
+    public static <T> TaskBuilder<T> builder() {
+        return new TaskBuilder<T>();
+    }
+    
+    public TaskBuilder<T> name(String name) {
+        this.name = name;
+        return this;
+    }
+    
+    public TaskBuilder<T> description(String description) {
+        this.description = description;
+        return this;
+    }
+    
+    /** whether task that is built has been explicitly specified to be a dynamic task 
+     * (ie a Task which is also a {@link TaskQueueingContext}
+     * whereby new tasks can be added after creation */
+    public TaskBuilder<T> dynamic(boolean dynamic) {
+        this.dynamic = dynamic;
+        return this;
+    }
+    
+    /** whether task that is built should be parallel; cannot (currently) also be dynamic */
+    public TaskBuilder<T> parallel(boolean parallel) {
+        this.parallel = parallel;
+        return this;
+    }
+    
+    public TaskBuilder<T> body(Callable<T> body) {
+        this.body = body;
+        return this;
+    }
+    
+    /** sets up a dynamic task not to fail even if children fail */
+    public TaskBuilder<T> swallowChildrenFailures(boolean swallowChildrenFailures) {
+        this.swallowChildrenFailures = swallowChildrenFailures;
+        return this;
+    }
+    
+    public TaskBuilder<T> body(Runnable body) {
+        this.body = JavaGroovyEquivalents.<T>toCallable(body);
+        return this;
+    }
+
+    /** adds a child to the given task; the semantics of how the child is executed is set using
+     * {@link #dynamic(boolean)} and {@link #parallel(boolean)} */
+    public TaskBuilder<T> add(TaskAdaptable<?> child) {
+        children.add(child);
+        return this;
+    }
+
+    public TaskBuilder<T> addAll(Iterable<? extends TaskAdaptable<?>> additionalChildren) {
+        Iterables.addAll(children, additionalChildren);
+        return this;
+    }
+
+    public TaskBuilder<T> add(TaskAdaptable<?>... additionalChildren) {
+        children.addAll(Arrays.asList(additionalChildren));
+        return this;
+    }
+
+    /** adds a tag to the given task */
+    public TaskBuilder<T> tag(Object tag) {
+        tags.add(tag);
+        return this;
+    }
+    
+    /** adds a flag to the given task */
+    public TaskBuilder<T> flag(String flag, Object value) {
+        flags.put(flag, value);
+        return this;
+    }
+
+    /** adds the given flags to the given task */
+    public TaskBuilder<T> flags(Map<String,Object> flags) {
+        this.flags.putAll(flags);
+        return this;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public Task<T> build() {
+        MutableMap<String, Object> taskFlags = MutableMap.copyOf(flags);
+        if (name!=null) taskFlags.put("displayName", name);
+        if (description!=null) taskFlags.put("description", description);
+        if (!tags.isEmpty()) taskFlags.put("tags", tags);
+        
+        if (Boolean.FALSE.equals(dynamic) && children.isEmpty()) {
+            if (swallowChildrenFailures!=null)
+                throw new IllegalArgumentException("Cannot set swallowChildrenFailures for non-dynamic task: "+this);
+            return new BasicTask<T>(taskFlags, body);
+        }
+        
+        // prefer dynamic set unless (a) user has said not dynamic, or (b) it's parallel (since there is no dynamic parallel yet)
+        // dynamic has better cancel (will interrupt the thread) and callers can submit tasks flexibly;
+        // however dynamic uses an extra thread and task and is noisy for contexts which don't need it
+        if (Boolean.TRUE.equals(dynamic) || (dynamic==null && !parallel)) {
+            if (parallel)
+                throw new UnsupportedOperationException("No implementation of parallel dynamic aggregate task available");
+            DynamicSequentialTask<T> result = new DynamicSequentialTask<T>(taskFlags, body);
+            if (swallowChildrenFailures!=null && swallowChildrenFailures.booleanValue()) result.swallowChildrenFailures();
+            for (TaskAdaptable t: children)
+                result.queue(t.asTask());
+            return result;
+        }
+        
+        // T must be of type List<V> for these to be valid
+        if (body != null) {
+            throw new UnsupportedOperationException("No implementation of non-dynamic task with both body and children");
+        }
+        if (swallowChildrenFailures!=null) {
+            throw new IllegalArgumentException("Cannot set swallowChildrenFailures for non-dynamic task: "+this);
+        }
+        
+        if (parallel)
+            return new ParallelTask(taskFlags, children);
+        else
+            return new SequentialTask(taskFlags, children);
+    }
+
+    /** returns a a factory based on this builder */
+    public TaskFactory<Task<T>> buildFactory() {
+        return new TaskFactory<Task<T>>() {
+            public Task<T> newTask() {
+                return build();
+            }
+        };
+    }
+    
+    @Override
+    public String toString() {
+        return super.toString()+"["+name+"]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/TaskInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/TaskInternal.java b/core/src/main/java/org/apache/brooklyn/core/util/task/TaskInternal.java
new file mode 100644
index 0000000..b4a6569
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/TaskInternal.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import org.apache.brooklyn.api.management.ExecutionManager;
+import org.apache.brooklyn.api.management.Task;
+
+import brooklyn.util.time.Duration;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.ExecutionList;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * All tasks being passed to the {@link ExecutionManager} should implement this.
+ * Users are strongly encouraged to use (or extend) {@link BasicTask}, rather than
+ * implementing a task from scratch.
+ * 
+ * The methods on this interface will change in subsequent releases. Because this is
+ * marked as beta, the normal deprecation policy for these methods does not apply.
+ * 
+ * @author aled
+ */
+@Beta
+public interface TaskInternal<T> extends Task<T> {
+    
+    /** sets the internal future object used to record the association to a job submitted to an {@link ExecutorService} */
+    void initInternalFuture(ListenableFuture<T> result);
+
+    /** returns the underlying future where this task's results will come in; see {@link #initInternalFuture(ListenableFuture)} */
+    Future<T> getInternalFuture();
+    
+    /** if the job is queued for submission (e.g. by another task) it can indicate that fact (and time) here;
+     * note tasks can (and often are) submitted without any queueing, in which case this value may be -1 */
+    long getQueuedTimeUtc();
+    
+    boolean isQueuedOrSubmitted();
+    boolean isQueuedAndNotSubmitted();
+    boolean isQueued();
+
+    /** marks the task as queued for execution */
+    void markQueued();
+
+    boolean cancel();
+    
+    boolean blockUntilStarted(Duration timeout);
+
+    /** allows a task user to specify why a task is blocked; for use immediately before a blocking/wait,
+     * and typically cleared immediately afterwards; referenced by management api to inspect a task
+     * which is blocking
+     * <p>
+     * returns previous details, in case caller wishes to recall and restore it (e.g. if it is doing a sub-blocking)
+     */
+    String setBlockingDetails(String blockingDetails);
+
+    /** as {@link #setBlockingDetails(String)} but records a task which is blocking,
+     * for use e.g. in a gui to navigate to the current active subtask
+     * <p>
+     * returns previous blocking task, in case caller wishes to recall and restore it
+     */
+    Task<?> setBlockingTask(Task<?> blockingTask);
+    
+    void resetBlockingDetails();
+    
+    void resetBlockingTask();
+
+    /** returns a textual message giving details while the task is blocked */
+    String getBlockingDetails();
+    
+    /** returns a task that this task is blocked on */
+    Task<?> getBlockingTask();
+    
+    void setExtraStatusText(Object extraStatus);
+    
+    Object getExtraStatusText();
+
+    void runListeners();
+
+    void setEndTimeUtc(long val);
+
+    void setThread(Thread thread);
+
+    Callable<T> getJob();
+    
+    void setJob(Callable<T> job);
+
+    ExecutionList getListeners();
+
+    void setSubmitTimeUtc(long currentTimeMillis);
+
+    void setSubmittedByTask(Task<?> task);
+    
+    Set<Object> getMutableTags();
+
+    void setStartTimeUtc(long currentTimeMillis);
+
+    void applyTagModifier(Function<Set<Object>,Void> modifier);
+    
+    /** if a task is a proxy for another one (used mainly for internal tasks),
+     * this returns the "real" task represented by this one */
+    Task<?> getProxyTarget();
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/TaskScheduler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/TaskScheduler.java b/core/src/main/java/org/apache/brooklyn/core/util/task/TaskScheduler.java
new file mode 100644
index 0000000..7c5d8a2
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/TaskScheduler.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import org.apache.brooklyn.api.management.Task;
+
+/**
+ * The scheduler is an internal mechanism to decorate {@link Task}s.
+ *
+ * It can control how the tasks are scheduled for execution (e.g. single-threaded execution,
+ * prioritised, etc).
+ */
+public interface TaskScheduler {
+    
+    public void injectExecutor(ExecutorService executor);
+
+    /**
+     * Called by {@link BasicExecutionManager} to schedule tasks.
+     */
+    public <T> Future<T> submit(Callable<T> c);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/TaskTags.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/TaskTags.java b/core/src/main/java/org/apache/brooklyn/core/util/task/TaskTags.java
new file mode 100644
index 0000000..e404e87
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/TaskTags.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskAdaptable;
+
+import com.google.common.base.Function;
+
+public class TaskTags {
+
+    /** marks a task which is allowed to fail without failing his parent */
+    public static final String INESSENTIAL_TASK = "inessential";
+
+    /** marks a task which is a subtask of another */
+    public static final String SUB_TASK_TAG = "SUB-TASK";
+
+    public static void addTagDynamically(TaskAdaptable<?> task, final Object tag) {
+        ((BasicTask<?>)task.asTask()).applyTagModifier(new Function<Set<Object>, Void>() {
+            public Void apply(@Nullable Set<Object> input) {
+                input.add(tag);
+                return null;
+            }
+        });
+    }
+    
+    public static void addTagsDynamically(TaskAdaptable<?> task, final Object tag1, final Object ...tags) {
+        ((BasicTask<?>)task.asTask()).applyTagModifier(new Function<Set<Object>, Void>() {
+            public Void apply(@Nullable Set<Object> input) {
+                input.add(tag1);
+                for (Object tag: tags) input.add(tag);
+                return null;
+            }
+        });
+    }
+
+    
+    public static boolean isInessential(Task<?> task) {
+        return hasTag(task, INESSENTIAL_TASK);
+    }
+
+    public static boolean hasTag(Task<?> task, Object tag) {
+        return task.getTags().contains(tag);
+    }
+    
+    public static <U,V extends TaskAdaptable<U>> V markInessential(V task) {
+        addTagDynamically(task, INESSENTIAL_TASK);
+        return task;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/org/apache/brooklyn/core/util/task/Tasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/util/task/Tasks.java b/core/src/main/java/org/apache/brooklyn/core/util/task/Tasks.java
new file mode 100644
index 0000000..d391a90
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/util/task/Tasks.java
@@ -0,0 +1,488 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.util.task;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.HasTaskChildren;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.api.management.TaskQueueingContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.ReferenceWithError;
+import brooklyn.util.repeat.Repeater;
+import brooklyn.util.time.CountdownTimer;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+
+public class Tasks {
+    
+    private static final Logger log = LoggerFactory.getLogger(Tasks.class);
+    
+    /** convenience for setting "blocking details" on any task where the current thread is running;
+     * typically invoked prior to a wait, for transparency to a user;
+     * then invoked with 'null' just after the wait */
+    public static String setBlockingDetails(String description) {
+        Task<?> current = current();
+        if (current instanceof TaskInternal)
+            return ((TaskInternal<?>)current).setBlockingDetails(description);
+        return null;
+    }
+    public static void resetBlockingDetails() {
+        Task<?> current = current();
+        if (current instanceof TaskInternal)
+            ((TaskInternal<?>)current).resetBlockingDetails(); 
+    }
+    public static Task<?> setBlockingTask(Task<?> blocker) {
+        Task<?> current = current();
+        if (current instanceof TaskInternal)
+            return ((TaskInternal<?>)current).setBlockingTask(blocker);
+        return null;
+    }
+    public static void resetBlockingTask() {
+        Task<?> current = current();
+        if (current instanceof TaskInternal)
+            ((TaskInternal<?>)current).resetBlockingTask(); 
+    }
+    
+    /** convenience for setting "blocking details" on any task where the current thread is running,
+     * while the passed code is executed; often used from groovy as
+     * <pre>{@code withBlockingDetails("sleeping 5s") { Thread.sleep(5000); } }</pre>
+     * If code block is null, the description is set until further notice (not cleareed). */
+    @SuppressWarnings("rawtypes")
+    public static <T> T withBlockingDetails(String description, Callable<T> code) throws Exception {
+        Task current = current();
+        if (code==null) {
+            log.warn("legacy invocation of withBlockingDetails with null code block, ignoring");
+            return null;
+        }
+        String prevBlockingDetails = null;
+        if (current instanceof TaskInternal) {
+            prevBlockingDetails = ((TaskInternal)current).setBlockingDetails(description);
+        } 
+        try {
+            return code.call();
+        } finally {
+            if (current instanceof TaskInternal)
+                ((TaskInternal)current).setBlockingDetails(prevBlockingDetails); 
+        }
+    }
+
+    /** the {@link Task} where the current thread is executing, if executing in a Task, otherwise null;
+     * if the current task is a proxy, this returns the target of that proxy */
+    @SuppressWarnings("rawtypes")
+    public static Task current() { 
+        return getFinalProxyTarget(BasicExecutionManager.getPerThreadCurrentTask().get());
+    }
+
+    public static Task<?> getFinalProxyTarget(Task<?> task) {
+        if (task==null) return null;
+        Task<?> proxy = ((TaskInternal<?>)task).getProxyTarget();
+        if (proxy==null || proxy.equals(task)) return task;
+        return getFinalProxyTarget(proxy);
+    }
+    
+    /** creates a {@link ValueResolver} instance which allows significantly more customization than
+     * the various {@link #resolveValue(Object, Class, ExecutionContext)} methods here */
+    public static <T> ValueResolver<T> resolving(Object v, Class<T> type) {
+        return new ValueResolver<T>(v, type);
+    }
+
+    public static ValueResolver.ResolverBuilderPretype resolving(Object v) {
+        return new ValueResolver.ResolverBuilderPretype(v);
+    }
+
+    /** @see #resolveValue(Object, Class, ExecutionContext, String) */
+    public static <T> T resolveValue(Object v, Class<T> type, @Nullable ExecutionContext exec) throws ExecutionException, InterruptedException {
+        return new ValueResolver<T>(v, type).context(exec).get();
+    }
+    
+    /** attempt to resolve the given value as the given type, waiting on futures, submitting if necessary,
+     * and coercing as allowed by TypeCoercions;
+     * contextMessage (optional) will be displayed in status reports while it waits (e.g. the name of the config key being looked up).
+     * if no execution context supplied (null) this method will throw an exception if the object is an unsubmitted task */
+    public static <T> T resolveValue(Object v, Class<T> type, @Nullable ExecutionContext exec, String contextMessage) throws ExecutionException, InterruptedException {
+        return new ValueResolver<T>(v, type).context(exec).description(contextMessage).get();
+    }
+    
+    /**
+     * @see #resolveDeepValue(Object, Class, ExecutionContext, String)
+     */
+    public static Object resolveDeepValue(Object v, Class<?> type, ExecutionContext exec) throws ExecutionException, InterruptedException {
+        return resolveDeepValue(v, type, exec, null);
+    }
+
+    /**
+     * Resolves the given object, blocking on futures and coercing it to the given type. If the object is a 
+     * map or iterable (or a list of map of maps, etc, etc) then walks these maps/iterables to convert all of 
+     * their values to the given type. For example, the following will return a list containing a map with "1"="true":
+     * 
+     *   {@code Object result = resolveDeepValue(ImmutableList.of(ImmutableMap.of(1, true)), String.class, exec)} 
+     *
+     * To perform a deep conversion of futures contained within Iterables or Maps without coercion of each element,
+     * the type should normally be Object, not the type of the collection. This differs from
+     * {@link #resolveValue(Object, Class, ExecutionContext, String)} which will accept Map and Iterable
+     * as the required type.
+     */
+    public static <T> T resolveDeepValue(Object v, Class<T> type, ExecutionContext exec, String contextMessage) throws ExecutionException, InterruptedException {
+        return new ValueResolver<T>(v, type).context(exec).deep(true).description(contextMessage).get();
+    }
+
+    /** sets extra status details on the current task, if possible (otherwise does nothing).
+     * the extra status is presented in Task.getStatusDetails(true)
+     */
+    public static void setExtraStatusDetails(String notes) {
+        Task<?> current = current();
+        if (current instanceof TaskInternal)
+            ((TaskInternal<?>)current).setExtraStatusText(notes); 
+    }
+
+    public static <T> TaskBuilder<T> builder() {
+        return TaskBuilder.<T>builder();
+    }
+    
+    private static Task<?>[] asTasks(TaskAdaptable<?> ...tasks) {
+        Task<?>[] result = new Task<?>[tasks.length];
+        for (int i=0; i<tasks.length; i++)
+            result[i] = tasks[i].asTask();
+        return result;
+    }
+
+    public static Task<List<?>> parallel(TaskAdaptable<?> ...tasks) {
+        return parallelInternal("parallelised tasks", asTasks(tasks));
+    }
+    public static Task<List<?>> parallel(String name, TaskAdaptable<?> ...tasks) {
+        return parallelInternal(name, asTasks(tasks));
+    }
+    public static Task<List<?>> parallel(Iterable<? extends TaskAdaptable<?>> tasks) {
+        return parallel(asTasks(Iterables.toArray(tasks, TaskAdaptable.class)));
+    }
+    public static Task<List<?>> parallel(String name, Iterable<? extends TaskAdaptable<?>> tasks) {
+        return parallelInternal(name, asTasks(Iterables.toArray(tasks, TaskAdaptable.class)));
+    }
+    private static Task<List<?>> parallelInternal(String name, Task<?>[] tasks) {
+        return Tasks.<List<?>>builder().name(name).parallel(true).add(tasks).build();
+    }
+
+    public static Task<List<?>> sequential(TaskAdaptable<?> ...tasks) {
+        return sequentialInternal("sequential tasks", asTasks(tasks));
+    }
+    public static Task<List<?>> sequential(String name, TaskAdaptable<?> ...tasks) {
+        return sequentialInternal(name, asTasks(tasks));
+    }
+    public static TaskFactory<?> sequential(TaskFactory<?> ...taskFactories) {
+        return sequentialInternal("sequential tasks", taskFactories);
+    }
+    public static TaskFactory<?> sequential(String name, TaskFactory<?> ...taskFactories) {
+        return sequentialInternal(name, taskFactories);
+    }
+    public static Task<List<?>> sequential(List<? extends TaskAdaptable<?>> tasks) {
+        return sequential(asTasks(Iterables.toArray(tasks, TaskAdaptable.class)));
+    }
+    public static Task<List<?>> sequential(String name, List<? extends TaskAdaptable<?>> tasks) {
+        return sequential(name, asTasks(Iterables.toArray(tasks, TaskAdaptable.class)));
+    }
+    private static Task<List<?>> sequentialInternal(String name, Task<?>[] tasks) {
+        return Tasks.<List<?>>builder().name(name).parallel(false).add(tasks).build();
+    }
+    private static TaskFactory<?> sequentialInternal(final String name, final TaskFactory<?> ...taskFactories) {
+        return new TaskFactory<TaskAdaptable<?>>() {
+            @Override
+            public TaskAdaptable<?> newTask() {
+                TaskBuilder<List<?>> tb = Tasks.<List<?>>builder().name(name).parallel(false);
+                for (TaskFactory<?> tf: taskFactories)
+                    tb.add(tf.newTask().asTask());
+                return tb.build();
+            }
+        };
+    }
+
+    /** returns the first tag found on the given task which matches the given type, looking up the submission hierarachy if necessary */
+    @SuppressWarnings("unchecked")
+    public static <T> T tag(@Nullable Task<?> task, Class<T> type, boolean recurseHierarchy) {
+        // support null task to make it easier for callers to walk hierarchies
+        if (task==null) return null;
+        for (Object tag: task.getTags())
+            if (type.isInstance(tag)) return (T)tag;
+        if (!recurseHierarchy) return null;
+        return tag(task.getSubmittedByTask(), type, true);
+    }
+    
+    public static boolean isAncestorCancelled(Task<?> t) {
+        if (t==null) return false;
+        if (t.isCancelled()) return true;
+        return isAncestorCancelled(t.getSubmittedByTask());
+    }
+
+    public static boolean isQueued(TaskAdaptable<?> task) {
+        return ((TaskInternal<?>)task.asTask()).isQueued();
+    }
+
+    public static boolean isSubmitted(TaskAdaptable<?> task) {
+        return ((TaskInternal<?>)task.asTask()).isSubmitted();
+    }
+    
+    public static boolean isQueuedOrSubmitted(TaskAdaptable<?> task) {
+        return ((TaskInternal<?>)task.asTask()).isQueuedOrSubmitted();
+    }
+    
+    /**
+     * Adds the given task to the given context. Does not throw an exception if the addition fails.
+     * @return true if the task was added, false otherwise.
+     */
+    public static boolean tryQueueing(TaskQueueingContext adder, TaskAdaptable<?> task) {
+        if (task==null || isQueued(task))
+            return false;
+        try {
+            adder.queue(task.asTask());
+            return true;
+        } catch (Exception e) {
+            if (log.isDebugEnabled())
+                log.debug("Could not add task "+task+" at "+adder+": "+e);
+            return false;
+        }        
+    }
+    
+    /** see also {@link #resolving(Object)} which gives much more control about submission, timeout, etc */
+    public static <T> Supplier<T> supplier(final TaskAdaptable<T> task) {
+        return new Supplier<T>() {
+            @Override
+            public T get() {
+                return task.asTask().getUnchecked();
+            }
+        };
+    }
+    
+    /** return all children tasks of the given tasks, if it has children, else empty list */
+    public static Iterable<Task<?>> children(Task<?> task) {
+        if (task instanceof HasTaskChildren)
+            return ((HasTaskChildren)task).getChildren();
+        return Collections.emptyList();
+    }
+    
+    /** returns failed tasks */
+    public static Iterable<Task<?>> failed(Iterable<Task<?>> subtasks) {
+        return Iterables.filter(subtasks, new Predicate<Task<?>>() {
+            @Override
+            public boolean apply(Task<?> input) {
+                return input.isError();
+            }
+        });
+    }
+    
+    /** returns the task, its children, and all its children, and so on;
+     * @param root task whose descendants should be iterated
+     * @param parentFirst whether to put parents before children or after
+     */
+    public static Iterable<Task<?>> descendants(Task<?> root, final boolean parentFirst) {
+        Iterable<Task<?>> descs = Iterables.concat(Iterables.transform(Tasks.children(root), new Function<Task<?>,Iterable<Task<?>>>() {
+            @Override
+            public Iterable<Task<?>> apply(Task<?> input) {
+                return descendants(input, parentFirst);
+            }
+        }));
+        if (parentFirst) return Iterables.concat(Collections.singleton(root), descs);
+        else return Iterables.concat(descs, Collections.singleton(root));
+    }
+
+    /** returns the error thrown by the task if {@link Task#isError()}, or null if no error or not done */
+    public static Throwable getError(Task<?> t) {
+        if (t==null) return null;
+        if (!t.isDone()) return null;
+        if (t.isCancelled()) return new CancellationException();
+        try {
+            t.get();
+            return null;
+        } catch (Throwable error) {
+            // do not propagate as we are pretty much guaranteed above that it wasn't this
+            // thread which originally threw the error
+            return error;
+        }
+    }
+    public static Task<Void> fail(final String name, final Throwable optionalError) {
+        return Tasks.<Void>builder().dynamic(false).name(name).body(new Runnable() { public void run() { 
+            if (optionalError!=null) throw Exceptions.propagate(optionalError); else throw new RuntimeException("Failed: "+name);
+        } }).build();
+    }
+    public static Task<Void> warning(final String message, final Throwable optionalError) {
+        log.warn(message);
+        return TaskTags.markInessential(fail(message, optionalError));
+    }
+
+    /** marks the current task inessential; this mainly matters if the task is running in a parent
+     * {@link TaskQueueingContext} and we don't want the parent to fail if this task fails
+     * <p>
+     * no-op (silently ignored) if not in a task */
+    public static void markInessential() {
+        Task<?> task = Tasks.current();
+        if (task==null) {
+            TaskQueueingContext qc = DynamicTasks.getTaskQueuingContext();
+            if (qc!=null) task = qc.asTask();
+        }
+        if (task!=null) {
+            TaskTags.markInessential(task);
+        }
+    }
+    
+    /** causes failures in subtasks of the current task not to fail the parent;
+     * no-op if not in a {@link TaskQueueingContext}.
+     * <p>
+     * essentially like a {@link #markInessential()} on all tasks in the current 
+     * {@link TaskQueueingContext}, including tasks queued subsequently */
+    @Beta
+    public static void swallowChildrenFailures() {
+        Preconditions.checkNotNull(DynamicTasks.getTaskQueuingContext(), "Task queueing context required here");
+        TaskQueueingContext qc = DynamicTasks.getTaskQueuingContext();
+        if (qc!=null) {
+            qc.swallowChildrenFailures();
+        }
+    }
+
+    /** as {@link TaskTags#addTagDynamically(TaskAdaptable, Object)} but for current task, skipping if no current task */
+    public static void addTagDynamically(Object tag) {
+        Task<?> t = Tasks.current();
+        if (t!=null) TaskTags.addTagDynamically(t, tag);
+    }
+    
+    /** 
+     * Workaround for limitation described at {@link Task#cancel(boolean)};
+     * internal method used to allow callers to wait for underlying tasks to finished in the case of cancellation.
+     * <p> 
+     * It is irritating that {@link FutureTask} sync's object clears the runner thread, 
+     * so even if {@link BasicTask#getInternalFuture()} is used, there is no means of determining if the underlying object is done.
+     * The {@link Task#getEndTimeUtc()} seems the only way.
+     *  
+     * @return true if tasks ended; false if timed out
+     **/ 
+    @Beta
+    public static boolean blockUntilInternalTasksEnded(Task<?> t, Duration timeout) {
+        CountdownTimer timer = timeout.countdownTimer();
+        
+        if (t==null)
+            return true;
+        
+        if (t instanceof ScheduledTask) {
+            boolean result = ((ScheduledTask)t).blockUntilNextRunFinished(timer.getDurationRemaining());
+            if (!result) return false;
+        }
+
+        t.blockUntilEnded(timer.getDurationRemaining());
+        
+        while (true) {
+            if (t.getEndTimeUtc()>=0) return true;
+            // above should be sufficient; but just in case, trying the below
+            Thread tt = t.getThread();
+            if (t instanceof ScheduledTask) {
+                ((ScheduledTask)t).blockUntilNextRunFinished(timer.getDurationRemaining());
+                return true;
+            } else {
+                if (tt==null || !tt.isAlive()) {
+                    if (!t.isCancelled()) {
+                        // may happen for a cancelled task, interrupted after submit but before start
+                        log.warn("Internal task thread is dead or null ("+tt+") but task not ended: "+t.getEndTimeUtc()+" ("+t+")");
+                    }
+                    return true;
+                }
+            }
+            if (timer.isExpired())
+                return false;
+            Time.sleep(Repeater.DEFAULT_REAL_QUICK_PERIOD);
+        }
+    }
+    
+    /** returns true if either the current thread or the current task is interrupted/cancelled */
+    public static boolean isInterrupted() {
+        if (Thread.currentThread().isInterrupted()) return true;
+        Task<?> t = current();
+        if (t==null) return false;
+        return t.isCancelled();
+    }
+
+    private static class WaitForRepeaterCallable implements Callable<Boolean> {
+        protected Repeater repeater;
+        protected boolean requireTrue;
+
+        public WaitForRepeaterCallable(Repeater repeater, boolean requireTrue) {
+            this.repeater = repeater;
+            this.requireTrue = requireTrue;
+        }
+
+        @Override
+        public Boolean call() {
+            ReferenceWithError<Boolean> result;
+            Tasks.setBlockingDetails(repeater.getDescription());
+            try {
+               result = repeater.runKeepingError();
+            } finally {
+                Tasks.resetBlockingDetails();
+            }
+
+            if (Boolean.TRUE.equals(result.getWithoutError()))
+                return true;
+            if (result.hasError()) 
+                throw Exceptions.propagate(result.getError());
+            if (requireTrue)
+                throw new IllegalStateException("timeout - "+repeater.getDescription());
+            return false;
+        }
+    }
+
+    /** @return a {@link TaskBuilder} which tests whether the repeater terminates with success in its configured timeframe,
+     * returning true or false depending on whether repeater succeed */
+    public static TaskBuilder<Boolean> testing(Repeater repeater) {
+        return Tasks.<Boolean>builder().body(new WaitForRepeaterCallable(repeater, false))
+            .name("waiting for condition")
+            .description("Testing whether " + getTimeoutString(repeater) + ": "+repeater.getDescription());
+    }
+
+    /** @return a {@link TaskBuilder} which requires that the repeater terminate with success in its configured timeframe,
+     * throwing if it does not */
+    public static TaskBuilder<?> requiring(Repeater repeater) {
+        return Tasks.<Boolean>builder().body(new WaitForRepeaterCallable(repeater, true))
+            .name("waiting for condition")
+            .description("Requiring " + getTimeoutString(repeater) + ": " + repeater.getDescription());
+    }
+    
+    private static String getTimeoutString(Repeater repeater) {
+        Duration timeout = repeater.getTimeLimit();
+        if (timeout==null || Duration.PRACTICALLY_FOREVER.equals(timeout))
+            return "eventually";
+        return "in "+timeout;
+    }
+
+}


[40/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/ResourceUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/ResourceUtils.java b/core/src/main/java/brooklyn/util/ResourceUtils.java
deleted file mode 100644
index 338b223..0000000
--- a/core/src/main/java/brooklyn/util/ResourceUtils.java
+++ /dev/null
@@ -1,639 +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;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.InetAddress;
-import java.net.JarURLConnection;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
-import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
-import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
-import org.apache.brooklyn.core.internal.BrooklynInitialization;
-import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.auth.Credentials;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.util.EntityUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpTool.HttpClientBuilder;
-import brooklyn.util.javalang.Threads;
-import brooklyn.util.net.Urls;
-import brooklyn.util.os.Os;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.text.DataUriSchemeParser;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Throwables;
-import com.google.common.collect.Lists;
-
-public class ResourceUtils {
-    
-    private static final Logger log = LoggerFactory.getLogger(ResourceUtils.class);
-    private static final List<Function<Object,BrooklynClassLoadingContext>> classLoaderProviders = Lists.newCopyOnWriteArrayList();
-
-    private BrooklynClassLoadingContext loader = null;
-    private String context = null;
-    private Object contextObject = null;
-    
-    static { BrooklynInitialization.initNetworking(); }
-    
-    /**
-     * Creates a {@link ResourceUtils} object with a specific class loader and context.
-     * <p>
-     * Use the provided {@link ClassLoader} object for class loading with the
-     * {@code contextObject} for context and the {@code contextMessage} string for
-     * error messages.
-     *
-     * @see ResourceUtils#create(Object, String)
-     * @see ResourceUtils#create(Object)
-     */
-    public static final ResourceUtils create(ClassLoader loader, Object contextObject, String contextMessage) {
-        return new ResourceUtils(loader, contextObject, contextMessage);
-    }
-
-    /**
-     * Creates a {@link ResourceUtils} object with a specific class loader and context.
-     * <p>
-     * Use the provided {@link BrooklynClassLoadingContext} object for class loading with the
-     * {@code contextObject} for context and the {@code contextMessage} string for
-     * error messages.
-     *
-     * @see ResourceUtils#create(Object, String)
-     * @see ResourceUtils#create(Object)
-     */
-    public static final ResourceUtils create(BrooklynClassLoadingContext loader, Object contextObject, String contextMessage) {
-        return new ResourceUtils(loader, contextObject, contextMessage);
-    }
-
-    /**
-     * Creates a {@link ResourceUtils} object with the given context.
-     * <p>
-     * Uses the {@link ClassLoader} of the given {@code contextObject} for class
-     * loading and the {@code contextMessage} string for error messages.
-     *
-     * @see ResourceUtils#create(ClassLoader, Object, String)
-     * @see ResourceUtils#create(Object)
-     */
-    public static final ResourceUtils create(Object contextObject, String contextMessage) {
-        return new ResourceUtils(contextObject, contextMessage);
-    }
-
-    /**
-     * Creates a {@link ResourceUtils} object with the given context.
-     * <p>
-     * Uses the {@link ClassLoader} of the given {@code contextObject} for class
-     * loading and its {@link Object#toString()} (preceded by the word 'for') as
-     * the string used in error messages.
-     *
-     * @see ResourceUtils#create(ClassLoader, Object, String)
-     * @see ResourceUtils#create(Object)
-     */
-    public static final ResourceUtils create(Object contextObject) {
-        return new ResourceUtils(contextObject);
-    }
-
-    /**
-     * Creates a {@link ResourceUtils} object with itself as the context.
-     *
-     * @see ResourceUtils#create(Object)
-     */
-    public static final ResourceUtils create() {
-        return new ResourceUtils(null);
-    }
-
-    public ResourceUtils(ClassLoader loader, Object contextObject, String contextMessage) {
-        this(getClassLoadingContextInternal(loader, contextObject), contextObject, contextMessage);
-    }
-    
-    public ResourceUtils(BrooklynClassLoadingContext loader, Object contextObject, String contextMessage) {
-        this.loader = loader;
-        this.contextObject = contextObject;
-        this.context = contextMessage;
-    }
-
-    public ResourceUtils(Object contextObject, String contextMessage) {
-        this(contextObject==null ? null : getClassLoadingContextInternal(null, contextObject), contextObject, contextMessage);
-    }
-
-    public ResourceUtils(Object contextObject) {
-        this(contextObject, Strings.toString(contextObject));
-    }
-    
-    /** used to register custom mechanisms for getting classloaders given an object */
-    public static void addClassLoaderProvider(Function<Object,BrooklynClassLoadingContext> provider) {
-        classLoaderProviders.add(provider);
-    }
-    
-    // TODO rework this class so it accepts but does not require a BCLC ?
-    @SuppressWarnings("deprecation")
-    protected static BrooklynClassLoadingContext getClassLoadingContextInternal(ClassLoader loader, Object contextObject) {
-        if (contextObject instanceof BrooklynClassLoadingContext)
-            return (BrooklynClassLoadingContext) contextObject;
-        
-        for (Function<Object,BrooklynClassLoadingContext> provider: classLoaderProviders) {
-            BrooklynClassLoadingContext result = provider.apply(contextObject);
-            if (result!=null) return result;
-        }
-
-        BrooklynClassLoadingContext bl = BrooklynLoaderTracker.getLoader();
-        ManagementContext mgmt = (bl!=null ? bl.getManagementContext() : null);
-
-        ClassLoader cl = loader;
-        if (cl==null) cl = contextObject instanceof Class ? ((Class<?>)contextObject).getClassLoader() : 
-            contextObject instanceof ClassLoader ? ((ClassLoader)contextObject) : 
-                contextObject.getClass().getClassLoader();
-            
-        return JavaBrooklynClassLoadingContext.create(mgmt, cl);
-    }
-    
-    /** This should not be exposed as it risks it leaking into places where it would be serialized.
-     * Better for callers use {@link CatalogUtils#getClassLoadingContext(org.apache.brooklyn.api.entity.Entity)} or similar. }.
-     */
-    private BrooklynClassLoadingContext getLoader() {
-        return (loader!=null ? loader : getClassLoadingContextInternal(null, contextObject!=null ? contextObject : this));
-    }
-
-    /**
-     * @return all resources in Brooklyn's {@link BrooklynClassLoadingContext} with the given name.
-     */
-    public Iterable<URL> getResources(String name) {
-        return getLoader().getResources(name);
-    }
-    
-    /**
-     * Takes a string which is treated as a URL (with some extended "schemes" also expected),
-     * or as a path to something either on the classpath (absolute only) or the local filesystem (relative or absolute, depending on leading slash)
-     * <p>
-     * URLs can be of the form <b>classpath://com/acme/Foo.properties</b>
-     * as well as <b>file:///home/...</b> and <b>http://acme.com/...</b>.
-     * <p>
-     * Throws exception if not found, using the context parameter passed into the constructor.
-     * <p>
-     * TODO may want OSGi, or typed object; should consider pax url
-     * 
-     * @return a stream, or throws exception (never returns null)
-     */
-    public InputStream getResourceFromUrl(String url) {
-        try {
-            if (url==null) throw new NullPointerException("Cannot read from null");
-            if (url=="") throw new NullPointerException("Cannot read from empty string");
-            String orig = url;
-            String protocol = Urls.getProtocol(url);
-            if (protocol!=null) {
-                if ("classpath".equals(protocol)) {
-                    try {
-                        return getResourceViaClasspath(url);
-                    } catch (IOException e) {
-                        //catch the above because both orig and modified url may be interesting
-                        throw new IOException("Error accessing "+orig+": "+e, e);
-                    }
-                }
-                if ("sftp".equals(protocol)) {
-                    try {
-                        return getResourceViaSftp(url);
-                    } catch (IOException e) {
-                        throw new IOException("Error accessing "+orig+": "+e, e);
-                    }
-                }
-
-                if ("file".equals(protocol))
-                    url = tidyFileUrl(url);
-                
-                if ("data".equals(protocol)) {
-                    return new DataUriSchemeParser(url).lax().parse().getDataAsInputStream();
-                }
-
-                if ("http".equals(protocol) || "https".equals(protocol)) {
-                    return getResourceViaHttp(url);
-                }
-
-                return new URL(url).openStream();
-            }
-
-            try {
-                //try as classpath reference, then as file
-                try {
-                    URL u = getLoader().getResource(url);
-                    if (u!=null) return u.openStream();
-                } catch (IllegalArgumentException e) {
-                    //Felix installs an additional URL to the system classloader
-                    //which throws an IllegalArgumentException when passed a
-                    //windows path. See ExtensionManager.java static initializer.
-
-                    //ignore, not a classpath resource
-                }
-                if (url.startsWith("/")) {
-                    //some getResource calls fail if argument starts with /
-                    String urlNoSlash = url;
-                    while (urlNoSlash.startsWith("/")) urlNoSlash = urlNoSlash.substring(1);
-                    URL u = getLoader().getResource(urlNoSlash);
-                    if (u!=null) return u.openStream();
-//                    //Class.getResource can require a /  (else it attempts to be relative) but Class.getClassLoader doesn't
-//                    u = getLoader().getResource("/"+urlNoSlash);
-//                    if (u!=null) return u.openStream();
-                }
-                File f;
-                // but first, if it starts with tilde, treat specially
-                if (url.startsWith("~/")) {
-                    f = new File(Os.home(), url.substring(2));
-                } else if (url.startsWith("~\\")) {
-                    f = new File(Os.home(), url.substring(2));
-                } else {
-                    f = new File(url);
-                }
-                if (f.exists()) return new FileInputStream(f);
-            } catch (IOException e) {
-                //catch the above because both u and modified url will be interesting
-                throw new IOException("Error accessing "+orig+": "+e, e);
-            }
-            throw new IOException("'"+orig+"' not found on classpath or filesystem");
-        } catch (Exception e) {
-            if (context!=null) {
-                throw new RuntimeException("Error getting resource '"+url+"' for "+context+": "+e, e);
-            } else {
-                throw Exceptions.propagate(e);
-            }
-        }
-    }
-    
-    private final static Pattern pattern = Pattern.compile("^file:/*~/+(.*)$");
-
-    public static URL tidy(URL url) {
-        // File class has helpful methods for URIs but not URLs. So we convert.
-        URI in;
-        try {
-            in = url.toURI();
-        } catch (URISyntaxException e) {
-            throw Exceptions.propagate(e);
-        }
-        URI out;
-
-        Matcher matcher = pattern.matcher(in.toString());
-        if (matcher.matches()) {
-            // home-relative
-            File home = new File(Os.home());
-            File file = new File(home, matcher.group(1));
-            out = file.toURI();
-        } else if (in.getScheme().equals("file:")) {
-            // some other file, so canonicalize
-            File file = new File(in);
-            out = file.toURI();
-        } else {
-            // some other scheme, so no-op
-            out = in;
-        }
-
-        URL urlOut;
-        try {
-            urlOut = out.toURL();
-        } catch (MalformedURLException e) {
-            throw Exceptions.propagate(e);
-        }
-        if (!urlOut.equals(url) && log.isDebugEnabled()) {
-            log.debug("quietly changing " + url + " to " + urlOut);
-        }
-        return urlOut;
-    }
-    
-    public static String tidyFileUrl(String url) {
-        try {
-            return tidy(new URL(url)).toString();
-        } catch (MalformedURLException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    /** @deprecated since 0.7.0; use method {@link Os#mergePaths(String...)} */ @Deprecated
-    public static String mergeFilePaths(String... items) {
-        return Os.mergePaths(items);
-    }
-    
-    /** @deprecated since 0.7.0; use method {@link Os#tidyPath(String)} */ @Deprecated
-    public static String tidyFilePath(String path) {
-        return Os.tidyPath(path);
-    }
-    
-    /** @deprecated since 0.7.0; use method {@link Urls#getProtocol(String)} */ @Deprecated
-    public static String getProtocol(String url) {
-        return Urls.getProtocol(url);
-    }
-    
-    private InputStream getResourceViaClasspath(String url) throws IOException {
-        assert url.startsWith("classpath:");
-        String subUrl = url.substring("classpath:".length());
-        while (subUrl.startsWith("/")) subUrl = subUrl.substring(1);
-        URL u = getLoader().getResource(subUrl);
-        if (u!=null) return u.openStream();
-        else throw new IOException(subUrl+" not found on classpath");
-    }
-    
-    private InputStream getResourceViaSftp(String url) throws IOException {
-        assert url.startsWith("sftp://");
-        String subUrl = url.substring("sftp://".length());
-        String user;
-        String address;
-        String path;
-        int atIndex = subUrl.indexOf("@");
-        int colonIndex = subUrl.indexOf(":", (atIndex > 0 ? atIndex : 0));
-        if (colonIndex <= 0 || colonIndex <= atIndex) {
-            throw new IllegalArgumentException("Invalid sftp url ("+url+"); IP or hostname must be specified, such as sftp://localhost:/path/to/file");
-        }
-        if (subUrl.length() <= (colonIndex+1)) {
-            throw new IllegalArgumentException("Invalid sftp url ("+url+"); must specify path of remote file, such as sftp://localhost:/path/to/file");
-        }
-        if (atIndex >= 0) {
-            user = subUrl.substring(0, atIndex);
-        } else {
-            user = null;
-        }
-        address = subUrl.substring(atIndex + 1, colonIndex);
-        path = subUrl.substring(colonIndex+1);
-        
-        // TODO messy way to get an SCP session 
-        SshMachineLocation machine = new SshMachineLocation(MutableMap.builder()
-                .putIfNotNull("user", user)
-                .put("address", InetAddress.getByName(address))
-                .build());
-        try {
-            final File tempFile = Os.newTempFile("brooklyn-sftp", "tmp");
-            tempFile.setReadable(true, true);
-            machine.copyFrom(path, tempFile.getAbsolutePath());
-            return new FileInputStream(tempFile) {
-                @Override
-                public void close() throws IOException {
-                    super.close();
-                    tempFile.delete();
-                }
-            };
-        } finally {
-            Streams.closeQuietly(machine);
-        }
-    }
-    
-    //For HTTP(S) targets use HttpClient so
-    //we can do authentication
-    private InputStream getResourceViaHttp(String resource) throws IOException {
-        URI uri = URI.create(resource);
-        HttpClientBuilder builder = HttpTool.httpClientBuilder()
-                .laxRedirect(true)
-                .uri(uri);
-        Credentials credentials = getUrlCredentials(uri.getRawUserInfo());
-        if (credentials != null) {
-            builder.credentials(credentials);
-        }
-        HttpClient client = builder.build();
-        HttpResponse result = client.execute(new HttpGet(resource));
-        int statusCode = result.getStatusLine().getStatusCode();
-        if (HttpTool.isStatusCodeHealthy(statusCode)) {
-            HttpEntity entity = result.getEntity();
-            if (entity != null) {
-                return entity.getContent();
-            } else {
-                return new ByteArrayInputStream(new byte[0]);
-            }
-        } else {
-            EntityUtils.consume(result.getEntity());
-            throw new IllegalStateException("Invalid response invoking " + resource + ": response code " + statusCode);
-        }
-    }
-
-    private Credentials getUrlCredentials(String userInfo) {
-        if (userInfo != null) {
-            String[] arr = userInfo.split(":");
-            String username;
-            String password = null;
-            if (arr.length == 1) {
-                username = urlDecode(arr[0]);
-            } else if (arr.length == 2) {
-                username = urlDecode(arr[0]);
-                password = urlDecode(arr[1]);
-            } else {
-                return null;
-            }
-            return new UsernamePasswordCredentials(username, password);
-        } else {
-            return null;
-        }
-    }
-
-    private String urlDecode(String str) {
-        try {
-            return URLDecoder.decode(str, "UTF-8");
-        } catch (UnsupportedEncodingException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    /** takes {@link #getResourceFromUrl(String)} and reads fully, into a string */
-    public String getResourceAsString(String url) {
-        try {
-            return readFullyString(getResourceFromUrl(url));
-        } catch (Exception e) {
-            log.debug("ResourceUtils got error reading "+url+(context==null?"":" "+context)+" (rethrowing): "+e);
-            throw Throwables.propagate(e);
-        }
-    }
-
-    /** allows failing-fast if URL cannot be read */
-    public String checkUrlExists(String url) {
-        return checkUrlExists(url, null);
-    }
-    
-    public String checkUrlExists(String url, String message) {
-        if (url==null) throw new NullPointerException("URL "+(message!=null ? message+" " : "")+"must not be null");
-        InputStream s;
-        try {
-            s = getResourceFromUrl(url);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            throw new IllegalArgumentException("Unable to access URL "+(message!=null ? message : "")+": "+url, e);
-        }
-        Streams.closeQuietly(s); 
-        return url;
-    }
-
-    /** tests whether the url exists, returning true or false */
-    public boolean doesUrlExist(String url) {
-        InputStream s = null;
-        try {
-            s = getResourceFromUrl(url);
-            return true;
-        } catch (Exception e) {
-            return false;
-        } finally {
-            Streams.closeQuietly(s);
-        }
-    }
-    
-    /** returns the first available URL */
-    public Optional<String> firstAvailableUrl(String ...urls) {
-        for (String url: urls) {
-            if (doesUrlExist(url)) return Optional.of(url);
-        }
-        return Optional.absent();
-    }
-    
-    /** returns the base directory or JAR from which the context is class-loaded, if possible;
-     * throws exception if not found */
-    public String getClassLoaderDir() {
-        if (contextObject==null) throw new IllegalArgumentException("No suitable context ("+context+") to auto-detect classloader dir");
-        Class<?> cc = contextObject instanceof Class ? (Class<?>)contextObject : contextObject.getClass();
-        return getClassLoaderDir(cc.getCanonicalName().replace('.', '/')+".class");
-    }
-    
-    public String getClassLoaderDir(String resourceInThatDir) {
-        resourceInThatDir = Strings.removeFromStart(resourceInThatDir, "/");
-        URL resourceUrl = getLoader().getResource(resourceInThatDir);
-        if (resourceUrl==null) throw new NoSuchElementException("Resource ("+resourceInThatDir+") not found");
-
-        URL containerUrl = getContainerUrl(resourceUrl, resourceInThatDir);
-
-        if (!"file".equals(containerUrl.getProtocol())) throw new IllegalStateException("Resource ("+resourceInThatDir+") not on file system (at "+containerUrl+")");
-
-        //convert from file: URL to File
-        File file;
-        try {
-            file = new File(containerUrl.toURI());
-        } catch (URISyntaxException e) {
-            throw new IllegalStateException("Resource ("+resourceInThatDir+") found at invalid URI (" + containerUrl + ")", e);
-        }
-        
-        if (!file.exists()) throw new IllegalStateException("Context class url substring ("+containerUrl+") not found on filesystem");
-        return file.getPath();
-        
-    }
-
-    public static URL getContainerUrl(URL url, String resourceInThatDir) {
-        //Switching from manual parsing of jar: and file: URLs to java provided functionality.
-        //The old code was breaking on any Windows path and instead of fixing it, using
-        //the provided Java APIs seemed like the better option since they are already tested
-        //on multiple platforms.
-        boolean isJar = "jar".equals(url.getProtocol());
-        if(isJar) {
-            try {
-                //let java handle the parsing of jar URL, no network connection is established.
-                //Strips the jar protocol:
-                //  jar:file:/<path to jar>!<resourceInThatDir>
-                //  becomes
-                //  file:/<path to jar>
-                JarURLConnection connection = (JarURLConnection) url.openConnection();
-                url = connection.getJarFileURL();
-            } catch (IOException e) {
-                throw new IllegalStateException(e);
-            }
-        } else {
-            //Remove the trailing resouceInThatDir path from the URL, thus getting the parent folder.
-            String path = url.toString();
-            int i = path.indexOf(resourceInThatDir);
-            if (i==-1) throw new IllegalStateException("Resource path ("+resourceInThatDir+") not in url substring ("+url+")");
-            String parent = path.substring(0, i);
-            try {
-                url = new URL(parent);
-            } catch (MalformedURLException e) {
-                throw new IllegalStateException("Resource ("+resourceInThatDir+") found at invalid URL parent (" + parent + ")", e);
-            }
-        }
-        return url;
-    }
-    
-    /** @deprecated since 0.7.0 use {@link Streams#readFullyString(InputStream) */ @Deprecated
-    public static String readFullyString(InputStream is) throws IOException {
-        return Streams.readFullyString(is);
-    }
-
-    /** @deprecated since 0.7.0 use {@link Streams#readFully(InputStream) */ @Deprecated
-    public static byte[] readFullyBytes(InputStream is) throws IOException {
-        return Streams.readFully(is);
-    }
-    
-    /** @deprecated since 0.7.0 use {@link Streams#copy(InputStream, OutputStream)} */ @Deprecated
-    public static void copy(InputStream input, OutputStream output) throws IOException {
-        Streams.copy(input, output);
-    }
-
-    /** @deprecated since 0.7.0; use same method in {@link Os} */ @Deprecated
-    public static File mkdirs(File dir) {
-        return Os.mkdirs(dir);
-    }
-
-    /** @deprecated since 0.7.0; use same method in {@link Os} */ @Deprecated
-    public static File writeToTempFile(InputStream is, String prefix, String suffix) {
-        return Os.writeToTempFile(is, prefix, suffix);
-    }
-    
-    /** @deprecated since 0.7.0; use same method in {@link Os} */ @Deprecated
-    public static File writeToTempFile(InputStream is, File tempDir, String prefix, String suffix) {
-        return Os.writeToTempFile(is, tempDir, prefix, suffix);
-    }
-
-    /** @deprecated since 0.7.0; use method {@link Os#writePropertiesToTempFile(Properties, String, String)} */ @Deprecated
-    public static File writeToTempFile(Properties props, String prefix, String suffix) {
-        return Os.writePropertiesToTempFile(props, prefix, suffix);
-    }
-    
-    /** @deprecated since 0.7.0; use method {@link Os#writePropertiesToTempFile(Properties, File, String, String)} */ @Deprecated
-    public static File writeToTempFile(Properties props, File tempDir, String prefix, String suffix) {
-        return Os.writePropertiesToTempFile(props, tempDir, prefix, suffix);
-    }
-
-    /** @deprecated since 0.7.0; use method {@link Threads#addShutdownHook(Runnable)} */ @Deprecated
-    public static Thread addShutdownHook(final Runnable task) {
-        return Threads.addShutdownHook(task);
-    }
-    /** @deprecated since 0.7.0; use method {@link Threads#removeShutdownHook(Thread)} */ @Deprecated
-    public static boolean removeShutdownHook(Thread hook) {
-        return Threads.removeShutdownHook(hook);
-    }
-
-    /** returns the items with exactly one "/" between items (whether or not the individual items start or end with /),
-     * except where character before the / is a : (url syntax) in which case it will permit multiple (will not remove any) 
-     * @deprecated since 0.7.0 use either {@link Os#mergePathsUnix(String...)} {@link Urls#mergePaths(String...) */ @Deprecated
-    public static String mergePaths(String ...items) {
-        return Urls.mergePaths(items);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/config/ConfigBag.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/config/ConfigBag.java b/core/src/main/java/brooklyn/util/config/ConfigBag.java
deleted file mode 100644
index 2f17748..0000000
--- a/core/src/main/java/brooklyn/util/config/ConfigBag.java
+++ /dev/null
@@ -1,588 +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.config;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.ConcurrentModificationException;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.config.ConfigKey.HasConfigKey;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.javalang.JavaClassNames;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Objects;
-import com.google.common.collect.Sets;
-
-/**
- * Stores config in such a way that usage can be tracked.
- * Either {@link ConfigKey} or {@link String} keys can be inserted;
- * they will be stored internally as strings.
- * It is recommended to use {@link ConfigKey} instances to access,
- * although in some cases (such as setting fields from flags, or copying a map)
- * it may be necessary to mark things as used, or put, when only a string key is available.
- * <p>
- * This bag is order-preserving and thread-safe except where otherwise indicated,
- * currently by synching on this instance (but that behaviour may change).
- * <p>
- * @author alex
- */
-public class ConfigBag {
-
-    private static final Logger log = LoggerFactory.getLogger(ConfigBag.class);
-
-    /** an immutable, empty ConfigBag */
-    public static final ConfigBag EMPTY = new ConfigBag().setDescription("immutable empty config bag").seal();
-    
-    protected String description;
-    
-    private Map<String,Object> config;
-    private final Map<String,Object> unusedConfig;
-    private final boolean live;
-    private boolean sealed = false;
-
-    /** creates a new ConfigBag instance, empty and ready for population */
-    public static ConfigBag newInstance() {
-        return new ConfigBag();
-    }
-
-    /**
-     * Creates an instance that is backed by a "live map" (e.g. storage in a datagrid).
-     * The order-preserving nature of this class is only guaranteed if the
-     * provided storage has those properties. External modifications to the store can cause
-     * {@link ConcurrentModificationException} to be thrown, here or elsewhere. 
-     */
-    public static ConfigBag newLiveInstance(Map<String,Object> storage) {
-        return new ConfigBag(checkNotNull(storage, "storage map must be specified"));
-    }
-
-    public static ConfigBag newInstance(Map<?, ?> config) {
-        ConfigBag result = new ConfigBag();
-        result.putAll(config);
-        return result;
-    }
-
-    /** creates a new ConfigBag instance which includes all of the supplied ConfigBag's values,
-     * but which tracks usage separately (already used values are marked as such,
-     * but uses in the original set will not be marked here, and vice versa) */
-    public static ConfigBag newInstanceCopying(final ConfigBag configBag) {
-        return new ConfigBag().copy(configBag).setDescription(configBag.getDescription());
-    }
-    
-    /** creates a new ConfigBag instance which includes all of the supplied ConfigBag's values,
-     * plus an additional set of &lt;ConfigKey,Object&gt; or &lt;String,Object&gt; pairs
-     * <p>
-     * values from the original set which are used here will be marked as used in the original set
-     * (note: this applies even for values which are overridden and the overridden value is used);
-     * however subsequent uses in the original set will not be marked here
-     */
-    @Beta
-    public static ConfigBag newInstanceExtending(final ConfigBag parentBag) {
-        return new ConfigBagExtendingParent(parentBag);
-    }
-
-    /** @see #newInstanceExtending(ConfigBag) */
-    private static class ConfigBagExtendingParent extends ConfigBag {
-        ConfigBag parentBag;
-        private ConfigBagExtendingParent(ConfigBag parentBag) {
-            this.parentBag = parentBag;
-            copy(parentBag);
-        }
-        @Override
-        public void markUsed(String key) {
-            super.markUsed(key);
-            if (parentBag!=null)
-                parentBag.markUsed(key);
-        }
-    }
-    
-    /** As {@link #newInstanceExtending(ConfigBag)} but also putting the supplied values. */
-    @Beta
-    public static ConfigBag newInstanceExtending(final ConfigBag configBag, Map<?,?> optionalAdditionalValues) {
-        return newInstanceExtending(configBag).putAll(optionalAdditionalValues);
-    }
-
-    /** @deprecated since 0.7.0, not used; kept only for rebind compatibility where the inner class is used 
-     * (now replaced by a static class above) */
-    @Beta @Deprecated
-    public static ConfigBag newInstanceWithInnerClass(final ConfigBag configBag, Map<?,?> optionalAdditionalValues) {
-        return new ConfigBag() {
-            @Override
-            public void markUsed(String key) {
-                super.markUsed(key);
-                configBag.markUsed(key);
-            }
-        }.copy(configBag).putAll(optionalAdditionalValues);
-    }
-
-    public ConfigBag() {
-        config = new LinkedHashMap<String,Object>();
-        unusedConfig = new LinkedHashMap<String,Object>();
-        live = false;
-    }
-    
-    private ConfigBag(Map<String,Object> storage) {
-        this.config = storage;
-        unusedConfig = new LinkedHashMap<String,Object>();
-        live = true;
-    }
-    
-    public ConfigBag setDescription(String description) {
-        if (sealed) 
-            throw new IllegalStateException("Cannot set description to '"+description+"': this config bag has been sealed and is now immutable.");
-        this.description = description;
-        return this;
-    }
-    
-    /** optional description used to provide context for operations */
-    public String getDescription() {
-        return description;
-    }
-    
-    /** current values for all entries 
-     * @return non-modifiable map of strings to object */
-    public synchronized Map<String,Object> getAllConfig() {
-        return MutableMap.copyOf(config).asUnmodifiable();
-    }
-
-    /** current values for all entries in a map where the keys are converted to {@link ConfigKey} instances */
-    public synchronized Map<ConfigKey<?>, ?> getAllConfigAsConfigKeyMap() {
-        Map<ConfigKey<?>,Object> result = MutableMap.of();
-        for (Map.Entry<String,Object> entry: config.entrySet()) {
-            result.put(ConfigKeys.newConfigKey(Object.class, entry.getKey()), entry.getValue());
-        }
-        return result;
-    }
-
-    /** Returns the internal map containing the current values for all entries;
-     * for use where the caller wants to modify this directly and knows it is safe to do so 
-     * <p>
-     * Accesses to the returned map must be synchronized on this bag if the 
-     * thread-safe behaviour is required. */ 
-    public Map<String,Object> getAllConfigMutable() {
-        if (live) {
-            // TODO sealed no longer works as before, because `config` is the backing storage map.
-            // Therefore returning it is dangerous! Even if we were to replace our field with an immutable copy,
-            // the underlying datagrid's map would still be modifiable. We need a way to switch the returned
-            // value's behaviour to sealable (i.e. wrapping the returned map).
-            return (sealed) ? MutableMap.copyOf(config).asUnmodifiable() : config;
-        } else {
-            return config;
-        }
-    }
-
-    /** current values for all entries which have not yet been used 
-     * @return non-modifiable map of strings to object */
-    public synchronized Map<String,Object> getUnusedConfig() {
-        return MutableMap.copyOf(unusedConfig).asUnmodifiable();
-    }
-
-    /** Returns the internal map containing the current values for all entries which have not yet been used;
-     * for use where the caller wants to modify this directly and knows it is safe to do so 
-     * <p>
-     * Accesses to the returned map must be synchronized on this bag if the 
-     * thread-safe behaviour is required. */ 
-    public Map<String,Object> getUnusedConfigMutable() {
-        return unusedConfig;
-    }
-
-    public ConfigBag putAll(Map<?,?> addlConfig) {
-        if (addlConfig==null) return this;
-        for (Map.Entry<?,?> e: addlConfig.entrySet()) {
-            putAsStringKey(e.getKey(), e.getValue());
-        }
-        return this;
-    }
-    
-    public ConfigBag putAll(ConfigBag addlConfig) {
-        return putAll(addlConfig.getAllConfig());
-    }
-    
-    public <T> ConfigBag putIfAbsent(ConfigKey<T> key, T value) {
-        return putIfAbsent(MutableMap.of(key, value));
-    }
-
-    public ConfigBag putAsStringKeyIfAbsent(Object key, Object value) {
-        return putIfAbsent(MutableMap.of(key, value));
-    }
-
-    public synchronized ConfigBag putIfAbsent(Map<?, ?> propertiesToSet) {
-        if (propertiesToSet==null)
-            return this;
-        for (Map.Entry<?, ?> entry: propertiesToSet.entrySet()) {
-            Object key = entry.getKey();
-            if (key instanceof HasConfigKey<?>)
-                key = ((HasConfigKey<?>)key).getConfigKey();
-            if (key instanceof ConfigKey<?>) {
-                if (!containsKey((ConfigKey<?>)key))
-                    putAsStringKey(key, entry.getValue());
-            } else if (key instanceof String) {
-                if (!containsKey((String)key))
-                    putAsStringKey(key, entry.getValue());
-            } else {
-                logInvalidKey(key);
-            }
-        }
-        return this;
-    }
-
-    public ConfigBag putIfAbsent(ConfigBag addlConfig) {
-        return putIfAbsent(addlConfig.getAllConfig());
-    }
-
-
-    @SuppressWarnings("unchecked")
-    public <T> T put(ConfigKey<T> key, T value) {
-        return (T) putStringKey(key.getName(), value);
-    }
-    
-    public <T> ConfigBag putIfNotNull(ConfigKey<T> key, T value) {
-        if (value!=null) put(key, value);
-        return this;
-    }
-
-    public <T> ConfigBag putIfAbsentAndNotNull(ConfigKey<T> key, T value) {
-        if (value!=null) putIfAbsent(key, value);
-        return this;
-    }
-
-    /** as {@link #put(ConfigKey, Object)} but returning this ConfigBag for fluent-style coding */
-    public <T> ConfigBag configure(ConfigKey<T> key, T value) {
-        putStringKey(key.getName(), value);
-        return this;
-    }
-    
-    public <T> ConfigBag configureStringKey(String key, T value) {
-        putStringKey(key, value);
-        return this;
-    }
-    
-    protected synchronized void putAsStringKey(Object key, Object value) {
-        if (key instanceof HasConfigKey<?>) key = ((HasConfigKey<?>)key).getConfigKey();
-        if (key instanceof ConfigKey<?>) key = ((ConfigKey<?>)key).getName();
-        if (key instanceof String) {
-            putStringKey((String)key, value);
-        } else {
-            logInvalidKey(key);
-        }
-    }
-
-    protected void logInvalidKey(Object key) {
-        String message = (key == null ? "Invalid key 'null'" : "Invalid key type "+key.getClass().getCanonicalName()+" ("+key+")") +
-                " being used for configuration, ignoring";
-        log.debug(message, new Throwable("Source of "+message));
-        log.warn(message);
-    }
-    
-    /** recommended to use {@link #put(ConfigKey, Object)} but there are times
-     * (e.g. when copying a map) where we want to put a string key directly 
-     */
-    public synchronized Object putStringKey(String key, Object value) {
-        if (sealed) 
-            throw new IllegalStateException("Cannot insert "+key+"="+value+": this config bag has been sealed and is now immutable.");
-        boolean isNew = !config.containsKey(key);
-        boolean isUsed = !isNew && !unusedConfig.containsKey(key);
-        Object old = config.put(key, value);
-        if (!isUsed) 
-            unusedConfig.put(key, value);
-        //if (!isNew && !isUsed) log.debug("updating config value which has already been used");
-        return old;
-    }
-    public Object putStringKeyIfHasValue(String key, Maybe<?> value) {
-        if (value.isPresent())
-            return putStringKey(key, value.get());
-        return null;
-    }
-    public Object putStringKeyIfNotNull(String key, Object value) {
-        if (value!=null)
-            return putStringKey(key, value);
-        return null;
-    }
-
-    public boolean containsKey(HasConfigKey<?> key) {
-        return containsKey(key.getConfigKey());
-    }
-
-    public boolean containsKey(ConfigKey<?> key) {
-        return containsKey(key.getName());
-    }
-
-    public synchronized boolean containsKey(String key) {
-        return config.containsKey(key);
-    }
-
-    /** returns the value of this config key, falling back to its default (use containsKey to see whether it was contained);
-     * also marks it as having been used (use peek to prevent marking as used)
-     */
-    public <T> T get(ConfigKey<T> key) {
-        return get(key, true);
-    }
-
-    /** gets a value from a string-valued key or null; ConfigKey is preferred, but this is useful in some contexts (e.g. setting from flags) */
-    public Object getStringKey(String key) {
-        return getStringKeyMaybe(key).orNull();
-    }
-    /** gets a {@link Maybe}-wrapped value from a string-valued key; ConfigKey is preferred, but this is useful in some contexts (e.g. setting from flags) */
-    public @Nonnull Maybe<Object> getStringKeyMaybe(String key) {
-        return getStringKeyMaybe(key, true);
-    }
-
-    /** gets a {@link Maybe}-wrapped value from a key, inferring the type of that key (e.g. {@link ConfigKey} or {@link String}) */
-    @Beta
-    public Maybe<Object> getObjKeyMaybe(Object key) {
-        if (key instanceof HasConfigKey<?>) key = ((HasConfigKey<?>)key).getConfigKey();
-        if (key instanceof ConfigKey<?>) key = ((ConfigKey<?>)key).getName();
-        if (key instanceof String) {
-            return getStringKeyMaybe((String)key, true);
-        } else {
-            logInvalidKey(key);
-            return Maybe.absent();
-        }
-    }
-
-    /** like get, but without marking it as used */
-    public <T> T peek(ConfigKey<T> key) {
-        return get(key, false);
-    }
-
-    /** returns the first key in the list for which a value is explicitly set, then defaulting to defaulting value of preferred key */
-    public synchronized <T> T getFirst(ConfigKey<T> preferredKey, ConfigKey<T> ...otherCurrentKeysInOrderOfPreference) {
-        if (containsKey(preferredKey)) 
-            return get(preferredKey);
-        for (ConfigKey<T> key: otherCurrentKeysInOrderOfPreference) {
-            if (containsKey(key)) 
-                return get(key);
-        }
-        return get(preferredKey);
-    }
-
-    /** convenience for @see #getWithDeprecation(ConfigKey[], ConfigKey...) */
-    public Object getWithDeprecation(ConfigKey<?> key, ConfigKey<?> ...deprecatedKeys) {
-        return getWithDeprecation(new ConfigKey[] { key }, deprecatedKeys);
-    }
-
-    /** returns the value for the first key in the list for which a value is set,
-     * warning if any of the deprecated keys have a value which is different to that set on the first set current key
-     * (including warning if a deprecated key has a value but no current key does) */
-    public synchronized Object getWithDeprecation(ConfigKey<?>[] currentKeysInOrderOfPreference, ConfigKey<?> ...deprecatedKeys) {
-        // Get preferred key (or null)
-        ConfigKey<?> preferredKeyProvidingValue = null;
-        Object result = null;
-        boolean found = false;
-        for (ConfigKey<?> key: currentKeysInOrderOfPreference) {
-            if (containsKey(key)) {
-                preferredKeyProvidingValue = key;
-                result = get(preferredKeyProvidingValue);
-                found = true;
-                break;
-            }
-        }
-        
-        // Check if any deprecated keys are set
-        ConfigKey<?> deprecatedKeyProvidingValue = null;
-        Object deprecatedResult = null;
-        boolean foundDeprecated = false;
-        for (ConfigKey<?> deprecatedKey: deprecatedKeys) {
-            Object x = null;
-            boolean foundX = false;
-            if (containsKey(deprecatedKey)) {
-                x = get(deprecatedKey);
-                foundX = true;
-            }
-            if (foundX) {
-                if (found) {
-                    if (!Objects.equal(result, x)) {
-                        log.warn("Conflicting value from deprecated key " +deprecatedKey+", value "+x+
-                                "; using preferred key "+preferredKeyProvidingValue+" value "+result);
-                    } else {
-                        log.info("Deprecated key " +deprecatedKey+" ignored; has same value as preferred key "+preferredKeyProvidingValue+" ("+result+")");
-                    }
-                } else if (foundDeprecated) {
-                    if (!Objects.equal(result, x)) {
-                        log.warn("Conflicting values from deprecated keys: using " +deprecatedKeyProvidingValue+" instead of "+deprecatedKey+
-                                " (value "+deprecatedResult+" instead of "+x+")");
-                    } else {
-                        log.info("Deprecated key " +deprecatedKey+" ignored; has same value as other deprecated key "+preferredKeyProvidingValue+" ("+deprecatedResult+")");
-                    }
-                } else {
-                    // new value, from deprecated key
-                    log.warn("Deprecated key " +deprecatedKey+" detected (supplying value "+x+"), "+
-                            "; recommend changing to preferred key '"+currentKeysInOrderOfPreference[0]+"'; this will not be supported in future versions");
-                    deprecatedResult = x;
-                    deprecatedKeyProvidingValue = deprecatedKey;
-                    foundDeprecated = true;
-                }
-            }
-        }
-        
-        if (found) {
-            return result;
-        } else if (foundDeprecated) {
-            return deprecatedResult;
-        } else {
-            return currentKeysInOrderOfPreference[0].getDefaultValue();
-        }
-    }
-
-    protected <T> T get(ConfigKey<T> key, boolean markUsed) {
-        // TODO for now, no evaluation -- maps / closure content / other smart (self-extracting) keys are NOT supported
-        // (need a clean way to inject that behaviour, as well as desired TypeCoercions)
-        // this method, and the coercion, is not synchronized, nor does it need to be, because the "get" is synchronized. 
-        return coerceFirstNonNullKeyValue(key, getStringKey(key.getName(), markUsed));
-    }
-
-    /** returns the first non-null value to be the type indicated by the key, or the keys default value if no non-null values are supplied */
-    public static <T> T coerceFirstNonNullKeyValue(ConfigKey<T> key, Object ...values) {
-        for (Object o: values)
-            if (o!=null) return TypeCoercions.coerce(o, key.getTypeToken());
-        return TypeCoercions.coerce(key.getDefaultValue(), key.getTypeToken());
-    }
-
-    protected Object getStringKey(String key, boolean markUsed) {
-        return getStringKeyMaybe(key, markUsed).orNull();
-    }
-    protected synchronized Maybe<Object> getStringKeyMaybe(String key, boolean markUsed) {
-        if (config.containsKey(key)) {
-            if (markUsed) markUsed(key);
-            return Maybe.of(config.get(key));
-        }
-        return Maybe.absent();
-    }
-
-    /** indicates that a string key in the config map has been accessed */
-    public synchronized void markUsed(String key) {
-        unusedConfig.remove(key);
-    }
-
-    public synchronized void clear() {
-        if (sealed) 
-            throw new IllegalStateException("Cannot clear this config bag has been sealed and is now immutable.");
-        config.clear();
-        unusedConfig.clear();
-    }
-    
-    public ConfigBag removeAll(ConfigKey<?> ...keys) {
-        for (ConfigKey<?> key: keys) remove(key);
-        return this;
-    }
-
-    public synchronized void remove(ConfigKey<?> key) {
-        remove(key.getName());
-    }
-
-    public ConfigBag removeAll(Iterable<String> keys) {
-        for (String key: keys) remove(key);
-        return this;
-    }
-
-    public synchronized void remove(String key) {
-        if (sealed) 
-            throw new IllegalStateException("Cannot remove "+key+": this config bag has been sealed and is now immutable.");
-        config.remove(key);
-        unusedConfig.remove(key);
-    }
-
-    public ConfigBag copy(ConfigBag other) {
-        // ensure locks are taken in a canonical order to prevent deadlock
-        if (other==null) {
-            synchronized (this) {
-                return copyWhileSynched(other);
-            }
-        }
-        if (System.identityHashCode(other) < System.identityHashCode(this)) {
-            synchronized (other) {
-                synchronized (this) {
-                    return copyWhileSynched(other);
-                }
-            }
-        } else {
-            synchronized (this) {
-                synchronized (other) {
-                    return copyWhileSynched(other);
-                }
-            }
-        }
-    }
-    
-    protected ConfigBag copyWhileSynched(ConfigBag other) {
-        if (sealed) 
-            throw new IllegalStateException("Cannot copy "+other+" to "+this+": this config bag has been sealed and is now immutable.");
-        putAll(other.getAllConfig());
-        markAll(Sets.difference(other.getAllConfig().keySet(), other.getUnusedConfig().keySet()));
-        setDescription(other.getDescription());
-        return this;
-    }
-
-    public synchronized int size() {
-        return config.size();
-    }
-    
-    public synchronized boolean isEmpty() {
-        return config.isEmpty();
-    }
-    
-    public ConfigBag markAll(Iterable<String> usedFlags) {
-        for (String flag: usedFlags)
-            markUsed(flag);
-        return this;
-    }
-
-    public synchronized boolean isUnused(ConfigKey<?> key) {
-        return unusedConfig.containsKey(key.getName());
-    }
-    
-    /** makes this config bag immutable; any attempts to change subsequently 
-     * (apart from marking fields as used) will throw an exception
-     * <p>
-     * copies will be unsealed however
-     * <p>
-     * returns this for convenience (fluent usage) */
-    public ConfigBag seal() {
-        sealed = true;
-        if (live) {
-            // TODO How to ensure sealed?!
-        } else {
-            config = getAllConfig();
-        }
-        return this;
-    }
-
-    // TODO why have both this and mutable
-    /** @see #getAllConfigMutable() */
-    public Map<String, Object> getAllConfigRaw() {
-        return getAllConfigMutable();
-    }
-    
-    @Override
-    public String toString() {
-        return JavaClassNames.simpleClassName(this)+"["+getAllConfigRaw()+"]";
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/crypto/FluentKeySigner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/crypto/FluentKeySigner.java b/core/src/main/java/brooklyn/util/crypto/FluentKeySigner.java
deleted file mode 100644
index a1bc125..0000000
--- a/core/src/main/java/brooklyn/util/crypto/FluentKeySigner.java
+++ /dev/null
@@ -1,192 +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.crypto;
-
-import java.math.BigInteger;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.apache.brooklyn.core.internal.BrooklynInitialization;
-import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.jce.X509Principal;
-
-import brooklyn.util.exceptions.Exceptions;
-
-/** A fluent API which simplifies generating certificates (signed keys) */
-/* NB - re deprecation - we use deprecated X509V3CertificateGenerator still
- * because the official replacement, X509v3CertificateBuilder, 
- * drags in an add'l dependency (bcmail) and is harder to use. */
-public class FluentKeySigner {
-
-    static { BrooklynInitialization.initSecureKeysBouncyCastleProvider(); }
-
-    protected X500Principal issuerPrincipal;
-    protected KeyPair issuerKey;
-
-    protected SecureRandom srand = new SecureRandom();
-    
-    protected Date validityStartDate, validityEndDate;
-    protected BigInteger serialNumber;
-    
-    protected String signatureAlgorithm = "MD5WithRSAEncryption";
-    protected AuthorityKeyIdentifier authorityKeyIdentifier;
-    protected X509Certificate authorityCertificate;
-
-    public FluentKeySigner(X500Principal issuerPrincipal, KeyPair issuerKey) {
-        this.issuerPrincipal = issuerPrincipal;
-        this.issuerKey = issuerKey;
-        validFromDaysAgo(7);
-        validForYears(10);
-    }
-    public FluentKeySigner(String issuerCommonName, KeyPair issuerKey) {
-        this(SecureKeys.getX500PrincipalWithCommonName(issuerCommonName), issuerKey);
-    }
-    
-    public FluentKeySigner(String issuerCommonName) {
-        this(issuerCommonName, SecureKeys.newKeyPair());
-    }
-
-    public FluentKeySigner(X509Certificate caCert, KeyPair caKey) {
-        this(caCert.getIssuerX500Principal(), caKey);
-        authorityCertificate(caCert);
-    }
-    
-    public KeyPair getKey() {
-        return issuerKey;
-    }
-    
-    public X500Principal getPrincipal() {
-        return issuerPrincipal;
-    }
-    
-    @SuppressWarnings("deprecation")
-    public String getCommonName() {
-//        TODO see deprecation note at top of file
-        // for modernising, would RFC4519Style.cn work ?
-        return (String) new X509Principal(issuerPrincipal.getName()).getValues(org.bouncycastle.asn1.x509.X509Name.CN).elementAt(0);
-    }
-    
-    public X509Certificate getAuthorityCertificate() {
-        return authorityCertificate;
-    }
-    
-    public FluentKeySigner validFromDaysAgo(long days) {
-        return validFrom(new Date( (System.currentTimeMillis() / (1000L*60*60*24) - days) * 1000L*60*60*24));            
-    }
-
-    public FluentKeySigner validFrom(Date d) {
-        validityStartDate = d;
-        return this;
-    }
-
-    public FluentKeySigner validForYears(long years) {
-        return validUntil(new Date( (System.currentTimeMillis() / (1000L*60*60*24) + 365*years) * 1000L*60*60*24));            
-    }
-
-    public FluentKeySigner validUntil(Date d) {
-        validityEndDate = d;
-        return this;
-    }
-
-    /** use a hard-coded serial number; or make one up, if null */
-    public FluentKeySigner serialNumber(BigInteger serialNumber) {
-        this.serialNumber = serialNumber;
-        return this;
-    }
-
-    public FluentKeySigner signatureAlgorithm(String signatureAlgorithm) {
-        this.signatureAlgorithm = signatureAlgorithm;
-        return this;
-    }
-
-    @SuppressWarnings("deprecation")
-    public FluentKeySigner authorityCertificate(X509Certificate certificate) {
-        try {
-            authorityKeyIdentifier(new org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure(certificate));
-            this.authorityCertificate = certificate;
-            return this;
-        } catch (CertificateParsingException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    public FluentKeySigner authorityKeyIdentifier(AuthorityKeyIdentifier authorityKeyIdentifier) {
-        this.authorityKeyIdentifier = authorityKeyIdentifier;
-        return this;
-    }
-    
-    public FluentKeySigner selfsign() {
-        if (authorityCertificate!=null) throw new IllegalStateException("Signer already has certificate");
-        authorityCertificate(newCertificateFor(getCommonName(), getKey()));
-        return this;
-    }
-
-    // TODO see note re deprecation at start of file
-    @SuppressWarnings("deprecation")
-    public X509Certificate newCertificateFor(X500Principal subject, PublicKey keyToCertify) {
-        try {
-            org.bouncycastle.x509.X509V3CertificateGenerator v3CertGen = new org.bouncycastle.x509.X509V3CertificateGenerator();
-
-            v3CertGen.setSerialNumber(
-                    serialNumber != null ? serialNumber :
-                        // must be positive
-                        BigInteger.valueOf(srand.nextLong()).abs().add(BigInteger.ONE));  
-            v3CertGen.setIssuerDN(issuerPrincipal);  
-            v3CertGen.setNotBefore(validityStartDate);  
-            v3CertGen.setNotAfter(validityEndDate);
-            v3CertGen.setSignatureAlgorithm(signatureAlgorithm);   
-
-            v3CertGen.setSubjectDN(subject);  
-            v3CertGen.setPublicKey(keyToCertify);  
-
-            v3CertGen.addExtension(X509Extension.subjectKeyIdentifier, false,
-                    new org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure(keyToCertify));
-
-            if (authorityKeyIdentifier!=null)
-                v3CertGen.addExtension(X509Extension.authorityKeyIdentifier, false,
-                        authorityKeyIdentifier);
-
-            X509Certificate pkCertificate = v3CertGen.generate(issuerKey.getPrivate(), "BC");
-            return pkCertificate;
-            
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    public X509Certificate newCertificateFor(String commonName, PublicKey key) {
-//        SecureKeys.getX509PrincipalWithCommonName(commonName)
-        return newCertificateFor(
-                SecureKeys.getX500PrincipalWithCommonName(commonName)
-//                new X509Principal("CN=" + commonName + ", OU=None, O=None, L=None, C=None")
-                , key);
-    }
-
-    public X509Certificate newCertificateFor(String commonName, KeyPair key) {
-        return newCertificateFor(commonName, key.getPublic());
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/crypto/SecureKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/crypto/SecureKeys.java b/core/src/main/java/brooklyn/util/crypto/SecureKeys.java
deleted file mode 100644
index 29fcf32..0000000
--- a/core/src/main/java/brooklyn/util/crypto/SecureKeys.java
+++ /dev/null
@@ -1,184 +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.crypto;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.StringWriter;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Security;
-
-import org.apache.brooklyn.core.internal.BrooklynInitialization;
-import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.jce.X509Principal;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMDecryptorProvider;
-import org.bouncycastle.openssl.PEMEncryptedKeyPair;
-import org.bouncycastle.openssl.PEMKeyPair;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.PEMWriter;
-import org.bouncycastle.openssl.PasswordFinder;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.stream.Streams;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Throwables;
-
-/**
- * Utility methods for generating and working with keys,
- * extending the parent class with useful things provided by BouncyCastle crypto library.
- * (Parent class is in a different project where BC is not included as a dependency.)
- */
-public class SecureKeys extends SecureKeysWithoutBouncyCastle {
-
-    private static final Logger log = LoggerFactory.getLogger(SecureKeys.class);
-    
-    static { BrooklynInitialization.initSecureKeysBouncyCastleProvider(); }
-    
-    public static void initBouncyCastleProvider() {
-        Security.addProvider(new BouncyCastleProvider());
-    }
-    
-    public static class PassphraseProblem extends IllegalStateException {
-        private static final long serialVersionUID = -3382824813899223447L;
-        public PassphraseProblem(String message) { super("Passphrase problem with this key: "+message); }
-        public PassphraseProblem(String message, Exception cause) { super("Passphrase problem with this key: "+message, cause); }
-    }
-    
-    private SecureKeys() {}
-    
-    /** RFC1773 order, with None for other values. Normally prefer X500Principal. */
-    public static X509Principal getX509PrincipalWithCommonName(String commonName) {
-        return new X509Principal("" + "C=None," + "L=None," + "O=None," + "OU=None," + "CN=" + commonName);
-    }
-
-    /** reads RSA or DSA / pem style private key files (viz {@link #toPem(KeyPair)}), extracting also the public key if possible
-     * @throws IllegalStateException on errors, in particular {@link PassphraseProblem} if that is the problem */
-    public static KeyPair readPem(InputStream input, final String passphrase) {
-        // TODO cache is only for fallback "reader" strategy (2015-01); delete when Parser confirmed working
-        byte[] cache = Streams.readFully(input);
-        input = new ByteArrayInputStream(cache);
-
-        try {
-            PEMParser pemParser = new PEMParser(new InputStreamReader(input));
-
-            Object object = pemParser.readObject();
-            pemParser.close();
-
-            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
-            KeyPair kp = null;
-            if (object==null) {
-                throw new IllegalStateException("PEM parsing failed: missing or invalid data");
-            } else if (object instanceof PEMEncryptedKeyPair) {
-                if (passphrase==null) throw new PassphraseProblem("passphrase required");
-                try {
-                    PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(passphrase.toCharArray());
-                    kp = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv));
-                } catch (Exception e) {
-                    Exceptions.propagateIfFatal(e);
-                    throw new PassphraseProblem("wrong passphrase", e);
-                }
-            } else  if (object instanceof PEMKeyPair) {
-                kp = converter.getKeyPair((PEMKeyPair) object);
-            } else if (object instanceof PrivateKeyInfo) {
-                PrivateKey privKey = converter.getPrivateKey((PrivateKeyInfo) object);
-                kp = new KeyPair(null, privKey);
-            } else {
-                throw new IllegalStateException("PEM parser support missing for: "+object);
-            }
-
-            return kp;
-
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-
-            // older code relied on PEMReader, now deprecated
-            // replaced with above based on http://stackoverflow.com/questions/14919048/bouncy-castle-pemreader-pemparser
-            // passes the same tests (Jan 2015) but leaving the old code as a fallback for the time being 
-
-            input = new ByteArrayInputStream(cache);
-            try {
-                Security.addProvider(new BouncyCastleProvider());
-                @SuppressWarnings("deprecation")
-                org.bouncycastle.openssl.PEMReader pr = new org.bouncycastle.openssl.PEMReader(new InputStreamReader(input), new PasswordFinder() {
-                    public char[] getPassword() {
-                        return passphrase!=null ? passphrase.toCharArray() : new char[0];
-                    }
-                });
-                @SuppressWarnings("deprecation")
-                KeyPair result = (KeyPair) pr.readObject();
-                pr.close();
-                if (result==null)
-                    throw Exceptions.propagate(e);
-                
-                log.warn("PEMParser failed when deprecated PEMReader succeeded, with "+result+"; had: "+e);
-
-                return result;
-
-            } catch (Exception e2) {
-                Exceptions.propagateIfFatal(e2);
-                throw Exceptions.propagate(e);
-            }
-        }
-    }
-
-    /** because KeyPair.equals is not implemented :( */
-    public static boolean equal(KeyPair k1, KeyPair k2) {
-        return Objects.equal(k2.getPrivate(), k1.getPrivate()) && Objects.equal(k2.getPublic(), k1.getPublic());
-    }
-
-    /** returns the PEM (base64, ie for id_rsa) string for the private key / key pair;
-     * this starts -----BEGIN PRIVATE KEY----- and ends similarly, like id_rsa.
-     * also see {@link #readPem(InputStream, String)} */
-    public static String toPem(KeyPair key) {
-        return stringPem(key);
-    }
-
-    /** returns id_rsa.pub style file, of public key */
-    public static String toPub(KeyPair key) {
-        return AuthorizedKeysParser.encodePublicKey(key.getPublic());
-    }
-    
-    /** opposite of {@link #toPub(KeyPair)}, given text */
-    public static PublicKey fromPub(String pubText) {
-        return AuthorizedKeysParser.decodePublicKey(pubText);
-    }
-
-    /** @deprecated since 0.7.0, use {@link #toPem(KeyPair)} */ @Deprecated
-    public static String stringPem(KeyPair key) {
-        try {
-            StringWriter sw = new StringWriter();
-            PEMWriter w = new PEMWriter(sw);
-            w.writeObject(key);
-            w.close();
-            return sw.toString();
-        } catch (IOException e) {
-            throw Throwables.propagate(e);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/file/ArchiveBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/file/ArchiveBuilder.java b/core/src/main/java/brooklyn/util/file/ArchiveBuilder.java
deleted file mode 100644
index 6985406..0000000
--- a/core/src/main/java/brooklyn/util/file/ArchiveBuilder.java
+++ /dev/null
@@ -1,423 +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.file;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Collections;
-import java.util.Map;
-import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-import java.util.zip.ZipOutputStream;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.file.ArchiveUtils.ArchiveType;
-import brooklyn.util.os.Os;
-
-import com.google.common.annotations.Beta;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.io.Files;
-
-/**
- * Build a Zip or Jar archive.
- * <p>
- * Supports creating temporary archives that will be deleted on exit, if no name is
- * specified. The created file must be a Java archive type, with the extension {@code .zip},
- * {@code .jar}, {@code .war} or {@code .ear}.
- * <p>
- * Example:
- * <pre> File zip = ArchiveBuilder.archive("data/archive.zip")
- *         .addAt(new File("./pom.xml"), "")
- *         .addDirContentsAt(new File("./src"), "src/")
- *         .addAt(new File("/tmp/Extra.java"), "src/main/java/")
- *         .addDirContentsAt(new File("/tmp/overlay/"), "")
- *         .create();
- * </pre>
- * <p>
- */
-@Beta
-public class ArchiveBuilder {
-
-    /**
-     * Create an {@link ArchiveBuilder} for an archive with the given name.
-     */
-    public static ArchiveBuilder archive(String archive) {
-        return new ArchiveBuilder(archive);
-    }
-
-    /**
-     * Create an {@link ArchiveBuilder} for a {@link ArchiveType#ZIP Zip} format archive.
-     */
-    public static ArchiveBuilder zip() {
-        return new ArchiveBuilder(ArchiveType.ZIP);
-    }
-
-    /**
-     * Create an {@link ArchiveBuilder} for a {@link ArchiveType#JAR Jar} format archive.
-     */
-    public static ArchiveBuilder jar() {
-        return new ArchiveBuilder(ArchiveType.JAR);
-    }
-
-    // TODO would be nice to support TAR and TGZ
-    // e.g. using commons-compress
-    // TarArchiveOutputStream out = new TarArchiveOutputStream(new GZIPOutputStream(bytes));
-    // but I think the way entries are done is slightly different so we'd need a bit of refactoring
-    
-    private final ArchiveType type;
-    private File archive;
-    private Manifest manifest;
-    private Multimap<String, File> entries = LinkedHashMultimap.create();
-
-    private ArchiveBuilder() {
-        this(ArchiveType.ZIP);
-    }
-
-    private ArchiveBuilder(String filename) {
-        this(ArchiveType.of(filename));
-
-        named(filename);
-    }
-
-    private ArchiveBuilder(ArchiveType type) {
-        checkNotNull(type);
-        checkArgument(ArchiveType.ZIP_ARCHIVES.contains(type));
-
-        this.type = type;
-        this.manifest = new Manifest();
-    }
-
-    /**
-     * Set the location of the generated archive file.
-     */
-    public ArchiveBuilder named(String name) {
-        checkNotNull(name);
-        String ext = Files.getFileExtension(name);
-        if (ext.isEmpty()) {
-            name = name + "." + type.toString();
-        } else if (type != ArchiveType.of(name)) {
-            throw new IllegalArgumentException(String.format("Extension for '%s' did not match archive type of %s", ext, type));
-        }
-        this.archive = new File(Os.tidyPath(name));
-        return this;
-    }
-
-    /**
-     * @see #named(String)
-     */
-    public ArchiveBuilder named(File file) {
-        checkNotNull(file);
-        return named(file.getPath());
-    }
-
-    /**
-     * Add a manifest entry with the given {@code key} and {@code value}.
-     */
-    public ArchiveBuilder manifest(Object key, Object value) {
-        checkNotNull(key, "key");
-        checkNotNull(value, "value");
-        manifest.getMainAttributes().put(key, value);
-        return this;
-    }
-
-    /**
-     * Add the file located at the {@code filePath} to the archive, 
-     * with some complicated base-name strategies.
-     *
-     * @deprecated since 0.7.0 use one of the other add methods which makes the strategy explicit */ @Deprecated
-    public ArchiveBuilder add(String filePath) {
-        checkNotNull(filePath, "filePath");
-        return add(new File(Os.tidyPath(filePath)));
-    }
-
-    /**
-     * Add the {@code file} to the archive.
-     * <p>
-     * If the file path is absolute, or points to a file above the current directory,
-     * the file is added to the archive as a top-level entry, using the file name only.
-     * For relative {@code filePath}s below the current directory, the file is added
-     * using the path given and is assumed to be located relative to the current
-     * working directory.
-     * <p>
-     * No checks for file existence are made at this stage.
-     *
-     * @see #entry(String, File)
-     * @deprecated since 0.7.0 use one of the other add methods which makes the strategy explicit */ @Deprecated
-    public ArchiveBuilder add(File file) {
-        checkNotNull(file, "file");
-        String filePath = Os.tidyPath(file.getPath());
-        if (file.isAbsolute() || filePath.startsWith("../")) {
-            return entry(Os.mergePaths(".", file.getName()), file);
-        } else {
-            return entry(Os.mergePaths(".", filePath), file);
-        }
-    }
-
-    /**
-     * Add the file located at the {@code fileSubPath}, relative to the {@code baseDir} on the local system,
-     * to the archive.
-     * <p>
-     * Uses the {@code fileSubPath} as the name of the file in the archive. Note that the
-     * file is found by concatenating the two path components using {@link Os#mergePaths(String...)},
-     * thus {@code fileSubPath} should not be absolute or point to a location above the current directory.
-     * <p>
-     * Use {@link #entry(String, String)} directly or {@link #entries(Map)} for complete
-     * control over file locations and names in the archive.
-     *
-     * @see #entry(String, String)
-     */
-    public ArchiveBuilder addFromLocalBaseDir(File baseDir, String fileSubPath) {
-        checkNotNull(baseDir, "baseDir");
-        checkNotNull(fileSubPath, "filePath");
-        return entry(Os.mergePaths(".", fileSubPath), Os.mergePaths(baseDir.getPath(), fileSubPath));
-    }
-    /** @deprecated since 0.7.0 use {@link #addFromLocalBaseDir(File, String)}, or
-     * one of the other add methods if adding relative to baseDir was not intended */ @Deprecated
-    public ArchiveBuilder addFromLocalBaseDir(String baseDir, String fileSubPath) {
-        return addFromLocalBaseDir(new File(baseDir), fileSubPath);
-    }
-    /** @deprecated since 0.7.0 use {@link #addFromLocalBaseDir(File, String)}, or
-     * one of the other add methods if adding relative to baseDir was not intended */ @Deprecated
-    public ArchiveBuilder add(String baseDir, String fileSubPath) {
-        return addFromLocalBaseDir(baseDir, fileSubPath);
-    }
-     
-    /** adds the given file to the archive, preserving its name but putting under the given directory in the archive (may be <code>""</code> or <code>"./"</code>) */
-    public ArchiveBuilder addAt(File file, String archiveParentDir) {
-        checkNotNull(archiveParentDir, "archiveParentDir");
-        checkNotNull(file, "file");
-        return entry(Os.mergePaths(archiveParentDir, file.getName()), file);
-    }
-
-    /**
-     * Add the contents of the directory named {@code dirName} to the archive.
-     *
-     * @see #addDir(File)
-     * @deprecated since 0.7.0 use {@link #addDirContentsAt(File, String) */ @Deprecated
-    public ArchiveBuilder addDir(String dirName) {
-        checkNotNull(dirName, "dirName");
-        return addDir(new File(Os.tidyPath(dirName)));
-    }
-
-    /**
-     * Add the contents of the directory {@code dir} to the archive.
-     * The directory's name is not included; use {@link #addAtRoot(File)} if you want that behaviour. 
-     * <p>
-     * Uses {@literal .} as the parent directory name for the contents.
-     *
-     * @see #entry(String, File)
-     */
-    public ArchiveBuilder addDirContentsAt(File dir, String archiveParentDir) {
-        checkNotNull(dir, "dir");
-        if (!dir.isDirectory()) throw new IllegalArgumentException(dir+" is not a directory; cannot add contents to archive");
-        return entry(archiveParentDir, dir);
-    }
-    /**
-     * As {@link #addDirContentsAt(File, String)}, 
-     * using {@literal .} as the parent directory name for the contents.
-     * 
-     * @deprecated since 0.7.0 use {@link #addDirContentsAt(File, String)
-     * to clarify API, argument types, and be explicit about where it should be installed,
-     * because JARs seem to require <code>""<code> whereas ZIPs might want <code>"./"</code>. */ @Deprecated
-    public ArchiveBuilder addDir(File dir) {
-        return addDirContentsAt(dir, ".");
-    }
-
-    /**
-     * Add the collection of {@code files} to the archive.
-     *
-     * @see #add(String)
-     * @deprecated since 0.7.0 use one of the other add methods if keeping this file's path was not intended */ @Deprecated
-    public ArchiveBuilder add(Iterable<String> files) {
-        checkNotNull(files, "files");
-        for (String filePath : files) {
-            add(filePath);
-        }
-        return this;
-    }
-
-    /**
-     * Add the collection of {@code files}, relative to the {@code baseDir}, to
-     * the archive.
-     *
-     * @see #add(String, String)
-     * @deprecated since 0.7.0 use one of the other add methods if keeping this file's path was not intended */ @Deprecated
-    public ArchiveBuilder add(String baseDir, Iterable<String> files) {
-        checkNotNull(baseDir, "baseDir");
-        checkNotNull(files, "files");
-        for (String filePath : files) {
-            add(baseDir, filePath);
-        }
-        return this;
-    }
-
-    /**
-     * Add the {@code file} to the archive with the path {@code entryPath}.
-     *
-     * @see #entry(String, File)
-     */
-    public ArchiveBuilder entry(String entryPath, String filePath) {
-        checkNotNull(entryPath, "entryPath");
-        checkNotNull(filePath, "filePath");
-        return entry(entryPath, new File(filePath));
-    }
-
-    /**
-     * Add the {@code file} to the archive with the path {@code entryPath}.
-     */
-    public ArchiveBuilder entry(String entryPath, File file) {
-        checkNotNull(entryPath, "entryPath");
-        checkNotNull(file, "file");
-        this.entries.put(entryPath, file);
-        return this;
-    }
-
-    /**
-     * Add a {@link Map} of entries to the archive.
-     * <p>
-     * The keys should be the names of the file entries to be added to the archive and
-     * the value should point to the actual {@link File} to be added.
-     * <p>
-     * This allows complete control over the directory structure of the eventual archive,
-     * as the entry names do not need to bear any relationship to the name or location
-     * of the files on the filesystem.
-     */
-    public ArchiveBuilder entries(Map<String, File> entries) {
-        checkNotNull(entries, "entries");
-        for (Map.Entry<String, File> entry: entries.entrySet())
-            this.entries.put(entry.getKey(), entry.getValue());
-        return this;
-    }
-
-    /**
-     * Generates the archive and outputs it to the given stream, ignoring any file name.
-     * <p>
-     * This will add a manifest file if the type is a Jar archive.
-     */
-    public void stream(OutputStream output) {
-        try {
-            ZipOutputStream target;
-            if (type == ArchiveType.ZIP) {
-                target = new ZipOutputStream(output);
-            } else {
-                manifest(Attributes.Name.MANIFEST_VERSION, "1.0");
-                target = new JarOutputStream(output, manifest);
-            }
-            for (String entry : entries.keySet()) {
-                addToArchive(entry, entries.get(entry), target);
-            }
-            target.close();
-        } catch (IOException ioe) {
-            throw Exceptions.propagate(ioe);
-        }
-    }
-
-    /**
-     * Generates the archive, saving it with the given name.
-     */
-    public File create(String archiveFile) {
-        return named(archiveFile).create();
-    }
-
-    /**
-     * Generates the archive.
-     * <p>
-     * If no name has been specified, the archive will be created as a temporary file with
-     * a unique name, that is deleted on exit. Otherwise, the given name will be used.
-     */
-    public File create() {
-        if (archive == null) {
-            File temp = Os.newTempFile("brooklyn-archive", type.toString());
-            temp.deleteOnExit();
-            named(temp);
-        }
-        try {
-            OutputStream output = new FileOutputStream(archive);
-            stream(output);
-            output.close();
-        } catch (IOException ioe) {
-            throw Exceptions.propagate(ioe);
-        }
-        return archive;
-    }
-
-    /**
-     * Recursively add files to the archive.
-     * <p>
-     * Code adapted from this <a href="http://stackoverflow.com/questions/1281229/how-to-use-jaroutputstream-to-create-a-jar-file">example</a>
-     * <p>
-     * <strong>Note</strong> {@link File} provides no support for symbolic links, and as such there is
-     * no way to ensure that a symbolic link to a directory is not followed when traversing the
-     * tree. In this case, iterables created by this traverser could contain files that are
-     * outside of the given directory or even be infinite if there is a symbolic link loop.
-     */
-    private void addToArchive(String path, Iterable<File> sources, ZipOutputStream target) throws IOException {
-        int size = Iterables.size(sources);
-        if (size==0) return;
-        boolean isDirectory;
-        if (size>1) {
-            // it must be directories if we are putting multiple things here 
-            isDirectory = true;
-        } else {
-            isDirectory = Iterables.getOnlyElement(sources).isDirectory();
-        }
-        
-        String name = path.replace("\\", "/");
-        if (isDirectory) {
-            name += "/";
-            JarEntry entry = new JarEntry(name);
-            
-            long lastModified=-1;
-            for (File source: sources)
-                if (source.lastModified()>lastModified)
-                    lastModified = source.lastModified();
-            
-            entry.setTime(lastModified);
-            target.putNextEntry(entry);
-            target.closeEntry();
-
-            for (File source: sources) {
-                if (!source.isDirectory()) {
-                    throw new IllegalStateException("Cannot add multiple items at a path in archive unless they are directories: "+sources+" at "+path+" is not valid.");
-                }
-                Iterable<File> children = Files.fileTreeTraverser().children(source);
-                for (File child : children) {
-                    addToArchive(Os.mergePaths(path, child.getName()), Collections.singleton(child), target);
-                }
-            }
-            return;
-        }
-
-        File source = Iterables.getOnlyElement(sources);
-        JarEntry entry = new JarEntry(name);
-        entry.setTime(source.lastModified());
-        target.putNextEntry(entry);
-        Files.asByteSource(source).copyTo(target);
-        target.closeEntry();
-    }
-}


[41/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/EntityTransientCopyInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/EntityTransientCopyInternal.java b/core/src/main/java/brooklyn/entity/basic/EntityTransientCopyInternal.java
index 63b49b5..56e73d1 100644
--- a/core/src/main/java/brooklyn/entity/basic/EntityTransientCopyInternal.java
+++ b/core/src/main/java/brooklyn/entity/basic/EntityTransientCopyInternal.java
@@ -38,12 +38,12 @@ import org.apache.brooklyn.api.mementos.EntityMemento;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.core.management.internal.EntityManagementSupport;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.entity.basic.EntityInternal.FeedSupport;
 import brooklyn.entity.proxying.EntityProxyImpl;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Maybe;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/Lifecycle.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/Lifecycle.java b/core/src/main/java/brooklyn/entity/basic/Lifecycle.java
index 9bb0177..97e3ada 100644
--- a/core/src/main/java/brooklyn/entity/basic/Lifecycle.java
+++ b/core/src/main/java/brooklyn/entity/basic/Lifecycle.java
@@ -24,7 +24,6 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import brooklyn.config.render.RendererHints;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.text.StringFunctions;
 
 import com.google.common.base.CaseFormat;
@@ -33,6 +32,7 @@ import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 
 /**
  * An enumeration representing the status of an {@link org.apache.brooklyn.api.entity.Entity}.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/MethodEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/MethodEffector.java b/core/src/main/java/brooklyn/entity/basic/MethodEffector.java
index 121f70f..f5978b7 100644
--- a/core/src/main/java/brooklyn/entity/basic/MethodEffector.java
+++ b/core/src/main/java/brooklyn/entity/basic/MethodEffector.java
@@ -28,6 +28,7 @@ import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.ParameterType;
 import org.apache.brooklyn.core.management.internal.EffectorUtils;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.codehaus.groovy.runtime.MethodClosure;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,7 +36,6 @@ import org.slf4j.LoggerFactory;
 import brooklyn.entity.annotation.EffectorParam;
 import brooklyn.util.GroovyJavaMethods;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
 
 import com.google.common.collect.Lists;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/Sanitizer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/Sanitizer.java b/core/src/main/java/brooklyn/entity/basic/Sanitizer.java
index 88ca756..7e81bb2 100644
--- a/core/src/main/java/brooklyn/entity/basic/Sanitizer.java
+++ b/core/src/main/java/brooklyn/entity/basic/Sanitizer.java
@@ -22,7 +22,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import brooklyn.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import com.google.api.client.util.Lists;
 import com.google.common.base.Predicate;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java b/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
index 2a551b4..bf4bd43 100644
--- a/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
+++ b/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
@@ -37,6 +37,7 @@ import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.EnricherSpec.ExtensibleEnricherSpec;
+import org.apache.brooklyn.core.util.task.ValueResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -57,7 +58,6 @@ import brooklyn.util.collections.QuorumCheck;
 import brooklyn.util.guava.Functionals;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.repeat.Repeater;
-import brooklyn.util.task.ValueResolver;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java b/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java
index eef3cf9..0119dd1 100644
--- a/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java
+++ b/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java
@@ -25,13 +25,13 @@ import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.core.management.internal.EntityManagementUtils;
 import org.apache.brooklyn.core.management.internal.EntityManagementUtils.CreationResult;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.effector.Effectors.EffectorBuilder;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.annotations.Beta;
 import com.google.gson.Gson;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/effector/AddEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/AddEffector.java b/core/src/main/java/brooklyn/entity/effector/AddEffector.java
index c0d986d..0db7c40 100644
--- a/core/src/main/java/brooklyn/entity/effector/AddEffector.java
+++ b/core/src/main/java/brooklyn/entity/effector/AddEffector.java
@@ -25,13 +25,13 @@ import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.ParameterType;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntityInitializer;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.effector.Effectors.EffectorBuilder;
 import brooklyn.event.basic.MapConfigKey;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.text.Strings;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/effector/AddSensor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/AddSensor.java b/core/src/main/java/brooklyn/entity/effector/AddSensor.java
index 8e29baa..2aa95a6 100644
--- a/core/src/main/java/brooklyn/entity/effector/AddSensor.java
+++ b/core/src/main/java/brooklyn/entity/effector/AddSensor.java
@@ -23,12 +23,12 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.entity.proxying.EntityInitializer;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.Boxing;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/effector/EffectorBody.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/EffectorBody.java b/core/src/main/java/brooklyn/entity/effector/EffectorBody.java
index 98c8153..1a60cbe 100644
--- a/core/src/main/java/brooklyn/entity/effector/EffectorBody.java
+++ b/core/src/main/java/brooklyn/entity/effector/EffectorBody.java
@@ -21,14 +21,14 @@ package brooklyn.entity.effector;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.TaskAdaptable;
 import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.task.DynamicSequentialTask;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
 
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.EntityInternal;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.task.DynamicSequentialTask;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.annotations.Beta;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java b/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
index 6094686..f9a702e 100644
--- a/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
+++ b/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
@@ -28,6 +28,11 @@ import org.apache.brooklyn.api.entity.ParameterType;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.TaskAdaptable;
 import org.apache.brooklyn.core.management.internal.EffectorUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicSequentialTask;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,12 +44,7 @@ import org.apache.brooklyn.location.basic.Machines;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 import org.apache.brooklyn.location.basic.WinRmMachineLocation;
 
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.javalang.Reflections;
-import brooklyn.util.task.DynamicSequentialTask;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/effector/Effectors.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/Effectors.java b/core/src/main/java/brooklyn/entity/effector/Effectors.java
index ff595e7..c0b6306 100644
--- a/core/src/main/java/brooklyn/entity/effector/Effectors.java
+++ b/core/src/main/java/brooklyn/entity/effector/Effectors.java
@@ -31,6 +31,8 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.ParameterType;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,8 +45,6 @@ import brooklyn.entity.effector.EffectorTasks.EffectorBodyTaskFactory;
 import brooklyn.entity.effector.EffectorTasks.EffectorMarkingTaskFactory;
 import brooklyn.entity.effector.EffectorTasks.EffectorTaskFactory;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/group/Cluster.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/group/Cluster.java b/core/src/main/java/brooklyn/entity/group/Cluster.java
index b618c66..ac066a7 100644
--- a/core/src/main/java/brooklyn/entity/group/Cluster.java
+++ b/core/src/main/java/brooklyn/entity/group/Cluster.java
@@ -19,11 +19,11 @@
 package brooklyn.entity.group;
 
 import org.apache.brooklyn.api.entity.Group;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.entity.trait.Resizable;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * Intended to represent a group of homogeneous entities in a single location.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/group/DynamicCluster.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/group/DynamicCluster.java b/core/src/main/java/brooklyn/entity/group/DynamicCluster.java
index ab14f49..435308e 100644
--- a/core/src/main/java/brooklyn/entity/group/DynamicCluster.java
+++ b/core/src/main/java/brooklyn/entity/group/DynamicCluster.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.annotation.Effector;
@@ -46,7 +47,6 @@ import brooklyn.entity.trait.MemberReplaceable;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicNotificationSensor;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.time.Duration;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java b/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java
index 5025026..1cdbd8e 100644
--- a/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java
+++ b/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java
@@ -38,6 +38,10 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskTags;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -62,13 +66,9 @@ import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.ReferenceWithError;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.javalang.Reflections;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskTags;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.StringPredicates;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/group/DynamicFabric.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/group/DynamicFabric.java b/core/src/main/java/brooklyn/entity/group/DynamicFabric.java
index f7decf9..dc23e7c 100644
--- a/core/src/main/java/brooklyn/entity/group/DynamicFabric.java
+++ b/core/src/main/java/brooklyn/entity/group/DynamicFabric.java
@@ -21,6 +21,7 @@ package brooklyn.entity.group;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.AbstractGroup;
@@ -31,7 +32,6 @@ import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.MapConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/group/DynamicMultiGroup.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/group/DynamicMultiGroup.java b/core/src/main/java/brooklyn/entity/group/DynamicMultiGroup.java
index 3a49634..d5a8c9a 100644
--- a/core/src/main/java/brooklyn/entity/group/DynamicMultiGroup.java
+++ b/core/src/main/java/brooklyn/entity/group/DynamicMultiGroup.java
@@ -25,13 +25,13 @@ import org.apache.brooklyn.api.entity.Group;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BasicGroup;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.DynamicGroup;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/group/QuarantineGroupImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/group/QuarantineGroupImpl.java b/core/src/main/java/brooklyn/entity/group/QuarantineGroupImpl.java
index 9f8c14e..4a6b75e 100644
--- a/core/src/main/java/brooklyn/entity/group/QuarantineGroupImpl.java
+++ b/core/src/main/java/brooklyn/entity/group/QuarantineGroupImpl.java
@@ -23,6 +23,8 @@ import java.util.Set;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,8 +34,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.trait.Startable;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/proxying/EntityProxyImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/proxying/EntityProxyImpl.java b/core/src/main/java/brooklyn/entity/proxying/EntityProxyImpl.java
index 647ad76..b121179 100644
--- a/core/src/main/java/brooklyn/entity/proxying/EntityProxyImpl.java
+++ b/core/src/main/java/brooklyn/entity/proxying/EntityProxyImpl.java
@@ -36,6 +36,9 @@ import org.apache.brooklyn.api.management.TaskAdaptable;
 import org.apache.brooklyn.core.management.internal.EffectorUtils;
 import org.apache.brooklyn.core.management.internal.EntityManagerInternal;
 import org.apache.brooklyn.core.management.internal.ManagementTransitionMode;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskTags;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,9 +48,6 @@ import brooklyn.entity.basic.EntityTransientCopyInternal;
 import brooklyn.entity.basic.EntityTransientCopyInternal.SpecialEntityTransientCopyInternal;
 import brooklyn.entity.effector.EffectorWithBody;
 import brooklyn.entity.rebind.RebindManagerImpl.RebindTracker;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskTags;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
index c2a4bfd..1c1a920 100644
--- a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
+++ b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
@@ -37,6 +37,8 @@ import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -50,9 +52,7 @@ import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.FlagUtils;
 import brooklyn.util.javalang.AggregateClassLoader;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/proxying/InternalLocationFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalLocationFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalLocationFactory.java
index 10338b8..464d2a4 100644
--- a/core/src/main/java/brooklyn/entity/proxying/InternalLocationFactory.java
+++ b/core/src/main/java/brooklyn/entity/proxying/InternalLocationFactory.java
@@ -27,15 +27,15 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.management.internal.LocalLocationManager;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
 
 import brooklyn.config.ConfigKey;
 
 import org.apache.brooklyn.location.basic.AbstractLocation;
 import org.apache.brooklyn.location.basic.LocationInternal;
 
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.FlagUtils;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
index aeb371c..7c53404 100644
--- a/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
+++ b/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
@@ -27,13 +27,13 @@ import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
index 52e87a6..6ee5eb2 100644
--- a/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
@@ -21,11 +21,11 @@ package brooklyn.entity.rebind;
 import org.apache.brooklyn.api.entity.rebind.RebindContext;
 import org.apache.brooklyn.api.mementos.CatalogItemMemento;
 import org.apache.brooklyn.core.catalog.internal.CatalogItemDtoAbstract;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.FlagUtils;
 
 public class BasicCatalogItemRebindSupport extends AbstractBrooklynObjectRebindSupport<CatalogItemMemento> {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/BasicEnricherRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/BasicEnricherRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/BasicEnricherRebindSupport.java
index d6b289f..e3e4598 100644
--- a/core/src/main/java/brooklyn/entity/rebind/BasicEnricherRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/BasicEnricherRebindSupport.java
@@ -20,10 +20,10 @@ package brooklyn.entity.rebind;
 
 import org.apache.brooklyn.api.entity.rebind.RebindContext;
 import org.apache.brooklyn.api.mementos.EnricherMemento;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
 
 import brooklyn.enricher.basic.AbstractEnricher;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.FlagUtils;
 
 public class BasicEnricherRebindSupport extends AbstractBrooklynObjectRebindSupport<EnricherMemento> {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/BasicFeedRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/BasicFeedRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/BasicFeedRebindSupport.java
index 5507e6b..f3358e2 100644
--- a/core/src/main/java/brooklyn/entity/rebind/BasicFeedRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/BasicFeedRebindSupport.java
@@ -20,10 +20,10 @@ package brooklyn.entity.rebind;
 
 import org.apache.brooklyn.api.entity.rebind.RebindContext;
 import org.apache.brooklyn.api.mementos.FeedMemento;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
 
 import brooklyn.event.feed.AbstractFeed;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.FlagUtils;
 
 public class BasicFeedRebindSupport extends AbstractBrooklynObjectRebindSupport<FeedMemento> {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/BasicLocationRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/BasicLocationRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/BasicLocationRebindSupport.java
index edf8867..7f1f286 100644
--- a/core/src/main/java/brooklyn/entity/rebind/BasicLocationRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/BasicLocationRebindSupport.java
@@ -26,6 +26,8 @@ import java.util.NoSuchElementException;
 import org.apache.brooklyn.api.entity.rebind.RebindContext;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.mementos.LocationMemento;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,8 +37,6 @@ import brooklyn.entity.rebind.dto.MementosGenerators;
 import org.apache.brooklyn.location.basic.AbstractLocation;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.TypeCoercions;
 
 import com.google.common.collect.Sets;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/BasicPolicyRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/BasicPolicyRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/BasicPolicyRebindSupport.java
index 9b22d65..4623225 100644
--- a/core/src/main/java/brooklyn/entity/rebind/BasicPolicyRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/BasicPolicyRebindSupport.java
@@ -20,10 +20,10 @@ package brooklyn.entity.rebind;
 
 import org.apache.brooklyn.api.entity.rebind.RebindContext;
 import org.apache.brooklyn.api.mementos.PolicyMemento;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
 
 import brooklyn.policy.basic.AbstractPolicy;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.FlagUtils;
 
 public class BasicPolicyRebindSupport extends AbstractBrooklynObjectRebindSupport<PolicyMemento> {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java b/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java
index d204ecc..2c55e7f 100644
--- a/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java
+++ b/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java
@@ -46,6 +46,8 @@ import org.apache.brooklyn.api.mementos.BrooklynMementoPersister;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
+import org.apache.brooklyn.core.util.task.ScheduledTask;
+import org.apache.brooklyn.core.util.task.Tasks;
 
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.EntityInternal;
@@ -56,8 +58,6 @@ import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.RuntimeInterruptedException;
 import brooklyn.util.repeat.Repeater;
-import brooklyn.util.task.ScheduledTask;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.CountdownTimer;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
index 1164fff..67d20ae 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
@@ -73,6 +73,7 @@ import org.apache.brooklyn.core.management.internal.EntityManagerInternal;
 import org.apache.brooklyn.core.management.internal.LocationManagerInternal;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.management.internal.ManagementTransitionMode;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
 
 import brooklyn.config.BrooklynLogging;
 import brooklyn.config.BrooklynLogging.LoggingLevel;
@@ -95,7 +96,6 @@ import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.FlagUtils;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.Reflections;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
index 862c86b..f810a98 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
@@ -46,6 +46,9 @@ import org.apache.brooklyn.api.mementos.TreeNode;
 import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
+import org.apache.brooklyn.core.util.task.ScheduledTask;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -65,9 +68,6 @@ import brooklyn.util.collections.QuorumCheck;
 import brooklyn.util.collections.QuorumCheck.QuorumChecks;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.RuntimeInterruptedException;
-import brooklyn.util.task.BasicExecutionContext;
-import brooklyn.util.task.ScheduledTask;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/dto/BasicLocationMemento.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/dto/BasicLocationMemento.java b/core/src/main/java/brooklyn/entity/rebind/dto/BasicLocationMemento.java
index a0578bb..6fbf66a 100644
--- a/core/src/main/java/brooklyn/entity/rebind/dto/BasicLocationMemento.java
+++ b/core/src/main/java/brooklyn/entity/rebind/dto/BasicLocationMemento.java
@@ -24,10 +24,10 @@ import java.util.Set;
 
 import org.apache.brooklyn.api.mementos.LocationMemento;
 import org.apache.brooklyn.api.mementos.TreeNode;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.Sanitizer;
-import brooklyn.util.config.ConfigBag;
 
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.Maps;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java b/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
index dbe6e26..5c52788 100644
--- a/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
+++ b/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
@@ -50,6 +50,8 @@ import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.EntityAdjunct;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.core.catalog.internal.CatalogItemDo;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.basic.AbstractEnricher;
@@ -64,8 +66,6 @@ import org.apache.brooklyn.location.basic.LocationInternal;
 
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.FlagUtils;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
index b5ca9c1..f146999 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
@@ -50,6 +50,7 @@ import org.apache.brooklyn.api.mementos.CatalogItemMemento;
 import org.apache.brooklyn.api.mementos.Memento;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.classloading.ClassLoaderFromBrooklynClassLoadingContext;
+import org.apache.brooklyn.core.util.xstream.XmlUtil;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.config.StringConfigMap;
@@ -66,7 +67,6 @@ import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
-import brooklyn.util.xstream.XmlUtil;
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java
index 68074e9..3db8106 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java
@@ -45,6 +45,7 @@ import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
 import org.apache.brooklyn.core.management.internal.LocalLocationManager;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.ResourceUtils;
 
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.BrooklynServerPaths;
@@ -56,7 +57,6 @@ import brooklyn.entity.rebind.transformer.CompoundTransformerLoader;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/persister/FileBasedObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/FileBasedObjectStore.java b/core/src/main/java/brooklyn/entity/rebind/persister/FileBasedObjectStore.java
index c1df659..764d55f 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/FileBasedObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/FileBasedObjectStore.java
@@ -36,6 +36,7 @@ import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.core.util.internal.ssh.process.ProcessTool;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,7 +45,6 @@ import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.FatalConfigurationRuntimeException;
-import brooklyn.util.internal.ssh.process.ProcessTool;
 import brooklyn.util.io.FileUtil;
 import brooklyn.util.os.Os;
 import brooklyn.util.os.Os.DeletionResult;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java b/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
index 99ee2e5..6ff75e5 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
@@ -47,6 +47,7 @@ import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.classloading.BrooklynClassLoadingContextSequential;
 import org.apache.brooklyn.core.management.classloading.ClassLoaderFromBrooklynClassLoadingContext;
 import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
+import org.apache.brooklyn.core.util.xstream.XmlSerializer;
 
 import brooklyn.entity.basic.BasicParameterType;
 import brooklyn.entity.effector.EffectorAndBody;
@@ -63,7 +64,6 @@ import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.text.Strings;
-import brooklyn.util.xstream.XmlSerializer;
 
 import com.thoughtworks.xstream.converters.Converter;
 import com.thoughtworks.xstream.converters.MarshallingContext;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java b/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java
index 33d9422..030cd53 100644
--- a/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java
@@ -26,13 +26,13 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.rebind.BrooklynObjectType;
 import org.apache.brooklyn.api.entity.rebind.RebindExceptionHandler;
 import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 
 import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
 import brooklyn.entity.rebind.transformer.impl.XsltTransformer;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java b/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java
index 8a2d4bb..b36328c 100644
--- a/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java
+++ b/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java
@@ -22,13 +22,13 @@ import java.util.Collection;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.rebind.transformer.CompoundTransformer.Builder;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.text.TemplateProcessor;
 import brooklyn.util.yaml.Yamls;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/trait/Startable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/trait/Startable.java b/core/src/main/java/brooklyn/entity/trait/Startable.java
index 19c7f9d..dad2152 100644
--- a/core/src/main/java/brooklyn/entity/trait/Startable.java
+++ b/core/src/main/java/brooklyn/entity/trait/Startable.java
@@ -23,6 +23,8 @@ import java.util.Collection;
 import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,8 +35,6 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.MethodEffector;
 import brooklyn.entity.effector.EffectorBody;
 import brooklyn.entity.effector.Effectors;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.task.Tasks;
 
 /**
  * This interface describes an {@link org.apache.brooklyn.api.entity.Entity} that can be started and stopped.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/entity/trait/StartableMethods.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/trait/StartableMethods.java b/core/src/main/java/brooklyn/entity/trait/StartableMethods.java
index 6188d14..0e14058 100644
--- a/core/src/main/java/brooklyn/entity/trait/StartableMethods.java
+++ b/core/src/main/java/brooklyn/entity/trait/StartableMethods.java
@@ -26,6 +26,9 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskTags;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,10 +36,7 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityPredicates;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.CompoundRuntimeException;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskTags;
 
 import com.google.common.base.Predicates;
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/basic/AttributeMap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/basic/AttributeMap.java b/core/src/main/java/brooklyn/event/basic/AttributeMap.java
index aa8dcc1..281adfd 100644
--- a/core/src/main/java/brooklyn/event/basic/AttributeMap.java
+++ b/core/src/main/java/brooklyn/event/basic/AttributeMap.java
@@ -26,12 +26,12 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.BrooklynLogging;
 import brooklyn.entity.basic.AbstractEntity;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/basic/AttributeSensorAndConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/basic/AttributeSensorAndConfigKey.java b/core/src/main/java/brooklyn/event/basic/AttributeSensorAndConfigKey.java
index fda8063..f9cec48 100644
--- a/core/src/main/java/brooklyn/event/basic/AttributeSensorAndConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/AttributeSensorAndConfigKey.java
@@ -22,6 +22,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,7 +34,6 @@ import brooklyn.entity.basic.BrooklynConfigKeys;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.feed.ConfigToAttributes;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
 
 /**
 * A {@link Sensor} describing an attribute that can be configured with inputs that are used to derive the final value.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java b/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java
index 1e41aad..f3b9204 100644
--- a/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java
@@ -28,6 +28,8 @@ import java.util.concurrent.ExecutionException;
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.core.util.internal.ConfigKeySelfExtracting;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,8 +37,6 @@ import brooklyn.config.ConfigInheritance;
 import brooklyn.config.ConfigKey;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.TypeTokens;
-import brooklyn.util.internal.ConfigKeySelfExtracting;
-import brooklyn.util.task.Tasks;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/basic/DependentConfiguration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/basic/DependentConfiguration.java b/core/src/main/java/brooklyn/event/basic/DependentConfiguration.java
index 44d8688..4f990cf 100644
--- a/core/src/main/java/brooklyn/event/basic/DependentConfiguration.java
+++ b/core/src/main/java/brooklyn/event/basic/DependentConfiguration.java
@@ -44,6 +44,14 @@ import org.apache.brooklyn.api.management.SubscriptionHandle;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.TaskAdaptable;
 import org.apache.brooklyn.api.management.TaskFactory;
+import org.apache.brooklyn.core.util.task.BasicExecutionContext;
+import org.apache.brooklyn.core.util.task.BasicTask;
+import org.apache.brooklyn.core.util.task.DeferredSupplier;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.ParallelTask;
+import org.apache.brooklyn.core.util.task.TaskInternal;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ValueResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -63,14 +71,6 @@ import brooklyn.util.exceptions.NotManagedException;
 import brooklyn.util.exceptions.RuntimeTimeoutException;
 import brooklyn.util.guava.Functionals;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.BasicExecutionContext;
-import brooklyn.util.task.BasicTask;
-import brooklyn.util.task.DeferredSupplier;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.ParallelTask;
-import brooklyn.util.task.TaskInternal;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ValueResolver;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.CountdownTimer;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/basic/PortAttributeSensorAndConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/basic/PortAttributeSensorAndConfigKey.java b/core/src/main/java/brooklyn/event/basic/PortAttributeSensorAndConfigKey.java
index fd7100a..ffd8229 100644
--- a/core/src/main/java/brooklyn/event/basic/PortAttributeSensorAndConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/PortAttributeSensorAndConfigKey.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.location.PortRange;
 import org.apache.brooklyn.api.location.PortSupplier;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.internal.BrooklynInitialization;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,7 +38,6 @@ import brooklyn.entity.basic.BrooklynConfigKeys;
 
 import org.apache.brooklyn.location.basic.Locations;
 
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 
 import com.google.common.base.Optional;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/basic/TemplatedStringAttributeSensorAndConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/basic/TemplatedStringAttributeSensorAndConfigKey.java b/core/src/main/java/brooklyn/event/basic/TemplatedStringAttributeSensorAndConfigKey.java
index 0a57b62..8537f49 100644
--- a/core/src/main/java/brooklyn/event/basic/TemplatedStringAttributeSensorAndConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/TemplatedStringAttributeSensorAndConfigKey.java
@@ -21,12 +21,12 @@ package brooklyn.event.basic;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.EntityInternal;
-import brooklyn.util.text.TemplateProcessor;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/feed/AttributePollHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/AttributePollHandler.java b/core/src/main/java/brooklyn/event/feed/AttributePollHandler.java
index fbfe65c..ff4ef6e 100644
--- a/core/src/main/java/brooklyn/event/feed/AttributePollHandler.java
+++ b/core/src/main/java/brooklyn/event/feed/AttributePollHandler.java
@@ -22,6 +22,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,8 +34,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.basic.Lifecycle.Transition;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/feed/Poller.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/Poller.java b/core/src/main/java/brooklyn/event/feed/Poller.java
index 1bd97cd..2f14f22 100644
--- a/core/src/main/java/brooklyn/event/feed/Poller.java
+++ b/core/src/main/java/brooklyn/event/feed/Poller.java
@@ -24,6 +24,9 @@ import java.util.concurrent.Callable;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.task.DynamicSequentialTask;
+import org.apache.brooklyn.core.util.task.ScheduledTask;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,9 +35,6 @@ import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.task.DynamicSequentialTask;
-import brooklyn.util.task.ScheduledTask;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/feed/http/HttpFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/http/HttpFeed.java b/core/src/main/java/brooklyn/event/feed/http/HttpFeed.java
index 342e430..39f008a 100644
--- a/core/src/main/java/brooklyn/event/feed/http/HttpFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/http/HttpFeed.java
@@ -29,6 +29,9 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
+import org.apache.brooklyn.core.util.http.HttpTool.HttpClientBuilder;
 import org.apache.http.auth.Credentials;
 import org.apache.http.auth.UsernamePasswordCredentials;
 import org.apache.http.client.HttpClient;
@@ -42,9 +45,6 @@ import brooklyn.event.feed.AbstractFeed;
 import brooklyn.event.feed.AttributePollHandler;
 import brooklyn.event.feed.DelegatingPollHandler;
 import brooklyn.event.feed.Poller;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpTool.HttpClientBuilder;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/feed/http/HttpPollConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/http/HttpPollConfig.java b/core/src/main/java/brooklyn/event/feed/http/HttpPollConfig.java
index 923995f..b27fede 100644
--- a/core/src/main/java/brooklyn/event/feed/http/HttpPollConfig.java
+++ b/core/src/main/java/brooklyn/event/feed/http/HttpPollConfig.java
@@ -24,12 +24,12 @@ import java.util.Map;
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 
 import brooklyn.event.feed.PollConfig;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Predicate;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/feed/http/HttpPollValue.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/http/HttpPollValue.java b/core/src/main/java/brooklyn/event/feed/http/HttpPollValue.java
index 81fe325..1df1f9a 100644
--- a/core/src/main/java/brooklyn/event/feed/http/HttpPollValue.java
+++ b/core/src/main/java/brooklyn/event/feed/http/HttpPollValue.java
@@ -21,7 +21,7 @@ package brooklyn.event.feed.http;
 import java.util.List;
 import java.util.Map;
 
-import brooklyn.util.http.HttpToolResponse;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 
 /** @deprecated since 0.7.0, use {@link HttpToolResponse}.
  * the old {@link HttpPollValue} concrete class has been renamed {@link HttpToolResponse}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/feed/http/HttpPolls.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/http/HttpPolls.java b/core/src/main/java/brooklyn/event/feed/http/HttpPolls.java
index 2e53618..94391ce 100644
--- a/core/src/main/java/brooklyn/event/feed/http/HttpPolls.java
+++ b/core/src/main/java/brooklyn/event/feed/http/HttpPolls.java
@@ -20,11 +20,10 @@ package brooklyn.event.feed.http;
 
 import java.net.URI;
 
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.http.impl.client.DefaultHttpClient;
 
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
-
 import com.google.common.collect.ImmutableMap;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java b/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java
index 3e7e6b2..f6ab2b5 100644
--- a/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java
+++ b/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java
@@ -20,8 +20,9 @@ package brooklyn.event.feed.http;
 
 import java.util.List;
 
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
+
 import brooklyn.util.guava.Functionals;
-import brooklyn.util.http.HttpToolResponse;
 
 import com.google.common.base.Function;
 import com.google.common.base.Functions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/feed/shell/ShellFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/shell/ShellFeed.java b/core/src/main/java/brooklyn/event/feed/shell/ShellFeed.java
index 7362666..5c82be7 100644
--- a/core/src/main/java/brooklyn/event/feed/shell/ShellFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/shell/ShellFeed.java
@@ -29,6 +29,9 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.core.util.task.system.internal.SystemProcessTaskFactory.ConcreteSystemProcessTaskFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,9 +45,6 @@ import brooklyn.event.feed.Poller;
 import brooklyn.event.feed.function.FunctionFeed;
 import brooklyn.event.feed.ssh.SshFeed;
 import brooklyn.event.feed.ssh.SshPollValue;
-import brooklyn.util.task.system.ProcessTaskFactory;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-import brooklyn.util.task.system.internal.SystemProcessTaskFactory.ConcreteSystemProcessTaskFactory;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Optional;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java b/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
index 200e2fb..46c751b 100644
--- a/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
@@ -30,6 +30,8 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.internal.ssh.SshTool;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,11 +41,11 @@ import brooklyn.event.feed.AbstractFeed;
 import brooklyn.event.feed.AttributePollHandler;
 import brooklyn.event.feed.DelegatingPollHandler;
 import brooklyn.event.feed.Poller;
+
 import org.apache.brooklyn.location.basic.Locations;
 import org.apache.brooklyn.location.basic.Machines;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.internal.ssh.SshTool;
+
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterFeed.java b/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterFeed.java
index 040d887..c06c013 100644
--- a/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterFeed.java
@@ -38,6 +38,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,9 +50,10 @@ import brooklyn.event.basic.Sensors;
 import brooklyn.event.feed.AbstractFeed;
 import brooklyn.event.feed.PollHandler;
 import brooklyn.event.feed.Poller;
+
 import org.apache.brooklyn.location.basic.WinRmMachineLocation;
+
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.time.Duration;
 
 import com.google.common.annotations.VisibleForTesting;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java b/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java
index ab303a0..afa015f 100644
--- a/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java
+++ b/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java
@@ -41,6 +41,10 @@ import org.apache.brooklyn.api.management.SubscriptionHandle;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.policy.EntityAdjunct;
 import org.apache.brooklyn.core.management.internal.SubscriptionTracker;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,10 +57,6 @@ import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java b/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java
index 825720a..467eb05 100644
--- a/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java
+++ b/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java
@@ -25,6 +25,8 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
+import org.apache.brooklyn.core.util.internal.ConfigKeySelfExtracting;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -38,9 +40,7 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.basic.Sanitizer;
 import brooklyn.event.basic.StructuredConfigKey;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.internal.ConfigKeySelfExtracting;
 
 public class ConfigMapImpl extends AbstractConfigMapImpl {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/BrooklynLanguageExtensions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/BrooklynLanguageExtensions.java b/core/src/main/java/brooklyn/util/BrooklynLanguageExtensions.java
deleted file mode 100644
index 86aac7e..0000000
--- a/core/src/main/java/brooklyn/util/BrooklynLanguageExtensions.java
+++ /dev/null
@@ -1,48 +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;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.brooklyn.core.internal.BrooklynInitialization;
-
-import brooklyn.util.internal.TimeExtras;
-
-/** @deprecated since 0.7.0 use {@link BrooklynInitialization} */
-public class BrooklynLanguageExtensions {
-
-    private BrooklynLanguageExtensions() {}
-    
-    private static AtomicBoolean done = new AtomicBoolean(false);
-    
-    public synchronized static void reinit() {
-        done.set(false);
-        init();
-    }
-    
-    /** performs the language extensions required for this project */
-    public synchronized static void init() {
-        if (done.getAndSet(true)) return;
-        TimeExtras.init();
-        BrooklynInitialization.initPortRanges();
-    }
-    
-    static { BrooklynInitialization.initLegacyLanguageExtensions(); }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/BrooklynMavenArtifacts.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/BrooklynMavenArtifacts.java b/core/src/main/java/brooklyn/util/BrooklynMavenArtifacts.java
deleted file mode 100644
index 50a5879..0000000
--- a/core/src/main/java/brooklyn/util/BrooklynMavenArtifacts.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util;
-
-import brooklyn.BrooklynVersion;
-import brooklyn.util.maven.MavenArtifact;
-import brooklyn.util.maven.MavenRetriever;
-import brooklyn.util.text.Strings;
-
-public class BrooklynMavenArtifacts {
-
-    public static MavenArtifact jar(String artifactId) {
-        return artifact(null, artifactId, "jar");
-    }
-    
-    public static MavenArtifact artifact(String subgroupUnderIoBrooklyn, String artifactId, String packaging) {
-        return artifact(subgroupUnderIoBrooklyn, artifactId, packaging, null);
-    }
-
-    public static MavenArtifact artifact(String subgroupUnderIoBrooklyn, String artifactId, String packaging, String classifier) {
-        return new MavenArtifact(
-                Strings.isEmpty(subgroupUnderIoBrooklyn) ? "org.apache.brooklyn" : "org.apache.brooklyn."+subgroupUnderIoBrooklyn,
-                artifactId, packaging, classifier, BrooklynVersion.get());
-    }
-
-    public static String localUrlForJar(String artifactId) {
-        return MavenRetriever.localUrl(jar(artifactId));
-    }
-    
-    public static String localUrl(String subgroupUnderIoBrooklyn, String artifactId, String packaging) {
-        return MavenRetriever.localUrl(artifact(subgroupUnderIoBrooklyn, artifactId, packaging));
-    }
-
-    public static String hostedUrlForJar(String artifactId) {
-        return MavenRetriever.hostedUrl(jar(artifactId));
-    }
-    
-    public static String hostedUrl(String subgroupUnderIoBrooklyn, String artifactId, String packaging) {
-        return MavenRetriever.hostedUrl(artifact(subgroupUnderIoBrooklyn, artifactId, packaging));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/main/java/brooklyn/util/BrooklynNetworkUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/BrooklynNetworkUtils.java b/core/src/main/java/brooklyn/util/BrooklynNetworkUtils.java
deleted file mode 100644
index 0c1d39d..0000000
--- a/core/src/main/java/brooklyn/util/BrooklynNetworkUtils.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.util;
-
-import java.net.InetAddress;
-
-import brooklyn.config.BrooklynServiceAttributes;
-import org.apache.brooklyn.location.geo.LocalhostExternalIpLoader;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.net.Networking;
-
-public class BrooklynNetworkUtils {
-
-    /** returns the externally-facing IP address from which this host comes, or 127.0.0.1 if not resolvable */
-    public static String getLocalhostExternalIp() {
-        return LocalhostExternalIpLoader.getLocalhostIpQuicklyOrDefault();
-    }
-
-    /** returns a IP address for localhost paying attention to a system property to prevent lookup in some cases */ 
-    public static InetAddress getLocalhostInetAddress() {
-        return TypeCoercions.coerce(JavaGroovyEquivalents.elvis(BrooklynServiceAttributes.LOCALHOST_IP_ADDRESS.getValue(), 
-                Networking.getLocalHost()), InetAddress.class);
-    }
-
-}


[06/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/qpid/QpidDestinationImpl.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/qpid/QpidDestinationImpl.java b/software/messaging/src/main/java/brooklyn/entity/messaging/qpid/QpidDestinationImpl.java
index 83e0af0..0dc6390 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/qpid/QpidDestinationImpl.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/qpid/QpidDestinationImpl.java
@@ -24,6 +24,7 @@ import javax.management.MalformedObjectNameException;
 import javax.management.ObjectName;
 
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,7 +34,6 @@ import brooklyn.entity.messaging.jms.JMSDestinationImpl;
 import brooklyn.event.feed.jmx.JmxFeed;
 import brooklyn.event.feed.jmx.JmxHelper;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.SetFromFlag;
 
 public abstract class QpidDestinationImpl extends JMSDestinationImpl implements QpidDestination {
     public static final Logger log = LoggerFactory.getLogger(QpidDestination.class);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/rabbit/RabbitBroker.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/rabbit/RabbitBroker.java b/software/messaging/src/main/java/brooklyn/entity/messaging/rabbit/RabbitBroker.java
index a641638..a70bfce 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/rabbit/RabbitBroker.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/rabbit/RabbitBroker.java
@@ -25,6 +25,7 @@ import com.google.common.annotations.Beta;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -35,7 +36,6 @@ import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * An {@link org.apache.brooklyn.api.entity.Entity} that represents a single Rabbit MQ broker instance, using AMQP 0-9-1.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/storm/Storm.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/storm/Storm.java b/software/messaging/src/main/java/brooklyn/entity/messaging/storm/Storm.java
index e38aa6a..ced7bef 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/storm/Storm.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/storm/Storm.java
@@ -22,6 +22,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.config.render.RendererHints;
@@ -32,7 +33,6 @@ import brooklyn.entity.zookeeper.ZooKeeperEnsemble;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * An {@link org.apache.brooklyn.api.entity.Entity} that represents a Storm node (UI, Nimbus or Supervisor).

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/storm/StormDeployment.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/storm/StormDeployment.java b/software/messaging/src/main/java/brooklyn/entity/messaging/storm/StormDeployment.java
index 92ce3aa..fda29fd 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/storm/StormDeployment.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/storm/StormDeployment.java
@@ -21,11 +21,11 @@ package brooklyn.entity.messaging.storm;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.trait.Startable;
-import brooklyn.util.flags.SetFromFlag;
 
 @Catalog(name="Storm Deployment", description="A Storm cluster. Apache Storm is a distributed realtime computation system. "
         + "Storm makes it easy to reliably process unbounded streams of data, doing for realtime processing "

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/messaging/storm/StormDeploymentImpl.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/messaging/storm/StormDeploymentImpl.java b/software/messaging/src/main/java/brooklyn/entity/messaging/storm/StormDeploymentImpl.java
index d5b79cc..cf90db8 100644
--- a/software/messaging/src/main/java/brooklyn/entity/messaging/storm/StormDeploymentImpl.java
+++ b/software/messaging/src/main/java/brooklyn/entity/messaging/storm/StormDeploymentImpl.java
@@ -24,6 +24,7 @@ import static brooklyn.entity.messaging.storm.Storm.Role.SUPERVISOR;
 import static brooklyn.entity.messaging.storm.Storm.Role.UI;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,7 +33,6 @@ import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.BasicStartableImpl;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.entity.zookeeper.ZooKeeperEnsemble;
-import brooklyn.util.ResourceUtils;
 
 public class StormDeploymentImpl extends BasicStartableImpl implements StormDeployment {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperEnsemble.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperEnsemble.java b/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperEnsemble.java
index b09e7f8..e5869c5 100644
--- a/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperEnsemble.java
+++ b/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperEnsemble.java
@@ -23,13 +23,13 @@ import java.util.List;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.reflect.TypeToken;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperNode.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperNode.java b/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperNode.java
index 4d6ecdf..504a894 100644
--- a/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperNode.java
+++ b/software/messaging/src/main/java/brooklyn/entity/zookeeper/ZooKeeperNode.java
@@ -21,6 +21,7 @@ package brooklyn.entity.zookeeper;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -28,7 +29,6 @@ import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * An {@link org.apache.brooklyn.api.entity.Entity} that represents a single Apache ZooKeeper instance.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/messaging/src/test/java/brooklyn/entity/messaging/storm/StormAbstractCloudLiveTest.java
----------------------------------------------------------------------
diff --git a/software/messaging/src/test/java/brooklyn/entity/messaging/storm/StormAbstractCloudLiveTest.java b/software/messaging/src/test/java/brooklyn/entity/messaging/storm/StormAbstractCloudLiveTest.java
index 758ace7..d79b2da 100644
--- a/software/messaging/src/test/java/brooklyn/entity/messaging/storm/StormAbstractCloudLiveTest.java
+++ b/software/messaging/src/test/java/brooklyn/entity/messaging/storm/StormAbstractCloudLiveTest.java
@@ -31,6 +31,8 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.file.ArchiveBuilder;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,9 +54,7 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.messaging.storm.topologies.ExclamationBolt;
 import brooklyn.entity.trait.Startable;
 import brooklyn.entity.zookeeper.ZooKeeperEnsemble;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.file.ArchiveBuilder;
 import brooklyn.util.os.Os;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/monitoring/src/main/java/org/apache/brooklyn/entity/monitoring/monit/MonitNode.java
----------------------------------------------------------------------
diff --git a/software/monitoring/src/main/java/org/apache/brooklyn/entity/monitoring/monit/MonitNode.java b/software/monitoring/src/main/java/org/apache/brooklyn/entity/monitoring/monit/MonitNode.java
index 7e0ca22..064a54b 100644
--- a/software/monitoring/src/main/java/org/apache/brooklyn/entity/monitoring/monit/MonitNode.java
+++ b/software/monitoring/src/main/java/org/apache/brooklyn/entity/monitoring/monit/MonitNode.java
@@ -27,6 +27,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.entity.trait.HasShortName;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Attributes;
@@ -35,7 +36,6 @@ import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.StringAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 @Catalog(name="Monit Node", description="Monit is a free open source utility for managing and monitoring, processes, programs, files, directories and filesystems on a UNIX system")
 @ImplementedBy(MonitNodeImpl.class)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServer.java
----------------------------------------------------------------------
diff --git a/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServer.java b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServer.java
index 032a471..fc27b19 100644
--- a/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServer.java
+++ b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServer.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.annotation.Effector;
@@ -37,8 +38,9 @@ import brooklyn.entity.basic.DynamicGroup;
 import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
+
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.flags.SetFromFlag;
+
 import brooklyn.util.net.Cidr;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenter.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenter.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenter.java
index dc01fd5..07e7f96 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenter.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenter.java
@@ -26,6 +26,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.nosql.cassandra.TokenGenerators.PosNeg63TokenGenerator;
 
 import brooklyn.config.ConfigKey;
@@ -37,7 +38,6 @@ import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Supplier;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenterImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenterImpl.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenterImpl.java
index a2ed4c4..5b5cb9b 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenterImpl.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenterImpl.java
@@ -35,6 +35,8 @@ import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -51,11 +53,9 @@ import brooklyn.entity.group.DynamicClusterImpl;
 
 import org.apache.brooklyn.location.basic.Machines;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNode.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNode.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNode.java
index 21fbbb7..f779de8 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNode.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNode.java
@@ -26,6 +26,7 @@ import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynConfigKeys;
@@ -38,8 +39,9 @@ import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
+
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.flags.SetFromFlag;
+
 import brooklyn.util.time.Duration;
 
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeDriver.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeDriver.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeDriver.java
index eab6672..befaa6d 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeDriver.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeDriver.java
@@ -18,8 +18,9 @@
  */
 package org.apache.brooklyn.entity.nosql.cassandra;
 
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+
 import brooklyn.entity.java.JavaSoftwareProcessDriver;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 
 public interface CassandraNodeDriver extends JavaSoftwareProcessDriver {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeImpl.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeImpl.java
index 2d82d19..b27b957 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeImpl.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeImpl.java
@@ -36,6 +36,8 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -59,11 +61,9 @@ import org.apache.brooklyn.location.basic.Machines;
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
 
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeSshDriver.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeSshDriver.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeSshDriver.java
index c3a57b0..7806979 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeSshDriver.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeSshDriver.java
@@ -31,6 +31,10 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.TaskWrapper;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -58,12 +62,8 @@ import brooklyn.util.net.Networking;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseCluster.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseCluster.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseCluster.java
index 65b7dd0..a46e1a8 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseCluster.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseCluster.java
@@ -26,12 +26,12 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.time.Duration;
 
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
index 3196428..fcb944d 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
@@ -33,6 +33,9 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -62,9 +65,6 @@ import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Functionals;
 import brooklyn.util.guava.IfFunctions;
 import brooklyn.util.math.MathPredicates;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.ByteSizeStrings;
 import brooklyn.util.text.StringFunctions;
 import brooklyn.util.text.Strings;
@@ -565,8 +565,8 @@ public class CouchbaseClusterImpl extends DynamicClusterImpl implements Couchbas
                                         .onFailureOrException(new Function<Object, Boolean>() {
                                             @Override
                                             public Boolean apply(Object input) {
-                                                if (input instanceof brooklyn.util.http.HttpToolResponse) {
-                                                    if (((brooklyn.util.http.HttpToolResponse) input).getResponseCode() == 404) {
+                                                if (input instanceof org.apache.brooklyn.core.util.http.HttpToolResponse) {
+                                                    if (((org.apache.brooklyn.core.util.http.HttpToolResponse) input).getResponseCode() == 404) {
                                                         return true;
                                                     }
                                                 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNode.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
index bd244b9..5f3269a 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
@@ -23,6 +23,7 @@ import java.net.URI;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.config.render.RendererHints;
@@ -36,7 +37,6 @@ import brooklyn.entity.effector.Effectors;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.text.ByteSizeStrings;
 
 @Catalog(name="CouchBase Node", description="Couchbase Server is an open source, distributed (shared-nothing architecture) "

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNodeImpl.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNodeImpl.java
index 07b34b4..d27f7db 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNodeImpl.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNodeImpl.java
@@ -30,6 +30,10 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.util.config.ConfigBag;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.http.auth.UsernamePasswordCredentials;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -47,15 +51,11 @@ import org.apache.brooklyn.location.cloud.CloudLocationConfig;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Functionals;
 import brooklyn.util.guava.MaybeFunctions;
 import brooklyn.util.guava.TypeTokens;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.net.Urls;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNodeSshDriver.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNodeSshDriver.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNodeSshDriver.java
index 41bef9f..f894937 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNodeSshDriver.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseNodeSshDriver.java
@@ -33,6 +33,12 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.Group;
 import org.apache.brooklyn.api.location.OsDetails;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.TaskTags;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.apache.http.auth.UsernamePasswordCredentials;
 
 import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver;
@@ -49,14 +55,8 @@ import org.apache.brooklyn.location.access.BrooklynAccessUtils;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.repeat.Repeater;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.TaskTags;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.NaturalOrderComparator;
 import brooklyn.util.text.StringEscapes.BashStringEscapes;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseSyncGateway.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseSyncGateway.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseSyncGateway.java
index 2e8dba9..07665a3 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseSyncGateway.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseSyncGateway.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.entity.nosql.couchbase;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -28,7 +29,6 @@ import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 @ImplementedBy(CouchbaseSyncGatewayImpl.class)
 public interface CouchbaseSyncGateway extends SoftwareProcess {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBCluster.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBCluster.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBCluster.java
index 2964da7..94b33c1 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBCluster.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBCluster.java
@@ -20,11 +20,11 @@ package org.apache.brooklyn.entity.nosql.couchdb;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * A cluster of {@link CouchDBNode}s based on {@link DynamicCluster} which can be resized by a policy if required.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNode.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNode.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNode.java
index f438de6..9d89349 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNode.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNode.java
@@ -20,13 +20,13 @@ package org.apache.brooklyn.entity.nosql.couchdb;
 
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.webapp.WebAppService;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * An {@link org.apache.brooklyn.api.entity.Entity} that represents a CouchDB node in a {@link CouchDBCluster}.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeImpl.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeImpl.java
index b200f6e..7f8f639 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeImpl.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeImpl.java
@@ -22,6 +22,7 @@ import java.util.concurrent.TimeUnit;
 
 import javax.annotation.Nullable;
 
+import org.apache.brooklyn.core.util.flags.TypeCoercions;
 import org.apache.brooklyn.entity.webapp.JavaWebAppSoftwareProcessImpl;
 import org.apache.brooklyn.entity.webapp.WebAppServiceMethods;
 import org.slf4j.Logger;
@@ -31,7 +32,6 @@ import brooklyn.entity.basic.SoftwareProcessImpl;
 import brooklyn.event.feed.http.HttpFeed;
 import brooklyn.event.feed.http.HttpPollConfig;
 import brooklyn.event.feed.http.HttpValueFunctions;
-import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Functionals;
 
 import com.google.common.base.Function;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchCluster.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchCluster.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchCluster.java
index f73a911..c8b9bd3 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchCluster.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchCluster.java
@@ -20,10 +20,10 @@ package org.apache.brooklyn.entity.nosql.elasticsearch;
 
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * A cluster of {@link ElasticSearchNode}s based on {@link DynamicCluster} which can be resized by a policy if required.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNode.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNode.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNode.java
index a2d82c0..d82e34b 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNode.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNode.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.entity.nosql.elasticsearch;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.webapp.WebAppServiceConstants;
 
 import brooklyn.config.ConfigKey;
@@ -31,8 +32,8 @@ import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.StringAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
+
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * An {@link org.apache.brooklyn.api.entity.Entity} that represents an ElasticSearch node

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeImpl.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeImpl.java
index 4478ac3..eb81a93 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeImpl.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeImpl.java
@@ -21,18 +21,20 @@ package org.apache.brooklyn.entity.nosql.elasticsearch;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 
 import brooklyn.entity.basic.SoftwareProcessImpl;
 import brooklyn.event.feed.http.HttpFeed;
 import brooklyn.event.feed.http.HttpPollConfig;
 import brooklyn.event.feed.http.HttpValueFunctions;
 import brooklyn.event.feed.http.JsonFunctions;
+
 import org.apache.brooklyn.location.access.BrooklynAccessUtils;
+
 import brooklyn.util.guava.Functionals;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.guava.MaybeFunctions;
 import brooklyn.util.guava.TypeTokens;
-import brooklyn.util.http.HttpToolResponse;
 
 import com.google.common.base.Function;
 import com.google.common.base.Functions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/AbstractMongoDBServer.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/AbstractMongoDBServer.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/AbstractMongoDBServer.java
index 81ea23c..b2c8410 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/AbstractMongoDBServer.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/AbstractMongoDBServer.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.entity.nosql.mongodb;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -26,7 +27,6 @@ import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.event.basic.AttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 public interface AbstractMongoDBServer extends SoftwareProcess, Entity {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBClient.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBClient.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBClient.java
index 3dbe34e..3bc3a88 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBClient.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBClient.java
@@ -28,10 +28,9 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.MethodEffector;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.nosql.mongodb.sharding.MongoDBShardedDeployment;
 
-import brooklyn.util.flags.SetFromFlag;
-
 import com.google.common.reflect.TypeToken;
 
 @ImplementedBy(MongoDBClientImpl.class)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSet.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSet.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSet.java
index 2ebd8bb..a19bd1e 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSet.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSet.java
@@ -23,13 +23,13 @@ import java.util.List;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.group.Cluster;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.reflect.TypeToken;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServer.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServer.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServer.java
index f45070a..0ef0274 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServer.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServer.java
@@ -23,13 +23,13 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.AttributeSensor.SensorPersistenceMode;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 @Catalog(name="MongoDB Server",
     description="MongoDB (from \"humongous\") is a scalable, high-performance, open source NoSQL database",

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/sharding/CoLocatedMongoDBRouter.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/sharding/CoLocatedMongoDBRouter.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/sharding/CoLocatedMongoDBRouter.java
index b9a088b..a458ec7 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/sharding/CoLocatedMongoDBRouter.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/sharding/CoLocatedMongoDBRouter.java
@@ -24,12 +24,12 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.SameServerEntity;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.reflect.TypeToken;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/sharding/MongoDBShardedDeployment.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/sharding/MongoDBShardedDeployment.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/sharding/MongoDBShardedDeployment.java
index ba8fc10..3bab3b8 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/sharding/MongoDBShardedDeployment.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/sharding/MongoDBShardedDeployment.java
@@ -24,6 +24,7 @@ import org.apache.brooklyn.api.entity.Group;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.nosql.mongodb.MongoDBReplicaSet;
 import org.apache.brooklyn.entity.nosql.mongodb.MongoDBServer;
 
@@ -31,7 +32,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.time.Duration;
 
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/redis/RedisSlave.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/redis/RedisSlave.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/redis/RedisSlave.java
index 3688057..84bfe0e 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/redis/RedisSlave.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/redis/RedisSlave.java
@@ -19,10 +19,10 @@
 package org.apache.brooklyn.entity.nosql.redis;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * A {@link RedisStore} configured as a slave.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/redis/RedisStore.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/redis/RedisStore.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/redis/RedisStore.java
index a1a6749..2673426 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/redis/RedisStore.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/redis/RedisStore.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.entity.nosql.redis;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
@@ -28,7 +29,6 @@ import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * An entity that represents a Redis key-value store service.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakCluster.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakCluster.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakCluster.java
index e1ee7b5..b657267 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakCluster.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakCluster.java
@@ -25,13 +25,13 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.time.Duration;
 
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakClusterImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakClusterImpl.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakClusterImpl.java
index 4e72bae..7197787 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakClusterImpl.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakClusterImpl.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,7 +46,6 @@ import brooklyn.entity.group.AbstractMembershipTrackingPolicy;
 import brooklyn.entity.group.DynamicClusterImpl;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.DependentConfiguration;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNode.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNode.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNode.java
index 502aa91..7519709 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNode.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNode.java
@@ -24,6 +24,7 @@ import java.util.List;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.annotation.Effector;
@@ -36,7 +37,6 @@ import brooklyn.entity.java.UsesJava;
 import brooklyn.event.basic.AttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNodeImpl.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNodeImpl.java
index 587ffe3..6d0cd81 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNodeImpl.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNodeImpl.java
@@ -29,6 +29,7 @@ import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.util.config.ConfigBag;
 import org.apache.brooklyn.entity.webapp.WebAppServiceMethods;
 
 import brooklyn.enricher.Enrichers;
@@ -43,7 +44,6 @@ import org.apache.brooklyn.location.access.BrooklynAccessUtils;
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
 
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Functionals;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNodeSshDriver.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNodeSshDriver.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNodeSshDriver.java
index f115ab3..cbeff70 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNodeSshDriver.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/riak/RiakNodeSshDriver.java
@@ -47,14 +47,14 @@ import brooklyn.entity.basic.lifecycle.ScriptHelper;
 import brooklyn.entity.software.SshEffectorTasks;
 
 import org.apache.brooklyn.api.location.OsDetails;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.ssh.SshTasks;
 import brooklyn.util.text.Strings;
 
 import com.google.common.base.Joiner;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/solr/SolrServer.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/solr/SolrServer.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/solr/SolrServer.java
index f813ced..a81a1da 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/solr/SolrServer.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/solr/SolrServer.java
@@ -22,6 +22,7 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynConfigKeys;
@@ -32,8 +33,9 @@ import brooklyn.entity.java.UsesJavaMXBeans;
 import brooklyn.entity.java.UsesJmx;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
+
 import org.apache.brooklyn.location.basic.PortRanges;
-import brooklyn.util.flags.SetFromFlag;
+
 import brooklyn.util.time.Duration;
 
 import com.google.common.collect.Maps;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/solr/SolrServerSshDriver.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/solr/SolrServerSshDriver.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/solr/SolrServerSshDriver.java
index 5f7335a..b66c06f 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/solr/SolrServerSshDriver.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/solr/SolrServerSshDriver.java
@@ -33,10 +33,10 @@ import org.slf4j.LoggerFactory;
 import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.file.ArchiveUtils;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.file.ArchiveUtils;
 import brooklyn.util.net.Networking;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenterTest.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenterTest.java b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenterTest.java
index 1841cf7..4a78aaa 100644
--- a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenterTest.java
+++ b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraDatacenterTest.java
@@ -27,6 +27,8 @@ import java.util.Set;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -42,9 +44,7 @@ import brooklyn.entity.basic.EntityInternal;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.javalang.JavaClassNames;
-import brooklyn.util.text.TemplateProcessor;
 import brooklyn.util.time.Duration;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterIntegrationTest.java b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterIntegrationTest.java
index 8e695c9..bad63b0 100644
--- a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterIntegrationTest.java
+++ b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterIntegrationTest.java
@@ -27,6 +27,8 @@ import java.net.URISyntaxException;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.http.client.methods.HttpGet;
 import org.bouncycastle.util.Strings;
@@ -41,8 +43,6 @@ import brooklyn.entity.group.DynamicCluster;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.feed.http.HttpValueFunctions;
 import brooklyn.test.Asserts;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeIntegrationTest.java b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeIntegrationTest.java
index 8d5f0d1..5e9b5cb 100644
--- a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeIntegrationTest.java
+++ b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeIntegrationTest.java
@@ -25,6 +25,8 @@ import java.net.URISyntaxException;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.http.client.methods.HttpGet;
@@ -41,9 +43,6 @@ import brooklyn.event.feed.http.HttpValueFunctions;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/osgi/src/main/java/org/apache/brooklyn/entity/osgi/karaf/KarafContainer.java
----------------------------------------------------------------------
diff --git a/software/osgi/src/main/java/org/apache/brooklyn/entity/osgi/karaf/KarafContainer.java b/software/osgi/src/main/java/org/apache/brooklyn/entity/osgi/karaf/KarafContainer.java
index 6f0bd72..b708e95 100644
--- a/software/osgi/src/main/java/org/apache/brooklyn/entity/osgi/karaf/KarafContainer.java
+++ b/software/osgi/src/main/java/org/apache/brooklyn/entity/osgi/karaf/KarafContainer.java
@@ -23,6 +23,7 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.annotation.Effector;
@@ -36,7 +37,6 @@ import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.MapConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * This sets up a Karaf OSGi container

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceImpl.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceImpl.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceImpl.java
index 790aece..5b54f38 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceImpl.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceImpl.java
@@ -36,6 +36,7 @@ import java.util.Set;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.Group;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.webapp.WebAppService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,7 +53,6 @@ import org.apache.brooklyn.location.geo.HostGeoInfo;
 
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.net.Networking;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingDnsService.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingDnsService.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingDnsService.java
index f98314c..b9b8a1f 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingDnsService.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingDnsService.java
@@ -22,6 +22,7 @@ import java.net.URI;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.dns.AbstractGeoDnsService;
 import org.apache.brooklyn.entity.webapp.WebAppServiceConstants;
 
@@ -30,7 +31,6 @@ import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 @ImplementedBy(GeoscalingDnsServiceImpl.class)
 public interface GeoscalingDnsService extends AbstractGeoDnsService {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingDnsServiceImpl.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingDnsServiceImpl.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingDnsServiceImpl.java
index c71c00a..48a01f0 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingDnsServiceImpl.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingDnsServiceImpl.java
@@ -25,6 +25,7 @@ import java.net.URI;
 import java.util.Collection;
 import java.util.Set;
 
+import org.apache.brooklyn.core.util.http.HttpTool;
 import org.apache.brooklyn.entity.dns.AbstractGeoDnsServiceImpl;
 import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.Domain;
 import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.SmartSubdomain;
@@ -33,9 +34,10 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.basic.ServiceStateLogic;
+
 import org.apache.brooklyn.location.geo.HostGeoInfo;
+
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.http.HttpTool;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGenerator.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGenerator.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGenerator.java
index e8def56..5c1e578 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGenerator.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGenerator.java
@@ -24,8 +24,9 @@ import java.util.Date;
 import java.util.Iterator;
 import java.util.TimeZone;
 
+import org.apache.brooklyn.core.util.ResourceUtils;
 import org.apache.brooklyn.location.geo.HostGeoInfo;
-import brooklyn.util.ResourceUtils;
+
 import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClient.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClient.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClient.java
index 7e5c3aa..afaac72 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClient.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClient.java
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.brooklyn.core.util.http.HttpTool;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
@@ -48,7 +49,6 @@ import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.w3c.tidy.Tidy;
 
-import brooklyn.util.http.HttpTool;
 import brooklyn.util.text.Strings;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractController.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractController.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractController.java
index ca8caa3..de8955d 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractController.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractController.java
@@ -22,13 +22,13 @@ import java.util.Set;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.entity.group.Cluster;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * Represents a controller mechanism for a {@link Cluster}.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractControllerImpl.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractControllerImpl.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractControllerImpl.java
index 5eeeee9..a61da01 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractControllerImpl.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractControllerImpl.java
@@ -33,6 +33,7 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -52,7 +53,6 @@ import org.apache.brooklyn.location.basic.Machines;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancer.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancer.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancer.java
index 2af3da2..b3d32c0 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancer.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancer.java
@@ -24,6 +24,7 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.Group;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.webapp.WebAppService;
 
 import brooklyn.config.ConfigKey;
@@ -35,7 +36,6 @@ import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.reflect.TypeToken;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/ProxySslConfig.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/ProxySslConfig.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/ProxySslConfig.java
index 93e1b7f..ad6bf82 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/ProxySslConfig.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/ProxySslConfig.java
@@ -21,12 +21,11 @@ package org.apache.brooklyn.entity.proxy;
 import java.io.Serializable;
 import java.util.Map;
 
+import org.apache.brooklyn.core.util.flags.FlagUtils;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.util.flags.FlagUtils;
-import brooklyn.util.flags.SetFromFlag;
-
 import com.google.common.base.Objects;
 
 public class ProxySslConfig implements Serializable {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxController.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxController.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxController.java
index de20631..1ae0ea8 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxController.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxController.java
@@ -24,6 +24,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.entity.trait.HasShortName;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.proxy.AbstractController;
 import org.apache.brooklyn.entity.proxy.ProxySslConfig;
 
@@ -35,7 +36,6 @@ import brooklyn.entity.basic.MethodEffector;
 import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxControllerImpl.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
index 3d65881..bd3840d 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
@@ -31,6 +31,10 @@ import org.apache.brooklyn.api.event.SensorEvent;
 import org.apache.brooklyn.api.event.SensorEventListener;
 import org.apache.brooklyn.api.management.SubscriptionHandle;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.file.ArchiveUtils;
+import org.apache.brooklyn.core.util.http.HttpTool;
+import org.apache.brooklyn.core.util.http.HttpToolResponse;
 import org.apache.brooklyn.entity.proxy.AbstractControllerImpl;
 import org.apache.brooklyn.entity.proxy.ProxySslConfig;
 import org.apache.brooklyn.entity.proxy.nginx.NginxController.NginxControllerInternal;
@@ -47,11 +51,7 @@ import brooklyn.event.feed.ConfigToAttributes;
 import brooklyn.event.feed.http.HttpFeed;
 import brooklyn.event.feed.http.HttpPollConfig;
 import brooklyn.event.feed.http.HttpValueFunctions;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.file.ArchiveUtils;
 import brooklyn.util.guava.Functionals;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.stream.Streams;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxSshDriver.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxSshDriver.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxSshDriver.java
index da9bbf9..56d3dc6 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxSshDriver.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxSshDriver.java
@@ -27,6 +27,10 @@ import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolver;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.OsDetails;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.Tasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks;
+import org.apache.brooklyn.core.util.task.ssh.SshTasks.OnFailingTask;
 import org.apache.brooklyn.entity.proxy.AbstractController;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -46,10 +50,6 @@ import brooklyn.util.net.Networking;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;
 import brooklyn.util.stream.Streams;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.task.ssh.SshTasks;
-import brooklyn.util.task.ssh.SshTasks.OnFailingTask;
 import brooklyn.util.text.Strings;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxTemplateConfigGenerator.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxTemplateConfigGenerator.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxTemplateConfigGenerator.java
index 34432dd7..3c079d6 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxTemplateConfigGenerator.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxTemplateConfigGenerator.java
@@ -21,14 +21,14 @@ package org.apache.brooklyn.entity.proxy.nginx;
 import java.util.Collection;
 import java.util.Map;
 
+import org.apache.brooklyn.core.util.ResourceUtils;
+import org.apache.brooklyn.core.util.text.TemplateProcessor;
 import org.apache.brooklyn.entity.proxy.ProxySslConfig;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
 
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.Multimap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/UrlMapping.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/UrlMapping.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/UrlMapping.java
index ac937ee..59ec8bb 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/UrlMapping.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/UrlMapping.java
@@ -23,6 +23,7 @@ import java.util.Collection;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.proxy.AbstractController;
 import org.apache.brooklyn.entity.proxy.ProxySslConfig;
 
@@ -32,7 +33,6 @@ import brooklyn.entity.basic.AbstractGroup;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.MethodEffector;
 import brooklyn.event.basic.Sensors;
-import brooklyn.util.flags.SetFromFlag;
 
 import com.google.common.reflect.TypeToken;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppCluster.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppCluster.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppCluster.java
index 8315e17..58e206c 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppCluster.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppCluster.java
@@ -24,6 +24,7 @@ import org.apache.brooklyn.api.entity.Group;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 import org.apache.brooklyn.entity.proxy.LoadBalancer;
 
 import brooklyn.config.ConfigKey;
@@ -39,7 +40,6 @@ import brooklyn.entity.trait.Resizable;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 /**
  * This entity contains the sub-groups and entities that go in to a single location (e.g. datacenter)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/DynamicWebAppClusterImpl.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/DynamicWebAppClusterImpl.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/DynamicWebAppClusterImpl.java
index 1c87941..5fe07ac 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/DynamicWebAppClusterImpl.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/DynamicWebAppClusterImpl.java
@@ -29,6 +29,10 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.core.util.task.DynamicTasks;
+import org.apache.brooklyn.core.util.task.TaskBuilder;
+import org.apache.brooklyn.core.util.task.TaskTags;
+import org.apache.brooklyn.core.util.task.Tasks;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,10 +46,6 @@ import brooklyn.entity.group.DynamicClusterImpl;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.TaskTags;
-import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/JavaWebAppService.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/JavaWebAppService.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/JavaWebAppService.java
index d863b19..b5e1c48 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/JavaWebAppService.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/JavaWebAppService.java
@@ -24,6 +24,7 @@ import java.util.Set;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.core.util.flags.SetFromFlag;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.annotation.Effector;
@@ -32,7 +33,6 @@ import brooklyn.entity.basic.MethodEffector;
 import brooklyn.entity.java.UsesJava;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.util.flags.SetFromFlag;
 
 public interface JavaWebAppService extends WebAppService, UsesJava {
 



[15/42] incubator-brooklyn git commit: [BROOKLYN-162] Refactor package in ./core/util

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/task/BasicTasksFutureTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/BasicTasksFutureTest.java b/core/src/test/java/brooklyn/util/task/BasicTasksFutureTest.java
deleted file mode 100644
index f1c1332..0000000
--- a/core/src/test/java/brooklyn/util/task/BasicTasksFutureTest.java
+++ /dev/null
@@ -1,224 +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 java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.brooklyn.api.management.Task;
-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;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Stopwatch;
-
-public class BasicTasksFutureTest {
-
-    private static final Logger log = LoggerFactory.getLogger(BasicTasksFutureTest.class);
-    
-    private BasicExecutionManager em;
-    private BasicExecutionContext ec;
-    private Map<Object,Object> data;
-    private ExecutorService ex;
-    private Semaphore started;
-    private Semaphore waitInTask;
-    private Semaphore cancelledWhileSleeping;
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() {
-        em = new BasicExecutionManager("mycontext");
-        ec = new BasicExecutionContext(em);
-        ex = Executors.newCachedThreadPool();
-        data = Collections.synchronizedMap(new LinkedHashMap<Object,Object>());
-        started = new Semaphore(0);
-        waitInTask = new Semaphore(0);
-        cancelledWhileSleeping = new Semaphore(0);
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (em != null) em.shutdownNow();
-        if (ex != null) ex.shutdownNow();
-    }
-
-    @Test
-    public void testBlockAndGetWithTimeoutsAndListenableFuture() throws InterruptedException {
-        Task<String> t = waitForSemaphore(Duration.FIVE_SECONDS, true, "x");
-        
-        Assert.assertFalse(t.blockUntilEnded(Duration.millis(1)));
-        Assert.assertFalse(t.blockUntilEnded(Duration.ZERO));
-        boolean didNotThrow = false;
-        
-        try { t.getUnchecked(Duration.millis(1)); didNotThrow = true; }
-        catch (Exception e) { /* expected */ }
-        Assert.assertFalse(didNotThrow);
-        
-        try { t.getUnchecked(Duration.ZERO); didNotThrow = true; }
-        catch (Exception e) { /* expected */ }
-        Assert.assertFalse(didNotThrow);
-
-        addFutureListener(t, "before");
-        ec.submit(t);
-        
-        Assert.assertFalse(t.blockUntilEnded(Duration.millis(1)));
-        Assert.assertFalse(t.blockUntilEnded(Duration.ZERO));
-        
-        try { t.getUnchecked(Duration.millis(1)); didNotThrow = true; }
-        catch (Exception e) { /* expected */ }
-        Assert.assertFalse(didNotThrow);
-        
-        try { t.getUnchecked(Duration.ZERO); didNotThrow = true; }
-        catch (Exception e) { /* expected */ }
-        Assert.assertFalse(didNotThrow);
-
-        addFutureListener(t, "during");
-            
-        synchronized (data) {
-            // now let it finish
-            waitInTask.release();
-            Assert.assertTrue(t.blockUntilEnded(Duration.TEN_SECONDS));
-
-            Assert.assertEquals(t.getUnchecked(Duration.millis(1)), "x");
-            Assert.assertEquals(t.getUnchecked(Duration.ZERO), "x");
-            
-            Assert.assertNull(data.get("before"));
-            Assert.assertNull(data.get("during"));
-            // can't set the data(above) until we release the lock (in assert call below)
-            assertSoonGetsData("before");
-            assertSoonGetsData("during");
-        }
-
-        // and see that a listener added late also runs
-        synchronized (data) {
-            addFutureListener(t, "after");
-            Assert.assertNull(data.get("after"));
-            assertSoonGetsData("after");
-        }
-    }
-
-    private void addFutureListener(Task<String> t, final String key) {
-        t.addListener(new Runnable() { public void run() {
-            synchronized (data) {
-                log.info("notifying for "+key);
-                data.notifyAll();
-                data.put(key, true);
-            }
-        }}, ex);
-    }
-
-    private void assertSoonGetsData(String key) throws InterruptedException {
-        for (int i=0; i<10; i++) {
-            if (Boolean.TRUE.equals(data.get(key))) {
-                log.info("got data for "+key);
-                return;
-            }
-            data.wait(Duration.ONE_SECOND.toMilliseconds());
-        }
-        Assert.fail("did not get data for '"+key+"' in time");
-    }
-
-    private <T> Task<T> waitForSemaphore(final Duration time, final boolean requireSemaphore, final T result) {
-        return Tasks.<T>builder().body(new Callable<T>() {
-            public T call() { 
-                try {
-                    started.release();
-                    log.info("waiting up to "+time+" to acquire before returning "+result);
-                    if (!waitInTask.tryAcquire(time.toMilliseconds(), TimeUnit.MILLISECONDS)) {
-                        log.info("did not get semaphore");
-                        if (requireSemaphore) Assert.fail("task did not get semaphore");
-                    } else {
-                        log.info("got semaphore");
-                    }
-                } catch (Exception e) {
-                    log.info("cancelled before returning "+result);
-                    cancelledWhileSleeping.release();
-                    throw Exceptions.propagate(e);
-                }
-                log.info("task returning "+result);
-                return result; 
-            }
-        }).build();
-    }
-
-    @Test
-    public void testCancelAfterStartTriggersListenableFuture() throws Exception {
-        doTestCancelTriggersListenableFuture(Duration.millis(50));
-    }
-    @Test
-    public void testCancelImmediateTriggersListenableFuture() throws Exception {
-        // if cancel fires after submit but before it passes to the executor,
-        // that needs handling separately; this doesn't guarantee this code path,
-        // but it happens sometimes (and it should be handled)
-        doTestCancelTriggersListenableFuture(Duration.ZERO);
-    }
-    public void doTestCancelTriggersListenableFuture(Duration delay) throws Exception {
-        Task<String> t = waitForSemaphore(Duration.TEN_SECONDS, true, "x");
-        addFutureListener(t, "before");
-
-        Stopwatch watch = Stopwatch.createStarted();
-        ec.submit(t);
-        
-        addFutureListener(t, "during");
-
-        log.info("test cancelling "+t+" ("+t.getClass()+") after "+delay);
-        // NB: two different code paths (callers to this method) for notifying futures 
-        // depending whether task is started 
-        Time.sleep(delay);
-
-        synchronized (data) {
-            t.cancel(true);
-            
-            assertSoonGetsData("before");
-            assertSoonGetsData("during");
-
-            addFutureListener(t, "after");
-            Assert.assertNull(data.get("after"));
-            assertSoonGetsData("after");
-        }
-        
-        Assert.assertTrue(t.isDone());
-        Assert.assertTrue(t.isCancelled());
-        try {
-            t.get();
-            Assert.fail("should have thrown CancellationException");
-        } catch (CancellationException e) { /* expected */ }
-        
-        Assert.assertTrue(watch.elapsed(TimeUnit.MILLISECONDS) < Duration.FIVE_SECONDS.toMilliseconds(), 
-            Time.makeTimeStringRounded(watch.elapsed(TimeUnit.MILLISECONDS))+" is too long; should have cancelled very quickly");
-
-        if (started.tryAcquire())
-            // if the task is begun, this should get released
-            Assert.assertTrue(cancelledWhileSleeping.tryAcquire(5, TimeUnit.SECONDS));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/task/CompoundTaskExecutionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/CompoundTaskExecutionTest.java b/core/src/test/java/brooklyn/util/task/CompoundTaskExecutionTest.java
deleted file mode 100644
index 9fe4ba0..0000000
--- a/core/src/test/java/brooklyn/util/task/CompoundTaskExecutionTest.java
+++ /dev/null
@@ -1,252 +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.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.Semaphore;
-
-import org.apache.brooklyn.api.management.Task;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-import org.testng.collections.Lists;
-
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-/**
- * Test the operation of the {@link CompoundTask} class.
- */
-public class CompoundTaskExecutionTest {
-
-    private static final Logger LOG = LoggerFactory.getLogger(CompoundTaskExecutionTest.class);
-
-    BasicExecutionManager em;
-    BasicExecutionContext ec;
-
-    @BeforeClass
-    public void setup() {
-        em = new BasicExecutionManager("mycontext");
-        ec = new BasicExecutionContext(em);
-    }
-
-    @AfterClass
-    public void teardown() {
-        if (em != null) em.shutdownNow();
-        em = null;
-    }
-
-    private BasicTask<String> taskReturning(final String val) {
-        return new BasicTask<String>(new Callable<String>() {
-                @Override public String call() {
-                    return val;
-                }
-            });
-    }
-
-    private BasicTask<String> slowTaskReturning(final String val, final Duration pauseTime) {
-        return new BasicTask<String>(new Callable<String>() {
-                @Override public String call() {
-                    Time.sleep(pauseTime);
-                    return val;
-                }
-            });
-    }
-
-
-    @Test
-    public void runSequenceTask() throws Exception {
-        BasicTask<String> t1 = taskReturning("a");
-        BasicTask<String> t2 = taskReturning("b");
-        BasicTask<String> t3 = taskReturning("c");
-        BasicTask<String> t4 = taskReturning("d");
-        Task<List<String>> tSequence = ec.submit(new SequentialTask<String>(t1, t2, t3, t4));
-        assertEquals(tSequence.get(), ImmutableList.of("a", "b", "c", "d"));
-    }
-
-    @Test
-    public void testSequentialTaskFailsWhenIntermediateTaskThrowsException() throws Exception {
-        BasicTask<String> t1 = taskReturning("a");
-        BasicTask<String> t2 = new BasicTask<String>(new Callable<String>() {
-                @Override public String call() throws Exception {
-                    throw new IllegalArgumentException("forced exception");
-                }
-            });
-        BasicTask<String> t3 = taskReturning("c");
-        SequentialTask<String> task = new SequentialTask<String>(t1, t2, t3);
-        Task<List<String>> tSequence = ec.submit(task);
-
-        try {
-            tSequence.get();
-            fail("t2 should have thrown an exception");
-        } catch (Exception e) {}
-
-        assertTrue(task.isDone());
-        assertTrue(task.isError());
-        assertTrue(t1.isDone());
-        assertFalse(t1.isError());
-        assertTrue(t2.isDone());
-        assertTrue(t2.isError());
-        // t3 not run because of t2 exception
-        assertFalse(t3.isDone());
-        assertFalse(t3.isBegun());
-    }
-
-    @Test
-    public void testParallelTaskFailsWhenIntermediateTaskThrowsException() throws Exception {
-        // differs from test above of SequentialTask in that expect t3 to be executed,
-        // despite t2 failing.
-        // TODO Do we expect tSequence.get() to block for everything to either fail or complete,
-        // and then to throw exception? Currently it does *not* do that so test was previously failing.
-
-        BasicTask<String> t1 = taskReturning("a");
-        BasicTask<String> t2 = new BasicTask<String>(new Callable<String>() {
-                @Override public String call() throws Exception {
-                    throw new IllegalArgumentException("forced exception");
-                }
-            });
-        BasicTask<String> t3 = slowTaskReturning("c", Duration.millis(100));
-        ParallelTask<String> task = new ParallelTask<String>(t1, t2, t3);
-        Task<List<String>> tSequence = ec.submit(task);
-
-        try {
-            tSequence.get();
-            fail("t2 should have thrown an exception");
-        } catch (Exception e) {}
-
-        assertTrue(task.isDone());
-        assertTrue(task.isError());
-        assertTrue(t1.isDone());
-        assertFalse(t1.isError());
-        assertTrue(t2.isDone());
-        assertTrue(t2.isError());
-        assertTrue(t3.isBegun());
-        assertTrue(t3.isDone());
-        assertFalse(t3.isError());
-    }
-
-    @Test
-    public void runParallelTask() throws Exception {
-        BasicTask<String> t1 = taskReturning("a");
-        BasicTask<String> t2 = taskReturning("b");
-        BasicTask<String> t3 = taskReturning("c");
-        BasicTask<String> t4 = taskReturning("d");
-        Task<List<String>> tSequence = ec.submit(new ParallelTask<String>(t4, t2, t1, t3));
-        assertEquals(new HashSet<String>(tSequence.get()), ImmutableSet.of("a", "b", "c", "d"));
-    }
-
-    @Test
-    public void runParallelTaskWithDelay() throws Exception {
-        final Semaphore locker = new Semaphore(0);
-        BasicTask<String> t1 = new BasicTask<String>(new Callable<String>() {
-                @Override public String call() {
-                    try {
-                        locker.acquire();
-                    } catch (InterruptedException e) {
-                        throw Throwables.propagate(e);
-                    }
-                    return "a";
-                }
-            });
-        BasicTask<String> t2 = taskReturning("b");
-        BasicTask<String> t3 = taskReturning("c");
-        BasicTask<String> t4 = taskReturning("d");
-        final Task<List<String>> tSequence = ec.submit(new ParallelTask<String>(t4, t2, t1, t3));
-
-        assertEquals(ImmutableSet.of(t2.get(), t3.get(), t4.get()), ImmutableSet.of("b", "c", "d"));
-        assertFalse(t1.isDone());
-        assertFalse(tSequence.isDone());
-
-        // get blocks until tasks have completed
-        Thread t = new Thread() {
-            @Override public void run() {
-                try {
-                    tSequence.get();
-                } catch (Exception e) {
-                    throw Throwables.propagate(e);
-                }
-                locker.release();
-            }
-        };
-        t.start();
-        Thread.sleep(30);
-        assertTrue(t.isAlive());
-
-        locker.release();
-
-        assertEquals(new HashSet<String>(tSequence.get()), ImmutableSet.of("a", "b", "c", "d"));
-        assertTrue(t1.isDone());
-        assertTrue(tSequence.isDone());
-
-        locker.acquire();
-    }
-
-    @Test
-    public void testComplexOrdering() throws Exception {
-        List<String> data = new CopyOnWriteArrayList<String>();
-        SequentialTask<String> taskA = new SequentialTask<String>(
-                appendAfterDelay(data, "a1"), appendAfterDelay(data, "a2"), appendAfterDelay(data, "a3"), appendAfterDelay(data, "a4"));
-        SequentialTask<String> taskB = new SequentialTask<String>(
-                appendAfterDelay(data, "b1"), appendAfterDelay(data, "b2"), appendAfterDelay(data, "b3"), appendAfterDelay(data, "b4"));
-        Task<List<String>> t = ec.submit(new ParallelTask<String>(taskA, taskB));
-        t.get();
-
-        LOG.debug("Tasks happened in order: {}", data);
-        assertEquals(data.size(), 8);
-        assertEquals(new HashSet<String>(data), ImmutableSet.of("a1", "a2", "a3", "a4", "b1", "b2", "b3", "b4"));
-
-        // a1, ..., a4 should be in order
-        List<String> as = Lists.newArrayList(), bs = Lists.newArrayList();
-        for (String value : data) {
-            ((value.charAt(0) == 'a') ? as : bs).add(value);
-        }
-        assertEquals(as, ImmutableList.of("a1", "a2", "a3", "a4"));
-        assertEquals(bs, ImmutableList.of("b1", "b2", "b3", "b4"));
-    }
-
-    private BasicTask<String> appendAfterDelay(final List<String> list, final String value) {
-        return new BasicTask<String>(new Callable<String>() {
-                @Override public String call() {
-                    try {
-                        Thread.sleep((int) (100 * Math.random()));
-                    } catch (InterruptedException e) {
-                        throw Throwables.propagate(e);
-                    }
-                    LOG.debug("running {}", value);
-                    list.add(value);
-                    return value;
-                }
-            });
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/task/DynamicSequentialTaskTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/DynamicSequentialTaskTest.java b/core/src/test/java/brooklyn/util/task/DynamicSequentialTaskTest.java
deleted file mode 100644
index a5985fe..0000000
--- a/core/src/test/java/brooklyn/util/task/DynamicSequentialTaskTest.java
+++ /dev/null
@@ -1,365 +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 java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.brooklyn.api.management.HasTaskChildren;
-import org.apache.brooklyn.api.management.Task;
-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;
-
-import brooklyn.test.Asserts;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.time.CountdownTimer;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
-public class DynamicSequentialTaskTest {
-
-    private static final Logger log = LoggerFactory.getLogger(DynamicSequentialTaskTest.class);
-    
-    public static final Duration TIMEOUT = Duration.TEN_SECONDS;
-    public static final Duration TINY_TIME = Duration.millis(20);
-    
-    BasicExecutionManager em;
-    BasicExecutionContext ec;
-    List<String> messages;
-    Semaphore cancellations;
-    Stopwatch stopwatch;
-    Map<String,Semaphore> monitorableJobSemaphoreMap;
-    Map<String,Task<String>> monitorableTasksMap;
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() {
-        em = new BasicExecutionManager("mycontext");
-        ec = new BasicExecutionContext(em);
-        cancellations = new Semaphore(0);
-        messages = new ArrayList<String>();
-        monitorableJobSemaphoreMap = MutableMap.of();
-        monitorableTasksMap = MutableMap.of();
-        monitorableTasksMap.clear();
-        stopwatch = Stopwatch.createStarted();
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (em != null) em.shutdownNow();
-    }
-
-    @Test
-    public void testSimple() throws InterruptedException, ExecutionException {
-        Callable<String> mainJob = new Callable<String>() {
-            public String call() {
-                log.info("main job - "+Tasks.current());
-                messages.add("main");
-                DynamicTasks.queue( sayTask("world") );
-                return "bye";
-            }            
-        };
-        DynamicSequentialTask<String> t = new DynamicSequentialTask<String>(mainJob);
-        // this should be added before anything added when the task is invoked
-        t.queue(sayTask("hello"));
-        
-        Assert.assertEquals(messages, Lists.newArrayList());
-        Assert.assertEquals(t.isBegun(), false);
-        Assert.assertEquals(Iterables.size(t.getChildren()), 1);
-        
-        ec.submit(t);
-        Assert.assertEquals(t.isSubmitted(), true);
-        Assert.assertEquals(t.getUnchecked(Duration.ONE_SECOND), "bye");
-        long elapsed = t.getEndTimeUtc() - t.getSubmitTimeUtc();
-        Assert.assertTrue(elapsed < 1000, "elapsed time should have been less than 1s but was "+
-                Time.makeTimeString(elapsed, true));
-        Assert.assertEquals(Iterables.size(t.getChildren()), 2);
-        Assert.assertEquals(messages.size(), 3, "expected 3 entries, but had "+messages);
-        // either main or hello can be first, but world should be last 
-        Assert.assertEquals(messages.get(2), "world");
-    }
-    
-    public Callable<String> sayCallable(final String message, final Duration duration, final String message2) {
-        return new Callable<String>() {
-            public String call() {
-                try {
-                    if (message != null) {
-                        log.info("saying: "+message+ " - "+Tasks.current());
-                        synchronized (messages) {
-                            messages.add(message);
-                            messages.notifyAll();
-                        }
-                    }
-                    if (message2 != null) {
-                        log.info("will say "+message2+" after "+duration);
-                    }
-                    if (duration != null && duration.toMilliseconds() > 0) {
-                        Thread.sleep(duration.toMillisecondsRoundingUp());
-                    }
-                } catch (InterruptedException e) {
-                    cancellations.release();
-                    throw Exceptions.propagate(e);
-                }
-                if (message2 != null) {
-                    log.info("saying: "+message2+ " - "+Tasks.current());
-                    synchronized (messages) {
-                        messages.add(message2);
-                        messages.notifyAll();
-                    }
-                }
-                return message;
-            }            
-        };
-    }
-    
-    public Task<String> sayTask(String message) {
-        return sayTask(message, null, null);
-    }
-    
-    public Task<String> sayTask(String message, Duration duration, String message2) {
-        return Tasks.<String>builder().body(sayCallable(message, duration, message2)).build();
-    }
-    
-    @Test
-    public void testComplex() throws InterruptedException, ExecutionException {
-        Task<List<?>> t = Tasks.sequential(
-                sayTask("1"),
-                sayTask("2"),
-                Tasks.parallel(sayTask("4"), sayTask("3")),
-                sayTask("5")
-            );
-        ec.submit(t);
-        Assert.assertEquals(t.get().size(), 4); 
-        Asserts.assertEqualsIgnoringOrder((List<?>)t.get().get(2), ImmutableSet.of("3", "4"));
-        Assert.assertTrue(messages.equals(Arrays.asList("1", "2", "3", "4", "5")) || messages.equals(Arrays.asList("1", "2", "4", "3", "5")), "messages="+messages);
-    }
-    
-    @Test
-    public void testCancelled() throws InterruptedException, ExecutionException {
-        Task<List<?>> t = Tasks.sequential(
-                sayTask("1"),
-                sayTask("2a", Duration.THIRTY_SECONDS, "2b"),
-                sayTask("3"));
-        ec.submit(t);
-        synchronized (messages) {
-            while (messages.size() <= 1)
-                messages.wait();
-        }
-        Assert.assertEquals(messages, Arrays.asList("1", "2a"));
-        Time.sleep(Duration.millis(50));
-        t.cancel(true);
-        Assert.assertTrue(t.isDone());
-        // 2 should get cancelled, and invoke the cancellation semaphore
-        // 3 should get cancelled and not run at all
-        Assert.assertEquals(messages, Arrays.asList("1", "2a"));
-        
-        // Need to ensure that 2 has been started; race where we might cancel it before its run method
-        // is even begun. Hence doing "2a; pause; 2b" where nothing is interruptable before pause.
-        Assert.assertTrue(cancellations.tryAcquire(10, TimeUnit.SECONDS));
-        
-        Iterator<Task<?>> ci = ((HasTaskChildren)t).getChildren().iterator();
-        Assert.assertEquals(ci.next().get(), "1");
-        Task<?> task2 = ci.next();
-        Assert.assertTrue(task2.isBegun());
-        Assert.assertTrue(task2.isDone());
-        Assert.assertTrue(task2.isCancelled());
-        
-        Task<?> task3 = ci.next();
-        Assert.assertFalse(task3.isBegun());
-        Assert.assertTrue(task2.isDone());
-        Assert.assertTrue(task2.isCancelled());
-        
-        // but we do _not_ get a mutex from task3 as it does not run (is not interrupted)
-        Assert.assertEquals(cancellations.availablePermits(), 0);
-    }
-
-    protected Task<String> monitorableTask(final String id) {
-        return monitorableTask(null, id, null);
-    }
-    protected Task<String> monitorableTask(final Runnable pre, final String id, final Callable<String> post) {
-        Task<String> t = Tasks.<String>builder().body(monitorableJob(pre, id, post)).build();
-        monitorableTasksMap.put(id, t);
-        return t;
-    }
-    protected Callable<String> monitorableJob(final String id) {
-        return monitorableJob(null, id, null);
-    }
-    protected Callable<String> monitorableJob(final Runnable pre, final String id, final Callable<String> post) {
-        monitorableJobSemaphoreMap.put(id, new Semaphore(0));
-        return new Callable<String>() {
-            @Override
-            public String call() throws Exception {
-                if (pre!=null) pre.run();
-                // wait for semaphore
-                if (!monitorableJobSemaphoreMap.get(id).tryAcquire(1, TIMEOUT.toMilliseconds(), TimeUnit.MILLISECONDS))
-                    throw new IllegalStateException("timeout for "+id);
-                synchronized (messages) {
-                    messages.add(id);
-                    messages.notifyAll();
-                }
-                if (post!=null) return post.call();
-                return id;
-            }
-        };
-    }
-    protected void releaseMonitorableJob(final String id) {
-        monitorableJobSemaphoreMap.get(id).release();
-    }
-    protected void waitForMessage(final String id) {
-        CountdownTimer timer = CountdownTimer.newInstanceStarted(TIMEOUT);
-        synchronized (messages) {
-            while (!timer.isExpired()) {
-                if (messages.contains(id)) return;
-                timer.waitOnForExpiryUnchecked(messages);
-            }
-        }
-        Assert.fail("Did not see message "+id);
-    }
-    protected void releaseAndWaitForMonitorableJob(final String id) {
-        releaseMonitorableJob(id);
-        waitForMessage(id);
-    }
-    
-    @Test
-    public void testChildrenRunConcurrentlyWithPrimary() {
-        Task<String> t = Tasks.<String>builder().dynamic(true)
-            .body(monitorableJob("main"))
-            .add(monitorableTask("1")).add(monitorableTask("2")).build();
-        ec.submit(t);
-        releaseAndWaitForMonitorableJob("1");
-        releaseAndWaitForMonitorableJob("main");
-        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
-        releaseMonitorableJob("2");
-        
-        Assert.assertTrue(t.blockUntilEnded(TIMEOUT));
-        Assert.assertEquals(messages, MutableList.of("1", "main", "2"));
-        Assert.assertTrue(stopwatch.elapsed(TimeUnit.MILLISECONDS) < TIMEOUT.toMilliseconds(), "took too long: "+stopwatch);
-        Assert.assertFalse(t.isError());
-    }
-    
-    protected static class FailRunnable implements Runnable {
-        @Override public void run() { throw new RuntimeException("Planned exception for test"); }
-    }
-    protected static class FailCallable implements Callable<String> {
-        @Override public String call() { throw new RuntimeException("Planned exception for test"); }
-    }
-    
-    @Test
-    public void testByDefaultChildrenFailureAbortsSecondaryFailsPrimaryButNotAbortsPrimary() {
-        Task<String> t1 = monitorableTask(null, "1", new FailCallable());
-        Task<String> t = Tasks.<String>builder().dynamic(true)
-            .body(monitorableJob("main"))
-            .add(t1).add(monitorableTask("2")).build();
-        ec.submit(t);
-        releaseAndWaitForMonitorableJob("1");
-        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
-        releaseMonitorableJob("main");
-        
-        Assert.assertTrue(t.blockUntilEnded(TIMEOUT));
-        Assert.assertEquals(messages, MutableList.of("1", "main"));
-        Assert.assertTrue(stopwatch.elapsed(TimeUnit.MILLISECONDS) < TIMEOUT.toMilliseconds(), "took too long: "+stopwatch);
-        Assert.assertTrue(t.isError());
-        Assert.assertTrue(t1.isError());
-    }
-
-    @Test
-    public void testWhenSwallowingChildrenFailureDoesNotAbortSecondaryOrFailPrimary() {
-        Task<String> t1 = monitorableTask(null, "1", new FailCallable());
-        Task<String> t = Tasks.<String>builder().dynamic(true)
-            .body(monitorableJob("main"))
-            .add(t1).add(monitorableTask("2")).swallowChildrenFailures(true).build();
-        ec.submit(t);
-        releaseAndWaitForMonitorableJob("1");
-        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
-        releaseAndWaitForMonitorableJob("2");
-        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
-        releaseMonitorableJob("main");
-        Assert.assertTrue(t.blockUntilEnded(TIMEOUT));
-        Assert.assertEquals(messages, MutableList.of("1", "2", "main"));
-        Assert.assertTrue(stopwatch.elapsed(TimeUnit.MILLISECONDS) < TIMEOUT.toMilliseconds(), "took too long: "+stopwatch);
-        Assert.assertFalse(t.isError());
-        Assert.assertTrue(t1.isError());
-    }
-
-    @Test
-    public void testInessentialChildrenFailureDoesNotAbortSecondaryOrFailPrimary() {
-        Task<String> t1 = monitorableTask(null, "1", new FailCallable());
-        TaskTags.markInessential(t1);
-        Task<String> t = Tasks.<String>builder().dynamic(true)
-            .body(monitorableJob("main"))
-            .add(t1).add(monitorableTask("2")).build();
-        ec.submit(t);
-        releaseAndWaitForMonitorableJob("1");
-        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
-        releaseAndWaitForMonitorableJob("2");
-        Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
-        releaseMonitorableJob("main");
-        Assert.assertTrue(t.blockUntilEnded(TIMEOUT));
-        Assert.assertEquals(messages, MutableList.of("1", "2", "main"));
-        Assert.assertTrue(stopwatch.elapsed(TimeUnit.MILLISECONDS) < TIMEOUT.toMilliseconds(), "took too long: "+stopwatch);
-        Assert.assertFalse(t.isError());
-        Assert.assertTrue(t1.isError());
-    }
-
-    @Test
-    public void testTaskBuilderUsingAddVarargChildren() {
-        Task<String> t = Tasks.<String>builder().dynamic(true)
-            .body(monitorableJob("main"))
-            .add(monitorableTask("1"), monitorableTask("2"))
-            .build();
-        ec.submit(t);
-        releaseAndWaitForMonitorableJob("1");
-        releaseAndWaitForMonitorableJob("2");
-        releaseAndWaitForMonitorableJob("main");
-        
-        Assert.assertEquals(messages, MutableList.of("1", "2", "main"));
-    }
-    
-    @Test
-    public void testTaskBuilderUsingAddAllChildren() {
-        Task<String> t = Tasks.<String>builder().dynamic(true)
-            .body(monitorableJob("main"))
-            .addAll(ImmutableList.of(monitorableTask("1"), monitorableTask("2")))
-            .build();
-        ec.submit(t);
-        releaseAndWaitForMonitorableJob("1");
-        releaseAndWaitForMonitorableJob("2");
-        releaseAndWaitForMonitorableJob("main");
-        
-        Assert.assertEquals(messages, MutableList.of("1", "2", "main"));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/task/NonBasicTaskExecutionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/NonBasicTaskExecutionTest.java b/core/src/test/java/brooklyn/util/task/NonBasicTaskExecutionTest.java
deleted file mode 100644
index 82e3919..0000000
--- a/core/src/test/java/brooklyn/util/task/NonBasicTaskExecutionTest.java
+++ /dev/null
@@ -1,126 +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.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertSame;
-import static org.testng.Assert.assertTrue;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.brooklyn.api.management.Task;
-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;
-
-/**
- * Test the operation of the {@link BasicTask} class.
- *
- * TODO clarify test purpose
- */
-public class NonBasicTaskExecutionTest {
-    private static final Logger log = LoggerFactory.getLogger(NonBasicTaskExecutionTest.class);
- 
-    private static final int TIMEOUT_MS = 10*1000;
-    
-    public static class ConcreteForwardingTask<T> extends ForwardingTask<T> {
-        private final TaskInternal<T> delegate;
-
-        ConcreteForwardingTask(TaskInternal<T> delegate) {
-            this.delegate = delegate;
-        }
-        
-        @Override
-        protected TaskInternal<T> delegate() {
-            return delegate;
-        }
-    }
-    
-    private BasicExecutionManager em;
-    private Map<Integer,String> data;
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        em = new BasicExecutionManager("mycontext");
-        data = Collections.synchronizedMap(new HashMap<Integer,String>());
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (em != null) em.shutdownNow();
-    }
-    
-    @Test
-    public void runSimpleTask() throws Exception {
-        TaskInternal<Object> t = new ConcreteForwardingTask<Object>(new BasicTask<Object>(new Callable<Object>() {
-            @Override public Object call() {
-                return data.put(1, "b");
-            }}));
-        data.put(1, "a");
-        Task<?> t2 = em.submit(MutableMap.of("tag", "A"), t);
-        assertEquals("a", t.get());
-        assertEquals("a", t2.get());
-        assertSame(t, t2, "t="+t+"; t2="+t2);
-        assertEquals("b", data.get(1));
-    }
-    
-    @Test
-    public void runBasicTaskWithWaits() throws Exception {
-        final CountDownLatch signalStarted = new CountDownLatch(1);
-        final CountDownLatch allowCompletion = new CountDownLatch(1);
-        final TaskInternal<Object> t = new ConcreteForwardingTask<Object>(new BasicTask<Object>(new Callable<Object>() {
-            @Override 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() {
-            @Override public void run() {
-                t.getStatusDetail(false).toLowerCase().contains("waiting");
-            }});
-        // "details="+t.getStatusDetail(false))
-        
-        allowCompletion.countDown();
-        assertEquals("a", t.get());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/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
deleted file mode 100644
index 1ae65f2..0000000
--- a/core/src/test/java/brooklyn/util/task/ScheduledExecutionTest.java
+++ /dev/null
@@ -1,287 +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.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.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.brooklyn.api.management.Task;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import brooklyn.test.Asserts;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.exceptions.RuntimeInterruptedException;
-import brooklyn.util.javalang.JavaClassNames;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.Lists;
-
-@SuppressWarnings({"unchecked","rawtypes"})
-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()).getSubmittedByTask();
-                        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 {
-        Duration PERIOD = Duration.millis(20);
-        BasicExecutionManager m = new BasicExecutionManager("mycontextid");
-        final AtomicInteger i = new AtomicInteger();
-        ScheduledTask t = new ScheduledTask(MutableMap.of("delay", PERIOD.times(2), "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()).getSubmittedByTask();
-                        i.incrementAndGet();
-                        if (i.get() >= 5) submitter.cancel();
-                        return i.get();
-                    }});
-            }});
-    
-        log.info(JavaClassNames.niceClassAndMethod()+" - 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 testScheduledTaskCancelOuter() throws Exception {
-        final Duration PERIOD = Duration.millis(20);
-        final Duration CYCLE_DELAY = Duration.ONE_SECOND;
-        // this should be enough to start the next cycle, but not so much that the cycle ends;
-        // and enough that when a task is interrupted it terminates within this period
-        final Duration SMALL_FRACTION_OF_CYCLE_DELAY = PERIOD.add(CYCLE_DELAY.multiply(0.1));
-        
-        BasicExecutionManager m = new BasicExecutionManager("mycontextid");
-        final AtomicInteger i = new AtomicInteger();
-        ScheduledTask t = new ScheduledTask(MutableMap.of("delay", PERIOD.times(2), "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));
-                        Time.sleep(CYCLE_DELAY);
-                        i.incrementAndGet();
-                        return i.get();
-                    }});
-            }});
-    
-        log.info(JavaClassNames.niceClassAndMethod()+" - 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)});
-        assertEquals(i.get(), 1);
-        
-        Time.sleep(SMALL_FRACTION_OF_CYCLE_DELAY);
-        assertEquals(t.get(), 2);
-        
-        Time.sleep(SMALL_FRACTION_OF_CYCLE_DELAY);
-        Stopwatch timer = Stopwatch.createUnstarted();
-        t.cancel(true);
-        t.blockUntilEnded();
-//      int finalResult = t.get()
-        log.info("blocked until ended ({}) {} {}, in {}", new Object[] {i, t, t.getStatusDetail(false), Duration.of(timer)});
-        try {
-            t.get();
-            Assert.fail("Should have failed getting result of cancelled "+t);
-        } catch (Exception e) {
-            /* expected */
-        }
-        assertEquals(i.get(), 2);
-        log.info("ended ({}) {} {}, in {}", new Object[] {i, t, t.getStatusDetail(false), Duration.of(timer)});
-        Assert.assertTrue(Duration.of(timer).isShorterThan(SMALL_FRACTION_OF_CYCLE_DELAY));
-    }
-
-    @Test(groups="Integration")
-    public void testScheduledTaskCancelInterrupts() throws Exception {
-        final Duration PERIOD = Duration.millis(20);
-        final Duration CYCLE_DELAY = Duration.ONE_SECOND;
-        // this should be enough to start the next cycle, but not so much that the cycle ends;
-        // and enough that when a task is interrupted it terminates within this period
-        final Duration SMALL_FRACTION_OF_CYCLE_DELAY = PERIOD.add(CYCLE_DELAY.multiply(0.1));
-        
-        BasicExecutionManager m = new BasicExecutionManager("mycontextid");
-        final Semaphore interruptedSemaphore = new Semaphore(0);
-        final AtomicInteger i = new AtomicInteger();
-        ScheduledTask t = new ScheduledTask(MutableMap.of("delay", PERIOD.times(2), "period", PERIOD), new Callable<Task<?>>() {
-            public Task<?> call() throws Exception {
-                return new BasicTask<Integer>(new Callable<Integer>() {
-                    public Integer call() {
-                        try {
-                            log.info("task running ("+i+"): "+Tasks.current()+" "+Tasks.current().getStatusDetail(false));
-                            Time.sleep(CYCLE_DELAY);
-                            i.incrementAndGet();
-                            return i.get();
-                        } catch (RuntimeInterruptedException e) {
-                            interruptedSemaphore.release();
-                            throw Exceptions.propagate(e);
-                        }
-                    }});
-            }});
-    
-        log.info(JavaClassNames.niceClassAndMethod()+" - 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)});
-        assertEquals(i.get(), 1);
-        
-        Time.sleep(SMALL_FRACTION_OF_CYCLE_DELAY);
-        assertEquals(t.get(), 2);
-        
-        Time.sleep(SMALL_FRACTION_OF_CYCLE_DELAY);
-        Stopwatch timer = Stopwatch.createUnstarted();
-        t.cancel(true);
-        t.blockUntilEnded();
-//      int finalResult = t.get()
-        log.info("blocked until ended ({}) {} {}, in {}", new Object[] {i, t, t.getStatusDetail(false), Duration.of(timer)});
-        try {
-            t.get();
-            Assert.fail("Should have failed getting result of cancelled "+t);
-        } catch (Exception e) {
-            /* expected */
-        }
-        assertEquals(i.get(), 2);
-        Assert.assertTrue(interruptedSemaphore.tryAcquire(1, SMALL_FRACTION_OF_CYCLE_DELAY.toMilliseconds(), TimeUnit.MILLISECONDS), "child thread was not interrupted");
-        log.info("ended ({}) {} {}, in {}", new Object[] {i, t, t.getStatusDetail(false), Duration.of(timer)});
-        Assert.assertTrue(Duration.of(timer).isShorterThan(SMALL_FRACTION_OF_CYCLE_DELAY));
-    }
-
-    @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/a4c0e5fd/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
deleted file mode 100644
index 265956d..0000000
--- a/core/src/test/java/brooklyn/util/task/SingleThreadedSchedulerTest.java
+++ /dev/null
@@ -1,192 +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.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;
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/task/TaskFinalizationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/TaskFinalizationTest.java b/core/src/test/java/brooklyn/util/task/TaskFinalizationTest.java
deleted file mode 100644
index 51750ca..0000000
--- a/core/src/test/java/brooklyn/util/task/TaskFinalizationTest.java
+++ /dev/null
@@ -1,62 +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 java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.brooklyn.api.management.Task;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Stopwatch;
-
-public class TaskFinalizationTest {
-
-    private static final Logger log = LoggerFactory.getLogger(TaskFinalizationTest.class);
-    
-    // integration because it can take a while (and finalizers aren't even guaranteed)
-    @Test(groups="Integration")
-    public void testFinalizerInvoked() throws InterruptedException {
-        BasicTask<?> t = new BasicTask<Void>(new Runnable() { public void run() { /* no op */ }});
-        final Semaphore x = new Semaphore(0);
-        t.setFinalizer(new BasicTask.TaskFinalizer() {
-            public void onTaskFinalization(Task<?> t) {
-                synchronized (x) { 
-                    x.release();
-                }
-            }
-        });
-        t = null;
-        Stopwatch watch = Stopwatch.createStarted();
-        for (int i=0; i<30; i++) {
-            System.gc(); System.gc();
-            if (x.tryAcquire(1, TimeUnit.SECONDS)) {
-                log.info("finalizer ran after "+Time.makeTimeStringRounded(watch));
-                return;
-            }
-        }
-        Assert.fail("finalizer did not run in time");
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/task/TasksTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/TasksTest.java b/core/src/test/java/brooklyn/util/task/TasksTest.java
deleted file mode 100644
index 58ce24f..0000000
--- a/core/src/test/java/brooklyn/util/task/TasksTest.java
+++ /dev/null
@@ -1,181 +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.event.basic.DependentConfiguration.attributeWhenReady;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.test.entity.TestApplication;
-import org.apache.brooklyn.test.entity.TestEntity;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.BrooklynAppUnitTestSupport;
-import brooklyn.entity.basic.EntityFunctions;
-import brooklyn.util.guava.Functionals;
-import brooklyn.util.repeat.Repeater;
-import brooklyn.util.time.Duration;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.Callables;
-
-
-public class TasksTest extends BrooklynAppUnitTestSupport {
-
-    private ExecutionContext executionContext;
-
-    @BeforeMethod(alwaysRun=true)
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        executionContext = app.getExecutionContext();
-    }
-    
-    @Test
-    public void testResolveNull() throws Exception {
-        assertResolvesValue(null, String.class, null);
-    }
-    
-    @Test
-    public void testResolveValueCastsToType() throws Exception {
-        assertResolvesValue(123, String.class, "123");
-    }
-    
-    @Test
-    public void testResolvesAttributeWhenReady() throws Exception {
-        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
-        assertResolvesValue(attributeWhenReady(app, TestApplication.MY_ATTRIBUTE), String.class, "myval");
-    }
-    
-    @Test
-    public void testResolvesMapWithAttributeWhenReady() throws Exception {
-        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
-        Map<?,?> orig = ImmutableMap.of("mykey", attributeWhenReady(app, TestApplication.MY_ATTRIBUTE));
-        Map<?,?> expected = ImmutableMap.of("mykey", "myval");
-        assertResolvesValue(orig, String.class, expected);
-    }
-    
-    @Test
-    public void testResolvesSetWithAttributeWhenReady() throws Exception {
-        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
-        Set<?> orig = ImmutableSet.of(attributeWhenReady(app, TestApplication.MY_ATTRIBUTE));
-        Set<?> expected = ImmutableSet.of("myval");
-        assertResolvesValue(orig, String.class, expected);
-    }
-    
-    @Test
-    public void testResolvesMapOfMapsWithAttributeWhenReady() throws Exception {
-        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
-        Map<?,?> orig = ImmutableMap.of("mykey", ImmutableMap.of("mysubkey", attributeWhenReady(app, TestApplication.MY_ATTRIBUTE)));
-        Map<?,?> expected = ImmutableMap.of("mykey", ImmutableMap.of("mysubkey", "myval"));
-        assertResolvesValue(orig, String.class, expected);
-    }
-    
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testResolvesIterableOfMapsWithAttributeWhenReady() throws Exception {
-        app.setAttribute(TestApplication.MY_ATTRIBUTE, "myval");
-        // using Iterables.concat so that orig is of type FluentIterable rather than List etc
-        Iterable<?> orig = Iterables.concat(ImmutableList.of(ImmutableMap.of("mykey", attributeWhenReady(app, TestApplication.MY_ATTRIBUTE))));
-        Iterable<Map<?,?>> expected = ImmutableList.<Map<?,?>>of(ImmutableMap.of("mykey", "myval"));
-        assertResolvesValue(orig, String.class, expected);
-    }
-    
-    private void assertResolvesValue(Object actual, Class<?> type, Object expected) throws Exception {
-        Object result = Tasks.resolveValue(actual, type, executionContext);
-        assertEquals(result, expected);
-    }
-    
-    @Test
-    public void testErrorsResolvingPropagatesOrSwallowedAllCorrectly() throws Exception {
-        app.setConfig(TestEntity.CONF_OBJECT, ValueResolverTest.newThrowTask(Duration.ZERO));
-        Task<Object> t = Tasks.builder().body(Functionals.callable(EntityFunctions.config(TestEntity.CONF_OBJECT), app)).build();
-        ValueResolver<Object> v = Tasks.resolving(t).as(Object.class).context(app.getExecutionContext());
-        
-        ValueResolverTest.assertThrowsOnMaybe(v);
-        ValueResolverTest.assertThrowsOnGet(v);
-        
-        v.swallowExceptions();
-        ValueResolverTest.assertMaybeIsAbsent(v);
-        ValueResolverTest.assertThrowsOnGet(v);
-        
-        v.defaultValue("foo");
-        ValueResolverTest.assertMaybeIsAbsent(v);
-        assertEquals(v.clone().get(), "foo");
-        assertResolvesValue(v, Object.class, "foo");
-    }
-
-    @Test
-    public void testRepeater() throws Exception {
-        Task<?> t;
-        
-        t = Tasks.requiring(Repeater.create().until(Callables.returning(true)).every(Duration.millis(1))).build();
-        app.getExecutionContext().submit(t);
-        t.get(Duration.TEN_SECONDS);
-        
-        t = Tasks.testing(Repeater.create().until(Callables.returning(true)).every(Duration.millis(1))).build();
-        app.getExecutionContext().submit(t);
-        Assert.assertEquals(t.get(Duration.TEN_SECONDS), true);
-        
-        t = Tasks.requiring(Repeater.create().until(Callables.returning(false)).limitIterationsTo(2).every(Duration.millis(1))).build();
-        app.getExecutionContext().submit(t);
-        try {
-            t.get(Duration.TEN_SECONDS);
-            Assert.fail("Should have failed");
-        } catch (Exception e) {
-            // expected
-        }
-
-        t = Tasks.testing(Repeater.create().until(Callables.returning(false)).limitIterationsTo(2).every(Duration.millis(1))).build();
-        app.getExecutionContext().submit(t);
-        Assert.assertEquals(t.get(Duration.TEN_SECONDS), false);
-    }
-
-    @Test
-    public void testRepeaterDescription() throws Exception{
-        final String description = "task description";
-        Repeater repeater = Repeater.create(description)
-            .repeat(Callables.returning(null))
-            .every(Duration.ONE_MILLISECOND)
-            .limitIterationsTo(1)
-            .until(new Callable<Boolean>() {
-                @Override
-                public Boolean call() {
-                    TaskInternal<?> current = (TaskInternal<?>)Tasks.current();
-                    assertEquals(current.getBlockingDetails(), description);
-                    return true;
-                }
-            });
-        Task<Boolean> t = Tasks.testing(repeater).build();
-        app.getExecutionContext().submit(t);
-        assertTrue(t.get(Duration.TEN_SECONDS));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/task/ValueResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/ValueResolverTest.java b/core/src/test/java/brooklyn/util/task/ValueResolverTest.java
deleted file mode 100644
index d50ff54..0000000
--- a/core/src/test/java/brooklyn/util/task/ValueResolverTest.java
+++ /dev/null
@@ -1,132 +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 java.util.concurrent.Callable;
-
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.Task;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.BrooklynAppUnitTestSupport;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-/**
- * see also {@link TasksTest} for more tests
- */
-@Test
-public class ValueResolverTest extends BrooklynAppUnitTestSupport {
-
-    private ExecutionContext executionContext;
-
-    @BeforeMethod(alwaysRun=true)
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        executionContext = app.getExecutionContext();
-    }
-    
-    public static final Task<String> newSleepTask(final Duration timeout, final String result) {
-        return Tasks.<String>builder().body(new Callable<String>() { 
-            public String call() { 
-                Time.sleep(timeout); 
-                return result; 
-            }}
-        ).build();
-    }
-    
-    public static final Task<String> newThrowTask(final Duration timeout) {
-        return Tasks.<String>builder().body(new Callable<String>() { 
-            public String call() {
-                Time.sleep(timeout); 
-                throw new IllegalStateException("intended, during tests");
-            }}
-        ).build();
-    }
-    
-    public void testTimeoutZero() {
-        Maybe<String> result = Tasks.resolving(newSleepTask(Duration.TEN_SECONDS, "foo")).as(String.class).context(executionContext).timeout(Duration.ZERO).getMaybe();
-        Assert.assertFalse(result.isPresent());
-    }
-    
-    public void testTimeoutBig() {
-        Maybe<String> result = Tasks.resolving(newSleepTask(Duration.ZERO, "foo")).as(String.class).context(executionContext).timeout(Duration.TEN_SECONDS).getMaybe();
-        Assert.assertEquals(result.get(), "foo");
-    }
-
-    public void testNoExecutionContextOnCompleted() {
-        Task<String> t = newSleepTask(Duration.ZERO, "foo");
-        executionContext.submit(t).getUnchecked();
-        Maybe<String> result = Tasks.resolving(t).as(String.class).timeout(Duration.ZERO).getMaybe();
-        Assert.assertEquals(result.get(), "foo");
-    }
-
-    public static Throwable assertThrowsOnMaybe(ValueResolver<?> result) {
-        try {
-            result = result.clone();
-            result.getMaybe();
-            Assert.fail("should have thrown");
-            return null;
-        } catch (Exception e) { return e; }
-    }
-    public static Throwable assertThrowsOnGet(ValueResolver<?> result) {
-        result = result.clone();
-        try {
-            result.get();
-            Assert.fail("should have thrown");
-            return null;
-        } catch (Exception e) { return e; }
-    }
-    public static <T> Maybe<T> assertMaybeIsAbsent(ValueResolver<T> result) {
-        result = result.clone();
-        Maybe<T> maybe = result.getMaybe();
-        Assert.assertFalse(maybe.isPresent());
-        return maybe;
-    }
-    
-    public void testSwallowError() {
-        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(executionContext).swallowExceptions();
-        assertMaybeIsAbsent(result);
-        assertThrowsOnGet(result);
-    }
-
-
-    public void testDontSwallowError() {
-        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(executionContext);
-        assertThrowsOnMaybe(result);
-        assertThrowsOnGet(result);
-    }
-
-    public void testDefaultWhenSwallowError() {
-        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(executionContext).swallowExceptions().defaultValue("foo");
-        assertMaybeIsAbsent(result);
-        Assert.assertEquals(result.get(), "foo");
-    }
-
-    public void testDefaultBeforeDelayAndError() {
-        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.TEN_SECONDS)).as(String.class).context(executionContext).timeout(Duration.ZERO).defaultValue("foo");
-        assertMaybeIsAbsent(result);
-        Assert.assertEquals(result.get(), "foo");
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4c0e5fd/core/src/test/java/brooklyn/util/task/ssh/SshTasksTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/ssh/SshTasksTest.java b/core/src/test/java/brooklyn/util/task/ssh/SshTasksTest.java
deleted file mode 100644
index 578164f..0000000
--- a/core/src/test/java/brooklyn/util/task/ssh/SshTasksTest.java
+++ /dev/null
@@ -1,207 +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.ssh;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.apache.brooklyn.api.location.LocationSpec;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.core.management.internal.LocalManagementContext;
-import org.apache.commons.io.FileUtils;
-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;
-
-import brooklyn.entity.basic.BrooklynConfigKeys;
-import brooklyn.entity.basic.Entities;
-
-import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-
-import brooklyn.util.net.Urls;
-import brooklyn.util.os.Os;
-import brooklyn.util.ssh.BashCommandsIntegrationTest;
-import brooklyn.util.task.system.ProcessTaskFactory;
-import brooklyn.util.task.system.ProcessTaskWrapper;
-
-/**
- * Some tests for {@link SshTasks}. Note more tests in {@link BashCommandsIntegrationTest}, 
- * {@link SshEffectorTasksTest}, and {@link SoftwareEffectorTest}.
- */
-public class SshTasksTest {
-
-    private static final Logger log = LoggerFactory.getLogger(SshTasksTest.class);
-    
-    ManagementContext mgmt;
-    SshMachineLocation host;
-    File tempDir;
-    
-    boolean failureExpected;
-
-    @BeforeMethod(alwaysRun=true)
-    public void setup() throws Exception {
-        mgmt = new LocalManagementContext();
-        
-        LocalhostMachineProvisioningLocation lhc = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
-        host = lhc.obtain();
-        clearExpectedFailure();
-        tempDir = Os.newTempDir(getClass());
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (mgmt != null) Entities.destroyAll(mgmt);
-        mgmt = null;
-        tempDir = Os.deleteRecursively(tempDir).asNullOrThrowing();
-        checkExpectedFailure();
-    }
-
-    protected void checkExpectedFailure() {
-        if (failureExpected) {
-            clearExpectedFailure();
-            Assert.fail("Test should have thrown an exception but it did not.");
-        }
-    }
-    
-    protected void clearExpectedFailure() {
-        failureExpected = false;
-    }
-
-    protected void setExpectingFailure() {
-        failureExpected = true;
-    }
-
-
-    protected <T> ProcessTaskWrapper<T> submit(final ProcessTaskFactory<T> tf) {
-        tf.machine(host);
-        ProcessTaskWrapper<T> t = tf.newTask();
-        mgmt.getExecutionManager().submit(t);
-        return t;
-    }
-
-    protected SshPutTaskWrapper submit(final SshPutTaskFactory tf) {
-        SshPutTaskWrapper t = tf.newTask();
-        mgmt.getExecutionManager().submit(t);
-        return t;
-    }
-
-    @Test(groups="Integration")
-    public void testSshEchoHello() {
-        ProcessTaskWrapper<Integer> t = submit(SshTasks.newSshExecTaskFactory(host, "sleep 1 ; echo hello world"));
-        Assert.assertFalse(t.isDone());
-        Assert.assertEquals(t.get(), (Integer)0);
-        Assert.assertEquals(t.getTask().getUnchecked(), (Integer)0);
-        Assert.assertEquals(t.getStdout().trim(), "hello world");
-    }
-
-    @Test(groups="Integration")
-    public void testCopyTo() throws IOException {
-        String fn = Urls.mergePaths(tempDir.getPath(), "f1");
-        SshPutTaskWrapper t = submit(SshTasks.newSshPutTaskFactory(host, fn).contents("hello world"));
-        t.block();
-        Assert.assertEquals(FileUtils.readFileToString(new File(fn)), "hello world");
-        // and make sure this doesn't throw
-        Assert.assertTrue(t.isDone());
-        Assert.assertTrue(t.isSuccessful());
-        Assert.assertEquals(t.get(), null);
-        Assert.assertEquals(t.getExitCode(), (Integer)0);
-    }
-    
-    @Test(groups="Integration")
-    public void testCopyToFailBadSubdir() throws IOException {
-        String fn = Urls.mergePaths(tempDir.getPath(), "non-existent-subdir/file");
-        SshPutTaskWrapper t = submit(SshTasks.newSshPutTaskFactory(host, fn).contents("hello world"));
-        //this doesn't fail
-        t.block();        
-        Assert.assertTrue(t.isDone());
-        setExpectingFailure();
-        try {
-            // but this does
-            t.get();
-        } catch (Exception e) {
-            log.info("The error if file cannot be written is: "+e);
-            clearExpectedFailure();
-        }
-        checkExpectedFailure();
-        // and the results indicate failure
-        Assert.assertFalse(t.isSuccessful());
-        Assert.assertNotNull(t.getException());
-        Assert.assertNotEquals(t.getExitCode(), (Integer)0);
-    }
-
-    @Test(groups="Integration")
-    public void testCopyToFailBadSubdirAllow() throws IOException {
-        String fn = Urls.mergePaths(tempDir.getPath(), "non-existent-subdir/file");
-        SshPutTaskWrapper t = submit(SshTasks.newSshPutTaskFactory(host, fn).contents("hello world").allowFailure());
-        //this doesn't fail
-        t.block();        
-        Assert.assertTrue(t.isDone());
-        // and this doesn't fail either
-        Assert.assertEquals(t.get(), null);
-        // but it's not successful
-        Assert.assertNotNull(t.getException());
-        Assert.assertFalse(t.isSuccessful());
-        // exit code probably null, but won't be zero
-        Assert.assertNotEquals(t.getExitCode(), (Integer)0);
-    }
-
-    @Test(groups="Integration")
-    public void testCopyToFailBadSubdirCreate() throws IOException {
-        String fn = Urls.mergePaths(tempDir.getPath(), "non-existent-subdir-to-create/file");
-        SshPutTaskWrapper t = submit(SshTasks.newSshPutTaskFactory(host, fn).contents("hello world").createDirectory());
-        t.block();
-        // directory should be created, and file readable now
-        Assert.assertEquals(FileUtils.readFileToString(new File(fn)), "hello world");
-        Assert.assertEquals(t.getExitCode(), (Integer)0);
-    }
-
-    @Test(groups="Integration")
-    public void testSshFetch() throws IOException {
-        String fn = Urls.mergePaths(tempDir.getPath(), "f2");
-        FileUtils.write(new File(fn), "hello fetched world");
-        
-        SshFetchTaskFactory tf = SshTasks.newSshFetchTaskFactory(host, fn);
-        SshFetchTaskWrapper t = tf.newTask();
-        mgmt.getExecutionManager().submit(t);
-
-        t.block();
-        Assert.assertTrue(t.isDone());
-        Assert.assertEquals(t.get(), "hello fetched world");
-        Assert.assertEquals(t.getBytes(), "hello fetched world".getBytes());
-    }
-
-    @Test(groups="Integration")
-    public void testSshWithHeaderProperty() {
-        host.setConfig(BrooklynConfigKeys.SSH_CONFIG_SCRIPT_HEADER, "#!/bin/bash -e\necho foo\n");
-        ProcessTaskWrapper<Integer> t = submit(SshTasks.newSshExecTaskFactory(host, "echo bar"));
-        Assert.assertTrue(t.block().getStdout().trim().matches("foo\\s+bar"), "mismatched output was: "+t.getStdout());
-    }
-
-    @Test(groups="Integration")
-    public void testSshIgnoringHeaderProperty() {
-        host.setConfig(BrooklynConfigKeys.SSH_CONFIG_SCRIPT_HEADER, "#!/bin/bash -e\necho foo\n");
-        ProcessTaskWrapper<Integer> t = submit(SshTasks.newSshExecTaskFactory(host, false, "echo bar"));
-        Assert.assertTrue(t.block().getStdout().trim().matches("bar"), "mismatched output was: "+t.getStdout());
-    }
-
-}