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/15 15:33:03 UTC

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

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 083953f1c -> 6caee5899


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 a14bc22..bfe82ba 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
@@ -50,6 +50,8 @@ import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.management.entitlement.Entitlements.StringAndArgument;
 
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.ConfigKey;
@@ -61,8 +63,6 @@ import brooklyn.entity.basic.BasicApplication;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.trait.Startable;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.entitlement.Entitlements.StringAndArgument;
 import brooklyn.policy.basic.AbstractPolicy;
 
 import org.apache.brooklyn.rest.domain.ApplicationSpec;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 f8b2402..491c1a7 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
@@ -30,10 +30,10 @@ import javax.ws.rs.ext.Provider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.yaml.snakeyaml.error.YAMLException;
-
-import brooklyn.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
 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;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
index 70b72f8..4c3ad5e 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
@@ -23,6 +23,7 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.ManagementContextInjectable;
 import org.codehaus.jackson.Version;
 import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
 import org.codehaus.jackson.map.ObjectMapper;
@@ -36,7 +37,6 @@ import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServiceAttributes;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.management.ManagementContextInjectable;
 
 public class BrooklynJacksonJsonProvider extends JacksonJsonProvider implements ManagementContextInjectable {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
index e4fc53a..5bf673d 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
@@ -33,6 +33,8 @@ import javax.servlet.Filter;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherAbstract;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.servlet.FilterHolder;
@@ -46,8 +48,6 @@ import org.slf4j.LoggerFactory;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.BrooklynServiceAttributes;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
 
 import org.apache.brooklyn.rest.filter.BrooklynPropertiesSecurityFilter;
 import org.apache.brooklyn.rest.filter.HaMasterCheckFilter;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
index 6d6996c..2a2a569 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
@@ -28,9 +28,9 @@ import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.BrooklynServiceAttributes;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.rest.security.provider.AnyoneSecurityProvider;
 
 import brooklyn.util.exceptions.Exceptions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/test/java/org/apache/brooklyn/rest/HaHotCheckTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/HaHotCheckTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/HaHotCheckTest.java
index e0ad096..e51fbbe 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/HaHotCheckTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/HaHotCheckTest.java
@@ -25,13 +25,11 @@ import javax.ws.rs.core.MediaType;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
-
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
-
 import org.apache.brooklyn.api.management.ha.HighAvailabilityManager;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.rest.filter.HaHotCheckResourceFilter;
 import org.apache.brooklyn.rest.filter.HaMasterCheckFilter;
 import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
index de4d778..e60e16a 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
@@ -42,9 +42,9 @@ import org.testng.annotations.Test;
 import org.testng.reporters.Files;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
 
 import brooklyn.catalog.internal.CatalogUtils;
-import brooklyn.management.osgi.OsgiStandaloneTest;
 import brooklyn.policy.autoscaling.AutoScalerPolicy;
 
 import org.apache.brooklyn.rest.domain.CatalogEntitySummary;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 6ab034d..c228b95 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
@@ -33,10 +33,10 @@ import org.eclipse.jetty.server.Server;
 import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
 
 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.rest.BrooklynRestApiLauncher;
 import org.apache.brooklyn.rest.BrooklynRestApiLauncherTestFixture;
 import org.apache.brooklyn.rest.security.provider.TestSecurityProvider;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java
index 8059278..13de9e9 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java
@@ -43,10 +43,10 @@ import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.EmptySoftwareProcess;
 import brooklyn.entity.basic.EmptySoftwareProcessDriver;
 import brooklyn.entity.basic.EmptySoftwareProcessImpl;
-import brooklyn.management.internal.ManagementContextInternal;
 
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.rest.domain.HighAvailabilitySummary;
 import org.apache.brooklyn.rest.domain.VersionSummary;
 import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java
index 42d1717..fd3504d 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java
@@ -42,12 +42,10 @@ import brooklyn.entity.basic.SoftwareProcessEntityTest;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.management.internal.LocalUsageManager;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-
-import brooklyn.management.internal.LocalUsageManager;
-import brooklyn.management.internal.ManagementContextInternal;
-
 import org.apache.brooklyn.rest.domain.ApplicationSpec;
 import org.apache.brooklyn.rest.domain.EntitySpec;
 import org.apache.brooklyn.rest.domain.Status;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
index 9d1e9bd..d6a8829 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
@@ -21,6 +21,8 @@ package org.apache.brooklyn.rest.testing;
 import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
+import org.apache.brooklyn.core.management.ManagementContextInjectable;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeMethod;
@@ -35,10 +37,6 @@ import com.sun.jersey.test.framework.LowLevelAppDescriptor;
 import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.location.basic.BasicLocationRegistry;
-
-import brooklyn.management.ManagementContextInjectable;
-import brooklyn.management.internal.LocalManagementContext;
-
 import org.apache.brooklyn.rest.BrooklynRestApi;
 import org.apache.brooklyn.rest.BrooklynRestApiLauncherTest;
 import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 71e161a..989ea3e 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
@@ -32,6 +32,7 @@ import org.apache.brooklyn.api.entity.Application;
 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.internal.LocalManagementContext;
 
 import brooklyn.catalog.internal.CatalogItemBuilder;
 import brooklyn.catalog.internal.CatalogTemplateItemDto;
@@ -40,7 +41,6 @@ import brooklyn.entity.basic.AbstractApplication;
 import brooklyn.entity.basic.BasicEntity;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.proxying.EntityProxy;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.policy.basic.AbstractPolicy;
 
 import org.apache.brooklyn.rest.domain.ApplicationSpec;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/utils/test-support/src/main/java/org/apache/brooklyn/test/BrooklynLeakListener.java
----------------------------------------------------------------------
diff --git a/utils/test-support/src/main/java/org/apache/brooklyn/test/BrooklynLeakListener.java b/utils/test-support/src/main/java/org/apache/brooklyn/test/BrooklynLeakListener.java
index 7064153..4f95feb 100644
--- a/utils/test-support/src/main/java/org/apache/brooklyn/test/BrooklynLeakListener.java
+++ b/utils/test-support/src/main/java/org/apache/brooklyn/test/BrooklynLeakListener.java
@@ -48,7 +48,7 @@ public class BrooklynLeakListener extends TestListenerAdapter {
      * depend on brooklyn-core, so LocalManagementContext may not be on the classpath.
      */
     private void tryTerminateAll(String context, ITestContext testContext) {
-        String clazzName = "brooklyn.management.internal.LocalManagementContext";
+        String clazzName = "org.apache.brooklyn.core.management.internal.LocalManagementContext";
         String message;
         int level;
         try {


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
index a8a8f5e..da84841 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
@@ -30,10 +30,10 @@ import javax.ws.rs.core.Response;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import brooklyn.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.rest.domain.ApiError;
 import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider;
+
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.net.Urls;
 import brooklyn.util.text.StringEscapes.JavaStringEscapes;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
index e60e16a..83690d8 100644
--- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
@@ -42,9 +42,9 @@ import org.testng.annotations.Test;
 import org.testng.reporters.Files;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
 
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.policy.autoscaling.AutoScalerPolicy;
 
 import org.apache.brooklyn.rest.domain.CatalogEntitySummary;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 989ea3e..484f6ab 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
@@ -32,11 +32,11 @@ import org.apache.brooklyn.api.entity.Application;
 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.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 brooklyn.catalog.internal.CatalogItemBuilder;
-import brooklyn.catalog.internal.CatalogTemplateItemDto;
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.AbstractApplication;
 import brooklyn.entity.basic.BasicEntity;
 import brooklyn.entity.basic.Entities;


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java
new file mode 100644
index 0000000..3914427
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.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.catalog.internal;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import brooklyn.basic.BrooklynObjectInternal;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.entity.rebind.RebindSupport;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.mementos.CatalogItemMemento;
+
+import brooklyn.config.ConfigKey;
+
+import com.google.common.base.Preconditions;
+
+public class CatalogItemDo<T,SpecT> implements CatalogItem<T,SpecT>, BrooklynObjectInternal {
+
+    protected final CatalogDo catalog;
+    protected final CatalogItemDtoAbstract<T,SpecT> itemDto;
+
+    protected volatile Class<T> javaClass; 
+    
+    public CatalogItemDo(CatalogDo catalog, CatalogItem<T,SpecT> itemDto) {
+        this.catalog = Preconditions.checkNotNull(catalog, "catalog");
+        this.itemDto = (CatalogItemDtoAbstract<T, SpecT>) Preconditions.checkNotNull(itemDto, "itemDto");
+    }
+
+    public CatalogItem<T,SpecT> getDto() {
+        return itemDto;
+    }
+    
+    /**
+     * Config not supported for catalog item. See {@link #getPlanYaml()}.
+     */
+    @Override
+    public ConfigurationSupportInternal config() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public <U> U setConfig(ConfigKey<U> key, U val) {
+        return config().set(key, val);
+    }
+
+    @Override
+    public CatalogItemType getCatalogItemType() {
+        return itemDto.getCatalogItemType();
+    }
+
+    @Override
+    public Class<T> getCatalogItemJavaType() {
+        return itemDto.getCatalogItemJavaType();
+    }
+
+    @Override
+    public String getId() {
+        return itemDto.getId();
+    }
+
+    @Override
+    public String getCatalogItemId() {
+        return itemDto.getCatalogItemId();
+    }
+
+    @Override
+    public void setDeprecated(boolean deprecated) {
+        itemDto.setDeprecated(deprecated);
+    }
+
+    @Override
+    public boolean isDeprecated() {
+        return itemDto.isDeprecated();
+    }
+
+    @Override
+    public void setCatalogItemId(String id) {
+        itemDto.setCatalogItemId(id);
+    }
+
+    @Override
+    public String getJavaType() {
+        return itemDto.getJavaType();
+    }
+
+    @Deprecated
+    @Override
+    public String getName() {
+        return getDisplayName();
+    }
+
+    @Deprecated
+    @Override
+    public String getRegisteredTypeName() {
+        return getSymbolicName();
+    }
+
+    @Override
+    public String getDisplayName() {
+        return itemDto.getDisplayName();
+    }
+
+    @Override
+    public TagSupport tags() {
+        return itemDto.tags();
+    }
+
+    @Override
+    public String getDescription() {
+        return itemDto.getDescription();
+    }
+
+    @Override
+    public String getIconUrl() {
+        return itemDto.getIconUrl();
+    }
+    
+    @Override
+    public String getSymbolicName() {
+        return itemDto.getSymbolicName();
+    }
+
+    @Override
+    public String getVersion() {
+        return itemDto.getVersion();
+    }
+
+    @Nonnull  // but it is still null sometimes, see in CatalogDo.loadJavaClass
+    @Override
+    public Collection<CatalogBundle> getLibraries() {
+        return itemDto.getLibraries();
+    }
+
+    /** @deprecated since 0.7.0 this is the legacy mechanism; still needed for policies and apps, but being phased out.
+     * new items should use {@link #getPlanYaml} and {@link #newClassLoadingContext} */
+    @Deprecated
+    public Class<T> getJavaClass() {
+        if (javaClass==null) loadJavaClass(null);
+        return javaClass;
+    }
+    
+    @SuppressWarnings("unchecked")
+    Class<? extends T> loadJavaClass(final ManagementContext mgmt) {
+        if (javaClass!=null) return javaClass;
+        javaClass = (Class<T>)CatalogUtils.newClassLoadingContext(mgmt, getId(), getLibraries(), catalog.getRootClassLoader()).loadClass(getJavaType());
+        return javaClass;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getCanonicalName()+"["+itemDto+"]";
+    }
+
+    @Override
+    public String toXmlString() {
+        return itemDto.toXmlString();
+    }
+
+    @Override
+    public Class<SpecT> getSpecType() {
+        return itemDto.getSpecType();
+    }
+
+    @Nullable @Override
+    public String getPlanYaml() {
+        return itemDto.getPlanYaml();
+    }
+
+    @Override
+    public RebindSupport<CatalogItemMemento> getRebindSupport() {
+        return itemDto.getRebindSupport();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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
new file mode 100644
index 0000000..5aa073b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
@@ -0,0 +1,404 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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 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;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynObject implements CatalogItem<T, SpecT> {
+
+    private static Logger LOG = LoggerFactory.getLogger(CatalogItemDtoAbstract.class);
+
+    private @SetFromFlag String symbolicName;
+    private @SetFromFlag String version = BasicBrooklynCatalog.NO_VERSION;
+
+    private @SetFromFlag String displayName;
+    private @SetFromFlag String description;
+    private @SetFromFlag String iconUrl;
+
+    private @SetFromFlag String javaType;
+    /**@deprecated since 0.7.0, left for deserialization backwards compatibility */
+    private @Deprecated @SetFromFlag String type;
+    private @SetFromFlag String planYaml;
+
+    private @SetFromFlag Collection<CatalogBundle> libraries;
+    private @SetFromFlag Set<Object> tags = Sets.newLinkedHashSet();
+    private @SetFromFlag boolean deprecated;
+
+    /**
+     * Config not supported for catalog item. See {@link #getPlanYaml()}.
+     */
+    @Override
+    public ConfigurationSupportInternal config() {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public <U> U setConfig(ConfigKey<U> key, U val) {
+        return config().set(key, val);
+    }
+    
+    @Override
+    public String getId() {
+        return getCatalogItemId();
+    }
+
+    @Override
+    public String getCatalogItemId() {
+        return CatalogUtils.getVersionedId(getSymbolicName(), getVersion());
+    }
+
+    @Override
+    public String getJavaType() {
+        if (javaType != null) return javaType;
+        return type;
+    }
+
+    @Deprecated
+    public String getName() {
+        return getDisplayName();
+    }
+
+    @Deprecated
+    public String getRegisteredTypeName() {
+        return getSymbolicName();
+    }
+
+    @Override
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public String getIconUrl() {
+        return iconUrl;
+    }
+
+    @Override
+    public String getSymbolicName() {
+        if (symbolicName != null) return symbolicName;
+        return getJavaType();
+    }
+
+    @Override
+    public String getVersion() {
+        // The property is set to NO_VERSION when the object is initialized so it's not supposed to be null ever.
+        // But xstream doesn't call constructors when reading from the catalog.xml file which results in null value
+        // for the version property. That's why we have to fix it in the getter.
+        if (version != null) {
+            return version;
+        } else {
+            return BasicBrooklynCatalog.NO_VERSION;
+        }
+    }
+
+    @Override
+    public boolean isDeprecated() {
+        return deprecated;
+    }
+
+    @Override
+    public void setDeprecated(boolean deprecated) {
+        this.deprecated = deprecated;
+    }
+
+    @Nonnull
+    @Override
+    public Collection<CatalogBundle> getLibraries() {
+        if (libraries != null) {
+            return ImmutableList.copyOf(libraries);
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    @Nullable @Override
+    public String getPlanYaml() {
+        return planYaml;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(symbolicName, planYaml, javaType, nullIfEmpty(libraries), version, getCatalogItemId());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        CatalogItemDtoAbstract<?,?> other = (CatalogItemDtoAbstract<?,?>) obj;
+        if (!Objects.equal(symbolicName, other.symbolicName)) return false;
+        if (!Objects.equal(planYaml, other.planYaml)) return false;
+        if (!Objects.equal(javaType, other.javaType)) return false;
+        if (!Objects.equal(nullIfEmpty(libraries), nullIfEmpty(other.libraries))) return false;
+        if (!Objects.equal(getCatalogItemId(), other.getCatalogItemId())) return false;
+        if (!Objects.equal(version, other.version)) return false;
+        if (!Objects.equal(deprecated, other.deprecated)) return false;
+        if (!Objects.equal(description, other.description)) return false;
+        if (!Objects.equal(displayName, other.displayName)) return false;
+        if (!Objects.equal(iconUrl, other.iconUrl)) return false;
+        if (!Objects.equal(tags, other.tags)) return false;
+        // 'type' not checked, because deprecated, 
+        // and in future we might want to allow it to be removed/blanked in some impls without affecting equality
+        // (in most cases it is the same as symbolicName so doesn't matter)
+        return true;
+    }
+
+    private static <T> Collection<T> nullIfEmpty(Collection<T> coll) {
+        if (coll==null || coll.isEmpty()) return null;
+        return coll;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName()+"["+getId()+"/"+getDisplayName()+"]";
+    }
+
+    public abstract Class<SpecT> getSpecType();
+
+    transient CatalogXmlSerializer serializer;
+
+    @Override
+    public String toXmlString() {
+        if (serializer==null) loadSerializer();
+        return serializer.toString(this);
+    }
+
+    private synchronized void loadSerializer() {
+        if (serializer == null) {
+            serializer = new CatalogXmlSerializer();
+        }
+    }
+
+    @Override
+    public RebindSupport<CatalogItemMemento> getRebindSupport() {
+        return new BasicCatalogItemRebindSupport(this);
+    }
+
+    @Override
+    public void setDisplayName(String newName) {
+        this.displayName = newName;
+    }
+
+    @Override
+    protected AbstractBrooklynObject configure(Map<?, ?> flags) {
+        FlagUtils.setFieldsFromFlags(flags, this);
+        return this;
+    }
+
+    @Override
+    public TagSupport tags() {
+        return new BasicTagSupport();
+    }
+
+    /*
+     * Using a custom tag support class rather than the one in AbstractBrooklynObject because
+     * when XStream unmarshals a catalog item with no tags (e.g. from any catalog.xml file)
+     * super.tags will be null, and any call to getTags throws a NullPointerException on the
+     * synchronized (tags) statement. It can't just be initialised here because super.tags is
+     * final.
+     */
+    private class BasicTagSupport implements TagSupport {
+
+        private void setTagsIfNull() {
+            // Possible if the class was unmarshalled by Xstream with no tags
+            synchronized (CatalogItemDtoAbstract.this) {
+                if (tags == null) {
+                    tags = Sets.newLinkedHashSet();
+                }
+            }
+        }
+
+        @Nonnull
+        @Override
+        public Set<Object> getTags() {
+            synchronized (CatalogItemDtoAbstract.this) {
+                setTagsIfNull();
+                return ImmutableSet.copyOf(tags);
+            }
+        }
+
+        @Override
+        public boolean containsTag(Object tag) {
+            synchronized (CatalogItemDtoAbstract.this) {
+                setTagsIfNull();
+                return tags.contains(tag);
+            }
+        }
+
+        @Override
+        public boolean addTag(Object tag) {
+            boolean result;
+            synchronized (CatalogItemDtoAbstract.this) {
+                setTagsIfNull();
+                result = tags.add(tag);
+            }
+            onTagsChanged();
+            return result;
+        }
+
+        @Override
+        public boolean addTags(Iterable<?> newTags) {
+            boolean result;
+            synchronized (CatalogItemDtoAbstract.this) {
+                setTagsIfNull();
+                result = Iterables.addAll(tags, newTags);
+            }
+            onTagsChanged();
+            return result;
+        }
+
+        @Override
+        public boolean removeTag(Object tag) {
+            boolean result;
+            synchronized (CatalogItemDtoAbstract.this) {
+                setTagsIfNull();
+                result = tags.remove(tag);
+            }
+            onTagsChanged();
+            return result;
+        }
+    }
+
+    @Override
+    @Deprecated
+    public void setCatalogItemId(String id) {
+        //no op, should be used by rebind code only
+    }
+
+    protected void setSymbolicName(String symbolicName) {
+        this.symbolicName = symbolicName;
+    }
+
+    protected void setVersion(String version) {
+        this.version = version;
+    }
+
+    protected void setDescription(String description) {
+        this.description = description;
+    }
+
+    protected void setIconUrl(String iconUrl) {
+        this.iconUrl = iconUrl;
+    }
+
+    protected void setJavaType(String javaType) {
+        this.javaType = javaType;
+        this.type = null;
+    }
+
+    protected void setPlanYaml(String planYaml) {
+        this.planYaml = planYaml;
+    }
+
+    protected void setLibraries(Collection<CatalogBundle> libraries) {
+        this.libraries = libraries;
+    }
+
+    protected void setTags(Set<Object> tags) {
+        this.tags = tags;
+    }
+
+    protected void setSerializer(CatalogXmlSerializer serializer) {
+        this.serializer = serializer;
+    }
+
+    /**
+     * Parses an instance of CatalogLibrariesDto from the given List. Expects the list entries
+     * to be either Strings or Maps of String -> String. Will skip items that are not.
+     */
+    public static Collection<CatalogBundle> parseLibraries(Collection<?> possibleLibraries) {
+        Collection<CatalogBundle> dto = MutableList.of();
+        for (Object object : possibleLibraries) {
+            if (object instanceof Map) {
+                Map<?, ?> entry = (Map<?, ?>) object;
+                String name = stringValOrNull(entry, "name");
+                String version = stringValOrNull(entry, "version");
+                String url = stringValOrNull(entry, "url");
+                dto.add(new CatalogBundleDto(name, version, url));
+            } else if (object instanceof String) {
+                String inlineRef = (String) object;
+
+                final String name;
+                final String version;
+                final String url;
+
+                //Infer reference type (heuristically)
+                if (inlineRef.contains("/") || inlineRef.contains("\\")) {
+                    //looks like an url/file path
+                    name = null;
+                    version = null;
+                    url = inlineRef;
+                } else if (CatalogUtils.looksLikeVersionedId(inlineRef)) {
+                    //looks like a name+version ref
+                    name = CatalogUtils.getIdFromVersionedId(inlineRef);
+                    version = CatalogUtils.getVersionFromVersionedId(inlineRef);
+                    url = null;
+                } else {
+                    //assume it to be relative url
+                    name = null;
+                    version = null;
+                    url = inlineRef;
+                }
+
+                dto.add(new CatalogBundleDto(name, version, url));
+            } else {
+                LOG.debug("Unexpected entry in libraries list neither string nor map: " + object);
+            }
+        }
+        return dto;
+    }
+
+    private static String stringValOrNull(Map<?, ?> map, String key) {
+        Object val = map.get(key);
+        return val != null ? String.valueOf(val) : null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLibrariesDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLibrariesDo.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLibrariesDo.java
new file mode 100644
index 0000000..99041d2
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLibrariesDo.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.catalog.internal;
+
+import java.util.Collection;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+
+import com.google.common.base.Preconditions;
+
+@Deprecated
+public class CatalogLibrariesDo implements CatalogItem.CatalogItemLibraries {
+
+    private final CatalogLibrariesDto librariesDto;
+
+
+    public CatalogLibrariesDo(CatalogLibrariesDto librariesDto) {
+        this.librariesDto = Preconditions.checkNotNull(librariesDto, "librariesDto");
+    }
+
+    @Override
+    public Collection<String> getBundles() {
+        return librariesDto.getBundles();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLibrariesDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLibrariesDto.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLibrariesDto.java
new file mode 100644
index 0000000..6f2aba8
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLibrariesDto.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.catalog.internal;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+
+import com.google.common.collect.ImmutableList;
+
+@Deprecated
+public class CatalogLibrariesDto implements CatalogItem.CatalogItemLibraries {
+
+    private final Collection<String> bundles;
+
+    public CatalogLibrariesDto() {
+        this.bundles = Collections.emptyList();
+    }
+
+    public CatalogLibrariesDto(Collection<String> bundles) {
+        this.bundles = bundles;
+    }
+
+    /**
+     * @return An immutable copy of the bundle URLs referenced by this object
+     */
+    @Override
+    public Collection<String> getBundles() {
+        if (bundles == null) {
+            // can be null on deserialization
+            return Collections.emptyList();
+        }
+        return ImmutableList.copyOf(bundles);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLocationItemDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLocationItemDto.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLocationItemDto.java
new file mode 100644
index 0000000..c8206ce
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogLocationItemDto.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+
+
+public class CatalogLocationItemDto extends CatalogItemDtoAbstract<Location,LocationSpec<?>> {
+    
+    @Override
+    public CatalogItemType getCatalogItemType() {
+        return CatalogItemType.LOCATION;
+    }
+
+    @Override
+    public Class<Location> getCatalogItemJavaType() {
+        return Location.class;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Override
+    public Class<LocationSpec<?>> getSpecType() {
+        return (Class)LocationSpec.class;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogPolicyItemDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogPolicyItemDto.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogPolicyItemDto.java
new file mode 100644
index 0000000..1c7e6fe
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogPolicyItemDto.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.policy.PolicySpec;
+
+
+public class CatalogPolicyItemDto extends CatalogItemDtoAbstract<Policy,PolicySpec<?>> {
+    
+    @Override
+    public CatalogItemType getCatalogItemType() {
+        return CatalogItemType.POLICY;
+    }
+
+    @Override
+    public Class<Policy> getCatalogItemJavaType() {
+        return Policy.class;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Override
+    public Class<PolicySpec<?>> getSpecType() {
+        return (Class)PolicySpec.class;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogTemplateItemDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogTemplateItemDto.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogTemplateItemDto.java
new file mode 100644
index 0000000..229a39c
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogTemplateItemDto.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.catalog.internal;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+
+public class CatalogTemplateItemDto extends CatalogItemDtoAbstract<Application,EntitySpec<? extends Application>> {
+
+    @Override
+    public CatalogItemType getCatalogItemType() {
+        return CatalogItemType.TEMPLATE;
+    }
+
+    @Override
+    public Class<Application> getCatalogItemJavaType() {
+        return Application.class;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Override
+    public Class<EntitySpec<? extends Application>> getSpecType() {
+        return (Class)EntitySpec.class;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
new file mode 100644
index 0000000..378e450
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import java.util.Collection;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.basic.BrooklynObjectInternal;
+
+import org.apache.brooklyn.api.basic.BrooklynObject;
+import org.apache.brooklyn.api.catalog.BrooklynCatalog;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
+import org.apache.brooklyn.core.management.classloading.BrooklynClassLoadingContextSequential;
+import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
+import org.apache.brooklyn.core.management.classloading.OsgiBrooklynClassLoadingContext;
+import org.apache.brooklyn.core.management.ha.OsgiManager;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+
+import brooklyn.config.BrooklynLogging;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.rebind.RebindManagerImpl.RebindTracker;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Time;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Joiner;
+import com.google.common.base.Stopwatch;
+
+public class CatalogUtils {
+    private static final Logger log = LoggerFactory.getLogger(CatalogUtils.class);
+
+    public static final char VERSION_DELIMITER = ':';
+
+    public static BrooklynClassLoadingContext newClassLoadingContext(ManagementContext mgmt, CatalogItem<?, ?> item) {
+        // TODO getLibraries() should never be null but sometimes it is still
+        // e.g. run CatalogResourceTest without the above check
+        if (item.getLibraries() == null) {
+            log.debug("CatalogItemDtoAbstract.getLibraries() is null.", new Exception("Trace for null CatalogItemDtoAbstract.getLibraries()"));
+        }
+        return newClassLoadingContext(mgmt, item.getId(), item.getLibraries());
+    }
+    
+    public static BrooklynClassLoadingContext getClassLoadingContext(Entity entity) {
+        ManagementContext mgmt = ((EntityInternal)entity).getManagementContext();
+        String catId = entity.getCatalogItemId();
+        if (Strings.isBlank(catId)) return JavaBrooklynClassLoadingContext.create(mgmt);
+        CatalogItem<?, ?> cat = getCatalogItemOptionalVersion(mgmt, catId);
+        if (cat==null) {
+            log.warn("Cannot load "+catId+" to get classloader for "+entity+"; will try with standard loader, but might fail subsequently");
+            return JavaBrooklynClassLoadingContext.create(mgmt);
+        }
+        return newClassLoadingContext(mgmt, cat);
+    }
+
+    public static BrooklynClassLoadingContext newClassLoadingContext(@Nullable ManagementContext mgmt, String catalogItemId, Collection<CatalogBundle> libraries) {
+        BrooklynClassLoadingContextSequential result = new BrooklynClassLoadingContextSequential(mgmt);
+
+        if (libraries!=null && !libraries.isEmpty()) {
+            result.add(new OsgiBrooklynClassLoadingContext(mgmt, catalogItemId, libraries));
+        }
+
+        BrooklynClassLoadingContext loader = BrooklynLoaderTracker.getLoader();
+        if (loader != null) {
+            result.add(loader);
+        }
+
+        result.addSecondary(JavaBrooklynClassLoadingContext.create(mgmt));
+        return result;
+    }
+
+    /**
+     * @deprecated since 0.7.0 only for legacy catalog items which provide a non-osgi loader; see {@link #newDefault(ManagementContext)}
+     */ @Deprecated
+    public static BrooklynClassLoadingContext newClassLoadingContext(@Nullable ManagementContext mgmt, String catalogItemId, Collection<CatalogBundle> libraries, ClassLoader customClassLoader) {
+        BrooklynClassLoadingContextSequential result = new BrooklynClassLoadingContextSequential(mgmt);
+
+        if (libraries!=null && !libraries.isEmpty()) {
+            result.add(new OsgiBrooklynClassLoadingContext(mgmt, catalogItemId, libraries));
+        }
+
+        BrooklynClassLoadingContext loader = BrooklynLoaderTracker.getLoader();
+        if (loader != null) {
+            result.add(loader);
+        }
+
+        result.addSecondary(JavaBrooklynClassLoadingContext.create(mgmt, customClassLoader));
+        return result;
+    }
+
+    /**
+     * Registers all bundles with the management context's OSGi framework.
+     */
+    public static void installLibraries(ManagementContext managementContext, @Nullable Collection<CatalogBundle> libraries) {
+        if (libraries == null) return;
+
+        ManagementContextInternal mgmt = (ManagementContextInternal) managementContext;
+        if (!libraries.isEmpty()) {
+            Maybe<OsgiManager> osgi = mgmt.getOsgiManager();
+            if (osgi.isAbsent()) {
+                throw new IllegalStateException("Unable to load bundles "+libraries+" because OSGi is not running.");
+            }
+            if (log.isDebugEnabled()) 
+                logDebugOrTraceIfRebinding(log, 
+                    "Loading bundles in {}: {}", 
+                    new Object[] {managementContext, Joiner.on(", ").join(libraries)});
+            Stopwatch timer = Stopwatch.createStarted();
+            for (CatalogBundle bundleUrl : libraries) {
+                osgi.get().registerBundle(bundleUrl);
+            }
+            if (log.isDebugEnabled()) 
+                logDebugOrTraceIfRebinding(log, 
+                    "Registered {} bundles in {}",
+                    new Object[]{libraries.size(), Time.makeTimeStringRounded(timer)});
+        }
+    }
+
+    /** Scans the given {@link BrooklynClassLoadingContext} to detect what catalog item id is in effect. */
+    public static String getCatalogItemIdFromLoader(BrooklynClassLoadingContext loader) {
+        if (loader instanceof OsgiBrooklynClassLoadingContext) {
+            return ((OsgiBrooklynClassLoadingContext)loader).getCatalogItemId();
+        } else {
+            return null;
+        }
+    }
+
+    public static void setCatalogItemIdOnAddition(Entity entity, BrooklynObject itemBeingAdded) {
+        if (entity.getCatalogItemId()!=null) {
+            if (itemBeingAdded.getCatalogItemId()==null) {
+                if (log.isDebugEnabled())
+                    BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(entity),
+                        "Catalog item addition: "+entity+" from "+entity.getCatalogItemId()+" applying its catalog item ID to "+itemBeingAdded);
+                ((BrooklynObjectInternal)itemBeingAdded).setCatalogItemId(entity.getCatalogItemId());
+            } else {
+                if (!itemBeingAdded.getCatalogItemId().equals(entity.getCatalogItemId())) {
+                    // not a problem, but something to watch out for
+                    log.debug("Cross-catalog item detected: "+entity+" from "+entity.getCatalogItemId()+" has "+itemBeingAdded+" from "+itemBeingAdded.getCatalogItemId());
+                }
+            }
+        } else if (itemBeingAdded.getCatalogItemId()!=null) {
+            if (log.isDebugEnabled())
+                BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(entity),
+                    "Catalog item addition: "+entity+" without catalog item ID has "+itemBeingAdded+" from "+itemBeingAdded.getCatalogItemId());
+        }
+    }
+
+    @Beta
+    public static void logDebugOrTraceIfRebinding(Logger log, String message, Object ...args) {
+        if (RebindTracker.isRebinding())
+            log.trace(message, args);
+        else
+            log.debug(message, args);
+    }
+
+    public static boolean looksLikeVersionedId(String versionedId) {
+        if (versionedId==null) return false;
+        int fi = versionedId.indexOf(VERSION_DELIMITER);
+        if (fi<0) return false;
+        int li = versionedId.lastIndexOf(VERSION_DELIMITER);
+        if (li!=fi) {
+            // if multiple colons, we say it isn't a versioned reference; the prefix in that case must understand any embedded versioning scheme
+            // this fixes the case of:  http://localhost:8080
+            return false;
+        }
+        String candidateVersion = versionedId.substring(li+1);
+        if (!candidateVersion.matches("[0-9]+(|(\\.|_).*)")) {
+            // version must start with a number, followed if by anything with full stop or underscore before any other characters
+            // e.g.  foo:1  or foo:1.1  or foo:1_SNAPSHOT all supported, but not e.g. foo:bar (or chef:cookbook or docker:my/image)
+            return false;
+        }
+        return true;
+    }
+
+    public static String getIdFromVersionedId(String versionedId) {
+        if (versionedId == null) return null;
+        int versionDelimiterPos = versionedId.lastIndexOf(VERSION_DELIMITER);
+        if (versionDelimiterPos != -1) {
+            return versionedId.substring(0, versionDelimiterPos);
+        } else {
+            return null;
+        }
+    }
+
+    public static String getVersionFromVersionedId(String versionedId) {
+        if (versionedId == null) return null;
+        int versionDelimiterPos = versionedId.lastIndexOf(VERSION_DELIMITER);
+        if (versionDelimiterPos != -1) {
+            return versionedId.substring(versionDelimiterPos+1);
+        } else {
+            return null;
+        }
+    }
+
+    public static String getVersionedId(String id, String version) {
+        // TODO null checks
+        return id + VERSION_DELIMITER + version;
+    }
+
+    //TODO Don't really like this, but it's better to have it here than on the interface to keep the API's 
+    //surface minimal. Could instead have the interface methods accept VerionedId object and have the helpers
+    //construct it as needed.
+    public static CatalogItem<?, ?> getCatalogItemOptionalVersion(ManagementContext mgmt, String versionedId) {
+        if (versionedId == null) return null;
+        if (looksLikeVersionedId(versionedId)) {
+            String id = getIdFromVersionedId(versionedId);
+            String version = getVersionFromVersionedId(versionedId);
+            return mgmt.getCatalog().getCatalogItem(id, version);
+        } else {
+            return mgmt.getCatalog().getCatalogItem(versionedId, BrooklynCatalog.DEFAULT_VERSION);
+        }
+    }
+
+    public static boolean isBestVersion(ManagementContext mgmt, CatalogItem<?,?> item) {
+        CatalogItem<?, ?> bestVersion = getCatalogItemOptionalVersion(mgmt, item.getSymbolicName());
+        if (bestVersion==null) return false;
+        return (bestVersion.getVersion().equals(item.getVersion()));
+    }
+
+    public static <T,SpecT> CatalogItem<T, SpecT> getCatalogItemOptionalVersion(ManagementContext mgmt, Class<T> type, String versionedId) {
+        if (looksLikeVersionedId(versionedId)) {
+            String id = getIdFromVersionedId(versionedId);
+            String version = getVersionFromVersionedId(versionedId);
+            return mgmt.getCatalog().getCatalogItem(type, id, version);
+        } else {
+            return mgmt.getCatalog().getCatalogItem(type, versionedId, BrooklynCatalog.DEFAULT_VERSION);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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
new file mode 100644
index 0000000..462e00e
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogXmlSerializer.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.catalog.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
+
+import brooklyn.basic.AbstractBrooklynObject;
+import brooklyn.util.xstream.EnumCaseForgivingSingleValueConverter;
+import brooklyn.util.xstream.XmlSerializer;
+
+public class CatalogXmlSerializer extends XmlSerializer<Object> {
+
+    @SuppressWarnings("deprecation")
+    public CatalogXmlSerializer() {
+        xstream.addDefaultImplementation(ArrayList.class, Collection.class);
+        
+        xstream.aliasType("list", List.class);
+        xstream.aliasType("map", Map.class);
+
+        xstream.useAttributeFor("id", String.class);
+
+        xstream.aliasType("catalog", CatalogDto.class);
+        xstream.useAttributeFor(CatalogDto.class, "url");
+        xstream.addImplicitCollection(CatalogDto.class, "catalogs", CatalogDto.class);
+        xstream.addImplicitCollection(CatalogDto.class, "entries", CatalogTemplateItemDto.class);
+        xstream.addImplicitCollection(CatalogDto.class, "entries", CatalogEntityItemDto.class);
+        xstream.addImplicitCollection(CatalogDto.class, "entries", CatalogPolicyItemDto.class);
+        xstream.addImplicitCollection(CatalogDto.class, "entries", CatalogLocationItemDto.class);
+
+        xstream.aliasType("template", CatalogTemplateItemDto.class);
+        xstream.aliasType("entity", CatalogEntityItemDto.class);
+        xstream.aliasType("policy", CatalogPolicyItemDto.class);
+        xstream.aliasType("location", CatalogPolicyItemDto.class);
+
+        xstream.aliasField("registeredType", CatalogItemDtoAbstract.class, "symbolicName");
+        xstream.aliasAttribute(CatalogItemDtoAbstract.class, "displayName", "name");
+        xstream.useAttributeFor(CatalogItemDtoAbstract.class, "type");
+        xstream.useAttributeFor(CatalogItemDtoAbstract.class, "version");
+        xstream.aliasType("bundle", CatalogBundleDto.class);
+        xstream.registerConverter(new CatalogBundleConverter(xstream.getMapper(), xstream.getReflectionProvider()));
+
+        xstream.useAttributeFor(CatalogClasspathDto.class, "scan");
+        xstream.addImplicitCollection(CatalogClasspathDto.class, "entries", "entry", String.class);
+        xstream.registerConverter(new EnumCaseForgivingSingleValueConverter(CatalogScanningModes.class));
+
+        // Note: the management context is being omitted because it is unnecessary for
+        // representations of catalogues generated with this serializer.
+        xstream.omitField(AbstractBrooklynObject.class, "managementContext");
+        xstream.omitField(AbstractBrooklynObject.class, "_legacyConstruction");
+        xstream.omitField(AbstractBrooklynObject.class, "hasWarnedOfNoManagementContextWhenPersistRequested");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/management/classloading/OsgiBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/classloading/OsgiBrooklynClassLoadingContext.java b/core/src/main/java/org/apache/brooklyn/core/management/classloading/OsgiBrooklynClassLoadingContext.java
index af5920b..84baa20 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/classloading/OsgiBrooklynClassLoadingContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/classloading/OsgiBrooklynClassLoadingContext.java
@@ -26,11 +26,11 @@ import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.entitlement.EntitlementClass;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
 import org.apache.brooklyn.core.management.ha.OsgiManager;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.util.guava.Maybe;
 
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 25d8281..bda7f6f 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
@@ -42,6 +42,8 @@ import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
 import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
 import org.apache.brooklyn.api.management.ha.MementoCopyMode;
 import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister.Delta;
+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.ha.BasicMasterChooser.AlphabeticMasterChooser;
 import org.apache.brooklyn.core.management.internal.BrooklynObjectManagementMode;
@@ -53,8 +55,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.BrooklynVersion;
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
-import brooklyn.catalog.internal.CatalogDto;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynTaskTags;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 1758846..58e2f60 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
@@ -50,13 +50,13 @@ import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
 import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityManager;
+import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
+import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
+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 brooklyn.catalog.internal.BasicBrooklynCatalog;
-import brooklyn.catalog.internal.CatalogInitialization;
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.StringConfigMap;
 import brooklyn.entity.basic.AbstractEntity;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 97a3d73..6c993d8 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
@@ -29,9 +29,9 @@ 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.core.catalog.internal.CatalogInitialization;
 import org.apache.brooklyn.core.management.ha.OsgiManager;
 
-import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.proxying.InternalEntityFactory;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentManagementContext.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentManagementContext.java
index 9d98792..693e254 100644
--- a/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentManagementContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentManagementContext.java
@@ -59,10 +59,10 @@ import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
 import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
 import org.apache.brooklyn.api.mementos.BrooklynMementoPersister;
 import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
+import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
 import org.apache.brooklyn.core.management.ha.OsgiManager;
 
-import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.StringConfigMap;
 import brooklyn.entity.basic.AbstractEntity;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 9a6466e..3cb9f14 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
@@ -40,9 +40,9 @@ 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.catalog.CatalogPredicates;
 import org.apache.brooklyn.core.management.internal.LocalLocationManager;
 
-import brooklyn.catalog.CatalogPredicates;
 import brooklyn.config.ConfigMap;
 import brooklyn.config.ConfigPredicates;
 import brooklyn.config.ConfigUtils;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/location/basic/CatalogLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/CatalogLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/CatalogLocationResolver.java
index 0866649..55902ee 100644
--- a/core/src/main/java/org/apache/brooklyn/location/basic/CatalogLocationResolver.java
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/CatalogLocationResolver.java
@@ -30,8 +30,7 @@ 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 brooklyn.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 
 /**
  * Given a location spec in the form {@code brooklyn.catalog:<symbolicName>:<version>}, 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/brooklyn/BrooklynVersionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/BrooklynVersionTest.java b/core/src/test/java/brooklyn/BrooklynVersionTest.java
index 179e698..ebcfbd9 100644
--- a/core/src/test/java/brooklyn/BrooklynVersionTest.java
+++ b/core/src/test/java/brooklyn/BrooklynVersionTest.java
@@ -34,10 +34,9 @@ import org.testng.annotations.Test;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
-import brooklyn.catalog.internal.CatalogEntityItemDto;
-import brooklyn.catalog.internal.CatalogItemBuilder;
-import brooklyn.catalog.internal.CatalogItemDtoAbstract;
-
+import org.apache.brooklyn.core.catalog.internal.CatalogEntityItemDto;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemDtoAbstract;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.core.management.osgi.OsgiTestResources;
 import org.apache.brooklyn.test.TestResourceUnavailableException;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 73cf5ce..d5590fc 100644
--- a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
+++ b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
@@ -49,13 +49,13 @@ import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
 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.catalog.CatalogPredicates;
+import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
+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 brooklyn.catalog.CatalogPredicates;
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
-import brooklyn.catalog.internal.CatalogDto;
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
deleted file mode 100644
index 25a050e..0000000
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
+++ /dev/null
@@ -1,149 +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.catalog.internal;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-
-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 org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
-import org.apache.brooklyn.core.management.internal.LocalManagementContext;
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-import org.apache.brooklyn.test.entity.TestApplication;
-import org.apache.brooklyn.test.entity.TestEntity;
-
-import brooklyn.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
-import brooklyn.entity.basic.Entities;
-import brooklyn.util.BrooklynMavenArtifacts;
-import brooklyn.util.maven.MavenRetriever;
-
-import com.google.common.collect.ImmutableList;
-
-public class CatalogDtoTest {
-
-    private static final Logger log = LoggerFactory.getLogger(CatalogDtoTest.class);
-
-    private LocalManagementContext managementContext;
-    
-    @BeforeMethod(alwaysRun = true)
-    public void setUp() throws Exception {
-        managementContext = LocalManagementContextForTests.newInstanceWithOsgi();
-    }
-    
-    @AfterMethod(alwaysRun = true)
-    public void tearDown() throws Exception {
-        if (managementContext != null) Entities.destroyAll(managementContext);
-    }
-
-    @Test(groups="Integration")
-    public void testCatalogLookup() {
-        CatalogDto root = buildExampleCatalog();
-        checkCatalogHealthy(root);
-    }
-    
-    @Test(groups="Integration")
-    public void testCatalogSerializeAndLookup() {
-        CatalogDto root = buildExampleCatalog();
-        CatalogXmlSerializer serializer = new CatalogXmlSerializer();
-        
-        String xml = serializer.toString(root);
-        log.info("Example catalog serialized as:\n"+xml);
-        
-        CatalogDto root2 = (CatalogDto) serializer.fromString(xml);
-        checkCatalogHealthy(root2);
-    }
-
-    protected void checkCatalogHealthy(CatalogDto root) {
-        assertEquals(root.catalogs.size(), 4);
-        CatalogDo loader = new CatalogDo(managementContext, root).load();
-        
-        // test app comes from jar, by default
-        CatalogItemDo<?,?> worker = loader.getIdCache().get(CatalogUtils.getVersionedId(TestApplication.class.getCanonicalName(), BasicBrooklynCatalog.NO_VERSION));
-        assertNotNull(worker);
-        assertEquals(worker.getDisplayName(), "Test App from JAR");
-        
-        // TODO can test scanned elements, links to other catalogs, etc
-    }
-
-    public CatalogDto buildExampleCatalog() {
-        CatalogDo root = new CatalogDo(
-                managementContext,
-                CatalogDto.newNamedInstance("My Local Catalog",
-                        "My favourite local settings, including remote catalogs -- intended partly as a teaching " +
-                        "example for what can be expressed, and how", "contents-built-in-test"));
-        root.setClasspathScanForEntities(CatalogScanningModes.NONE);
-
-        String bundleUrl = MavenRetriever.localUrl(BrooklynMavenArtifacts.artifact("", "brooklyn-core", "jar", "tests"));
-        CatalogDo testEntitiesJavaCatalog = new CatalogDo(
-                managementContext,
-                CatalogDto.newNamedInstance("Test Entities from Java", null, "test-java"));
-        testEntitiesJavaCatalog.setClasspathScanForEntities(CatalogScanningModes.NONE);
-        testEntitiesJavaCatalog.addToClasspath(bundleUrl);
-        testEntitiesJavaCatalog.addEntry(CatalogItemBuilder.newTemplate(TestApplication.class.getCanonicalName(), BasicBrooklynCatalog.NO_VERSION)
-                .displayName("Test App from JAR")
-                .javaType(TestApplication.class.getCanonicalName())
-                .build());
-        testEntitiesJavaCatalog.addEntry(CatalogItemBuilder.newEntity(TestEntity.class.getCanonicalName(), BasicBrooklynCatalog.NO_VERSION)
-                .displayName("Test Entity from JAR")
-                .javaType(TestEntity.class.getCanonicalName())
-                .build());
-        root.addCatalog(testEntitiesJavaCatalog.dto);
-
-        CatalogDo testEntitiesJavaCatalogScanning = new CatalogDo(
-                managementContext,
-                CatalogDto.newNamedInstance("Test Entities from Java Scanning", null, "test-java-scan"));
-        testEntitiesJavaCatalogScanning.addToClasspath(bundleUrl);
-        testEntitiesJavaCatalogScanning.setClasspathScanForEntities(CatalogScanningModes.ANNOTATIONS);
-        root.addCatalog(testEntitiesJavaCatalogScanning.dto);
-
-        CatalogDo osgiCatalog = new CatalogDo(
-                managementContext,
-                CatalogDto.newNamedInstance("Test Entities from OSGi",
-                        "A catalog whose entries define their libraries as a list of OSGi bundles", "test-osgi-defined"));
-        osgiCatalog.setClasspathScanForEntities(CatalogScanningModes.NONE);
-        CatalogEntityItemDto osgiEntity = CatalogItemBuilder.newEntity(TestEntity.class.getCanonicalName(), "Test Entity from OSGi")
-                // NB: this is not actually an OSGi bundle, but it's okay as we don't instantiate the bundles ahead of time (currently)
-                .libraries(ImmutableList.<CatalogBundle>of(new CatalogBundleDto(null, null, bundleUrl)))
-                .build();
-        testEntitiesJavaCatalog.addEntry(osgiEntity);
-        root.addCatalog(osgiCatalog.dto);
-
-        root.addCatalog(CatalogDto.newLinkedInstance("classpath://brooklyn-catalog-empty.xml"));
-        return root.dto;
-    }
-    
-    @Test
-    public void testVersionedIdSplitter() {
-        String id = "simple.id";
-        String version = "0.1.2";
-        String versionedId = CatalogUtils.getVersionedId(id, version);
-        
-        Assert.assertNull(CatalogUtils.getIdFromVersionedId(null));
-        Assert.assertNull(CatalogUtils.getVersionFromVersionedId(null));
-        Assert.assertNull(CatalogUtils.getIdFromVersionedId(id));
-        Assert.assertNull(CatalogUtils.getVersionFromVersionedId(version));
-        Assert.assertEquals(CatalogUtils.getIdFromVersionedId(versionedId), id);
-        Assert.assertEquals(CatalogUtils.getVersionFromVersionedId(versionedId), version);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/brooklyn/catalog/internal/CatalogItemComparatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogItemComparatorTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogItemComparatorTest.java
deleted file mode 100644
index 3e6fe6b..0000000
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogItemComparatorTest.java
+++ /dev/null
@@ -1,80 +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.catalog.internal;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
-import org.testng.annotations.Test;
-import org.apache.brooklyn.api.catalog.CatalogItem;
-
-public class CatalogItemComparatorTest {
-    private static final String RC2 = "10.5.8-rc2";
-    private static final String STABLE = "10.5.8";
-
-    @Test
-    public void testComparison() {
-        compare("0.0.1", "0.0.2", 1);
-        compare("0.0.2", "0.0.1", -1);
-        compare("0.0.1-qual", "0.0.2", 1);
-        compare("0.0.1.qual", "0.0.2", 1);
-        compare("0.0.1-qual", "0.0.1_qual", 0);
-        compare("0.0.1.qual", "0.0.1.qual", 0);
-        compare("0.0.1", "0.0.2-SNAPSHOT", -1);
-        compare("0.0.1", "0.0.2.SNAPSHOT", -1);
-        compare("0.0.0_SNAPSHOT", "0.0.1-SNAPSHOT-20141111114709760", 1);
-        compare("0.0.0.SNAPSHOT", "0.0.1.SNAPSHOT-20141111114709760", 1);
-        compare("2.0", "2.0.1-BUILD", 1);
-        compare("2.0", "2.0.1.BUILD", 1);
-        compare("2.0.1", "2.0-BUILD", -1);
-        compare("2.0.1", "2.0.0.BUILD", -1);
-        compare("2.0", "2.0-BUILD", -1);
-        // Note not true for .qualifier: compare("2.0", "2.0.0.BUILD", -1);
-        compare("2.1", "2.0-BUILD", -1);
-        compare("2.1", "2.0.0.BUILD", -1);
-        compare("1", "1.3", 1);
-        compare("1-beta", "1-rc2", 1);
-        // Note not true for .qualifier: compare("1.0.0.beta", "1.0.0.rc2", 1);
-        compare("1-beta1", "1-beta10", 1);
-
-        compare(STABLE, "10.5", -1);
-        compare(STABLE, STABLE, 0);
-
-        compare(STABLE, "10.6", 1);
-        compare(STABLE, "10.5.8.1", 1);
-
-        compare("10.5.8-rc2", "10.5.8-rc3", 1) ;
-        compare("10.5.8-rc2", "10.5.8-rc1", -1);
-
-        compare(STABLE, RC2, -1);
-
-        CatalogItemComparator cmp = CatalogItemComparator.INSTANCE;
-        assertTrue(cmp.compare(v(RC2), v("10.5.8-beta1")) == cmp.compare(v(RC2), v("10.5.8-beta3")));
-    }
-    
-    private void compare(String v1, String v2, int expected) {
-        CatalogItemComparator cmp = CatalogItemComparator.INSTANCE;
-        assertEquals(cmp.compare(v(v1), v(v2)), expected);
-        assertEquals(cmp.compare(v(v2), v(v1)), -expected);
-    }
-    
-    private CatalogItem<?, ?> v(String version) {
-        return CatalogItemBuilder.newEntity("xxx", version).build();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java
deleted file mode 100644
index 2e92897..0000000
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.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.catalog.internal;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
-
-import brooklyn.util.ResourceUtils;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.Iterables;
-
-public class CatalogLoadTest {
-
-    CatalogXmlSerializer serializer;
-
-    @BeforeMethod
-    public void setUp() {
-        serializer = new CatalogXmlSerializer();
-    }
-
-    private String loadFile(String file) {
-        return ResourceUtils.create(this).getResourceAsString(file);
-    }
-
-    // CAMP YAML parsing not available in core, so YAML catalog tests are in camp, e.g. CatalogYamlEntitiesTest 
-    
-    @Test
-    public void testLoadXmlCatalog() {
-        CatalogDto catalog = (CatalogDto) serializer.fromString(
-                loadFile("classpath://brooklyn/catalog/internal/osgi-catalog.xml"));
-        assertNotNull(catalog);
-        assertEquals(catalog.name, "OSGi catalogue");
-        assertEquals(Iterables.size(catalog.getUniqueEntries()), 1, "Catalog entries = " + Joiner.on(", ").join(catalog.getUniqueEntries()));
-
-        CatalogItemDtoAbstract<?,?> template = Iterables.getOnlyElement(catalog.getUniqueEntries());
-        assertEquals(template.getDisplayName(), "Entity name");
-        assertEquals(template.getVersion(), "9.1.3");
-        assertEquals(template.getJavaType(), "com.example.ExampleApp");
-        assertEquals(template.getLibraries().size(), 2,
-                "Template bundles=" + Joiner.on(", ").join(template.getLibraries()));
-        
-        boolean foundBundle1 = false, foundBundle2 = false;
-        for (CatalogBundle bundle : template.getLibraries()) {
-            if (bundle.getUrl().equals("file://path/to/bundle.jar")) {
-                foundBundle1 = true;
-            }
-            if (bundle.getUrl().equals("http://www.url.com/for/bundle.jar")) {
-                foundBundle2 = true;
-            }
-        }
-        assertTrue(foundBundle1);
-        assertTrue(foundBundle2);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
deleted file mode 100644
index c417421..0000000
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
+++ /dev/null
@@ -1,199 +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.catalog.internal;
-
-import java.net.URLEncoder;
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.Test;
-import org.apache.brooklyn.api.catalog.BrooklynCatalog;
-import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.proxying.EntitySpec;
-import org.apache.brooklyn.core.management.internal.LocalManagementContext;
-
-import brooklyn.catalog.CatalogPredicates;
-import brooklyn.catalog.internal.MyCatalogItems.MySillyAppTemplate;
-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;
-
-import com.google.common.base.Predicates;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
-public class CatalogScanTest {
-
-    private static final Logger log = LoggerFactory.getLogger(CatalogScanTest.class);
-
-    private BrooklynCatalog defaultCatalog, annotsCatalog, fullCatalog;
-    
-    private List<LocalManagementContext> managementContexts = Lists.newCopyOnWriteArrayList();
-
-    @AfterMethod(alwaysRun = true)
-    public void tearDown(){
-        for (LocalManagementContext managementContext : managementContexts) {
-            Entities.destroyAll(managementContext);
-        }
-        managementContexts.clear();
-    }
-    
-    private LocalManagementContext newManagementContext(BrooklynProperties props) {
-        LocalManagementContext result = new LocalManagementContext(props);
-        managementContexts.add(result);
-        return result;
-    }
-    
-    private synchronized void loadFullCatalog() {
-        if (fullCatalog!=null) return;
-        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
-        props.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL.getName(), 
-                "data:,"+Urls.encode("<catalog><classpath scan=\"types\"/></catalog>"));
-        fullCatalog = newManagementContext(props).getCatalog();        
-        log.info("ENTITIES loaded for FULL: "+fullCatalog.getCatalogItems(Predicates.alwaysTrue()));
-    }
-    
-    private synchronized void loadTheDefaultCatalog(boolean lookOnDiskForDefaultCatalog) {
-        if (defaultCatalog!=null) return;
-        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
-        props.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL.getName(),
-            // if default catalog is picked up from the system, we might get random stuff from ~/.brooklyn/ instead of the default;
-            // useful as an integration check that we default correctly, but irritating for people to use if they have such a catalog installed
-            (lookOnDiskForDefaultCatalog ? "" :
-                "data:,"+Urls.encode(new ResourceUtils(this).getResourceAsString("classpath:/brooklyn/default.catalog.bom"))));
-        LocalManagementContext managementContext = newManagementContext(props);
-        defaultCatalog = managementContext.getCatalog();        
-        log.info("ENTITIES loaded for DEFAULT: "+defaultCatalog.getCatalogItems(Predicates.alwaysTrue()));
-    }
-    
-    @SuppressWarnings("deprecation")
-    private synchronized void loadAnnotationsOnlyCatalog() {
-        if (annotsCatalog!=null) return;
-        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
-        props.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL.getName(),
-                "data:,"+URLEncoder.encode("<catalog><classpath scan=\"annotations\"/></catalog>"));
-        LocalManagementContext managementContext = newManagementContext(props);
-        annotsCatalog = managementContext.getCatalog();
-        log.info("ENTITIES loaded with annotation: "+annotsCatalog.getCatalogItems(Predicates.alwaysTrue()));
-    }
-    
-    @Test
-    public void testLoadAnnotations() {
-        loadAnnotationsOnlyCatalog();
-        BrooklynCatalog c = annotsCatalog;
-        
-        Iterable<CatalogItem<Object,Object>> bases = c.getCatalogItems(CatalogPredicates.displayName(Predicates.containsPattern("MyBaseEntity")));
-        Assert.assertEquals(Iterables.size(bases), 0, "should have been empty: "+bases);
-        
-        Iterable<CatalogItem<Object,Object>> asdfjkls = c.getCatalogItems(CatalogPredicates.displayName(Predicates.containsPattern("__asdfjkls__shouldnotbefound")));
-        Assert.assertEquals(Iterables.size(asdfjkls), 0);
-        
-        Iterable<CatalogItem<Object,Object>> silly1 = c.getCatalogItems(CatalogPredicates.displayName(Predicates.equalTo("MySillyAppTemplate")));
-        Iterable<CatalogItem<Object,Object>> silly2 = c.getCatalogItems(CatalogPredicates.javaType(Predicates.equalTo(MySillyAppTemplate.class.getName())));
-        CatalogItem<Object, Object> silly1El = Iterables.getOnlyElement(silly1);
-        Assert.assertEquals(silly1El, Iterables.getOnlyElement(silly2));
-        
-        CatalogItem<Application,EntitySpec<? extends Application>> s1 = c.getCatalogItem(Application.class, silly1El.getSymbolicName(), silly1El.getVersion());
-        Assert.assertEquals(s1, silly1El);
-        
-        Assert.assertEquals(s1.getDescription(), "Some silly app test");
-        
-        Class<? extends Application> app = c.loadClass(s1);
-        Assert.assertEquals(MySillyAppTemplate.class, app);
-        
-        String xml = ((BasicBrooklynCatalog)c).toXmlString();
-        log.info("Catalog is:\n"+xml);
-        Assert.assertTrue(xml.indexOf("Some silly app test") >= 0);
-    }
-
-    @Test
-    public void testAnnotationLoadsSomeApps() {
-        loadAnnotationsOnlyCatalog();
-        Iterable<CatalogItem<Object,Object>> silly1 = annotsCatalog.getCatalogItems(CatalogPredicates.displayName(Predicates.equalTo("MySillyAppTemplate")));
-        Assert.assertEquals(Iterables.getOnlyElement(silly1).getDescription(), "Some silly app test");
-    }
-    
-    @Test
-    public void testAnnotationLoadsSomeAppBuilders() {
-        loadAnnotationsOnlyCatalog();
-        Iterable<CatalogItem<Object,Object>> silly1 = annotsCatalog.getCatalogItems(CatalogPredicates.displayName(Predicates.equalTo("MySillyAppBuilderTemplate")));
-        Assert.assertEquals(Iterables.getOnlyElement(silly1).getDescription(), "Some silly app builder test");
-    }
-    
-    @Test
-    public void testMoreTypesThanAnnotations() {
-        loadAnnotationsOnlyCatalog();
-        loadFullCatalog();
-        
-        int numFromAnnots = Iterables.size(annotsCatalog.getCatalogItems(Predicates.alwaysTrue()));
-        int numFromTypes = Iterables.size(fullCatalog.getCatalogItems(Predicates.alwaysTrue()));
-        
-        Assert.assertTrue(numFromAnnots < numFromTypes, "full="+numFromTypes+" annots="+numFromAnnots);
-    }
-    
-    @Test
-    public void testMoreTypesThanAnnotationsForApps() {
-        loadAnnotationsOnlyCatalog();
-        loadFullCatalog();
-        
-        int numFromAnnots = Iterables.size(annotsCatalog.getCatalogItems(CatalogPredicates.IS_TEMPLATE));
-        int numFromTypes = Iterables.size(fullCatalog.getCatalogItems(CatalogPredicates.IS_TEMPLATE));
-        
-        Assert.assertTrue(numFromAnnots < numFromTypes, "full="+numFromTypes+" annots="+numFromAnnots);
-    }
-    
-    @Test
-    public void testAnnotationIsDefault() {
-        doTestAnnotationIsDefault(false);
-    }
-    
-    // see comment in load method; likely fails if a custom catalog is installed in ~/.brooklyn/
-    @Test(groups="Integration", enabled=false)
-    public void testAnnotationIsDefaultOnDisk() {
-        doTestAnnotationIsDefault(true);
-    }
-
-    private void doTestAnnotationIsDefault(boolean lookOnDiskForDefaultCatalog) {
-        loadTheDefaultCatalog(false);
-        int numInDefault = Iterables.size(defaultCatalog.getCatalogItems(Predicates.alwaysTrue()));
-        
-        loadAnnotationsOnlyCatalog();
-        int numFromAnnots = Iterables.size(annotsCatalog.getCatalogItems(Predicates.alwaysTrue()));
-        
-        Assert.assertEquals(numInDefault, numFromAnnots);
-        Assert.assertTrue(numInDefault>0, "Expected more than 0 entries");
-    }
-
-    // a simple test asserting no errors when listing the real catalog, and listing them for reference
-    // also useful to test variants in a stored catalog to assert they all load
-    // TODO integration tests which build up catalogs assuming other things are installed
-    @Test
-    public void testListCurrentCatalogItems() {
-        LocalManagementContext mgmt = newManagementContext(BrooklynProperties.Factory.newDefault());
-        log.info("ITEMS\n"+Strings.join(mgmt.getCatalog().getCatalogItems(), "\n"));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/brooklyn/catalog/internal/CatalogTestUtils.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogTestUtils.java b/core/src/test/java/brooklyn/catalog/internal/CatalogTestUtils.java
deleted file mode 100644
index b420860..0000000
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogTestUtils.java
+++ /dev/null
@@ -1,46 +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.catalog.internal;
-
-import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.api.entity.proxying.EntitySpec;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
-
-import com.google.common.annotations.Beta;
-
-public class CatalogTestUtils {
-
-    /** creates entity spec with the java type of a catalog item;
-     * would be nice to have this in {@link CatalogUtils},
-     * but the logic for parsing the yaml is buried in camp code,
-     * so it's a little bit hard to make this a first class method.
-     * <p>
-     * (this impl ignores many things, including config and location.)
-     */
-    @Beta
-    public static EntitySpec<?> createEssentialEntitySpec(ManagementContext mgmt, CatalogItem<?, ?> catalogItem) {
-        BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, catalogItem);
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        EntitySpec<?> spec = EntitySpec.create( (Class)loader.loadClass(catalogItem.getJavaType()) );
-        spec.catalogItemId(catalogItem.getId());
-        return spec;
-    }
-
-}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.java b/core/src/main/java/org/apache/brooklyn/core/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
new file mode 100644
index 0000000..33aa4ca
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.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.management.classloading;
+
+import java.net.URL;
+
+import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
+
+public class ClassLoaderFromBrooklynClassLoadingContext extends ClassLoader {
+
+    /** Constructs a {@link ClassLoader} which delegates to the given {@link BrooklynClassLoadingContext} */
+    public static ClassLoader of(BrooklynClassLoadingContext clc) {
+        return new ClassLoaderFromBrooklynClassLoadingContext(clc);
+    }
+    
+    protected final BrooklynClassLoadingContext clc;
+
+    protected ClassLoaderFromBrooklynClassLoadingContext(BrooklynClassLoadingContext clc) {
+        this.clc = clc;
+    }
+    
+    @Override
+    public Class<?> findClass(String className) throws ClassNotFoundException {
+        Class<?> result = clc.loadClass(className);
+        if (result!=null) return result;
+        
+        // last resort. see comment in XStream CompositeClassLoader
+        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+        if (contextClassLoader != null) {
+            result = contextClassLoader.loadClass(className);
+            if (result!=null) return result;
+        }
+        return null;
+    }
+    
+    @Override
+    protected URL findResource(String name) {
+        URL result = clc.getResource(name);
+        if (result!=null) return result;
+        
+        // last resort. see comment in XStream CompositeClassLoader
+        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+        if (contextClassLoader != null) {
+            result = contextClassLoader.getResource(name);
+            if (result!=null) return result;
+        }
+        return null;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/classloading/JavaBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/classloading/JavaBrooklynClassLoadingContext.java b/core/src/main/java/org/apache/brooklyn/core/management/classloading/JavaBrooklynClassLoadingContext.java
new file mode 100644
index 0000000..99fa15b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/classloading/JavaBrooklynClassLoadingContext.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.management.classloading;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+
+import com.google.common.base.Objects;
+
+public class JavaBrooklynClassLoadingContext extends AbstractBrooklynClassLoadingContext {
+
+    // on deserialization this loader is replaced with the catalog's root loader;
+    // may cause problems for non-osgi catalog items, but that's a reasonable trade-off,
+    // should this be serialized (e.g. in SpecialFlagsTransformer) in such a case!
+    private final transient ClassLoader loader;
+
+    /**
+     * @deprecated since 0.7.0 only for legacy catalog items which provide a non-osgi loader; see {@link #newDefault(ManagementContext)}
+     */ @Deprecated
+    public static JavaBrooklynClassLoadingContext create(ClassLoader loader) {
+        return new JavaBrooklynClassLoadingContext(null, checkNotNull(loader, "loader"));
+    }
+    
+    /**
+     * At least one of mgmt or loader must not be null.
+     * @deprecated since 0.7.0 only for legacy catalog items which provide a non-osgi loader; see {@link #newDefault(ManagementContext)}
+     */ @Deprecated
+    public static JavaBrooklynClassLoadingContext create(ManagementContext mgmt, ClassLoader loader) {
+        checkState(mgmt != null || loader != null, "mgmt and loader must not both be null");
+        return new JavaBrooklynClassLoadingContext(mgmt, loader);
+    }
+    
+    public static JavaBrooklynClassLoadingContext create(ManagementContext mgmt) {
+        return new JavaBrooklynClassLoadingContext(checkNotNull(mgmt, "mgmt"), null);
+    }
+
+    @Deprecated /** @deprecated since 0.7.0 use {@link #create(ManagementContext)} */
+    public static JavaBrooklynClassLoadingContext newDefault(ManagementContext mgmt) {
+        return new JavaBrooklynClassLoadingContext(checkNotNull(mgmt, "mgmt"), null);
+    }
+
+    @Deprecated /** @deprecated since 0.7.0 will become private; use one of the static methods to instantiate */
+    public JavaBrooklynClassLoadingContext(ManagementContext mgmt, ClassLoader loader) {
+        super(mgmt);
+        this.loader = loader;
+    }
+    
+    private ClassLoader getClassLoader() {
+        if (loader != null) return loader;
+        if (mgmt!=null) return mgmt.getCatalogClassLoader();
+        return JavaBrooklynClassLoadingContext.class.getClassLoader();
+    }
+    
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public Maybe<Class<?>> tryLoadClass(String className) {
+        try {
+            return (Maybe) Maybe.of(getClassLoader().loadClass(className));
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            return Maybe.absent("Invalid class: "+className, e);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "java:"+getClassLoader();
+    }
+    
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(super.hashCode(), getClassLoader());
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (!super.equals(obj)) return false;
+        if (!(obj instanceof JavaBrooklynClassLoadingContext)) return false;
+        if (!Objects.equal(getClassLoader(), ((JavaBrooklynClassLoadingContext)obj).getClassLoader())) return false;
+        return true;
+    }
+
+    @Override
+    public URL getResource(String name) {
+        return getClassLoader().getResource(name);
+    }
+
+    @Override
+    public Iterable<URL> getResources(String name) {
+        Enumeration<URL> resources;
+        try {
+            resources = getClassLoader().getResources(name);
+        } catch (IOException e) {
+            throw Exceptions.propagate(e);
+        }
+        return Collections.list(resources);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/classloading/OsgiBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/classloading/OsgiBrooklynClassLoadingContext.java b/core/src/main/java/org/apache/brooklyn/core/management/classloading/OsgiBrooklynClassLoadingContext.java
new file mode 100644
index 0000000..af5920b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/classloading/OsgiBrooklynClassLoadingContext.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.classloading;
+
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.entitlement.EntitlementClass;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.management.ha.OsgiManager;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+
+import brooklyn.catalog.internal.CatalogUtils;
+import brooklyn.util.guava.Maybe;
+
+import com.google.common.base.Objects;
+
+public class OsgiBrooklynClassLoadingContext extends AbstractBrooklynClassLoadingContext {
+
+    private final String catalogItemId;
+    private boolean hasBundles = false;
+    private transient Collection<CatalogBundle> _bundles;
+
+    public OsgiBrooklynClassLoadingContext(ManagementContext mgmt, String catalogItemId, Collection<CatalogBundle> bundles) {
+        super(mgmt);
+        this._bundles = bundles;
+        this.hasBundles = bundles!=null && !bundles.isEmpty();
+        this.catalogItemId = catalogItemId;
+    }
+
+    public Collection<CatalogBundle> getBundles() {
+        if (_bundles!=null || !hasBundles) return _bundles;
+        CatalogItem<?, ?> cat = CatalogUtils.getCatalogItemOptionalVersion(mgmt, catalogItemId);
+        if (cat==null) {
+            throw new IllegalStateException("Catalog item not found for "+catalogItemId+"; cannot create loading context");
+        }
+        _bundles = cat.getLibraries();
+        return _bundles;
+    }
+    
+    @Override
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public Maybe<Class<?>> tryLoadClass(String className) {
+        Maybe<Class<Object>> clazz = null;
+        Maybe<OsgiManager> osgi = null;
+        if (mgmt!=null) {
+            osgi = ((ManagementContextInternal)mgmt).getOsgiManager();
+            if (osgi.isPresent() && getBundles()!=null && !getBundles().isEmpty()) {
+                if (!Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, catalogItemId))
+                    return Maybe.absent("Not entitled to use this catalog entry");
+                
+                clazz = osgi.get().tryResolveClass(className, getBundles());
+                if (clazz.isPresent())
+                    return (Maybe)clazz;
+            }
+        }
+        
+        if (clazz!=null) { 
+            // if OSGi bundles were defined and failed, then use its error message
+            return (Maybe)clazz;
+        }
+        // else determine best message
+        if (mgmt==null) return Maybe.absent("No mgmt context available for loading "+className);
+        if (osgi!=null && osgi.isAbsent()) return Maybe.absent("OSGi not available on mgmt for loading "+className);
+        if (!hasBundles)
+            return Maybe.absent("No bundles available for loading "+className);
+        return Maybe.absent("Inconsistent state ("+mgmt+"/"+osgi+"/"+getBundles()+" loading "+className);
+    }
+
+    @Override
+    public String toString() {
+        return "OSGi:"+catalogItemId+"["+getBundles()+"]";
+    }
+    
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(super.hashCode(), getBundles(), catalogItemId);
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (!super.equals(obj)) return false;
+        if (!(obj instanceof OsgiBrooklynClassLoadingContext)) return false;
+
+        OsgiBrooklynClassLoadingContext other = (OsgiBrooklynClassLoadingContext)obj;
+        if (!catalogItemId.equals(other.catalogItemId)) return false;
+        if (!Objects.equal(getBundles(), other.getBundles())) return false;
+        return true;
+    }
+
+    @Override
+    public URL getResource(String name) {
+        if (mgmt != null && isEntitledToSeeCatalogItem()) {
+            Maybe<OsgiManager> osgi = ((ManagementContextInternal) mgmt).getOsgiManager();
+            if (osgi.isPresent() && hasBundles) {
+                return osgi.get().getResource(name, getBundles());
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Iterable<URL> getResources(String name) {
+        if (mgmt != null && isEntitledToSeeCatalogItem()) {
+            Maybe<OsgiManager> osgi = ((ManagementContextInternal) mgmt).getOsgiManager();
+            if (osgi.isPresent() && hasBundles) {
+                return osgi.get().getResources(name, getBundles());
+            }
+        }
+        return Collections.emptyList();
+    }
+
+    public String getCatalogItemId() {
+        return catalogItemId;
+    }
+
+    /**
+     * @return true if the current entitlement context may {@link Entitlements#SEE_CATALOG_ITEM see}
+     * {@link #getCatalogItemId}.
+     */
+    private boolean isEntitledToSeeCatalogItem() {
+        return Entitlements.isEntitled(mgmt.getEntitlementManager(),
+                Entitlements.SEE_CATALOG_ITEM,
+                catalogItemId);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/entitlement/BasicEntitlementClassDefinition.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/entitlement/BasicEntitlementClassDefinition.java b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/BasicEntitlementClassDefinition.java
new file mode 100644
index 0000000..07544a6
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/BasicEntitlementClassDefinition.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.management.entitlement;
+
+import org.apache.brooklyn.api.management.entitlement.EntitlementClass;
+
+import com.google.common.base.Objects;
+import com.google.common.reflect.TypeToken;
+
+
+public class BasicEntitlementClassDefinition<T> implements EntitlementClass<T> {
+
+    private final String identifier;
+    private final TypeToken<T> argumentType;
+
+    public BasicEntitlementClassDefinition(String identifier, TypeToken<T> argumentType) {
+        this.identifier = identifier;
+        this.argumentType = argumentType;
+    }
+    
+    public BasicEntitlementClassDefinition(String identifier, Class<T> argumentType) {
+        this.identifier = identifier;
+        this.argumentType = TypeToken.of(argumentType);
+    }
+    
+    @Override
+    public String entitlementClassIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public TypeToken<T> entitlementClassArgumentType() {
+        return argumentType;
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this).add("identitifier", identifier).add("argumentType", argumentType).toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/entitlement/EntitlementManagerAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/entitlement/EntitlementManagerAdapter.java b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/EntitlementManagerAdapter.java
new file mode 100644
index 0000000..a331a48
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/EntitlementManagerAdapter.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.entitlement;
+
+import org.apache.brooklyn.api.entity.Entity;
+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.entitlement.Entitlements.EntitlementClassesHandler;
+import org.apache.brooklyn.core.management.entitlement.Entitlements.EntityAndItem;
+import org.apache.brooklyn.core.management.entitlement.Entitlements.StringAndArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * provides an easy entry point to supplying entitlements, by providing the dispatch and defining the additional methods
+ * which have to be supplied.
+ * <p>
+ * note that this class may change as versions change, deliberately breaking backwards compatibility
+ * to ensure all permissions are used.
+ * <p>
+ * @since 0.7.0 */
+@Beta
+public abstract class EntitlementManagerAdapter implements EntitlementManager {
+
+    private static final Logger log = LoggerFactory.getLogger(EntitlementManagerAdapter.class);
+    
+    protected class Handler implements EntitlementClassesHandler<Boolean> {
+        final EntitlementContext context;
+        protected Handler(EntitlementContext context) {
+            this.context = context;
+        }
+        
+        @Override
+        public Boolean handleSeeCatalogItem(String catalogItemId) {
+            return isEntitledToSeeCatalogItem(context, catalogItemId);
+        }
+        @Override
+        public Boolean handleAddCatalogItem(Object catalogItemBeingAdded) {
+            return isEntitledToAddCatalogItem(context, catalogItemBeingAdded);
+        }
+        @Override
+        public Boolean handleModifyCatalogItem(StringAndArgument catalogItemIdAndModification) {
+            return isEntitledToModifyCatalogItem(context, catalogItemIdAndModification==null ? null : catalogItemIdAndModification.getString(),
+                catalogItemIdAndModification==null ? null : catalogItemIdAndModification.getArgument());
+        }
+        
+        @Override
+        public Boolean handleSeeEntity(Entity entity) {
+            return isEntitledToSeeEntity(context, entity);
+        }
+        @Override
+        public Boolean handleSeeSensor(EntityAndItem<String> sensorInfo) {
+            return isEntitledToSeeSensor(context, sensorInfo.getEntity(), sensorInfo.getItem());
+        }
+        @Override
+        public Boolean handleInvokeEffector(EntityAndItem<StringAndArgument> effectorInfo) {
+            StringAndArgument item = effectorInfo.getItem();
+            return isEntitledToInvokeEffector(context, effectorInfo.getEntity(), item==null ? null : item.getString(), item==null ? null : item.getArgument());
+        }
+        @Override
+        public Boolean handleModifyEntity(Entity entity) {
+            return isEntitledToModifyEntity(context, entity);
+        }
+
+        @Override
+        public Boolean handleDeployApplication(Object app) {
+            return isEntitledToDeployApplication(context, app);
+        }
+
+        @Override
+        public Boolean handleSeeAllServerInfo() {
+            return isEntitledToSeeAllServerInfo(context);
+        }
+
+        @Override
+        public Boolean handleSeeServerStatus() {
+            return isEntitledToSeeServerStatus(context);
+        }
+
+        @Override
+        public Boolean handleRoot() {
+            return isEntitledToRoot(context);
+        }
+    }
+    
+    @Override
+    public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
+        if (log.isTraceEnabled()) {
+            log.trace("Checking entitlement of "+context+" to "+entitlementClass+" "+entitlementClassArgument);
+        }
+        
+        if (isEntitledToRoot( context )) return true;
+        
+        Handler handler = new Handler(context);
+        return Entitlements.EntitlementClassesEnum.of(entitlementClass).handle(
+            handler, entitlementClassArgument);
+    }
+
+    protected abstract boolean isEntitledToSeeCatalogItem(EntitlementContext context, String catalogItemId);
+    /** passes item to be added, either yaml, or possibly null if any addition allowed (eg when resetting) */
+    protected abstract boolean isEntitledToAddCatalogItem(EntitlementContext context, Object catalogItemBeingAdded);
+    /** passes item being modified, as ID and description of modification, both possibly null if any modification is allowed (eg when resetting) */
+    protected abstract boolean isEntitledToModifyCatalogItem(EntitlementContext context, String catalogItemId, Object catalogItemModification);
+    protected abstract boolean isEntitledToSeeSensor(EntitlementContext context, Entity entity, String sensorName);
+    protected abstract boolean isEntitledToSeeEntity(EntitlementContext context, Entity entity);
+    /** arguments might be null, a map, or a list, depending how/where invoked */
+    protected abstract boolean isEntitledToInvokeEffector(EntitlementContext context, Entity entity, String effectorName, Object arguments);
+    protected abstract boolean isEntitledToModifyEntity(EntitlementContext context, Entity entity);
+    protected abstract boolean isEntitledToDeployApplication(EntitlementContext context, Object app);
+    protected abstract boolean isEntitledToSeeAllServerInfo(EntitlementContext context);
+    protected abstract boolean isEntitledToSeeServerStatus(EntitlementContext context);
+    protected abstract boolean isEntitledToRoot(EntitlementContext context);
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/entitlement/EntitlementPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/entitlement/EntitlementPredicates.java b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/EntitlementPredicates.java
new file mode 100644
index 0000000..085fb9e
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/EntitlementPredicates.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.management.entitlement;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.management.entitlement.EntitlementClass;
+import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
+
+import com.google.common.base.Predicate;
+
+public class EntitlementPredicates {
+
+    public static <T> Predicate<T> isEntitled(final EntitlementManager entitlementManager, final EntitlementClass<T> entitlementClass) {
+
+        return new Predicate<T>() {
+            @Override
+            public boolean apply(@Nullable T t) {
+                return Entitlements.isEntitled(entitlementManager, entitlementClass, t);
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..6bf9329
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/Entitlements.java
@@ -0,0 +1,418 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.entitlement;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+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.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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.reflect.TypeToken;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.BrooklynTaskTags;
+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 */
+@Beta
+public class Entitlements {
+
+    private static final Logger log = LoggerFactory.getLogger(Entitlements.class);
+    
+    // ------------------- individual permissions
+    
+    public static EntitlementClass<String> SEE_CATALOG_ITEM = new BasicEntitlementClassDefinition<String>("catalog.see", String.class); 
+    public static EntitlementClass<Object> ADD_CATALOG_ITEM = new BasicEntitlementClassDefinition<Object>("catalog.add", Object.class); 
+    public static EntitlementClass<StringAndArgument> MODIFY_CATALOG_ITEM = new BasicEntitlementClassDefinition<StringAndArgument>("catalog.modify", StringAndArgument.class); 
+    
+    public static EntitlementClass<Entity> SEE_ENTITY = new BasicEntitlementClassDefinition<Entity>("entity.see", Entity.class);
+    public static EntitlementClass<EntityAndItem<String>> SEE_SENSOR = new BasicEntitlementClassDefinition<EntityAndItem<String>>("sensor.see", EntityAndItem. typeToken(String.class));
+    // string is effector name; argument may be a map or a list, depending how the args were supplied
+    public static EntitlementClass<EntityAndItem<StringAndArgument>> INVOKE_EFFECTOR = new BasicEntitlementClassDefinition<EntityAndItem<StringAndArgument>>("effector.invoke", EntityAndItem.typeToken(StringAndArgument.class));
+    public static EntitlementClass<Entity> MODIFY_ENTITY = new BasicEntitlementClassDefinition<Entity>("entity.modify", Entity.class);
+    
+    /** the permission to deploy an application, where parameter is some representation of the app to be deployed (spec instance or yaml plan) */
+    public static EntitlementClass<Object> DEPLOY_APPLICATION = new BasicEntitlementClassDefinition<Object>("app.deploy", Object.class);
+
+    /** catch-all for catalog, locations, scripting, usage, etc - exporting persistence, shutting down, etc;
+     * this is significantly more powerful than {@link #SERVER_STATUS}.
+     * NB: this may be refactored and deprecated in future */
+    public static EntitlementClass<Void> SEE_ALL_SERVER_INFO = new BasicEntitlementClassDefinition<Void>("server.info.all.see", Void.class);
+
+    /** permission to see general server status info: basically HA status; not nearly as much as {@link #SEE_ALL_SERVER_INFO} */
+    public static EntitlementClass<Void> SERVER_STATUS = new BasicEntitlementClassDefinition<Void>("server.status", Void.class);
+    
+    /** permission to run untrusted code or embedded scripts at the server; 
+     * secondary check required for any operation which could potentially grant root-level access */ 
+    public static EntitlementClass<Void> ROOT = new BasicEntitlementClassDefinition<Void>("root", Void.class);
+
+    @SuppressWarnings("unchecked")
+    public static enum EntitlementClassesEnum {
+        ENTITLEMENT_SEE_CATALOG_ITEM(SEE_CATALOG_ITEM) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleSeeCatalogItem((String)argument); } },
+        ENTITLEMENT_ADD_CATALOG_ITEM(ADD_CATALOG_ITEM) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleAddCatalogItem(argument); } },
+        ENTITLEMENT_MODIFY_CATALOG_ITEM(MODIFY_CATALOG_ITEM) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleModifyCatalogItem((StringAndArgument)argument); } },
+        
+        ENTITLEMENT_SEE_ENTITY(SEE_ENTITY) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleSeeEntity((Entity)argument); } },
+        ENTITLEMENT_SEE_SENSOR(SEE_SENSOR) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleSeeSensor((EntityAndItem<String>)argument); } },
+        ENTITLEMENT_INVOKE_EFFECTOR(INVOKE_EFFECTOR) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleInvokeEffector((EntityAndItem<StringAndArgument>)argument); } },
+        ENTITLEMENT_MODIFY_ENTITY(MODIFY_ENTITY) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleModifyEntity((Entity)argument); } },
+        
+        ENTITLEMENT_DEPLOY_APPLICATION(DEPLOY_APPLICATION) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleDeployApplication(argument); } },
+        
+        ENTITLEMENT_SEE_ALL_SERVER_INFO(SEE_ALL_SERVER_INFO) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleSeeAllServerInfo(); } },
+        ENTITLEMENT_SERVER_STATUS(SERVER_STATUS) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleSeeServerStatus(); } },
+        ENTITLEMENT_ROOT(ROOT) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleRoot(); } },
+        ;
+        
+        private EntitlementClass<?> entitlementClass;
+
+        private EntitlementClassesEnum(EntitlementClass<?> specificClass) {
+            this.entitlementClass = specificClass;
+        }
+        public EntitlementClass<?> getEntitlementClass() {
+            return entitlementClass;
+        }
+
+        public abstract <T> T handle(EntitlementClassesHandler<T> handler, Object argument);
+        
+        public static EntitlementClassesEnum of(EntitlementClass<?> entitlementClass) {
+            for (EntitlementClassesEnum x: values()) {
+                if (entitlementClass.equals(x.getEntitlementClass())) return x;
+            }
+            return null;
+        }
+    }
+    
+    public interface EntitlementClassesHandler<T> {
+        public T handleSeeCatalogItem(String catalogItemId);
+        public T handleSeeServerStatus();
+        public T handleAddCatalogItem(Object catalogItemBeingAdded);
+        public T handleModifyCatalogItem(StringAndArgument catalogItemIdAndModification);
+        public T handleSeeEntity(Entity entity);
+        public T handleSeeSensor(EntityAndItem<String> sensorInfo);
+        public T handleInvokeEffector(EntityAndItem<StringAndArgument> effectorInfo);
+        public T handleModifyEntity(Entity entity);
+        public T handleDeployApplication(Object app);
+        public T handleSeeAllServerInfo();
+        public T handleRoot();
+    }
+    
+    protected static class Pair<T1,T2> {
+        protected final T1 p1;
+        protected final T2 p2;
+        protected Pair(T1 p1, T2 p2) { this.p1 = p1; this.p2 = p2; }
+    }
+    public static class EntityAndItem<T> extends Pair<Entity,T> {
+        public static <TT> TypeToken<EntityAndItem<TT>> typeToken(Class<TT> type) {
+            return new TypeToken<Entitlements.EntityAndItem<TT>>() {
+                private static final long serialVersionUID = -738154831809025407L;
+            };
+        }
+        public EntityAndItem(Entity entity, T item) { super (entity, item); }
+        public Entity getEntity() { return p1; }
+        public T getItem() { return p2; }
+        public static <T> EntityAndItem<T> of(Entity entity, T item) {
+            return new EntityAndItem<T>(entity, item);
+        }
+    }
+    
+    public static class StringAndArgument extends Pair<String,Object> {
+        public StringAndArgument(String string, Object argument) { super(string, argument); }
+        public String getString() { return p1; }
+        public Object getArgument() { return p2; }
+        public static StringAndArgument of(String string, Object argument) {
+            return new StringAndArgument(string, argument);
+        }
+    }
+
+    /** 
+     * These lifecycle operations are currently treated as effectors. This may change in the future.
+     * @since 0.7.0 */
+    @Beta
+    public static class LifecycleEffectors {
+        public static final String DELETE = "delete";
+    }
+    
+    // ------------- permission sets -------------
+    
+    /** always ALLOW access to everything */
+    public static EntitlementManager root() {
+        return new EntitlementManager() {
+            @Override
+            public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> permission, T typeArgument) {
+                return true;
+            }
+            @Override
+            public String toString() {
+                return "Entitlements.root";
+            }
+        };
+    }
+
+    /** always DENY access to anything which requires entitlements */
+    public static EntitlementManager minimal() {
+        return new EntitlementManager() {
+            @Override
+            public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> permission, T typeArgument) {
+                return false;
+            }
+            @Override
+            public String toString() {
+                return "Entitlements.minimal";
+            }
+        };
+    }
+
+    public static class FineGrainedEntitlements {
+    
+        private static final Joiner COMMA_JOINER = Joiner.on(',');
+
+        public static EntitlementManager anyOf(final EntitlementManager... checkers) {
+            return anyOf(Arrays.asList(checkers));
+        }
+        
+        public static EntitlementManager anyOf(final Iterable<? extends EntitlementManager> checkers) {
+            return new EntitlementManager() {
+                @Override
+                public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> permission, T typeArgument) {
+                    for (EntitlementManager checker: checkers)
+                        if (checker.isEntitled(context, permission, typeArgument))
+                            return true;
+                    return false;
+                }
+                @Override
+                public String toString() {
+                    return "Entitlements.anyOf(" + COMMA_JOINER.join(checkers) + ")";
+                }
+            };
+        }
+        
+        public static EntitlementManager allOf(final EntitlementManager... checkers) {
+            return allOf(Arrays.asList(checkers));
+        }
+        
+        public static EntitlementManager allOf(final Iterable<? extends EntitlementManager> checkers) {
+            return new EntitlementManager() {
+                @Override
+                public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> permission, T typeArgument) {
+                    for (EntitlementManager checker: checkers)
+                        if (checker.isEntitled(context, permission, typeArgument))
+                            return true;
+                    return false;
+                }
+                @Override
+                public String toString() {
+                    return "Entitlements.allOf(" + COMMA_JOINER.join(checkers) + ")";
+                }
+            };
+        }
+
+        public static <U> EntitlementManager allowing(EntitlementClass<U> permission, Predicate<U> test) {
+            return new SinglePermissionEntitlementChecker<U>(permission, test);
+        }
+
+        public static <U> EntitlementManager allowing(EntitlementClass<U> permission) {
+            return new SinglePermissionEntitlementChecker<U>(permission, Predicates.<U>alwaysTrue());
+        }
+
+        public static class SinglePermissionEntitlementChecker<U> implements EntitlementManager {
+            final EntitlementClass<U> permission;
+            final Predicate<U> test;
+            
+            protected SinglePermissionEntitlementChecker(EntitlementClass<U> permission, Predicate<U> test) {
+                this.permission = permission;
+                this.test = test;
+            }
+            
+            @SuppressWarnings("unchecked")
+            @Override
+            public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> permission, T typeArgument) {
+                if (!Objects.equal(this.permission, permission)) return false;
+                return test.apply((U)typeArgument);
+            }
+            @Override
+            public String toString() {
+                return "Entitlements.allowing(" + permission + " -> " + test + ")";
+            }
+        }
+        public static EntitlementManager seeNonSecretSensors() {
+            return allowing(SEE_SENSOR, new Predicate<EntityAndItem<String>>() {
+                @Override
+                public boolean apply(EntityAndItem<String> input) {
+                    if (input == null) return false;
+                    return !Entities.isSecret(input.getItem());
+                }
+                @Override
+                public String toString() {
+                    return "Predicates.nonSecret";
+                }
+            });
+        }
+        
+    }
+    
+    /** allow read-only */
+    public static EntitlementManager readOnly() {
+        return FineGrainedEntitlements.anyOf(
+            FineGrainedEntitlements.allowing(SEE_ENTITY),
+            FineGrainedEntitlements.seeNonSecretSensors()
+        );
+    }
+
+    /** allow healthcheck */
+    public static EntitlementManager serverStatusOnly() {
+        return FineGrainedEntitlements.allowing(SERVER_STATUS);
+    }
+
+    // ------------- lookup conveniences -------------
+
+    private static class PerThreadEntitlementContextHolder {
+        public static final ThreadLocal<EntitlementContext> perThreadEntitlementsContextHolder = new ThreadLocal<EntitlementContext>();
+    }
+
+    /** 
+     * Finds the currently applicable {@link EntitlementContext} by examining the current thread
+     * then by investigating the current task, its submitter, etc. */
+    // NOTE: entitlements are propagated to tasks whenever they are created, as tags
+    // (see BrooklynTaskTags.tagForEntitlement and BasicExecutionContext.submitInternal).
+    // It might be cheaper to only do this lookup, not to propagate as tags, and to ensure
+    // all entitlement operations are wrapped in a task at source; but currently we do not
+    // do that so we need at least to set entitlement on the outermost task.
+    // Setting it on tasks submitted by a task is not strictly necessary (i.e. in BasicExecutionContext)
+    // but seems cheap enough, and means checking entitlements is fast, if we choose to do that more often.
+    public static EntitlementContext getEntitlementContext() {
+        EntitlementContext context;
+        context = PerThreadEntitlementContextHolder.perThreadEntitlementsContextHolder.get();
+        if (context!=null) return context;
+        
+        Task<?> task = Tasks.current();
+        while (task!=null) {
+            context = BrooklynTaskTags.getEntitlement(task);
+            if (context!=null) return context;
+            task = task.getSubmittedByTask();
+        }
+        
+        // no entitlements set -- assume entitlements not used, or system internal
+        return null;
+    }
+
+    public static void setEntitlementContext(EntitlementContext context) {
+        EntitlementContext oldContext = PerThreadEntitlementContextHolder.perThreadEntitlementsContextHolder.get();
+        if (oldContext!=null && context!=null) {
+            log.warn("Changing entitlement context from "+oldContext+" to "+context+"; context should have been reset or extended, not replaced");
+            log.debug("Trace for entitlement context duplicate overwrite", new Throwable("Trace for entitlement context overwrite"));
+        }
+        PerThreadEntitlementContextHolder.perThreadEntitlementsContextHolder.set(context);
+    }
+    
+    public static void clearEntitlementContext() {
+        PerThreadEntitlementContextHolder.perThreadEntitlementsContextHolder.set(null);
+    }
+    
+    public static <T> boolean isEntitled(EntitlementManager checker, EntitlementClass<T> permission, T typeArgument) {
+        return checker.isEntitled(getEntitlementContext(), permission, typeArgument);
+    }
+
+    /** throws {@link NotEntitledException} if entitlement not available for current {@link #getEntitlementContext()} */
+    public static <T> void checkEntitled(EntitlementManager checker, EntitlementClass<T> permission, T typeArgument) {
+        if (!isEntitled(checker, permission, typeArgument)) {
+            throw new NotEntitledException(getEntitlementContext(), permission, typeArgument);
+        }
+    }
+    /** throws {@link NotEntitledException} if entitlement not available for current {@link #getEntitlementContext()} 
+     * @since 0.7.0
+     * @deprecated since 0.7.0, use {@link #checkEntitled(EntitlementManager, EntitlementClass, Object)};
+     * kept briefly because there is some downstream usage*/
+    public static <T> void requireEntitled(EntitlementManager checker, EntitlementClass<T> permission, T typeArgument) {
+        checkEntitled(checker, permission, typeArgument);
+    }
+    
+    // ----------------- initialization ----------------
+
+    public final static String ENTITLEMENTS_CONFIG_PREFIX = "brooklyn.entitlements";
+    
+    public static ConfigKey<String> GLOBAL_ENTITLEMENT_MANAGER = ConfigKeys.newStringConfigKey(ENTITLEMENTS_CONFIG_PREFIX+".global", 
+        "Class for entitlements in effect globally; "
+        + "short names 'minimal', 'readonly', or 'root' are permitted here, with the default 'root' giving full access to all declared users; "
+        + "or supply the name of an "+EntitlementManager.class+" class to instantiate, taking a 1-arg BrooklynProperties constructor or a 0-arg constructor",
+        "root");
+    
+    public static EntitlementManager newManager(ManagementContext mgmt, BrooklynProperties brooklynProperties) {
+        return newGlobalManager(mgmt, brooklynProperties);
+    }
+    private static EntitlementManager newGlobalManager(ManagementContext mgmt, BrooklynProperties brooklynProperties) {
+        return load(mgmt, brooklynProperties, brooklynProperties.getConfig(GLOBAL_ENTITLEMENT_MANAGER));
+    }
+    
+    public static EntitlementManager load(@Nullable ManagementContext mgmt, BrooklynProperties brooklynProperties, String type) {
+        if ("root".equalsIgnoreCase(type)) return root();
+        if ("readonly".equalsIgnoreCase(type) || "read_only".equalsIgnoreCase(type)) return readOnly();
+        if ("minimal".equalsIgnoreCase(type)) return minimal();
+        if (Strings.isNonBlank(type)) {
+            try {
+                ClassLoader cl = mgmt==null ? null : ((ManagementContextInternal)mgmt).getCatalogClassLoader();
+                if (cl==null) cl = Entitlements.class.getClassLoader();
+                Class<?> clazz = cl.loadClass(type);
+                return (EntitlementManager) instantiate(clazz, ImmutableList.of(
+                        new Object[] {mgmt, brooklynProperties},
+                        new Object[] {mgmt},
+                        new Object[] {brooklynProperties},
+                        new Object[0]));
+            } catch (Exception e) { 
+                throw Exceptions.propagate(e); 
+            }
+        }
+        throw new IllegalStateException("Invalid entitlement manager specified: '"+type+"'");
+    }
+    
+    private static Object instantiate(Class<?> clazz, List<Object[]> constructorArgOptions) {
+        try {
+            for (Object[] constructorArgOption : constructorArgOptions) {
+                Optional<?> result = Reflections.invokeConstructorWithArgs(clazz, constructorArgOption);
+                if (result.isPresent()) return result.get();
+            }
+        } catch (Exception e) { 
+            throw Exceptions.propagate(e); 
+        }
+        throw new IllegalStateException("No matching constructor to instantiate "+clazz);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/entitlement/NotEntitledException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/entitlement/NotEntitledException.java b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/NotEntitledException.java
new file mode 100644
index 0000000..aab7c0d
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/NotEntitledException.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.management.entitlement;
+
+import org.apache.brooklyn.api.management.entitlement.EntitlementClass;
+import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
+
+
+public class NotEntitledException extends RuntimeException {
+
+    private static final long serialVersionUID = -4001882260980589181L;
+    
+    EntitlementContext entitlementContext;
+    EntitlementClass<?> permission;
+    Object typeArgument;
+    
+    public <T> NotEntitledException(EntitlementContext entitlementContext, EntitlementClass<T> permission, T typeArgument) {
+        this.entitlementContext = entitlementContext;
+        this.permission = permission;
+        this.typeArgument = typeArgument;
+    }
+    
+    @Override
+    public String toString() {
+        return super.toString()+"["+entitlementContext+":"+permission+(typeArgument!=null ? "("+typeArgument+")" : "")+"]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManager.java b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManager.java
new file mode 100644
index 0000000..c5914e8
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManager.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.management.entitlement;
+
+import java.util.Map;
+
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.ConfigKey;
+import brooklyn.config.ConfigPredicates;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Preconditions;
+
+public class PerUserEntitlementManager implements EntitlementManager {
+
+    private static final Logger log = LoggerFactory.getLogger(PerUserEntitlementManager.class);
+    
+    public final static String PER_USER_ENTITLEMENTS_CONFIG_PREFIX = Entitlements.ENTITLEMENTS_CONFIG_PREFIX+".perUser";
+    
+    public final static ConfigKey<String> DEFAULT_MANAGER = ConfigKeys.newStringConfigKey(PER_USER_ENTITLEMENTS_CONFIG_PREFIX+
+        ".default", "Default entitlements manager for users without further specification", "minimal");
+    
+    protected final EntitlementManager defaultManager;
+    protected final Map<String,EntitlementManager> perUserManagers = MutableMap.of();
+
+    private final static ThreadLocal<Boolean> ACTIVE = new ThreadLocal<Boolean>();
+    
+    private static EntitlementManager load(BrooklynProperties properties, String type) {
+        if (Boolean.TRUE.equals(ACTIVE.get())) {
+            // prevent infinite loop
+            throw new IllegalStateException("Cannot set "+PerUserEntitlementManager.class.getName()+" within config for itself");
+        }
+        try {
+            ACTIVE.set(true);
+            return Entitlements.load(null, properties, type);
+        } finally {
+            ACTIVE.remove();
+        }
+    }
+    
+    public PerUserEntitlementManager(BrooklynProperties properties) {
+        this(load(properties, properties.getConfig(DEFAULT_MANAGER)));
+        
+        BrooklynProperties users = properties.submap(ConfigPredicates.startingWith(PER_USER_ENTITLEMENTS_CONFIG_PREFIX+"."));
+        for (Map.Entry<ConfigKey<?>,?> key: users.getAllConfig().entrySet()) {
+            if (key.getKey().getName().equals(DEFAULT_MANAGER.getName())) continue;
+            String user = Strings.removeFromStart(key.getKey().getName(), PER_USER_ENTITLEMENTS_CONFIG_PREFIX+".");
+            addUser(user, load(properties, Strings.toString(key.getValue())));
+        }
+        
+        log.info(getClass().getSimpleName()+" created with "+perUserManagers.size()+" user"+Strings.s(perUserManagers)+" and "
+            + "default "+defaultManager+" (users: "+perUserManagers+")");
+    }
+    
+    public PerUserEntitlementManager(EntitlementManager defaultManager) {
+        this.defaultManager = Preconditions.checkNotNull(defaultManager);
+    }
+
+    public void addUser(String user, EntitlementManager managerForThisUser) {
+        perUserManagers.put(Preconditions.checkNotNull(user, "user"), Preconditions.checkNotNull(managerForThisUser, "managerForThisUser"));
+    }
+
+    @Override
+    public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
+        EntitlementManager entitlementInEffect = null;
+        if (context==null || context.user()==null) {
+            // no user means it is running as an internal process, always has root
+            entitlementInEffect = Entitlements.root(); 
+        } else {
+            if (context!=null) entitlementInEffect = perUserManagers.get(context.user());
+            if (entitlementInEffect==null) entitlementInEffect = defaultManager;
+        }
+        return entitlementInEffect.isEntitled(context, entitlementClass, entitlementClassArgument);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManagerWithDefault.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManagerWithDefault.java b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManagerWithDefault.java
new file mode 100644
index 0000000..b4c7e38
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManagerWithDefault.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.management.entitlement;
+
+import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
+
+@Deprecated
+/** @deprecated since 0.7.0 use {@link PerUserEntitlementManager} */
+public class PerUserEntitlementManagerWithDefault extends PerUserEntitlementManager {
+
+    public PerUserEntitlementManagerWithDefault(EntitlementManager defaultManager) {
+        super(defaultManager);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/entitlement/WebEntitlementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/entitlement/WebEntitlementContext.java b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/WebEntitlementContext.java
new file mode 100644
index 0000000..2efed42
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/entitlement/WebEntitlementContext.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.entitlement;
+
+import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
+
+import brooklyn.util.javalang.JavaClassNames;
+
+/**
+ * Indicates an authenticated web request as the entitlements context;
+ * note user may still be null if no authentication was requested
+ */
+public class WebEntitlementContext implements EntitlementContext {
+
+    final String user;
+    final String sourceIp;
+    final String requestUri;
+    
+    /**
+     * A mostly-unique identifier for the inbound request, to distinguish
+     * between duplicate requests and for cross-referencing with URIs
+     */
+    final String requestUniqueIdentifier;
+    
+    public WebEntitlementContext(String user, String sourceIp, String requestUri, String requestUniqueIdentifier) {
+        this.user = user;
+        this.sourceIp = sourceIp;
+        this.requestUri = requestUri;
+        this.requestUniqueIdentifier = requestUniqueIdentifier;
+    }
+    
+    @Override public String user() { return user; }
+    public String sourceIp() { return sourceIp; }
+    public String requestUri() { return requestUri; }
+    public String requestUniqueIdentifier() { return requestUniqueIdentifier; }
+
+    @Override
+    public String toString() {
+        return JavaClassNames.simpleClassName(getClass())+"["+user+"@"+sourceIp+":"+requestUniqueIdentifier+"]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/ha/BasicMasterChooser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/ha/BasicMasterChooser.java b/core/src/main/java/org/apache/brooklyn/core/management/ha/BasicMasterChooser.java
new file mode 100644
index 0000000..034a6bd
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/ha/BasicMasterChooser.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.management.ha;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.trait.Identifiable;
+import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.text.NaturalOrderComparator;
+import brooklyn.util.time.Duration;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.Ordering;
+
+/**
+ * @since 0.7.0
+ * 
+ * @author aled
+ */
+@Beta
+public abstract class BasicMasterChooser implements MasterChooser {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BasicMasterChooser.class);
+
+    protected static class ScoredRecord<T extends Comparable<T>> implements Identifiable, Comparable<ScoredRecord<T>> {
+        String id;
+        ManagementNodeSyncRecord record;
+        T score;
+        
+        @Override
+        public String getId() {
+            return id;
+        }
+
+        @Override
+        public int compareTo(ScoredRecord<T> o) {
+            return score.compareTo(o.score);
+        }
+    }
+    
+    public ManagementNodeSyncRecord choose(ManagementPlaneSyncRecord memento, Duration heartbeatTimeout, String ownNodeId) {
+        if (LOG.isDebugEnabled()) LOG.debug("Choosing new master from "+memento.getManagementNodes());
+        ManagementNodeSyncRecord me = memento.getManagementNodes().get(ownNodeId);
+        if (me==null) {
+            LOG.warn("Management node details not known when choosing new master: "+memento+" / "+ownNodeId);
+            return null;
+        }
+        Long nowIsh = me.getRemoteTimestamp();
+        if (nowIsh==null) {
+            LOG.warn("Management node for self missing timestamp when choosing new master: "+memento);
+            return null;
+        }
+        
+        List<ScoredRecord<?>> contenders = filterHealthy(memento, heartbeatTimeout, nowIsh);
+        
+        if (!contenders.isEmpty()) {
+            return pick(contenders);
+        } else {
+            LOG.info("No valid management node found for choosing new master: contender="+memento.getManagementNodes());
+            return null;
+        }        
+    }
+
+    /** pick the best contender; argument guaranteed to be non-null and non-empty,
+     * filtered for health reasons */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    protected ManagementNodeSyncRecord pick(List<ScoredRecord<?>> contenders) {
+        ScoredRecord min = null;
+        for (ScoredRecord x: contenders) {
+            if (min==null || x.score.compareTo(min.score)<0) min = x;
+        }
+        return min.record;
+    }
+
+    public static class AlphabeticChooserScore implements Comparable<AlphabeticChooserScore> {
+        long priority;
+        int versionBias;
+        String brooklynVersion;
+        int statePriority;
+        String nodeId;
+        
+        @Override
+        public int compareTo(AlphabeticChooserScore o) {
+            if (o==null) return -1;
+            return ComparisonChain.start()
+                // invert the order where we prefer higher values
+                .compare(o.priority, this.priority)
+                .compare(o.versionBias, this.versionBias)
+                .compare(o.brooklynVersion, this.brooklynVersion, 
+                    Ordering.from(NaturalOrderComparator.INSTANCE).nullsFirst())
+                .compare(o.statePriority, this.statePriority)
+                .compare(this.nodeId, o.nodeId, Ordering.usingToString().nullsLast())
+                .result();
+        }
+    }
+    
+    /** comparator which prefers, in order:
+     * <li> higher explicit priority
+     * <li> non-snapshot Brooklyn version, then any Brooklyn version, and lastly null version 
+     *      (using {@link NaturalOrderComparator} so e.g. v10 > v3.20 > v3.9 )
+     * <li> higher version (null last)
+     * <li> node which reports it's master, hot standby, then standby 
+     * <li> finally (common case): lower (alphabetically) node id
+     */
+    public static class AlphabeticMasterChooser extends BasicMasterChooser {
+        final boolean preferHotStandby;
+        public AlphabeticMasterChooser(boolean preferHotStandby) { this.preferHotStandby = preferHotStandby; }
+        public AlphabeticMasterChooser() { this.preferHotStandby = true; }
+        @Override
+        protected AlphabeticChooserScore score(ManagementNodeSyncRecord contender) {
+            AlphabeticChooserScore score = new AlphabeticChooserScore();
+            score.priority = contender.getPriority()!=null ? contender.getPriority() : 0;
+            score.brooklynVersion = contender.getBrooklynVersion();
+            score.versionBias = contender.getBrooklynVersion()==null ? -2 :
+                contender.getBrooklynVersion().toLowerCase().indexOf("snapshot")>=0 ? -1 :
+                0;
+            if (preferHotStandby) {
+                // other master should be preferred before we get invoked, but including for good measure
+                score.statePriority = contender.getStatus()==ManagementNodeState.MASTER ? 3 :
+                    contender.getStatus()==ManagementNodeState.HOT_STANDBY ? 2 :
+                        contender.getStatus()==ManagementNodeState.STANDBY ? 1 : -1;
+            } else {
+                score.statePriority = 0;
+            }
+            score.nodeId = contender.getNodeId();
+            return score;
+        }
+    }
+    
+    /**
+     * Filters the {@link ManagementPlaneSyncRecord#getManagementNodes()} to only those in an appropriate state, 
+     * and with heartbeats that have not timed out.
+     */
+    protected List<ScoredRecord<?>> filterHealthy(ManagementPlaneSyncRecord memento, Duration heartbeatTimeout, long nowIsh) {
+        long oldestAcceptableTimestamp = nowIsh - heartbeatTimeout.toMilliseconds();
+        List<ScoredRecord<?>> contenders = MutableList.of();
+        for (ManagementNodeSyncRecord contender : memento.getManagementNodes().values()) {
+            boolean statusOk = (contender.getStatus() == ManagementNodeState.STANDBY || contender.getStatus() == ManagementNodeState.HOT_STANDBY || contender.getStatus() == ManagementNodeState.MASTER);
+            Long remoteTimestamp = contender.getRemoteTimestamp();
+            boolean heartbeatOk;
+            if (remoteTimestamp==null) {
+                throw new IllegalStateException("Missing remote timestamp when performing master election: "+this+" / "+contender);
+                // if the above exception happens in some contexts we could either fallback to local, or fail:
+//                remoteTimestamp = contender.getLocalTimestamp();
+                // or
+//                heartbeatOk=false;
+            } else {
+                heartbeatOk = remoteTimestamp >= oldestAcceptableTimestamp;
+            }
+            if (statusOk && heartbeatOk) {
+                contenders.add(newScoredRecord(contender));
+            }
+            if (LOG.isTraceEnabled()) LOG.trace("Filtering choices of new master: contender="+contender+"; statusOk="+statusOk+"; heartbeatOk="+heartbeatOk);
+        }
+        return contenders;
+    }
+
+    @VisibleForTesting
+    //Java 6 compiler workaround, using parameterized types fails
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    protected List<ScoredRecord<?>> sort(List<ScoredRecord<?>> input) {
+        ArrayList copy = new ArrayList<ScoredRecord<?>>(input);
+        Collections.sort(copy);
+        return copy;
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    protected ScoredRecord<?> newScoredRecord(ManagementNodeSyncRecord contender) {
+        ScoredRecord r = new ScoredRecord();
+        r.id = contender.getNodeId();
+        r.record = contender;
+        r.score = score(contender);
+        return r;
+    }
+
+    protected abstract Comparable<?> score(ManagementNodeSyncRecord contender);
+    
+}


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

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


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

Branch: refs/heads/master
Commit: 6602f694a2ac625e895de264d83a0407bc512fbe
Parents: 083953f
Author: Hadrian Zbarcea <ha...@apache.org>
Authored: Fri Aug 14 23:35:04 2015 -0400
Committer: Hadrian Zbarcea <ha...@apache.org>
Committed: Fri Aug 14 23:35:04 2015 -0400

----------------------------------------------------------------------
 .../src/main/java/brooklyn/BrooklynVersion.java |    2 +-
 .../brooklyn/basic/AbstractBrooklynObject.java  |    2 +-
 .../brooklyn/basic/BasicConfigurableObject.java |    2 +-
 .../brooklyn/catalog/CatalogPredicates.java     |    2 +-
 .../catalog/internal/BasicBrooklynCatalog.java  |    2 +-
 .../catalog/internal/CatalogClasspathDo.java    |    2 +-
 .../brooklyn/catalog/internal/CatalogDo.java    |    2 +-
 .../catalog/internal/CatalogInitialization.java |    4 +-
 .../brooklyn/catalog/internal/CatalogUtils.java |   10 +-
 .../brooklyn/config/BrooklynServerPaths.java    |    2 +-
 .../entity/basic/AbstractApplication.java       |    2 +-
 .../brooklyn/entity/basic/AbstractEffector.java |    2 +-
 .../brooklyn/entity/basic/AbstractEntity.java   |    8 +-
 .../entity/basic/AbstractGroupImpl.java         |    2 +-
 .../entity/basic/BrooklynShutdownHooks.java     |    2 +-
 .../brooklyn/entity/basic/DynamicGroupImpl.java |    4 +-
 .../java/brooklyn/entity/basic/Entities.java    |   10 +-
 .../brooklyn/entity/basic/EntityInternal.java   |    2 +-
 .../basic/EntityTransientCopyInternal.java      |    2 +-
 .../brooklyn/entity/basic/MethodEffector.java   |    2 +-
 .../entity/effector/AddChildrenEffector.java    |    4 +-
 .../brooklyn/entity/effector/EffectorTasks.java |    4 +-
 .../entity/proxying/EntityProxyImpl.java        |    6 +-
 .../entity/proxying/InternalEntityFactory.java  |    2 +-
 .../entity/proxying/InternalFactory.java        |    3 +-
 .../proxying/InternalLocationFactory.java       |    4 +-
 .../entity/proxying/InternalPolicyFactory.java  |    2 +-
 .../rebind/InitialFullRebindIteration.java      |    8 +-
 .../brooklyn/entity/rebind/RebindIteration.java |   12 +-
 .../entity/rebind/RebindManagerImpl.java        |    4 +-
 .../BrooklynMementoPersisterToObjectStore.java  |    2 +-
 .../persister/BrooklynPersistenceUtils.java     |    6 +-
 .../rebind/persister/XmlMementoSerializer.java  |    6 +-
 ...platedStringAttributeSensorAndConfigKey.java |    2 +-
 .../internal/storage/DataGridFactory.java       |    4 +-
 .../impl/inmemory/InMemoryDataGridFactory.java  |    3 +-
 .../management/ManagementContextInjectable.java |   33 -
 .../AbstractBrooklynClassLoadingContext.java    |   82 --
 .../BrooklynClassLoadingContextSequential.java  |  136 ---
 ...ssLoaderFromBrooklynClassLoadingContext.java |   66 --
 .../JavaBrooklynClassLoadingContext.java        |  123 --
 .../OsgiBrooklynClassLoadingContext.java        |  147 ---
 .../BasicEntitlementClassDefinition.java        |   56 -
 .../entitlement/EntitlementManagerAdapter.java  |  134 ---
 .../entitlement/EntitlementPredicates.java      |   40 -
 .../management/entitlement/Entitlements.java    |  418 -------
 .../entitlement/NotEntitledException.java       |   44 -
 .../entitlement/PerUserEntitlementManager.java  |  100 --
 .../PerUserEntitlementManagerWithDefault.java   |   31 -
 .../entitlement/WebEntitlementContext.java      |   57 -
 .../management/ha/BasicMasterChooser.java       |  204 ----
 .../ha/HighAvailabilityManagerImpl.java         | 1105 ------------------
 .../ha/ManagementPlaneSyncRecordDeltaImpl.java  |  122 --
 ...ntPlaneSyncRecordPersisterToObjectStore.java |  355 ------
 .../brooklyn/management/ha/MasterChooser.java   |   40 -
 .../brooklyn/management/ha/OsgiManager.java     |  312 -----
 .../internal/AbstractManagementContext.java     |  485 --------
 .../internal/AbstractSubscriptionManager.java   |  141 ---
 .../management/internal/AccessManager.java      |   41 -
 .../internal/AsyncCollectionChangeAdapter.java  |   83 --
 .../internal/BasicSubscriptionContext.java      |  180 ---
 .../internal/BrooklynGarbageCollector.java      |  626 ----------
 .../internal/BrooklynObjectManagementMode.java  |   31 -
 .../internal/BrooklynObjectManagerInternal.java |   36 -
 .../internal/CollectionChangeListener.java      |   24 -
 .../management/internal/EffectorUtils.java      |  356 ------
 .../internal/EntityChangeListener.java          |   79 --
 .../internal/EntityManagementSupport.java       |  478 --------
 .../internal/EntityManagementUtils.java         |  326 ------
 .../internal/EntityManagerInternal.java         |   32 -
 ...PropertyChangeToCollectionChangeAdapter.java |   65 --
 .../management/internal/LocalAccessManager.java |  111 --
 .../management/internal/LocalEntityManager.java |  818 -------------
 .../internal/LocalLocationManager.java          |  463 --------
 .../internal/LocalManagementContext.java        |  421 -------
 .../internal/LocalSubscriptionManager.java      |  292 -----
 .../management/internal/LocalUsageManager.java  |  430 -------
 .../internal/LocationManagerInternal.java       |   28 -
 .../internal/ManagementContextInternal.java     |  122 --
 .../internal/ManagementTransitionInfo.java      |   48 -
 .../internal/ManagementTransitionMode.java      |  127 --
 .../internal/NonDeploymentAccessManager.java    |   98 --
 .../internal/NonDeploymentEntityManager.java    |  196 ----
 .../internal/NonDeploymentLocationManager.java  |  146 ---
 .../NonDeploymentManagementContext.java         |  639 ----------
 .../internal/NonDeploymentUsageManager.java     |  132 ---
 .../internal/QueueingSubscriptionManager.java   |  148 ---
 .../management/internal/Subscription.java       |   66 --
 .../internal/SubscriptionTracker.java           |  137 ---
 .../management/internal/UsageListener.java      |  104 --
 .../management/internal/UsageManager.java       |  166 ---
 .../management/usage/ApplicationUsage.java      |  126 --
 .../management/usage/LocationUsage.java         |  135 ---
 .../policy/basic/AbstractEntityAdjunct.java     |    2 +-
 .../main/java/brooklyn/util/ResourceUtils.java  |    4 +-
 .../util/task/BasicExecutionContext.java        |    2 +-
 .../java/brooklyn/util/task/CompoundTask.java   |    2 +-
 .../util/task/DynamicSequentialTask.java        |    2 +-
 .../brooklyn/util/text/TemplateProcessor.java   |    2 +-
 .../management/ManagementContextInjectable.java |   33 +
 .../AbstractBrooklynClassLoadingContext.java    |   82 ++
 .../BrooklynClassLoadingContextSequential.java  |  136 +++
 ...ssLoaderFromBrooklynClassLoadingContext.java |   66 ++
 .../JavaBrooklynClassLoadingContext.java        |  123 ++
 .../OsgiBrooklynClassLoadingContext.java        |  147 +++
 .../BasicEntitlementClassDefinition.java        |   56 +
 .../entitlement/EntitlementManagerAdapter.java  |  133 +++
 .../entitlement/EntitlementPredicates.java      |   40 +
 .../management/entitlement/Entitlements.java    |  418 +++++++
 .../entitlement/NotEntitledException.java       |   44 +
 .../entitlement/PerUserEntitlementManager.java  |  100 ++
 .../PerUserEntitlementManagerWithDefault.java   |   31 +
 .../entitlement/WebEntitlementContext.java      |   57 +
 .../core/management/ha/BasicMasterChooser.java  |  204 ++++
 .../ha/HighAvailabilityManagerImpl.java         | 1105 ++++++++++++++++++
 .../ha/ManagementPlaneSyncRecordDeltaImpl.java  |  122 ++
 ...ntPlaneSyncRecordPersisterToObjectStore.java |  355 ++++++
 .../core/management/ha/MasterChooser.java       |   40 +
 .../core/management/ha/OsgiManager.java         |  312 +++++
 .../internal/AbstractManagementContext.java     |  485 ++++++++
 .../internal/AbstractSubscriptionManager.java   |  141 +++
 .../core/management/internal/AccessManager.java |   41 +
 .../internal/AsyncCollectionChangeAdapter.java  |   83 ++
 .../internal/BasicSubscriptionContext.java      |  180 +++
 .../internal/BrooklynGarbageCollector.java      |  626 ++++++++++
 .../internal/BrooklynObjectManagementMode.java  |   31 +
 .../internal/BrooklynObjectManagerInternal.java |   36 +
 .../internal/CollectionChangeListener.java      |   24 +
 .../core/management/internal/EffectorUtils.java |  356 ++++++
 .../internal/EntityChangeListener.java          |   79 ++
 .../internal/EntityManagementSupport.java       |  478 ++++++++
 .../internal/EntityManagementUtils.java         |  326 ++++++
 .../internal/EntityManagerInternal.java         |   32 +
 ...PropertyChangeToCollectionChangeAdapter.java |   65 ++
 .../management/internal/LocalAccessManager.java |  111 ++
 .../management/internal/LocalEntityManager.java |  818 +++++++++++++
 .../internal/LocalLocationManager.java          |  463 ++++++++
 .../internal/LocalManagementContext.java        |  421 +++++++
 .../internal/LocalSubscriptionManager.java      |  292 +++++
 .../management/internal/LocalUsageManager.java  |  430 +++++++
 .../internal/LocationManagerInternal.java       |   28 +
 .../internal/ManagementContextInternal.java     |  122 ++
 .../internal/ManagementTransitionInfo.java      |   48 +
 .../internal/ManagementTransitionMode.java      |  127 ++
 .../internal/NonDeploymentAccessManager.java    |   98 ++
 .../internal/NonDeploymentEntityManager.java    |  196 ++++
 .../internal/NonDeploymentLocationManager.java  |  146 +++
 .../NonDeploymentManagementContext.java         |  639 ++++++++++
 .../internal/NonDeploymentUsageManager.java     |  132 +++
 .../internal/QueueingSubscriptionManager.java   |  148 +++
 .../core/management/internal/Subscription.java  |   66 ++
 .../internal/SubscriptionTracker.java           |  137 +++
 .../core/management/internal/UsageListener.java |  103 ++
 .../core/management/internal/UsageManager.java  |  166 +++
 .../core/management/usage/ApplicationUsage.java |  126 ++
 .../core/management/usage/LocationUsage.java    |  135 +++
 .../location/basic/AbstractLocation.java        |    4 +-
 .../location/basic/BasicLocationRegistry.java   |    2 +-
 .../location/basic/ByonLocationResolver.java    |    2 +-
 .../test/java/brooklyn/BrooklynVersionTest.java |    5 +-
 .../brooklyn/camp/lite/CampYamlLiteTest.java    |    4 +-
 .../catalog/internal/CatalogDtoTest.java        |    2 +-
 .../catalog/internal/CatalogScanTest.java       |    2 +-
 .../catalog/internal/CatalogVersioningTest.java |    2 +-
 .../entity/BrooklynAppLiveTestSupport.java      |    2 +-
 .../entity/BrooklynAppUnitTestSupport.java      |    2 +-
 .../brooklyn/entity/EffectorMetadataTest.java   |    2 +-
 .../entity/EffectorSayHiGroovyTest.groovy       |    2 +-
 .../java/brooklyn/entity/EffectorSayHiTest.java |    2 +-
 .../downloads/BasicDownloadsRegistryTest.java   |    3 +-
 .../DownloadProducerFromLocalRepoTest.java      |    3 +-
 .../DownloadProducerFromPropertiesTest.java     |    3 +-
 .../entity/effector/EffectorBasicTest.java      |    4 +-
 .../ApplicationBuilderOverridingTest.java       |    2 +-
 .../entity/proxying/EntityProxyTest.java        |    2 +-
 .../proxying/InternalEntityFactoryTest.java     |    2 +-
 .../rebind/ActivePartialRebindVersionTest.java  |    4 +-
 .../entity/rebind/RebindCatalogEntityTest.java  |    2 +-
 .../entity/rebind/RebindCatalogItemTest.java    |    4 +-
 ...talogWhenCatalogPersistenceDisabledTest.java |    2 +-
 .../entity/rebind/RebindEntityTest.java         |    2 +-
 .../entity/rebind/RebindFailuresTest.java       |    2 +-
 .../brooklyn/entity/rebind/RebindFeedTest.java  |    2 +-
 .../entity/rebind/RebindFeedWithHaTest.java     |    2 +-
 .../entity/rebind/RebindTestFixture.java        |    4 +-
 .../brooklyn/entity/rebind/RebindTestUtils.java |    6 +-
 .../persister/XmlMementoSerializerTest.java     |    5 +-
 .../transformer/CompoundTransformerTest.java    |    2 +-
 .../event/feed/ConfigToAttributesTest.java      |    4 +-
 .../entitlement/AcmeEntitlementManager.java     |   50 -
 .../entitlement/AcmeEntitlementManagerTest.java |   59 -
 .../AcmeEntitlementManagerTestFixture.java      |  155 ---
 .../entitlement/EntitlementsTest.java           |  207 ----
 .../entitlement/EntityEntitlementTest.java      |  183 ---
 ...PerUserEntitlementManagerPropertiesTest.java |   52 -
 .../HighAvailabilityManagerFileBasedTest.java   |   47 -
 ...ilabilityManagerInMemoryIntegrationTest.java |   96 --
 .../ha/HighAvailabilityManagerInMemoryTest.java |  144 ---
 .../HighAvailabilityManagerSplitBrainTest.java  |  472 --------
 .../ha/HighAvailabilityManagerTestFixture.java  |  284 -----
 .../brooklyn/management/ha/HotStandbyTest.java  |  665 -----------
 .../ha/ImmutableManagementPlaneSyncRecord.java  |   57 -
 ...agementPlaneSyncRecordPersisterInMemory.java |   99 --
 .../management/ha/MasterChooserTest.java        |  146 ---
 .../ha/MutableManagementPlaneSyncRecord.java    |   62 -
 .../management/ha/TestEntityFailingRebind.java  |   55 -
 .../brooklyn/management/ha/WarmStandbyTest.java |  153 ---
 .../management/internal/AccessManagerTest.java  |  145 ---
 .../internal/EntityExecutionManagerTest.java    |  479 --------
 .../LocalManagementContextInstancesTest.java    |   86 --
 .../internal/LocalManagementContextTest.java    |  126 --
 .../internal/LocalSubscriptionManagerTest.java  |  181 ---
 .../brooklyn/management/osgi/OsgiPathTest.java  |  105 --
 .../management/osgi/OsgiStandaloneTest.java     |  259 ----
 .../management/osgi/OsgiTestResources.java      |   74 --
 .../osgi/OsgiVersionMoreEntityTest.java         |  459 --------
 .../EntityCleanupLongevityTestFixture.java      |    8 +-
 .../brooklyn/util/task/ssh/SshTasksTest.java    |    2 +-
 .../util/task/system/SystemTasksTest.java       |    2 +-
 .../util/text/TemplateProcessorTest.java        |    2 +-
 .../entitlement/AcmeEntitlementManager.java     |   52 +
 .../entitlement/AcmeEntitlementManagerTest.java |   60 +
 .../AcmeEntitlementManagerTestFixture.java      |  158 +++
 .../entitlement/EntitlementsTest.java           |  208 ++++
 .../entitlement/EntityEntitlementTest.java      |  185 +++
 ...PerUserEntitlementManagerPropertiesTest.java |   52 +
 .../HighAvailabilityManagerFileBasedTest.java   |   47 +
 ...ilabilityManagerInMemoryIntegrationTest.java |   96 ++
 .../ha/HighAvailabilityManagerInMemoryTest.java |  145 +++
 .../HighAvailabilityManagerSplitBrainTest.java  |  474 ++++++++
 .../ha/HighAvailabilityManagerTestFixture.java  |  287 +++++
 .../core/management/ha/HotStandbyTest.java      |  667 +++++++++++
 .../ha/ImmutableManagementPlaneSyncRecord.java  |   57 +
 ...agementPlaneSyncRecordPersisterInMemory.java |  100 ++
 .../core/management/ha/MasterChooserTest.java   |  146 +++
 .../ha/MutableManagementPlaneSyncRecord.java    |   62 +
 .../management/ha/TestEntityFailingRebind.java  |   55 +
 .../core/management/ha/WarmStandbyTest.java     |  155 +++
 .../management/internal/AccessManagerTest.java  |  146 +++
 .../internal/EntityExecutionManagerTest.java    |  478 ++++++++
 .../LocalManagementContextInstancesTest.java    |   87 ++
 .../internal/LocalManagementContextTest.java    |  127 ++
 .../internal/LocalSubscriptionManagerTest.java  |  181 +++
 .../core/management/osgi/OsgiPathTest.java      |  105 ++
 .../management/osgi/OsgiStandaloneTest.java     |  259 ++++
 .../core/management/osgi/OsgiTestResources.java |   74 ++
 .../osgi/OsgiVersionMoreEntityTest.java         |  459 ++++++++
 .../PortForwardManagerLocationResolverTest.java |    2 +-
 ...regatingMachineProvisioningLocationTest.java |    3 +-
 .../basic/ByonLocationResolverTest.java         |    2 +-
 ...ixedListMachineProvisioningLocationTest.java |    2 +-
 .../basic/HostLocationResolverTest.java         |    2 +-
 .../basic/LocalhostLocationResolverTest.java    |    2 +-
 ...ocalhostMachineProvisioningLocationTest.java |    2 +-
 .../LocalhostProvisioningAndAccessTest.java     |    2 +-
 .../location/basic/LocationPredicatesTest.java  |    2 +-
 .../location/basic/LocationRegistryTest.java    |    2 +-
 .../basic/MultiLocationResolverTest.java        |    2 +-
 .../location/basic/MultiLocationTest.java       |    2 +-
 .../SingleMachineLocationResolverTest.java      |    2 +-
 .../SingleMachineProvisioningLocationTest.java  |    3 +-
 .../SshMachineLocationReuseIntegrationTest.java |    2 +-
 .../entity/LocalManagementContextForTests.java  |    5 +-
 ...nMementoPersisterJcloudsObjectStoreTest.java |    2 +-
 ...tyToBlobStorePersistencePerformanceTest.java |    2 +-
 ...ailabilityManagerJcloudsObjectStoreTest.java |    4 +-
 .../JcloudsBlobStoreBasedObjectStoreTest.java   |    2 +-
 .../jclouds/AbstractJcloudsLiveTest.java        |    4 +-
 .../jclouds/BailOutJcloudsLocation.java         |    2 +-
 .../jclouds/BrooklynMachinePoolLiveTest.java    |    3 +-
 .../JcloudsByonLocationResolverAwsLiveTest.java |    4 +-
 ...dsByonLocationResolverSoftlayerLiveTest.java |    2 +-
 .../JcloudsByonLocationResolverTest.java        |    2 +-
 .../jclouds/JcloudsLocationMetadataTest.java    |    3 +-
 .../jclouds/JcloudsLocationResolverTest.java    |    2 +-
 .../location/jclouds/JcloudsLocationTest.java   |    2 +-
 .../jclouds/RebindJcloudsLocationLiveTest.java  |    2 +-
 .../provider/CarrenzaLocationLiveTest.groovy    |    2 +-
 .../zone/AwsAvailabilityZoneExtensionTest.java  |    3 +-
 .../entity/brooklyn/BrooklynMetricsImpl.java    |    4 +-
 .../enricher/DeltaEnrichersTests.groovy         |    2 +-
 .../entity/AbstractGoogleComputeLiveTest.java   |    2 +-
 .../entity/AbstractSoftlayerLiveTest.java       |    2 +-
 ...SoftwareProcessSshDriverIntegrationTest.java |    2 +-
 .../entity/java/VanillaJavaAppRebindTest.java   |    4 +-
 .../pool/ServerPoolLocationResolverTest.java    |    2 +-
 .../entity/pool/ServerPoolRebindTest.java       |    2 +-
 .../entity/software/AbstractDockerLiveTest.java |    2 +-
 .../usage/ApplicationUsageTrackingTest.java     |    7 +-
 .../usage/LocationUsageTrackingTest.java        |    5 +-
 .../usage/RecordingLegacyUsageListener.java     |    7 +-
 .../usage/RecordingUsageListener.java           |    7 +-
 .../management/usage/UsageListenerTest.java     |    8 +-
 .../basic/WinRmMachineLocationLiveTest.java     |    3 +-
 .../mariadb/MariaDbIntegrationTest.java         |    4 +-
 .../postgresql/PostgreSqlIntegrationTest.java   |    3 +-
 .../rubyrep/RubyRepIntegrationTest.java         |    3 +-
 .../storm/StormAbstractCloudLiveTest.java       |    2 +-
 .../nosql/couchbase/CouchbaseOfflineTest.java   |    3 +-
 .../entity/dns/AbstractGeoDnsServiceTest.java   |    2 +-
 .../brooklyn/entity/proxy/UrlMappingTest.java   |    2 +-
 .../nginx/NginxRebindWithHaIntegrationTest.java |    2 +-
 ...namicWebAppClusterRebindIntegrationTest.java |    4 +-
 ...namicWebAppClusterRebindIntegrationTest.java |    3 +-
 .../impl/hazelcast/HazelcastDataGrid.java       |    2 +-
 .../hazelcast/HazelcastDataGridFactory.java     |    3 +-
 .../impl/hazelcast/HazelcastStorageTest.java    |    4 +-
 .../app/SampleLocalhostIntegrationTest.java     |    2 +-
 .../brooklyn/sample/app/SampleUnitTest.java     |    2 +-
 .../BrooklynCampPlatformLauncherAbstract.java   |    2 +-
 .../BrooklynAssemblyTemplateInstantiator.java   |    4 +-
 .../BrooklynComponentTemplateResolver.java      |    6 +-
 .../spi/creation/BrooklynEntityMatcher.java     |    2 +-
 .../brooklyn/spi/dsl/methods/DslComponent.java  |    2 +-
 .../camp/brooklyn/ApplicationsYamlTest.java     |    2 +-
 .../camp/brooklyn/AbstractYamlRebindTest.java   |    2 +-
 .../camp/brooklyn/AbstractYamlTest.java         |    2 +-
 .../BrooklynYamlTypeInstantiatorTest.java       |    2 +-
 .../camp/brooklyn/DslAndRebindYamlTest.java     |    2 +-
 .../camp/brooklyn/EntitiesYamlTest.java         |    2 +-
 ...aWebAppWithDslYamlRebindIntegrationTest.java |    2 +-
 .../brooklyn/camp/brooklyn/ObjectsYamlTest.java |    2 +-
 .../camp/brooklyn/ReferencedYamlTest.java       |    2 +-
 .../catalog/AbstractCatalogXmlTest.java         |    4 +-
 .../CatalogOsgiVersionMoreEntityTest.java       |    2 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java |    4 +-
 .../catalog/CatalogYamlLocationTest.java        |    4 +-
 .../brooklyn/catalog/CatalogYamlPolicyTest.java |    2 +-
 .../catalog/CatalogYamlTemplateTest.java        |    6 +-
 .../org/apache/brooklyn/cli/CloudExplorer.java  |    2 +-
 .../main/java/org/apache/brooklyn/cli/Main.java |    4 +-
 .../brooklyn/launcher/BrooklynLauncher.java     |   10 +-
 .../brooklyn/launcher/BrooklynWebServer.java    |    4 +-
 .../camp/BrooklynCampPlatformLauncher.java      |    4 +-
 .../launcher/config/BrooklynGlobalConfig.java   |    4 +-
 .../BrooklynLauncherHighAvailabilityTest.java   |    2 +-
 .../brooklyn/launcher/BrooklynLauncherTest.java |    4 +-
 .../launcher/BrooklynWebServerTest.java         |    4 +-
 .../brooklyn/launcher/WebAppRunnerTest.java     |    5 +-
 .../blueprints/AbstractBlueprintTest.java       |    2 +-
 .../org/apache/brooklyn/qa/load/LoadTest.java   |    2 +-
 .../ApplicationResourceIntegrationTest.java     |    4 +-
 .../rest/client/BrooklynApiRestClientTest.java  |    4 +-
 .../BrooklynPropertiesSecurityFilter.java       |    4 +-
 .../resources/AbstractBrooklynRestResource.java |    2 +-
 .../brooklyn/rest/resources/AccessResource.java |    4 +-
 .../rest/resources/ApplicationResource.java     |   12 +-
 .../rest/resources/CatalogResource.java         |    4 +-
 .../rest/resources/EffectorResource.java        |    7 +-
 .../rest/resources/EntityConfigResource.java    |    2 +-
 .../brooklyn/rest/resources/EntityResource.java |    8 +-
 .../rest/resources/PolicyConfigResource.java    |    2 +-
 .../brooklyn/rest/resources/SensorResource.java |    2 +-
 .../brooklyn/rest/resources/ServerResource.java |    4 +-
 .../brooklyn/rest/resources/UsageResource.java  |   10 +-
 .../rest/transform/AccessTransformer.java       |    2 +-
 .../rest/util/BrooklynRestResourceUtils.java    |    4 +-
 .../rest/util/DefaultExceptionMapper.java       |    4 +-
 .../util/json/BrooklynJacksonJsonProvider.java  |    2 +-
 .../brooklyn/rest/BrooklynRestApiLauncher.java  |    4 +-
 .../BrooklynRestApiLauncherTestFixture.java     |    2 +-
 .../apache/brooklyn/rest/HaHotCheckTest.java    |    6 +-
 .../rest/resources/CatalogResourceTest.java     |    2 +-
 .../ServerResourceIntegrationTest.java          |    4 +-
 .../rest/resources/ServerResourceTest.java      |    2 +-
 .../rest/resources/UsageResourceTest.java       |    6 +-
 .../rest/testing/BrooklynRestApiTest.java       |    6 +-
 .../util/BrooklynRestResourceUtilsTest.java     |    2 +-
 .../brooklyn/test/BrooklynLeakListener.java     |    2 +-
 369 files changed, 16794 insertions(+), 16765 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 f147303..dbc7869 100644
--- a/core/src/main/java/brooklyn/BrooklynVersion.java
+++ b/core/src/main/java/brooklyn/BrooklynVersion.java
@@ -44,8 +44,8 @@ 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 brooklyn.management.classloading.OsgiBrooklynClassLoadingContext;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.osgi.Osgis;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 be49f7b..c990ed9 100644
--- a/core/src/main/java/brooklyn/basic/AbstractBrooklynObject.java
+++ b/core/src/main/java/brooklyn/basic/AbstractBrooklynObject.java
@@ -24,13 +24,13 @@ 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.proxying.InternalFactory;
 import brooklyn.entity.rebind.RebindManagerImpl;
-import brooklyn.management.internal.ManagementContextInternal;
 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/6602f694/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 06cdd40..d02f1bd 100644
--- a/core/src/main/java/brooklyn/basic/BasicConfigurableObject.java
+++ b/core/src/main/java/brooklyn/basic/BasicConfigurableObject.java
@@ -22,12 +22,12 @@ import org.apache.brooklyn.api.entity.trait.Configurable;
 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 brooklyn.camp.brooklyn.api.HasBrooklynManagementContext;
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.config.ConfigMap;
-import brooklyn.management.ManagementContextInjectable;
 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/6602f694/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/CatalogPredicates.java b/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
index a873726..07dcbe0 100644
--- a/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
+++ b/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
@@ -30,9 +30,9 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
 
 import brooklyn.catalog.internal.CatalogUtils;
-import brooklyn.management.entitlement.Entitlements;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index f129f4c..88474e7 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -52,6 +52,7 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
@@ -59,7 +60,6 @@ import brooklyn.config.BrooklynServerConfig;
 
 import org.apache.brooklyn.location.basic.BasicLocationRegistry;
 
-import brooklyn.management.internal.ManagementContextInternal;
 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/6602f694/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java
index 3acd165..b81b439 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java
@@ -38,9 +38,9 @@ import org.apache.brooklyn.api.entity.Entity;
 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 brooklyn.entity.basic.ApplicationBuilder;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.javalang.AggregateClassLoader;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
index b6c62a7..25fabff 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
@@ -25,11 +25,11 @@ import java.util.List;
 import java.util.Map;
 
 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.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.javalang.AggregateClassLoader;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
index 42d8688..07eac9b 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
@@ -27,10 +27,10 @@ import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 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 brooklyn.config.BrooklynServerConfig;
-import brooklyn.management.ManagementContextInjectable;
-import brooklyn.management.internal.ManagementContextInternal;
 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/6602f694/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
index e8cd0d3..302e17c 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
@@ -34,16 +34,16 @@ import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.core.management.classloading.BrooklynClassLoadingContextSequential;
+import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
+import org.apache.brooklyn.core.management.classloading.OsgiBrooklynClassLoadingContext;
+import org.apache.brooklyn.core.management.ha.OsgiManager;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 
 import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
 import brooklyn.config.BrooklynLogging;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.rebind.RebindManagerImpl.RebindTracker;
-import brooklyn.management.classloading.BrooklynClassLoadingContextSequential;
-import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
-import brooklyn.management.classloading.OsgiBrooklynClassLoadingContext;
-import brooklyn.management.ha.OsgiManager;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 f082496..a865d5b 100644
--- a/core/src/main/java/brooklyn/config/BrooklynServerPaths.java
+++ b/core/src/main/java/brooklyn/config/BrooklynServerPaths.java
@@ -24,11 +24,11 @@ import java.util.Map;
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.commons.io.FileUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/entity/basic/AbstractApplication.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/AbstractApplication.java b/core/src/main/java/brooklyn/entity/basic/AbstractApplication.java
index 65ef309..79c2ef1 100644
--- a/core/src/main/java/brooklyn/entity/basic/AbstractApplication.java
+++ b/core/src/main/java/brooklyn/entity/basic/AbstractApplication.java
@@ -24,13 +24,13 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ServiceStateLogic.ServiceProblemsLogic;
 import brooklyn.entity.trait.StartableMethods;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.RuntimeInterruptedException;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 26ced4a..bf29732 100644
--- a/core/src/main/java/brooklyn/entity/basic/AbstractEffector.java
+++ b/core/src/main/java/brooklyn/entity/basic/AbstractEffector.java
@@ -26,13 +26,13 @@ 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.management.internal.EffectorUtils;
 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.management.internal.EffectorUtils;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.task.DynamicSequentialTask;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 89a691e..dc005d8 100644
--- a/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
+++ b/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
@@ -53,6 +53,10 @@ import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.EntityAdjunct;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+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.commons.lang3.builder.EqualsBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -80,10 +84,6 @@ import brooklyn.internal.storage.impl.BasicReference;
 
 import org.apache.brooklyn.location.basic.Locations;
 
-import brooklyn.management.internal.EffectorUtils;
-import brooklyn.management.internal.EntityManagementSupport;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.management.internal.SubscriptionTracker;
 import brooklyn.policy.basic.AbstractEntityAdjunct;
 import brooklyn.policy.basic.AbstractEntityAdjunct.AdjunctTagSupport;
 import brooklyn.policy.basic.AbstractPolicy;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/entity/basic/AbstractGroupImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/AbstractGroupImpl.java b/core/src/main/java/brooklyn/entity/basic/AbstractGroupImpl.java
index 07287dc..79abdb0 100644
--- a/core/src/main/java/brooklyn/entity/basic/AbstractGroupImpl.java
+++ b/core/src/main/java/brooklyn/entity/basic/AbstractGroupImpl.java
@@ -28,11 +28,11 @@ 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.proxying.EntitySpec;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.internal.BrooklynFeatureEnablement;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.SetFromLiveMap;
 
 import com.google.common.base.Optional;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 b614e0f..6032fff 100644
--- a/core/src/main/java/brooklyn/entity/basic/BrooklynShutdownHooks.java
+++ b/core/src/main/java/brooklyn/entity/basic/BrooklynShutdownHooks.java
@@ -26,11 +26,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.trait.Startable;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.exceptions.Exceptions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 2fe8d46..7ffe8a0 100644
--- a/core/src/main/java/brooklyn/entity/basic/DynamicGroupImpl.java
+++ b/core/src/main/java/brooklyn/entity/basic/DynamicGroupImpl.java
@@ -28,13 +28,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.management.Task;
+import org.apache.brooklyn.core.management.internal.CollectionChangeListener;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.BrooklynLogging;
 import brooklyn.config.BrooklynLogging.LoggingLevel;
-import brooklyn.management.internal.CollectionChangeListener;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.GroovyJavaMethods;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.task.Tasks;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 1f09903..c768726 100644
--- a/core/src/main/java/brooklyn/entity/basic/Entities.java
+++ b/core/src/main/java/brooklyn/entity/basic/Entities.java
@@ -59,6 +59,11 @@ import org.apache.brooklyn.api.management.TaskAdaptable;
 import org.apache.brooklyn.api.management.TaskFactory;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.management.internal.EffectorUtils;
+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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -75,11 +80,6 @@ import brooklyn.event.basic.DependentConfiguration;
 import org.apache.brooklyn.location.basic.LocationInternal;
 import org.apache.brooklyn.location.basic.Locations;
 
-import brooklyn.management.internal.EffectorUtils;
-import brooklyn.management.internal.EntityManagerInternal;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.management.internal.NonDeploymentManagementContext;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 a735750..085079f 100644
--- a/core/src/main/java/brooklyn/entity/basic/EntityInternal.java
+++ b/core/src/main/java/brooklyn/entity/basic/EntityInternal.java
@@ -32,10 +32,10 @@ import org.apache.brooklyn.api.management.ExecutionContext;
 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 brooklyn.basic.BrooklynObjectInternal;
 import brooklyn.config.ConfigKey;
-import brooklyn.management.internal.EntityManagementSupport;
 import brooklyn.util.config.ConfigBag;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 a25d17e..63b49b5 100644
--- a/core/src/main/java/brooklyn/entity/basic/EntityTransientCopyInternal.java
+++ b/core/src/main/java/brooklyn/entity/basic/EntityTransientCopyInternal.java
@@ -37,12 +37,12 @@ import org.apache.brooklyn.api.management.ManagementContext;
 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 brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.entity.basic.EntityInternal.FeedSupport;
 import brooklyn.entity.proxying.EntityProxyImpl;
-import brooklyn.management.internal.EntityManagementSupport;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Maybe;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 4d07344..121f70f 100644
--- a/core/src/main/java/brooklyn/entity/basic/MethodEffector.java
+++ b/core/src/main/java/brooklyn/entity/basic/MethodEffector.java
@@ -27,12 +27,12 @@ import java.util.Map;
 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.codehaus.groovy.runtime.MethodClosure;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.annotation.EffectorParam;
-import brooklyn.management.internal.EffectorUtils;
 import brooklyn.util.GroovyJavaMethods;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.flags.TypeCoercions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 b7ce04f..eef3cf9 100644
--- a/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java
+++ b/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java
@@ -23,14 +23,14 @@ import java.util.Map;
 
 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.effector.Effectors.EffectorBuilder;
-import brooklyn.management.internal.EntityManagementUtils;
-import brooklyn.management.internal.EntityManagementUtils.CreationResult;
 import brooklyn.util.config.ConfigBag;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 e7657ea..6094686 100644
--- a/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
+++ b/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
@@ -27,16 +27,18 @@ 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.api.management.TaskAdaptable;
+import org.apache.brooklyn.core.management.internal.EffectorUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.ConfigKeys;
+
 import org.apache.brooklyn.location.basic.Machines;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 import org.apache.brooklyn.location.basic.WinRmMachineLocation;
-import brooklyn.management.internal.EffectorUtils;
+
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.javalang.Reflections;
 import brooklyn.util.task.DynamicSequentialTask;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 1eccb92..647ad76 100644
--- a/core/src/main/java/brooklyn/entity/proxying/EntityProxyImpl.java
+++ b/core/src/main/java/brooklyn/entity/proxying/EntityProxyImpl.java
@@ -33,6 +33,9 @@ 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.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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,9 +45,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.management.internal.EffectorUtils;
-import brooklyn.management.internal.EntityManagerInternal;
-import brooklyn.management.internal.ManagementTransitionMode;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.task.DynamicTasks;
 import brooklyn.util.task.TaskTags;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 42c532f..c2a4bfd 100644
--- a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
+++ b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
@@ -36,6 +36,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.management.internal.ManagementContextInternal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,7 +46,6 @@ import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/entity/proxying/InternalFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalFactory.java
index 3b77526..8517664 100644
--- a/core/src/main/java/brooklyn/entity/proxying/InternalFactory.java
+++ b/core/src/main/java/brooklyn/entity/proxying/InternalFactory.java
@@ -23,7 +23,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
 import java.lang.reflect.InvocationTargetException;
 import java.util.Map;
 
-import brooklyn.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.javalang.Reflections;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 58c7dfc..10338b8 100644
--- a/core/src/main/java/brooklyn/entity/proxying/InternalLocationFactory.java
+++ b/core/src/main/java/brooklyn/entity/proxying/InternalLocationFactory.java
@@ -25,14 +25,14 @@ import java.util.Map.Entry;
 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.management.internal.LocalLocationManager;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 
 import brooklyn.config.ConfigKey;
 
 import org.apache.brooklyn.location.basic.AbstractLocation;
 import org.apache.brooklyn.location.basic.LocationInternal;
 
-import brooklyn.management.internal.LocalLocationManager;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.flags.FlagUtils;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 52fd7ba..aeb371c 100644
--- a/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
+++ b/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
@@ -26,11 +26,11 @@ 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.management.internal.ManagementContextInternal;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.basic.AbstractEntity;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/entity/rebind/InitialFullRebindIteration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/InitialFullRebindIteration.java b/core/src/main/java/brooklyn/entity/rebind/InitialFullRebindIteration.java
index f9e42b3..8c5eed2 100644
--- a/core/src/main/java/brooklyn/entity/rebind/InitialFullRebindIteration.java
+++ b/core/src/main/java/brooklyn/entity/rebind/InitialFullRebindIteration.java
@@ -26,16 +26,16 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.rebind.RebindExceptionHandler;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
 import org.apache.brooklyn.api.mementos.BrooklynMementoPersister;
+import org.apache.brooklyn.core.management.internal.BrooklynObjectManagementMode;
+import org.apache.brooklyn.core.management.internal.EntityManagerInternal;
+import org.apache.brooklyn.core.management.internal.LocationManagerInternal;
+import org.apache.brooklyn.core.management.internal.ManagementTransitionMode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.BrooklynLogging;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.rebind.persister.PersistenceActivityMetrics;
-import brooklyn.management.internal.BrooklynObjectManagementMode;
-import brooklyn.management.internal.EntityManagerInternal;
-import brooklyn.management.internal.LocationManagerInternal;
-import brooklyn.management.internal.ManagementTransitionMode;
 import brooklyn.util.text.Strings;
 
 import com.google.common.base.Preconditions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 9051242..30803e2 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
@@ -64,6 +64,12 @@ import org.apache.brooklyn.api.mementos.TreeNode;
 import org.apache.brooklyn.api.mementos.BrooklynMementoManifest.EntityMementoManifest;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.management.internal.BrooklynObjectManagementMode;
+import org.apache.brooklyn.core.management.internal.BrooklynObjectManagerInternal;
+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 brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.catalog.internal.CatalogUtils;
@@ -85,12 +91,6 @@ import brooklyn.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.location.basic.AbstractLocation;
 import org.apache.brooklyn.location.basic.LocationInternal;
 
-import brooklyn.management.internal.BrooklynObjectManagementMode;
-import brooklyn.management.internal.BrooklynObjectManagerInternal;
-import brooklyn.management.internal.EntityManagerInternal;
-import brooklyn.management.internal.LocationManagerInternal;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.management.internal.ManagementTransitionMode;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 25a396b..8221472 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
@@ -43,6 +43,8 @@ import org.apache.brooklyn.api.management.ha.MementoCopyMode;
 import org.apache.brooklyn.api.mementos.BrooklynMementoPersister;
 import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
 import org.apache.brooklyn.api.mementos.TreeNode;
+import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -57,8 +59,6 @@ import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils.CreateBackupMod
 import brooklyn.entity.rebind.persister.PersistenceActivityMetrics;
 import brooklyn.entity.rebind.transformer.CompoundTransformer;
 import brooklyn.internal.BrooklynFeatureEnablement;
-import brooklyn.management.ha.HighAvailabilityManagerImpl;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.QuorumCheck;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 9495988..be51b44 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
@@ -48,6 +48,7 @@ import org.apache.brooklyn.api.mementos.BrooklynMementoPersister;
 import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
 import org.apache.brooklyn.api.mementos.CatalogItemMemento;
 import org.apache.brooklyn.api.mementos.Memento;
+import org.apache.brooklyn.core.management.classloading.ClassLoaderFromBrooklynClassLoadingContext;
 
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.ConfigKey;
@@ -59,7 +60,6 @@ import brooklyn.entity.rebind.dto.BrooklynMementoImpl;
 import brooklyn.entity.rebind.dto.BrooklynMementoManifestImpl;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessor;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessorWithLock;
-import brooklyn.management.classloading.ClassLoaderFromBrooklynClassLoadingContext;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.CompoundRuntimeException;
 import brooklyn.util.exceptions.Exceptions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 a171d18..68074e9 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java
@@ -42,6 +42,9 @@ import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
 import org.apache.brooklyn.api.mementos.Memento;
 import org.apache.brooklyn.api.policy.Enricher;
 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 brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.BrooklynServerPaths;
@@ -53,9 +56,6 @@ import brooklyn.entity.rebind.transformer.CompoundTransformerLoader;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 
-import brooklyn.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
-import brooklyn.management.internal.LocalLocationManager;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 82c9c7a..fe5a677 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
@@ -42,6 +42,9 @@ import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingConte
 import org.apache.brooklyn.api.mementos.BrooklynMementoPersister.LookupContext;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
+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 brooklyn.catalog.internal.CatalogBundleDto;
 import brooklyn.catalog.internal.CatalogUtils;
@@ -58,9 +61,6 @@ import brooklyn.entity.rebind.dto.BasicPolicyMemento;
 import brooklyn.entity.rebind.dto.MutableBrooklynMemento;
 import brooklyn.event.basic.BasicAttributeSensor;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.management.classloading.BrooklynClassLoadingContextSequential;
-import brooklyn.management.classloading.ClassLoaderFromBrooklynClassLoadingContext;
-import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.text.Strings;
 import brooklyn.util.xstream.XmlSerializer;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 d5e46cf..0a57b62 100644
--- a/core/src/main/java/brooklyn/event/basic/TemplatedStringAttributeSensorAndConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/TemplatedStringAttributeSensorAndConfigKey.java
@@ -20,12 +20,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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.EntityInternal;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.text.TemplateProcessor;
 
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/internal/storage/DataGridFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/internal/storage/DataGridFactory.java b/core/src/main/java/brooklyn/internal/storage/DataGridFactory.java
index 8513ebb..d5264c1 100644
--- a/core/src/main/java/brooklyn/internal/storage/DataGridFactory.java
+++ b/core/src/main/java/brooklyn/internal/storage/DataGridFactory.java
@@ -18,13 +18,13 @@
  */
 package brooklyn.internal.storage;
 
-import brooklyn.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 
 /**
  * A factory for creating a {@link DataGrid}.
  *
  * Implementations of this interface should have a public no arg constructor; this constructor will be
- * called through reflection in the {@link brooklyn.management.internal.LocalManagementContext}.
+ * called through reflection in the {@link org.apache.brooklyn.core.management.internal.LocalManagementContext}.
  */
 public interface DataGridFactory {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/internal/storage/impl/inmemory/InMemoryDataGridFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/internal/storage/impl/inmemory/InMemoryDataGridFactory.java b/core/src/main/java/brooklyn/internal/storage/impl/inmemory/InMemoryDataGridFactory.java
index 9be9134..4f0fbac 100644
--- a/core/src/main/java/brooklyn/internal/storage/impl/inmemory/InMemoryDataGridFactory.java
+++ b/core/src/main/java/brooklyn/internal/storage/impl/inmemory/InMemoryDataGridFactory.java
@@ -18,9 +18,10 @@
  */
 package brooklyn.internal.storage.impl.inmemory;
 
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+
 import brooklyn.internal.storage.DataGrid;
 import brooklyn.internal.storage.DataGridFactory;
-import brooklyn.management.internal.ManagementContextInternal;
 
 public class InMemoryDataGridFactory implements DataGridFactory {
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/ManagementContextInjectable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ManagementContextInjectable.java b/core/src/main/java/brooklyn/management/ManagementContextInjectable.java
deleted file mode 100644
index 772dee3..0000000
--- a/core/src/main/java/brooklyn/management/ManagementContextInjectable.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.management;
-
-import org.apache.brooklyn.api.management.ManagementContext;
-
-/**
- * Provides a way for the {@link ManagementContext} to be injected directly.
- */
-public interface ManagementContextInjectable {
-
-    /**
-     * Sets the {@link ManagementContext} reference.
-     */
-    public void injectManagementContext(ManagementContext managementContext);
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java
deleted file mode 100644
index fc975eb..0000000
--- a/core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package brooklyn.management.classloading;
-
-import javax.annotation.Nullable;
-
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
-
-import brooklyn.util.guava.Maybe;
-
-import com.google.common.base.Objects;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-public abstract class AbstractBrooklynClassLoadingContext implements BrooklynClassLoadingContext {
-
-    protected final ManagementContext mgmt;
-
-    public AbstractBrooklynClassLoadingContext(ManagementContext mgmt) {
-        this.mgmt = mgmt;
-    }
-
-    @Override
-    public ManagementContext getManagementContext() {
-        return mgmt;
-    }
-    
-    @Override
-    public Class<?> loadClass(String className) {
-        return tryLoadClass(className).get();
-    }
-
-    @Override
-    // this is the only one left for subclasses
-    public abstract Maybe<Class<?>> tryLoadClass(String className);
-
-    @Override
-    public <T> Class<? extends T> loadClass(String className, @Nullable Class<T> supertype) {
-        return tryLoadClass(className, supertype).get();
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    @Override
-    public <T> Maybe<Class<? extends T>> tryLoadClass(String className, @Nullable Class<T> supertype) {
-        Maybe<Class<?>> result = tryLoadClass(className);
-        if (result.isAbsent()) return (Maybe)result;
-        Class<?> clazz = result.get();
-        if (supertype==null || supertype.isAssignableFrom(clazz)) return (Maybe)result;
-        throw new ClassCastException(className+" is not an instance of "+supertype);
-    }
-
-    @Override
-    public abstract String toString();
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(mgmt);
-    }
-    
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof BrooklynClassLoadingContext)) return false;
-        if (!Objects.equal(mgmt, ((BrooklynClassLoadingContext)obj).getManagementContext())) return false;
-        return true;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java b/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java
deleted file mode 100644
index a1270bc..0000000
--- a/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java
+++ /dev/null
@@ -1,136 +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.management.classloading;
-
-import java.net.URL;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableSet;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.guava.Maybe;
-
-import com.google.common.base.Objects;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
-public final class BrooklynClassLoadingContextSequential extends AbstractBrooklynClassLoadingContext {
-
-    private static final Logger log = LoggerFactory.getLogger(BrooklynClassLoadingContextSequential.class);
-    
-    private final List<BrooklynClassLoadingContext> primaries = MutableList.<BrooklynClassLoadingContext>of();
-    // secondaries used to put java classloader last
-    private final Set<BrooklynClassLoadingContext> secondaries = MutableSet.<BrooklynClassLoadingContext>of();
-
-    public BrooklynClassLoadingContextSequential(ManagementContext mgmt, BrooklynClassLoadingContext ...targets) {
-        super(mgmt);
-        for (BrooklynClassLoadingContext target: targets)
-            add(target);
-    }
-    
-    public void add(BrooklynClassLoadingContext target) {
-        if (target instanceof BrooklynClassLoadingContextSequential) {
-            for (BrooklynClassLoadingContext targetN: ((BrooklynClassLoadingContextSequential)target).primaries )
-                add(targetN);
-            for (BrooklynClassLoadingContext targetN: ((BrooklynClassLoadingContextSequential)target).secondaries )
-                addSecondary(targetN);
-        } else {
-            this.primaries.add( target );
-        }
-    }
-
-    public void addSecondary(BrooklynClassLoadingContext target) {
-        if (!(target instanceof JavaBrooklynClassLoadingContext)) {
-            // support for legacy catalog classloader only
-            log.warn("Only Java classloaders should be secondary");
-        }
-        this.secondaries.add( target );
-    }
-    
-    public Maybe<Class<?>> tryLoadClass(String className) {
-        List<Throwable> errors = MutableList.of();
-        for (BrooklynClassLoadingContext target: primaries) {
-            Maybe<Class<?>> clazz = target.tryLoadClass(className);
-            if (clazz.isPresent())
-                return clazz;
-            errors.add( ((Maybe.Absent<?>)clazz).getException() );
-        }
-        boolean noPrimaryErrors = errors.isEmpty();
-        for (BrooklynClassLoadingContext target: secondaries) {
-            Maybe<Class<?>> clazz = target.tryLoadClass(className);
-            if (clazz.isPresent())
-                return clazz;
-            if (noPrimaryErrors)
-                errors.add( ((Maybe.Absent<?>)clazz).getException() );
-        }
-
-        return Maybe.absent(Exceptions.create("Unable to load "+className+" from "+primaries, errors));
-    }
-
-    @Override
-    public URL getResource(String resourceInThatDir) {
-        for (BrooklynClassLoadingContext target: primaries) {
-            URL result = target.getResource(resourceInThatDir);
-            if (result!=null) return result;
-        }
-        for (BrooklynClassLoadingContext target: secondaries) {
-            URL result = target.getResource(resourceInThatDir);
-            if (result!=null) return result;
-        }
-        return null;
-    }
-
-    @Override
-    public Iterable<URL> getResources(String name) {
-        List<Iterable<URL>> resources = Lists.newArrayList();
-        for (BrooklynClassLoadingContext target : primaries) {
-            resources.add(target.getResources(name));
-        }
-        for (BrooklynClassLoadingContext target : secondaries) {
-            resources.add(target.getResources(name));
-        }
-        return Iterables.concat(resources);
-    }
-
-    @Override
-    public String toString() {
-        return "classload:"+primaries+";"+secondaries;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(super.hashCode(), primaries, secondaries);
-    }
-    
-    @Override
-    public boolean equals(Object obj) {
-        if (!super.equals(obj)) return false;
-        if (!(obj instanceof BrooklynClassLoadingContextSequential)) return false;
-        if (!Objects.equal(primaries, ((BrooklynClassLoadingContextSequential)obj).primaries)) return false;
-        if (!Objects.equal(secondaries, ((BrooklynClassLoadingContextSequential)obj).secondaries)) return false;
-        return true;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
deleted file mode 100644
index 104b3a1..0000000
--- a/core/src/main/java/brooklyn/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
+++ /dev/null
@@ -1,66 +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.management.classloading;
-
-import java.net.URL;
-
-import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
-
-public class ClassLoaderFromBrooklynClassLoadingContext extends ClassLoader {
-
-    /** Constructs a {@link ClassLoader} which delegates to the given {@link BrooklynClassLoadingContext} */
-    public static ClassLoader of(BrooklynClassLoadingContext clc) {
-        return new ClassLoaderFromBrooklynClassLoadingContext(clc);
-    }
-    
-    protected final BrooklynClassLoadingContext clc;
-
-    protected ClassLoaderFromBrooklynClassLoadingContext(BrooklynClassLoadingContext clc) {
-        this.clc = clc;
-    }
-    
-    @Override
-    public Class<?> findClass(String className) throws ClassNotFoundException {
-        Class<?> result = clc.loadClass(className);
-        if (result!=null) return result;
-        
-        // last resort. see comment in XStream CompositeClassLoader
-        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
-        if (contextClassLoader != null) {
-            result = contextClassLoader.loadClass(className);
-            if (result!=null) return result;
-        }
-        return null;
-    }
-    
-    @Override
-    protected URL findResource(String name) {
-        URL result = clc.getResource(name);
-        if (result!=null) return result;
-        
-        // last resort. see comment in XStream CompositeClassLoader
-        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
-        if (contextClassLoader != null) {
-            result = contextClassLoader.getResource(name);
-            if (result!=null) return result;
-        }
-        return null;
-    }
-    
-}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..53d31eb
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalEntityManager.java
@@ -0,0 +1,818 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import groovy.util.ObservableList;
+
+import java.lang.reflect.Proxy;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.Group;
+import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.api.entity.proxying.EntityTypeRegistry;
+import org.apache.brooklyn.api.management.AccessController;
+import org.apache.brooklyn.api.management.Task;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.BrooklynLogging;
+import brooklyn.entity.basic.AbstractEntity;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.basic.EntityPredicates;
+import brooklyn.entity.proxying.BasicEntityTypeRegistry;
+import brooklyn.entity.proxying.EntityProxy;
+import brooklyn.entity.proxying.EntityProxyImpl;
+import brooklyn.entity.proxying.InternalEntityFactory;
+import brooklyn.entity.proxying.InternalPolicyFactory;
+import brooklyn.entity.trait.Startable;
+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;
+
+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.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+public class LocalEntityManager implements EntityManagerInternal {
+
+    private static final Logger log = LoggerFactory.getLogger(LocalEntityManager.class);
+
+    private final LocalManagementContext managementContext;
+    private final BasicEntityTypeRegistry entityTypeRegistry;
+    private final InternalEntityFactory entityFactory;
+    private final InternalPolicyFactory policyFactory;
+    
+    /** Entities that have been created, but have not yet begun to be managed */
+    protected final Map<String,Entity> preRegisteredEntitiesById = Collections.synchronizedMap(new WeakHashMap<String, Entity>());
+
+    /** Entities that are in the process of being managed, but where management is not yet complete */
+    protected final Map<String,Entity> preManagedEntitiesById = Collections.synchronizedMap(new WeakHashMap<String, Entity>());
+    
+    /** Proxies of the managed entities */
+    protected final ConcurrentMap<String,Entity> entityProxiesById = Maps.newConcurrentMap();
+    
+    /** Real managed entities */
+    protected final Map<String,Entity> entitiesById = Maps.newLinkedHashMap();
+    
+    /** Management mode for each entity */
+    protected final Map<String,ManagementTransitionMode> entityModesById = Collections.synchronizedMap(Maps.<String,ManagementTransitionMode>newLinkedHashMap());
+
+    /** Proxies of the managed entities */
+    protected final ObservableList entities = new ObservableList();
+    
+    /** Proxies of the managed entities that are applications */
+    protected final Set<Application> applications = Sets.newConcurrentHashSet();
+
+    private final BrooklynStorage storage;
+    private final Map<String,String> entityTypes;
+    private final Set<String> applicationIds;
+
+    public LocalEntityManager(LocalManagementContext managementContext) {
+        this.managementContext = checkNotNull(managementContext, "managementContext");
+        this.storage = managementContext.getStorage();
+        this.entityTypeRegistry = new BasicEntityTypeRegistry();
+        this.policyFactory = new InternalPolicyFactory(managementContext);
+        this.entityFactory = new InternalEntityFactory(managementContext, entityTypeRegistry, policyFactory);
+        
+        entityTypes = storage.getMap("entities");
+        applicationIds = SetFromLiveMap.create(storage.<String,Boolean>getMap("applications"));
+    }
+
+    public InternalEntityFactory getEntityFactory() {
+        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
+        return entityFactory;
+    }
+
+    public InternalPolicyFactory getPolicyFactory() {
+        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
+        return policyFactory;
+    }
+
+    @Override
+    public EntityTypeRegistry getEntityTypeRegistry() {
+        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
+        return entityTypeRegistry;
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T extends Entity> T createEntity(EntitySpec<T> spec) {
+        try {
+            T entity = entityFactory.createEntity(spec);
+            Entity proxy = ((AbstractEntity)entity).getProxy();
+            return (T) checkNotNull(proxy, "proxy for entity %s, spec %s", entity, spec);
+        } catch (Throwable e) {
+            log.warn("Failed to create entity using spec "+spec+" (rethrowing)", e);
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    @Override
+    public <T extends Entity> T createEntity(Map<?,?> config, Class<T> type) {
+        return createEntity(EntitySpec.create(config, type));
+    }
+
+    @Override
+    public <T extends Policy> T createPolicy(PolicySpec<T> spec) {
+        try {
+            return policyFactory.createPolicy(spec);
+        } catch (Throwable e) {
+            log.warn("Failed to create policy using spec "+spec+" (rethrowing)", e);
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    @Override
+    public <T extends Enricher> T createEnricher(EnricherSpec<T> spec) {
+        try {
+            return policyFactory.createEnricher(spec);
+        } catch (Throwable e) {
+            log.warn("Failed to create enricher using spec "+spec+" (rethrowing)", e);
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    @Override
+    public Collection<Entity> getEntities() {
+        return ImmutableList.copyOf(entityProxiesById.values());
+    }
+    
+    @Override
+    public Collection<String> getEntityIds() {
+        return ImmutableList.copyOf(entityProxiesById.keySet());
+    }
+    
+    @Override
+    public Collection<Entity> getEntitiesInApplication(Application application) {
+        Predicate<Entity> predicate = EntityPredicates.applicationIdEqualTo(application.getId());
+        return ImmutableList.copyOf(Iterables.filter(entityProxiesById.values(), predicate));
+    }
+
+    @Override
+    public Collection<Entity> findEntities(Predicate<? super Entity> filter) {
+        return ImmutableList.copyOf(Iterables.filter(entityProxiesById.values(), filter));
+    }
+    
+    @Override
+    public Collection<Entity> findEntitiesInApplication(Application application, Predicate<? super Entity> filter) {
+        Predicate<Entity> predicate = Predicates.and(EntityPredicates.applicationIdEqualTo(application.getId()), filter);
+        return ImmutableList.copyOf(Iterables.filter(entityProxiesById.values(), predicate));
+    }
+
+    @Override
+    public Iterable<Entity> getAllEntitiesInApplication(Application application) {
+        Predicate<Entity> predicate = EntityPredicates.applicationIdEqualTo(application.getId());
+        Iterable<Entity> allentities = Iterables.concat(preRegisteredEntitiesById.values(), preManagedEntitiesById.values(), entityProxiesById.values());
+        Iterable<Entity> result = Iterables.filter(allentities, predicate);
+        return ImmutableSet.copyOf(Iterables.transform(result, new Function<Entity, Entity>() {
+            @Override public Entity apply(Entity input) {
+                return Entities.proxy(input);
+            }}));
+    }
+
+    @Override
+    public Entity getEntity(String id) {
+        return entityProxiesById.get(id);
+    }
+    
+    Collection<Application> getApplications() {
+        return ImmutableList.copyOf(applications);
+    }
+    
+    @Override
+    public boolean isManaged(Entity e) {
+        return (isRunning() && getEntity(e.getId()) != null);
+    }
+    
+    boolean isPreRegistered(Entity e) {
+        return preRegisteredEntitiesById.containsKey(e.getId());
+    }
+    
+    void prePreManage(Entity entity) {
+        if (isPreRegistered(entity)) {
+            log.warn(""+this+" redundant call to pre-pre-manage entity "+entity+"; skipping", 
+                    new Exception("source of duplicate pre-pre-manage of "+entity));
+            return;
+        }
+        preRegisteredEntitiesById.put(entity.getId(), entity);
+    }
+    
+    @Override
+    public ManagementTransitionMode getLastManagementTransitionMode(String itemId) {
+        return entityModesById.get(itemId);
+    }
+    
+    @Override
+    public void setManagementTransitionMode(Entity item, ManagementTransitionMode mode) {
+        entityModesById.put(item.getId(), mode);
+    }
+    
+    // TODO synchronization issues here. We guard with isManaged(), but if another thread executing 
+    // concurrently then the managed'ness could be set after our check but before we do 
+    // onManagementStarting etc. However, we can't just synchronize because we're calling alien code 
+    // (the user might override entity.onManagementStarting etc).
+    // 
+    // TODO We need to do some check about isPreManaged - i.e. is there another thread (or is this a
+    // re-entrant call) where the entity is not yet full managed (i.e. isManaged==false) but we're in
+    // the middle of managing it.
+    // 
+    // TODO Also see LocalLocationManager.manage(Entity), if fixing things here
+    @Override
+    public void manage(Entity e) {
+        if (isManaged(e)) {
+            log.warn(""+this+" redundant call to start management of entity (and descendants of) "+e+"; skipping", 
+                    new Exception("source of duplicate management of "+e));
+            return;
+        }
+        manageRecursive(e, ManagementTransitionMode.guessing(BrooklynObjectManagementMode.NONEXISTENT, BrooklynObjectManagementMode.MANAGED_PRIMARY));
+    }
+
+    @Override
+    public void manageRebindedRoot(Entity item) {
+        ManagementTransitionMode mode = getLastManagementTransitionMode(item.getId());
+        Preconditions.checkNotNull(mode, "Mode not set for rebinding %s", item);
+        manageRecursive(item, mode);
+    }
+    
+    protected void checkManagementAllowed(Entity item) {
+        AccessController.Response access = managementContext.getAccessController().canManageEntity(item);
+        if (!access.isAllowed()) {
+            throw new IllegalStateException("Access controller forbids management of "+item+": "+access.getMsg());
+        }
+    }
+    
+    /* TODO we sloppily use "recursive" to ensure ordering of parent-first in many places
+     * (which may not be necessary but seems like a good idea),
+     * and also to collect many entities when doing a big rebind,
+     * ensuring all have #manageNonRecursive called before calling #onManagementStarted.
+     * 
+     * it would be better to have a manageAll(Map<Entity,ManagementTransitionMode> items)
+     * method which did that in two phases, allowing us to selectively rebind, 
+     * esp when we come to want supporting different modes and different brooklyn nodes.
+     * 
+     * the impl of manageAll could sort them with parents before children,
+     * (and manageRecursive could simply populate a map and delegate to manageAll).
+     * 
+     * manageRebindRoot would then go, and the (few) callers would construct the map.
+     * 
+     * similarly we might want an unmanageAll(), 
+     * although possibly all unmanagement should be recursive, if we assume an entity's ancestors are always at least proxied
+     * (and the non-recursive RO path here could maybe be dropped)
+     */
+    
+    /** Applies management lifecycle callbacks (onManagementStarting, for all beforehand, then onManagementStopped, for all after) */
+    protected void manageRecursive(Entity e, final ManagementTransitionMode initialMode) {
+        checkManagementAllowed(e);
+
+        final List<EntityInternal> allEntities =  Lists.newArrayList();
+        Predicate<EntityInternal> manageEntity = new Predicate<EntityInternal>() { public boolean apply(EntityInternal it) {
+            ManagementTransitionMode mode = getLastManagementTransitionMode(it.getId());
+            if (mode==null) {
+                setManagementTransitionMode(it, mode = initialMode);
+            }
+            
+            Boolean isReadOnlyFromEntity = it.getManagementSupport().isReadOnlyRaw();
+            if (isReadOnlyFromEntity==null) {
+                if (mode.isReadOnly()) {
+                    // should have been marked by rebinder
+                    log.warn("Read-only entity "+it+" not marked as such on call to manage; marking and continuing");
+                }
+                it.getManagementSupport().setReadOnly(mode.isReadOnly());
+            } else {
+                if (!isReadOnlyFromEntity.equals(mode.isReadOnly())) {
+                    log.warn("Read-only status at entity "+it+" ("+isReadOnlyFromEntity+") not consistent with management mode "+mode);
+                }
+            }
+            
+            if (it.getManagementSupport().isDeployed()) {
+                if (mode.wasNotLoaded()) {
+                    // silently bail out
+                    return false;
+                } else {
+                    if (mode.wasPrimary() && mode.isPrimary()) {
+                        // active partial rebind; continue
+                    } else if (mode.wasReadOnly() && mode.isReadOnly()) {
+                        // reload in RO mode
+                    } else {
+                        // on initial non-RO rebind, should not have any deployed instances
+                        log.warn("Already deployed "+it+" when managing "+mode+"/"+initialMode+"; ignoring this and all descendants");
+                        return false;
+                    }
+                }
+            }
+            
+            // check RO status is consistent
+            boolean isNowReadOnly = Boolean.TRUE.equals( ((EntityInternal)it).getManagementSupport().isReadOnly() );
+            if (mode.isReadOnly()!=isNowReadOnly) {
+                throw new IllegalStateException("Read-only status mismatch for "+it+": "+mode+" / RO="+isNowReadOnly);
+            }
+
+            allEntities.add(it);
+            preManageNonRecursive(it, mode);
+            it.getManagementSupport().onManagementStarting( new ManagementTransitionInfo(managementContext, mode) ); 
+            return manageNonRecursive(it, mode);
+        } };
+        boolean isRecursive = true;
+        if (initialMode.wasPrimary() && initialMode.isPrimary()) {
+            // already managed, so this shouldn't be recursive 
+            // (in ActivePartialRebind we cheat, calling in to this method then skipping recursion).
+            // it also falls through to here when doing a redundant promotion,
+            // in that case we *should* be recursive; determine by checking whether a child exists and is preregistered.
+            // the TODO above removing manageRebindRoot in favour of explicit mgmt list would clean this up a lot!
+            Entity aChild = Iterables.getFirst(e.getChildren(), null);
+            if (aChild!=null && isPreRegistered(aChild)) {
+                log.debug("Managing "+e+" in mode "+initialMode+", doing this recursively because a child is preregistered");
+            } else {
+                log.debug("Managing "+e+" but skipping recursion, as mode is "+initialMode);
+                isRecursive = false;
+            }
+        }
+        if (!isRecursive) {
+            manageEntity.apply( (EntityInternal)e );
+        } else {
+            recursively(e, manageEntity);
+        }
+        
+        for (EntityInternal it : allEntities) {
+            if (!it.getManagementSupport().isFullyManaged()) {
+                ManagementTransitionMode mode = getLastManagementTransitionMode(it.getId());
+                ManagementTransitionInfo info = new ManagementTransitionInfo(managementContext, mode);
+                
+                it.getManagementSupport().onManagementStarted(info);
+                managementContext.getRebindManager().getChangeListener().onManaged(it);
+            }
+        }
+    }
+    
+    @Override
+    public void unmanage(final Entity e) {
+        unmanage(e, ManagementTransitionMode.guessing(BrooklynObjectManagementMode.MANAGED_PRIMARY, BrooklynObjectManagementMode.NONEXISTENT));
+    }
+    
+    public void unmanage(final Entity e, final ManagementTransitionMode mode) {
+        unmanage(e, mode, false);
+    }
+    
+    private void unmanage(final Entity e, ManagementTransitionMode mode, boolean hasBeenReplaced) {
+        if (shouldSkipUnmanagement(e)) return;
+        final ManagementTransitionInfo info = new ManagementTransitionInfo(managementContext, mode);
+        
+        if (hasBeenReplaced) {
+            // we are unmanaging an old instance after having replaced it
+            // don't unmanage or even clear its fields, because there might be references to it
+            
+            if (mode.wasReadOnly()) {
+                // if coming *from* read only; nothing needed
+            } else {
+                if (!mode.wasPrimary()) {
+                    log.warn("Unexpected mode "+mode+" for unmanage-replace "+e+" (applying anyway)");
+                }
+                // migrating away or in-place active partial rebind:
+                ((EntityInternal)e).getManagementSupport().onManagementStopping(info);
+                stopTasks(e);
+                ((EntityInternal)e).getManagementSupport().onManagementStopped(info);
+            }
+            // do not remove from maps below, bail out now
+            return;
+            
+        } else if (mode.wasReadOnly() && mode.isNoLongerLoaded()) {
+            // we are unmanaging an instance (secondary); either stopping here or primary destroyed elsewhere
+            ((EntityInternal)e).getManagementSupport().onManagementStopping(info);
+            unmanageNonRecursive(e);
+            stopTasks(e);
+            ((EntityInternal)e).getManagementSupport().onManagementStopped(info);
+            managementContext.getRebindManager().getChangeListener().onUnmanaged(e);
+            if (managementContext.getGarbageCollector() != null) managementContext.getGarbageCollector().onUnmanaged(e);
+            
+        } else if (mode.wasPrimary() && mode.isNoLongerLoaded()) {
+            // unmanaging a primary; currently this is done recursively
+            
+            /* TODO tidy up when it is recursive and when it isn't; if something is being unloaded or destroyed,
+             * that probably *is* recursive, but the old mode might be different if in some cases things are read-only.
+             * or maybe nothing needs to be recursive, we just make sure the callers (e.g. HighAvailabilityModeImpl.clearManagedItems)
+             * call in a good order
+             * 
+             * see notes above about recursive/manage/All/unmanageAll
+             */
+            
+            // Need to store all child entities as onManagementStopping removes a child from the parent entity
+            final List<EntityInternal> allEntities =  Lists.newArrayList();        
+            recursively(e, new Predicate<EntityInternal>() { public boolean apply(EntityInternal it) {
+                if (shouldSkipUnmanagement(it)) return false;
+                allEntities.add(it);
+                it.getManagementSupport().onManagementStopping(info);
+                return true;
+            } });
+
+            for (EntityInternal it : allEntities) {
+                if (shouldSkipUnmanagement(it)) continue;
+                unmanageNonRecursive(it);
+                stopTasks(it);
+            }
+            for (EntityInternal it : allEntities) {
+                it.getManagementSupport().onManagementStopped(info);
+                managementContext.getRebindManager().getChangeListener().onUnmanaged(it);
+                if (managementContext.getGarbageCollector() != null) managementContext.getGarbageCollector().onUnmanaged(e);
+            }
+            
+        } else {
+            log.warn("Invalid mode for unmanage: "+mode+" on "+e+" (ignoring)");
+        }
+        
+        preRegisteredEntitiesById.remove(e.getId());
+        preManagedEntitiesById.remove(e.getId());
+        entityProxiesById.remove(e.getId());
+        entitiesById.remove(e.getId());
+        entityModesById.remove(e.getId());
+    }
+    
+    private void stopTasks(Entity entity) {
+        stopTasks(entity, null);
+    }
+    
+    /** stops all tasks (apart from any current one or its descendants) on this entity,
+     * optionally -- if a timeout is given -- waiting for completion and warning on incomplete tasks */
+    @Beta
+    public void stopTasks(Entity entity, @Nullable Duration timeout) {
+        CountdownTimer timeleft = timeout==null ? null : timeout.countdownTimer();
+        // try forcibly interrupting tasks on managed entities
+        Collection<Exception> exceptions = MutableSet.of();
+        try {
+            Set<Task<?>> tasksCancelled = MutableSet.of();
+            for (Task<?> t: managementContext.getExecutionContext(entity).getTasks()) {
+                if (entity.equals(BrooklynTaskTags.getContextEntity(Tasks.current())) && hasTaskAsAncestor(t, Tasks.current())) {
+                    // don't cancel if we are running inside a task on the target entity and
+                    // the task being considered is one we have submitted -- e.g. on "stop" don't cancel ourselves!
+                    // but if our current task is from another entity we probably do want to cancel them (we are probably invoking unmanage)
+                    continue;
+                }
+                
+                if (!t.isDone()) {
+                    try {
+                        log.debug("Cancelling "+t+" on "+entity);
+                        tasksCancelled.add(t);
+                        t.cancel(true);
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        log.debug("Error cancelling "+t+" on "+entity+" (will warn when all tasks are cancelled): "+e, e);
+                        exceptions.add(e);
+                    }
+                }
+            }
+            
+            if (timeleft!=null) {
+                Set<Task<?>> tasksIncomplete = MutableSet.of();
+                // go through all tasks, not just cancelled ones, in case there are previously cancelled ones which are not complete
+                for (Task<?> t: managementContext.getExecutionContext(entity).getTasks()) {
+                    if (hasTaskAsAncestor(t, Tasks.current()))
+                        continue;
+                    if (!Tasks.blockUntilInternalTasksEnded(t, timeleft.getDurationRemaining())) {
+                        tasksIncomplete.add(t);
+                    }
+                }
+                if (!tasksIncomplete.isEmpty()) {
+                    log.warn("Incomplete tasks when stopping "+entity+": "+tasksIncomplete);
+                }
+                if (log.isTraceEnabled())
+                    log.trace("Cancelled "+tasksCancelled+" tasks for "+entity+", with "+
+                            timeleft.getDurationRemaining()+" remaining (of "+timeout+"): "+tasksCancelled);
+            } else {
+                if (log.isTraceEnabled())
+                    log.trace("Cancelled "+tasksCancelled+" tasks for "+entity+": "+tasksCancelled);
+            }
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            log.warn("Error inspecting tasks to cancel on unmanagement: "+e, e);
+        }
+        if (!exceptions.isEmpty())
+            log.warn("Error when cancelling tasks for "+entity+" on unmanagement: "+Exceptions.create(exceptions));
+    }
+
+    private boolean hasTaskAsAncestor(Task<?> t, Task<?> potentialAncestor) {
+        if (t==null || potentialAncestor==null) return false;
+        if (t.equals(potentialAncestor)) return true;
+        return hasTaskAsAncestor(t.getSubmittedByTask(), potentialAncestor);
+    }
+
+    /**
+     * activates management when effector invoked, warning unless context is acceptable
+     * (currently only acceptable context is "start")
+     */
+    void manageIfNecessary(Entity entity, Object context) {
+        if (!isRunning()) {
+            return; // TODO Still a race for terminate being called, and then isManaged below returning false
+        } else if (((EntityInternal)entity).getManagementSupport().wasDeployed()) {
+            return;
+        } else if (isManaged(entity)) {
+            return;
+        } else if (isPreManaged(entity)) {
+            return;
+        } else if (Boolean.TRUE.equals(((EntityInternal)entity).getManagementSupport().isReadOnly())) {
+            return;
+        } else {
+            Entity rootUnmanaged = entity;
+            while (true) {
+                Entity candidateUnmanagedParent = rootUnmanaged.getParent();
+                if (candidateUnmanagedParent == null || isManaged(candidateUnmanagedParent) || isPreManaged(candidateUnmanagedParent))
+                    break;
+                rootUnmanaged = candidateUnmanagedParent;
+            }
+            if (context == Startable.START.getName())
+                log.info("Activating local management for {} on start", rootUnmanaged);
+            else
+                log.warn("Activating local management for {} due to effector invocation on {}: {}", new Object[]{rootUnmanaged, entity, context});
+            manage(rootUnmanaged);
+        }
+    }
+
+    private void recursively(Entity e, Predicate<EntityInternal> action) {
+        Entity otherPreregistered = preRegisteredEntitiesById.get(e.getId());
+        if (otherPreregistered!=null) {
+            // if something has been pre-registered, prefer it
+            // (e.g. if we recursing through children, we might have a proxy from previous iteration;
+            // the most recent will have been pre-registered)
+            e = otherPreregistered;
+        }
+            
+        boolean success = action.apply( (EntityInternal)e );
+        if (!success) {
+            return; // Don't manage children if action false/unnecessary for parent
+        }
+        for (Entity child : e.getChildren()) {
+            recursively(child, action);
+        }
+    }
+
+    /**
+     * Whether the entity is in the process of being managed.
+     */
+    private synchronized boolean isPreManaged(Entity e) {
+        return preManagedEntitiesById.containsKey(e.getId());
+    }
+
+    /**
+     * Should ensure that the entity is now known about, but should not be accessible from other entities yet.
+     * 
+     * Records that the given entity is about to be managed (used for answering {@link isPreManaged(Entity)}.
+     * Note that refs to the given entity are stored in a a weak hashmap so if the subsequent management
+     * attempt fails then this reference to the entity will eventually be discarded (if no-one else holds 
+     * a reference).
+     */
+    private synchronized boolean preManageNonRecursive(Entity e, ManagementTransitionMode mode) {
+        Entity realE = toRealEntity(e);
+        
+        Object old = preManagedEntitiesById.put(e.getId(), realE);
+        preRegisteredEntitiesById.remove(e.getId());
+        
+        if (old!=null && mode.wasNotLoaded()) {
+            if (old.equals(e)) {
+                log.warn("{} redundant call to pre-start management of entity {}, mode {}; ignoring", new Object[] { this, e, mode });
+            } else {
+                throw new IllegalStateException("call to pre-manage entity "+e+" ("+mode+") but different entity "+old+" already known under that id at "+this);
+            }
+            return false;
+        } else {
+            if (log.isTraceEnabled()) log.trace("{} pre-start management of entity {}, mode {}", 
+                new Object[] { this, e, mode });
+            return true;
+        }
+    }
+
+    /**
+     * Should ensure that the entity is now managed somewhere, and known about in all the lists.
+     * Returns true if the entity has now become managed; false if it was already managed (anything else throws exception)
+     * @param isOrWasReadOnly 
+     */
+    private synchronized boolean manageNonRecursive(Entity e, ManagementTransitionMode mode) {
+        Entity old = entitiesById.get(e.getId());
+        
+        if (old!=null && mode.wasNotLoaded()) {
+            if (old.equals(e)) {
+                log.warn("{} redundant call to start management of entity {}; ignoring", this, e);
+            } else {
+                throw new IllegalStateException("call to manage entity "+e+" ("+mode+") but different entity "+old+" already known under that id at "+this);
+            }
+            return false;
+        }
+        
+        BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(e), 
+            "{} starting management of entity {}", this, e);
+        Entity realE = toRealEntity(e);
+        
+        Entity oldProxy = entityProxiesById.get(e.getId());
+        Entity proxyE;
+        if (oldProxy!=null) {
+            if (mode.wasNotLoaded()) {
+                throw new IllegalStateException("call to manage entity "+e+" from unloaded state ("+mode+") but already had proxy "+oldProxy+" already known under that id at "+this);
+            }
+            // make the old proxy point at this new delegate
+            // (some other tricks done in the call below)
+            ((EntityProxyImpl)(Proxy.getInvocationHandler(oldProxy))).resetDelegate(oldProxy, oldProxy, realE);
+            proxyE = oldProxy;
+        } else {
+            proxyE = toProxyEntityIfAvailable(e);
+        }
+        entityProxiesById.put(e.getId(), proxyE);
+        entityTypes.put(e.getId(), realE.getClass().getName());
+        entitiesById.put(e.getId(), realE);
+
+        preManagedEntitiesById.remove(e.getId());
+        if ((e instanceof Application) && (e.getParent()==null)) {
+            applications.add((Application)proxyE);
+            applicationIds.add(e.getId());
+        }
+        if (!entities.contains(proxyE)) 
+            entities.add(proxyE);
+        
+        if (old!=null && old!=e) {
+            // passing the transition info will ensure the right shutdown steps invoked for old instance
+            unmanage(old, mode, true);
+        }
+        
+        return true;
+    }
+
+    /**
+     * Should ensure that the entity is no longer managed anywhere, remove from all lists.
+     * Returns true if the entity has been removed from management; if it was not previously managed (anything else throws exception) 
+     */
+    private boolean unmanageNonRecursive(Entity e) {
+        /*
+         * When method is synchronized, hit deadlock: 
+         * 1. thread called unmanage() on a member of a group, so we got the lock and called group.removeMember;
+         *    this ties to synchronize on AbstractGroupImpl.members 
+         * 2. another thread was doing AbstractGroupImpl.addMember, which is synchronized on AbstractGroupImpl.members;
+         *    it tries to call Entities.manage(child) which calls LocalEntityManager.getEntity(), which is
+         *    synchronized on this.
+         * 
+         * We MUST NOT call alien code from within the management framework while holding locks. 
+         * The AbstractGroup.removeMember is effectively alien because a user could override it, and because
+         * it is entity specific.
+         * 
+         * TODO Does getting then removing from groups risk this entity being added to other groups while 
+         * this is happening? Should abstractEntity.onManagementStopped or some such remove the entity
+         * from its groups?
+         */
+        
+        if (!getLastManagementTransitionMode(e.getId()).isReadOnly()) {
+            e.clearParent();
+            Collection<Group> groups = e.getGroups();
+            for (Group group : groups) {
+                if (!Entities.isNoLongerManaged(group)) group.removeMember(e);
+            }
+            if (e instanceof Group) {
+                Collection<Entity> members = ((Group)e).getMembers();
+                for (Entity member : members) {
+                    if (!Entities.isNoLongerManaged(member)) member.removeGroup((Group)e);
+                }
+            }
+        } else {
+            log.debug("No relations being updated on unmanage of read only {}", e);
+        }
+
+        synchronized (this) {
+            Entity proxyE = toProxyEntityIfAvailable(e);
+            if (e instanceof Application) {
+                applications.remove(proxyE);
+                applicationIds.remove(e.getId());
+            }
+
+            entities.remove(proxyE);
+            entityProxiesById.remove(e.getId());
+            entityModesById.remove(e.getId());
+            Object old = entitiesById.remove(e.getId());
+
+            entityTypes.remove(e.getId());
+            if (old==null) {
+                log.warn("{} call to stop management of unknown entity (already unmanaged?) {}; ignoring", this, e);
+                return false;
+            } else if (!old.equals(e)) {
+                // shouldn't happen...
+                log.error("{} call to stop management of entity {} removed different entity {}", new Object[] { this, e, old });
+                return true;
+            } else {
+                if (log.isDebugEnabled()) log.debug("{} stopped management of entity {}", this, e);
+                return true;
+            }
+        }
+    }
+
+    void addEntitySetListener(CollectionChangeListener<Entity> listener) {
+        //must notify listener in a different thread to avoid deadlock (issue #378)
+        AsyncCollectionChangeAdapter<Entity> wrappedListener = new AsyncCollectionChangeAdapter<Entity>(managementContext.getExecutionManager(), listener);
+        entities.addPropertyChangeListener(new GroovyObservablesPropertyChangeToCollectionChangeAdapter(wrappedListener));
+    }
+
+    void removeEntitySetListener(CollectionChangeListener<Entity> listener) {
+        AsyncCollectionChangeAdapter<Entity> wrappedListener = new AsyncCollectionChangeAdapter<Entity>(managementContext.getExecutionManager(), listener);
+        entities.removePropertyChangeListener(new GroovyObservablesPropertyChangeToCollectionChangeAdapter(wrappedListener));
+    }
+    
+    private boolean shouldSkipUnmanagement(Entity e) {
+        if (e==null) {
+            log.warn(""+this+" call to unmanage null entity; skipping",  
+                new IllegalStateException("source of null unmanagement call to "+this));
+            return true;
+        }
+        if (!isManaged(e)) {
+            log.warn("{} call to stop management of unknown entity (already unmanaged?) {}; skipping, and all descendants", this, e);
+            return true;
+        }
+        return false;
+    }
+    
+    private Entity toProxyEntityIfAvailable(Entity e) {
+        checkNotNull(e, "entity");
+        
+        if (e instanceof EntityProxy) {
+            return e;
+        } else if (e instanceof AbstractEntity) {
+            Entity result = ((AbstractEntity)e).getProxy();
+            return (result == null) ? e : result;
+        } else {
+            // If we don't already know about the proxy, then use the real thing; presumably it's 
+            // the legacy way of creating the entity so didn't get a preManage() call
+
+            return e;
+        }
+    }
+    
+    private Entity toRealEntity(Entity e) {
+        checkNotNull(e, "entity");
+        
+        if (e instanceof AbstractEntity) {
+            return e;
+        } else {
+            Entity result = toRealEntityOrNull(e.getId());
+            if (result == null) {
+                throw new IllegalStateException("No concrete entity known for entity "+e+" ("+e.getId()+", "+e.getEntityType().getName()+")");
+            }
+            return result;
+        }
+    }
+
+    public boolean isKnownEntityId(String id) {
+        return entitiesById.containsKey(id) || preManagedEntitiesById.containsKey(id) || preRegisteredEntitiesById.containsKey(id);
+    }
+    
+    private Entity toRealEntityOrNull(String id) {
+        Entity result;
+        // prefer the preRegistered and preManaged entities, during hot proxying, they should be newer
+        result = preRegisteredEntitiesById.get(id);
+        if (result==null)
+            result = preManagedEntitiesById.get(id);
+        if (result==null)
+            entitiesById.get(id);
+        return result;
+    }
+    
+    private boolean isRunning() {
+        return managementContext.isRunning();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..54708f5
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalLocationManager.java
@@ -0,0 +1,463 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.Closeable;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.brooklyn.api.location.Location;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.BrooklynLogging;
+import brooklyn.config.BrooklynLogging.LoggingLevel;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.proxying.InternalLocationFactory;
+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;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+
+public class LocalLocationManager implements LocationManagerInternal {
+
+    @Beta /* expect to remove when API returns LocationSpec or similar */
+    public static final ConfigKey<Boolean> CREATE_UNMANAGED = ConfigKeys.newBooleanConfigKey("brooklyn.internal.location.createUnmanaged",
+        "If set on a location or spec, causes the manager to create it in an unmanaged state (for peeking)", false);
+    
+    private static final Logger log = LoggerFactory.getLogger(LocalLocationManager.class);
+
+    private final LocalManagementContext managementContext;
+    private final InternalLocationFactory locationFactory;
+    
+    protected final Map<String,Location> locationsById = Maps.newLinkedHashMap();
+    private final Map<String, Location> preRegisteredLocationsById = Maps.newLinkedHashMap();
+
+    /** Management mode for each location */
+    protected final Map<String,ManagementTransitionMode> locationModesById = Maps.newLinkedHashMap();
+
+    private final BrooklynStorage storage;
+    private Map<String, String> locationTypes;
+
+    private static AtomicLong LOCATION_CNT = new AtomicLong(0);
+    
+    public LocalLocationManager(LocalManagementContext managementContext) {
+        this.managementContext = checkNotNull(managementContext, "managementContext");
+        this.locationFactory = new InternalLocationFactory(managementContext);
+        
+        this.storage = managementContext.getStorage();
+        locationTypes = storage.getMap("locations");
+    }
+
+    public InternalLocationFactory getLocationFactory() {
+        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
+        return locationFactory;
+        
+    }
+
+    @Override
+    public <T extends Location> T createLocation(LocationSpec<T> spec) {
+        try {
+            boolean createUnmanaged = ConfigBag.coerceFirstNonNullKeyValue(CREATE_UNMANAGED, 
+                spec.getConfig().get(CREATE_UNMANAGED), spec.getFlags().get(CREATE_UNMANAGED.getName()));
+            if (createUnmanaged) {
+                spec.removeConfig(CREATE_UNMANAGED);
+            }
+
+            T loc = locationFactory.createLocation(spec);
+            if (!createUnmanaged) {
+                manage(loc);
+            } else {
+                // remove references
+                Location parent = loc.getParent();
+                if (parent!=null) {
+                    ((AbstractLocation)parent).removeChild(loc);
+                }
+                preRegisteredLocationsById.remove(loc.getId());
+            }
+            
+            return loc;
+        } catch (Throwable e) {
+            log.warn("Failed to create location using spec "+spec+" (rethrowing)", e);
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    @Override
+    public <T extends Location> T createLocation(Map<?,?> config, Class<T> type) {
+        return createLocation(LocationSpec.create(config, type));
+    }
+
+    @Override
+    public synchronized Collection<Location> getLocations() {
+        return ImmutableList.copyOf(locationsById.values());
+    }
+    
+    @Override
+    public Collection<String> getLocationIds() {
+        return ImmutableList.copyOf(locationsById.keySet());
+    }
+
+    @Override
+    public synchronized Location getLocation(String id) {
+        return locationsById.get(id);
+    }
+    
+    public synchronized Location getLocationEvenIfPreManaged(String id) {
+        Location result = locationsById.get(id);
+        if (result == null) {
+            result = preRegisteredLocationsById.get(id);
+        }
+        return result;
+    }
+    
+    @Override
+    public boolean isManaged(Location loc) {
+        return (isRunning() && loc != null && getLocation(loc.getId()) != null);
+    }
+    
+    synchronized boolean isPreRegistered(Location loc) {
+        return preRegisteredLocationsById.containsKey(loc.getId());
+    }
+    
+    public boolean isKnownLocationId(String id) {
+        return preRegisteredLocationsById.containsKey(id) || locationsById.containsKey(id);
+    }
+    
+    synchronized void prePreManage(Location loc) {
+        if (isPreRegistered(loc)) {
+            log.warn(""+this+" redundant call to pre-pre-manage location "+loc+"; skipping", 
+                    new Exception("source of duplicate pre-pre-manage of "+loc));
+            return;
+        }
+        preRegisteredLocationsById.put(loc.getId(), loc);
+    }
+    
+    @Override
+    public ManagementTransitionMode getLastManagementTransitionMode(String itemId) {
+        return locationModesById.get(itemId);
+    }
+    
+    @Override
+    public void setManagementTransitionMode(Location item, ManagementTransitionMode mode) {
+        locationModesById.put(item.getId(), mode);
+    }
+
+    // TODO synchronization issues here: see comment in LocalEntityManager.manage(Entity)
+    /** management on creation */
+    @Override
+    public Location manage(Location loc) {
+        if (isManaged(loc)) {
+            // TODO put log.warn back in if/when manage(Location) becomes private; or could even have assert.
+            // Can be stricter about contract.
+            return loc;
+        }
+        
+        Location parent = loc.getParent();
+        if (parent != null && !managementContext.getLocationManager().isManaged(parent)) {
+            log.warn("Parent location "+parent+" of "+loc+" is not managed; attempting to manage it (in future this may be disallowed)");
+            return manage(parent);
+        } else {
+            return manageRecursive(loc, ManagementTransitionMode.guessing(BrooklynObjectManagementMode.NONEXISTENT, BrooklynObjectManagementMode.MANAGED_PRIMARY));
+        }
+    }
+    
+    @Override
+    public void manageRebindedRoot(Location item) {
+        ManagementTransitionMode mode = getLastManagementTransitionMode(item.getId());
+        Preconditions.checkNotNull(mode, "Mode not set for rebinding %s", item);
+        manageRecursive(item, mode);
+    }
+
+    protected void checkManagementAllowed(Location item) {
+        AccessController.Response access = managementContext.getAccessController().canManageLocation(item);
+        if (!access.isAllowed()) {
+            throw new IllegalStateException("Access controller forbids management of "+item+": "+access.getMsg());
+        }        
+    }
+
+    protected Location manageRecursive(Location loc, final ManagementTransitionMode initialMode) {
+        // TODO see comments in LocalEntityManager about recursive management / manageRebindRoot v manageAll
+        
+        AccessController.Response access = managementContext.getAccessController().canManageLocation(loc);
+        if (!access.isAllowed()) {
+            throw new IllegalStateException("Access controller forbids management of "+loc+": "+access.getMsg());
+        }
+
+        long count = LOCATION_CNT.incrementAndGet();
+        if (log.isDebugEnabled()) {
+            String msg = "Managing location " + loc + " ("+initialMode+"), from " + Tasks.current()+" / "+Entitlements.getEntitlementContext();
+            LoggingLevel level = (!initialMode.wasNotLoaded() || initialMode.isReadOnly() ? LoggingLevel.TRACE : LoggingLevel.DEBUG);
+            if (count % 100 == 0) {
+                // include trace periodically in case we get leaks or too much location management
+                BrooklynLogging.log(log, level,
+                    msg, new Exception("Informational stack trace of call to manage location "+loc+" ("+count+" calls; "+getLocations().size()+" currently managed)"));
+            } else {
+                BrooklynLogging.log(log, level, msg);
+            }
+        }
+
+        recursively(loc, new Predicate<AbstractLocation>() { public boolean apply(AbstractLocation it) {
+            ManagementTransitionMode mode = getLastManagementTransitionMode(it.getId());
+            if (mode==null) {
+                setManagementTransitionMode(it, mode = initialMode);
+            }
+            
+            if (it.isManaged()) {
+                if (mode.wasNotLoaded()) {
+                    // silently bail out
+                    return false;
+                } else {
+                    // on rebind, we just replace, fall through to below
+                }
+            }
+            
+            boolean result = manageNonRecursive(it, mode);
+            if (result) {
+                it.setManagementContext(managementContext);
+                if (mode.isPrimary()) {
+                    it.onManagementStarted();
+                    if (mode.isCreating()) {
+                        // Never record event on rebind; this isn't the location (e.g. the VM) being "created"
+                        // so don't tell listeners that.
+                        // TODO The location-event history should be persisted; currently it is lost on
+                        // rebind, unless there is a listener that is persisting the state externally itself.
+                        recordLocationEvent(it, Lifecycle.CREATED);
+                    }
+                }
+                managementContext.getRebindManager().getChangeListener().onManaged(it);
+            }
+            return result;
+        } });
+        return loc;
+    }
+    
+    @Override
+    public void unmanage(final Location loc) {
+        unmanage(loc, ManagementTransitionMode.guessing(BrooklynObjectManagementMode.MANAGED_PRIMARY, BrooklynObjectManagementMode.NONEXISTENT));
+    }
+    
+    public void unmanage(final Location loc, final ManagementTransitionMode mode) {
+        unmanage(loc, mode, false);
+    }
+    
+    private void unmanage(final Location loc, final ManagementTransitionMode mode, boolean hasBeenReplaced) {
+        if (shouldSkipUnmanagement(loc)) return;
+
+        if (hasBeenReplaced) {
+            // we are unmanaging an old instance after having replaced it; 
+            // don't unmanage or even clear its fields, because there might be references to it
+            
+            if (mode.wasReadOnly()) {
+                // if coming *from* read only; nothing needed
+            } else {
+                if (!mode.wasPrimary()) {
+                    log.warn("Unexpected mode "+mode+" for unmanage-replace "+loc+" (applying anyway)");
+                }
+                // migrating away or in-place active partial rebind:
+                managementContext.getRebindManager().getChangeListener().onUnmanaged(loc);
+                if (managementContext.gc != null) managementContext.gc.onUnmanaged(loc);
+            }
+            // do not remove from maps below, bail out now
+            return;
+
+        } else if ((mode.wasPrimary() && mode.isReadOnly()) || (mode.wasReadOnly() && mode.isNoLongerLoaded())) {
+            if (mode.isReadOnly() && mode.wasPrimary()) {
+                // TODO shouldn't this fall into "hasBeenReplaced" above?
+                log.debug("Unmanaging on demotion: "+loc+" ("+mode+")");
+            }
+            // we are unmanaging an instance whose primary management is elsewhere (either we were secondary, or we are being demoted)
+            unmanageNonRecursiveRemoveFromRecords(loc, mode);
+            managementContext.getRebindManager().getChangeListener().onUnmanaged(loc);
+            if (managementContext.gc != null) managementContext.gc.onUnmanaged(loc);
+            unmanageNonRecursiveClearItsFields(loc, mode);
+            
+        } else if (mode.isNoLongerLoaded()) {
+            // Need to store all child entities as onManagementStopping removes a child from the parent entity
+            
+            // As above, see TODO in LocalEntityManager about recursive management / unmanagement v manageAll/unmanageAll
+            recursively(loc, new Predicate<AbstractLocation>() { public boolean apply(AbstractLocation it) {
+                if (shouldSkipUnmanagement(it)) return false;
+                boolean result = unmanageNonRecursiveRemoveFromRecords(it, mode);
+                if (result) {
+                    ManagementTransitionMode mode = getLastManagementTransitionMode(it.getId());
+                    if (mode==null) {
+                        // ad hoc creation e.g. tests
+                        log.debug("Missing transition mode for "+it+" when unmanaging; assuming primary/destroying");
+                        mode = ManagementTransitionMode.guessing(BrooklynObjectManagementMode.MANAGED_PRIMARY, BrooklynObjectManagementMode.NONEXISTENT);
+                    }
+                    if (mode.wasPrimary()) it.onManagementStopped();
+                    managementContext.getRebindManager().getChangeListener().onUnmanaged(it);
+                    if (mode.isDestroying()) recordLocationEvent(it, Lifecycle.DESTROYED);
+                    if (managementContext.gc != null) managementContext.gc.onUnmanaged(it);
+                }
+                unmanageNonRecursiveClearItsFields(loc, mode);
+                return result;
+            } });
+            
+        } else {
+            log.warn("Invalid mode for unmanage: "+mode+" on "+loc+" (ignoring)");
+        }
+        
+        if (loc instanceof Closeable) {
+            Streams.closeQuietly( (Closeable)loc );
+        }
+        
+        locationsById.remove(loc.getId());
+        preRegisteredLocationsById.remove(loc.getId());
+        locationModesById.remove(loc.getId());
+        locationTypes.remove(loc.getId());
+    }
+    
+    /**
+     * Adds this location event to the usage record for the given location (creating the usage 
+     * record if one does not already exist).
+     */
+    private void recordLocationEvent(LocationInternal loc, Lifecycle state) {
+        try {
+            managementContext.getUsageManager().recordLocationEvent(loc, state);
+        } catch (RuntimeInterruptedException e) {
+            throw e;
+        } catch (RuntimeException e) {
+            log.warn("Failed to store location lifecycle event for "+loc+" (ignoring)", e);
+        }
+    }
+
+    private void recursively(Location e, Predicate<AbstractLocation> action) {
+        boolean success = action.apply( (AbstractLocation)e );
+        if (!success) {
+            return; // Don't manage children if action false/unnecessary for parent
+        }
+        for (Location child : e.getChildren()) {
+            recursively(child, action);
+        }
+    }
+
+    /**
+     * Should ensure that the location is now managed somewhere, and known about in all the lists.
+     * Returns true if the location has now become managed; false if it was already managed (anything else throws exception)
+     * @param rebindPrimary true if rebinding primary, false if rebinding as copy, null if creating (not rebinding)
+     */
+    private synchronized boolean manageNonRecursive(Location loc, ManagementTransitionMode mode) {
+        Location old = locationsById.put(loc.getId(), loc);
+        preRegisteredLocationsById.remove(loc.getId());
+
+        locationTypes.put(loc.getId(), loc.getClass().getName());
+        
+        if (old!=null && mode.wasNotLoaded()) {
+            if (old.equals(loc)) {
+                log.warn("{} redundant call to start management of location {}", this, loc);
+            } else {
+                throw new IllegalStateException("call to manage location "+loc+" but different location "+old+" already known under that id at "+this);
+            }
+            return false;
+        }
+
+        if (old!=null && old!=loc) {
+            // passing the transition info will ensure the right shutdown steps invoked for old instance
+            unmanage(old, mode, true);
+        }
+        
+        return true;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private synchronized void unmanageNonRecursiveClearItsFields(Location loc, ManagementTransitionMode mode) {
+        if (mode.isDestroying()) {
+            ((AbstractLocation)loc).setParent(null, true);
+            
+            Location parent = ((AbstractLocation)loc).getParent();
+            if (parent instanceof ProvisioningLocation<?>) {
+                try {
+                    ((ProvisioningLocation)parent).release(loc);
+                } catch (Exception e) {
+                    Exceptions.propagateIfFatal(e);
+                    log.debug("Error releasing "+loc+" in its parent "+parent+": "+e);
+                }
+            }
+        } else {
+            // if not destroying, don't change the parent's children list
+            ((AbstractLocation)loc).setParent(null, false);
+        }
+        // clear config to help with GC; i know you're not supposed to, but this seems to help, else config bag is littered with refs to entities etc
+        // FIXME relies on config().getLocalBag() returning the underlying bag!
+        ((AbstractLocation)loc).config().getLocalBag().clear();
+    }
+    
+    /**
+     * Should ensure that the location is no longer managed anywhere, remove from all lists.
+     * Returns true if the location has been removed from management; if it was not previously managed (anything else throws exception) 
+     */
+    private synchronized boolean unmanageNonRecursiveRemoveFromRecords(Location loc, ManagementTransitionMode mode) {
+        Object old = locationsById.remove(loc.getId());
+        locationTypes.remove(loc.getId());
+        locationModesById.remove(loc.getId());
+        
+        if (old==null) {
+            log.warn("{} call to stop management of unknown location (already unmanaged?) {}; ignoring", this, loc);
+            return false;
+        } else if (!old.equals(loc)) {
+            // shouldn't happen...
+            log.error("{} call to stop management of location {} removed different location {}; ignoring", new Object[] { this, loc, old });
+            return true;
+        } else {
+            if (log.isDebugEnabled()) log.debug("{} stopped management of location {}", this, loc);
+            return true;
+        }
+    }
+
+    private boolean shouldSkipUnmanagement(Location loc) {
+        if (loc==null) {
+            log.warn(""+this+" call to unmanage null location; skipping",  
+                new IllegalStateException("source of null unmanagement call to "+this));
+            return true;
+        }
+        if (!isManaged(loc)) {
+            log.warn("{} call to stop management of unknown location (already unmanaged?) {}; skipping, and all descendants", this, loc);
+            return true;
+        }
+        return false;
+    }
+    
+    private boolean isRunning() {
+        return managementContext.isRunning();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..306dcd8
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalManagementContext.java
@@ -0,0 +1,421 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static brooklyn.util.JavaGroovyEquivalents.elvis;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.brooklyn.api.entity.Application;
+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.AccessController;
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.ExecutionManager;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.SubscriptionManager;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.TaskAdaptable;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.management.ha.OsgiManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.BrooklynProperties.Factory.Builder;
+import brooklyn.entity.drivers.downloads.BasicDownloadsManager;
+import brooklyn.entity.effector.Effectors;
+import brooklyn.entity.proxying.InternalEntityFactory;
+import brooklyn.entity.proxying.InternalLocationFactory;
+import brooklyn.entity.proxying.InternalPolicyFactory;
+import brooklyn.internal.BrooklynFeatureEnablement;
+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;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * A local (single node) implementation of the {@link ManagementContext} API.
+ */
+public class LocalManagementContext extends AbstractManagementContext {
+    
+    private static final Logger log = LoggerFactory.getLogger(LocalManagementContext.class);
+
+    private static final Set<LocalManagementContext> INSTANCES = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<LocalManagementContext, Boolean>()));
+    
+    private final Builder builder;
+    
+    private final List<ManagementContext.PropertiesReloadListener> reloadListeners = new CopyOnWriteArrayList<ManagementContext.PropertiesReloadListener>();
+
+    @VisibleForTesting
+    static Set<LocalManagementContext> getInstances() {
+        synchronized (INSTANCES) {
+            return ImmutableSet.copyOf(INSTANCES);
+        }
+    }
+
+    // Note also called reflectively by BrooklynLeakListener
+    public static void logAll(Logger logger){
+        for (LocalManagementContext context : getInstances()) {
+            logger.warn("Management Context "+context+" running, creation stacktrace:\n" + Throwables.getStackTraceAsString(context.constructionStackTrace));
+        }
+    }
+
+    /** terminates all (best effort); returns count of sessions closed; if exceptions thrown, returns negative number.
+     * semantics might change, particular in dealing with interminable mgmt contexts. */
+    // Note also called reflectively by BrooklynLeakListener
+    @Beta
+    public static int terminateAll() {
+        int closed=0,dangling=0;
+        for (LocalManagementContext context : getInstances()) {
+            try {
+                context.terminate();
+                closed++;
+            }catch (Throwable t) {
+                Exceptions.propagateIfFatal(t);
+                log.warn("Failed to terminate management context", t);
+                dangling++;
+            }
+        }
+        if (dangling>0) return -dangling;
+        return closed;
+    }
+
+    private String managementPlaneId;
+    private String managementNodeId;
+    private BasicExecutionManager execution;
+    private SubscriptionManager subscriptions;
+    private LocalEntityManager entityManager;
+    private final LocalLocationManager locationManager;
+    private final LocalAccessManager accessManager;
+    private final LocalUsageManager usageManager;
+    private OsgiManager osgiManager;
+    
+    public final Throwable constructionStackTrace = new Throwable("for construction stacktrace").fillInStackTrace();
+    
+    private final Map<String, Object> brooklynAdditionalProperties;
+
+    /**
+     * Creates a LocalManagement with default BrooklynProperties.
+     */
+    public LocalManagementContext() {
+        this(BrooklynProperties.Factory.builderDefault());
+    }
+
+    public LocalManagementContext(BrooklynProperties brooklynProperties) {
+        this(brooklynProperties, (DataGridFactory)null);
+    }
+
+    /**
+     * Creates a new LocalManagementContext.
+     *
+     * @param brooklynProperties the BrooklynProperties.
+     * @param datagridFactory the DataGridFactory to use. If this instance is null, it means that the system
+     *                        is going to use BrooklynProperties to figure out which instance to load or otherwise
+     *                        use a default instance.
+     */
+    @VisibleForTesting
+    public LocalManagementContext(BrooklynProperties brooklynProperties, DataGridFactory datagridFactory) {
+        this(Builder.fromProperties(brooklynProperties), datagridFactory);
+    }
+    
+    public LocalManagementContext(Builder builder) {
+        this(builder, null, null);
+    }
+    
+    public LocalManagementContext(Builder builder, DataGridFactory datagridFactory) {
+        this(builder, null, datagridFactory);
+    }
+
+    public LocalManagementContext(Builder builder, Map<String, Object> brooklynAdditionalProperties) {
+        this(builder, brooklynAdditionalProperties, null);
+    }
+    
+    public LocalManagementContext(BrooklynProperties brooklynProperties, Map<String, Object> brooklynAdditionalProperties) {
+        this(Builder.fromProperties(brooklynProperties), brooklynAdditionalProperties, null);
+    }
+    
+    public LocalManagementContext(Builder builder, Map<String, Object> brooklynAdditionalProperties, DataGridFactory datagridFactory) {
+        super(builder.build(), datagridFactory);
+        
+        checkNotNull(configMap, "brooklynProperties");
+        
+        // TODO in a persisted world the planeId may be injected
+        this.managementPlaneId = Strings.makeRandomId(8);
+        this.managementNodeId = Strings.makeRandomId(8);
+        this.builder = builder;
+        this.brooklynAdditionalProperties = brooklynAdditionalProperties;
+        if (brooklynAdditionalProperties != null)
+            configMap.addFromMap(brooklynAdditionalProperties);
+        
+        BrooklynFeatureEnablement.init(configMap);
+        
+        this.locationManager = new LocalLocationManager(this);
+        this.accessManager = new LocalAccessManager();
+        this.usageManager = new LocalUsageManager(this);
+        
+        if (configMap.getConfig(OsgiManager.USE_OSGI)) {
+            this.osgiManager = new OsgiManager(this);
+            osgiManager.start();
+        }
+        
+        INSTANCES.add(this);
+        log.debug("Created management context "+this);
+    }
+
+    @Override
+    public String getManagementPlaneId() {
+        return managementPlaneId;
+    }
+    
+    @Override
+    public String getManagementNodeId() {
+        return managementNodeId;
+    }
+    
+    @Override
+    public void prePreManage(Entity entity) {
+        getEntityManager().prePreManage(entity);
+    }
+
+    @Override
+    public void prePreManage(Location location) {
+        getLocationManager().prePreManage(location);
+    }
+
+    @Override
+    public synchronized Collection<Application> getApplications() {
+        return getEntityManager().getApplications();
+    }
+
+    @Override
+    public void addEntitySetListener(CollectionChangeListener<Entity> listener) {
+        getEntityManager().addEntitySetListener(listener);
+    }
+
+    @Override
+    public void removeEntitySetListener(CollectionChangeListener<Entity> listener) {
+        getEntityManager().removeEntitySetListener(listener);
+    }
+
+    @Override
+    protected void manageIfNecessary(Entity entity, Object context) {
+        getEntityManager().manageIfNecessary(entity, context);
+    }
+
+    @Override
+    public synchronized LocalEntityManager getEntityManager() {
+        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
+
+        if (entityManager == null) {
+            entityManager = new LocalEntityManager(this);
+        }
+        return entityManager;
+    }
+
+    @Override
+    public InternalEntityFactory getEntityFactory() {
+        return getEntityManager().getEntityFactory();
+    }
+
+    @Override
+    public InternalLocationFactory getLocationFactory() {
+        return getLocationManager().getLocationFactory();
+    }
+
+    @Override
+    public InternalPolicyFactory getPolicyFactory() {
+        return getEntityManager().getPolicyFactory();
+    }
+
+    @Override
+    public synchronized LocalLocationManager getLocationManager() {
+        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
+        return locationManager;
+    }
+
+    @Override
+    public synchronized LocalAccessManager getAccessManager() {
+        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
+        return accessManager;
+    }
+
+    @Override
+    public synchronized LocalUsageManager getUsageManager() {
+        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
+        return usageManager;
+    }
+    
+    @Override
+    public synchronized Maybe<OsgiManager> getOsgiManager() {
+        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
+        if (osgiManager==null) return Maybe.absent("OSGi not available in this instance"); 
+        return Maybe.of(osgiManager);
+    }
+
+    @Override
+    public synchronized AccessController getAccessController() {
+        return getAccessManager().getAccessController();
+    }
+    
+    @Override
+    public synchronized  SubscriptionManager getSubscriptionManager() {
+        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
+
+        if (subscriptions == null) {
+            subscriptions = new LocalSubscriptionManager(getExecutionManager());
+        }
+        return subscriptions;
+    }
+
+    @Override
+    public synchronized ExecutionManager getExecutionManager() {
+        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
+
+        if (execution == null) {
+            execution = new BasicExecutionManager(getManagementNodeId());
+            gc = new BrooklynGarbageCollector(configMap, execution, getStorage());
+        }
+        return execution;
+    }
+    
+    @Override
+    public void terminate() {
+        INSTANCES.remove(this);
+        super.terminate();
+        if (osgiManager!=null) {
+            osgiManager.stop();
+            osgiManager = null;
+        }
+        if (usageManager != null) usageManager.terminate();
+        if (execution != null) execution.shutdownNow();
+        if (gc != null) gc.shutdownNow();
+    }
+
+    @Override
+    protected void finalize() {
+        terminate();
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public <T> Task<T> runAtEntity(Map flags, Entity entity, Callable<T> c) {
+        manageIfNecessary(entity, elvis(Arrays.asList(flags.get("displayName"), flags.get("description"), flags, c)));
+        return runAtEntity(entity, Tasks.<T>builder().dynamic(true).body(c).flags(flags).build());
+    }
+
+    protected <T> Task<T> runAtEntity(Entity entity, TaskAdaptable<T> task) {
+        getExecutionContext(entity).submit(task);
+        if (DynamicTasks.getTaskQueuingContext()!=null) {
+            // put it in the queueing context so it appears in the GUI
+            // mark it inessential as this is being invoked from code,
+            // the caller will do 'get' to handle errors
+            TaskTags.markInessential(task);
+            DynamicTasks.getTaskQueuingContext().queue(task.asTask());
+        }
+        return task.asTask();
+    }
+    
+    @Override
+    protected <T> Task<T> runAtEntity(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters) {
+        manageIfNecessary(entity, eff);
+        // prefer to submit this from the current execution context so it sets up correct cross-context chaining
+        ExecutionContext ec = BasicExecutionContext.getCurrentExecutionContext();
+        if (ec == null) {
+            log.debug("Top-level effector invocation: {} on {}", eff, entity);
+            ec = getExecutionContext(entity);
+        }
+        return runAtEntity(entity, Effectors.invocation(entity, eff, parameters));
+    }
+
+    @Override
+    public boolean isManagedLocally(Entity e) {
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return LocalManagementContext.class.getSimpleName()+"["+getManagementPlaneId()+"-"+getManagementNodeId()+"]";
+    }
+
+    @Override
+    public void reloadBrooklynProperties() {
+        log.info("Reloading brooklyn properties from " + builder);
+        if (builder.hasDelegateOriginalProperties())
+            log.warn("When reloading, mgmt context "+this+" properties are fixed, so reload will be of limited utility");
+        
+        BrooklynProperties properties = builder.build();
+        configMap = properties;
+        if (brooklynAdditionalProperties != null) {
+            log.info("Reloading additional brooklyn properties from " + brooklynAdditionalProperties);
+            configMap.addFromMap(brooklynAdditionalProperties);
+        }
+        this.downloadsManager = BasicDownloadsManager.newDefault(configMap);
+        this.entitlementManager = Entitlements.newManager(this, configMap);
+        
+        clearLocationRegistry();
+        
+        BrooklynFeatureEnablement.init(configMap);
+        
+        // Notify listeners that properties have been reloaded
+        for (PropertiesReloadListener listener : reloadListeners) {
+            listener.reloaded();
+        }
+    }
+
+    @VisibleForTesting
+    public void clearLocationRegistry() {
+        // Force reload of location registry
+        this.locationRegistry = null;
+    }
+
+    @Override
+    public void addPropertiesReloadListener(PropertiesReloadListener listener) {
+        reloadListeners.add(checkNotNull(listener, "listener"));
+    }
+
+    @Override
+    public void removePropertiesReloadListener(PropertiesReloadListener listener) {
+        reloadListeners.remove(listener);
+    }
+
+    public void noteStartupComplete() {
+        startupComplete = true;
+    }
+}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..8a555ed
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiVersionMoreEntityTest.java
@@ -0,0 +1,459 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.osgi;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.Framework;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.api.basic.BrooklynObject;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+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.api.management.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+
+import brooklyn.catalog.internal.CatalogEntityItemDto;
+import brooklyn.catalog.internal.CatalogItemBuilder;
+import brooklyn.catalog.internal.CatalogItemDtoAbstract;
+import brooklyn.catalog.internal.CatalogTestUtils;
+import brooklyn.catalog.internal.CatalogUtils;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.effector.Effectors;
+import brooklyn.entity.proxying.InternalEntityFactory;
+import brooklyn.entity.proxying.InternalPolicyFactory;
+
+import org.apache.brooklyn.test.TestResourceUnavailableException;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+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;
+import com.google.common.collect.Iterables;
+
+
+/** 
+ * Tests that OSGi entities load correctly and have the right catalog information set.
+ * Note further tests done elsewhere using CAMP YAML (referring to methods in this class).
+ */
+public class OsgiVersionMoreEntityTest {
+   
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_URL = "classpath:"+OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
+
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_V1_PATH = OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_V1_PATH;
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_V1_URL = "classpath:"+BROOKLYN_TEST_MORE_ENTITIES_V1_PATH;
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_PATH = OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_V2_PATH;
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_URL = "classpath:"+BROOKLYN_TEST_MORE_ENTITIES_V2_PATH;
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH = OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH;
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL = "classpath:"+BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH;
+    
+    public static final String TEST_VERSION = "0.1.0";
+
+    public static final String EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V1 = "Hi BROOKLYN from V1";
+    public static final String EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2 = "HI BROOKLYN FROM V2";
+    public static final String EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2_EVIL_TWIN = "HO BROOKLYN FROM V2 EVIL TWIN";
+    
+    protected LocalManagementContext mgmt;
+    protected TestApplication app;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+        app = TestApplication.Factory.newManagedInstanceForTests(mgmt);
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws BundleException, IOException, InterruptedException {
+        Entities.destroyAll(mgmt);
+    }
+    
+    /**
+     * Test fix for
+     * java.lang.NoClassDefFoundError: brooklyn.event.AttributeSensor not found by io.brooklyn.brooklyn-test-osgi-entities [41]
+     */
+    @Test
+    public void testEntityProxy() throws Exception {
+        File storageTempDir = Os.newTempDir("osgi-standalone");
+        Framework framework = Osgis.newFrameworkStarted(storageTempDir.getAbsolutePath(), true, null);
+        
+        try {
+            ManagementContextInternal managementContext;
+            InternalEntityFactory factory;
+
+            managementContext = new LocalManagementContextForTests();
+            InternalPolicyFactory policyFactory = new InternalPolicyFactory(managementContext);
+            factory = new InternalEntityFactory(managementContext, managementContext.getEntityManager().getEntityTypeRegistry(), policyFactory);
+
+            TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+            Bundle bundle = Osgis.install(framework, BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+            @SuppressWarnings("unchecked")
+            Class<? extends Entity> bundleCls = (Class<? extends Entity>) bundle.loadClass("brooklyn.osgi.tests.SimpleEntityImpl");
+            @SuppressWarnings("unchecked")
+            Class<? extends Entity> bundleInterface = (Class<? extends Entity>) bundle.loadClass("brooklyn.osgi.tests.SimpleEntity");
+
+            @SuppressWarnings("unchecked")
+            EntitySpec<Entity> spec = (EntitySpec<Entity>) (((EntitySpec<Entity>)EntitySpec.create(bundleInterface))).impl(bundleCls);
+            Entity entity = bundleCls.newInstance();
+            factory.createEntityProxy(spec, entity);
+
+            if (managementContext != null) Entities.destroyAll(managementContext);
+        } finally {
+            OsgiStandaloneTest.tearDownOsgiFramework(framework, storageTempDir);
+        }
+    }
+    
+    protected CatalogItem<?, ?> addCatalogItemWithTypeAsName(String type, String version, String ...libraries) {
+        return addCatalogItemWithNameAndType(type, version, type, libraries);
+    }
+    protected CatalogItem<?, ?> addCatalogItemWithNameAndType(String symName, String version, String type, String ...libraries) {
+        return addCatalogItemWithNameAndType(mgmt, symName, version, type, libraries);
+    }
+
+    @SuppressWarnings("deprecation")
+    static CatalogItem<?, ?> addCatalogItemWithNameAndType(ManagementContext mgmt, String symName, String version, String type, String ...libraries) {
+        CatalogEntityItemDto c1 = newCatalogItemWithNameAndType(symName, version, type, libraries);
+        mgmt.getCatalog().addItem(c1);
+        CatalogItem<?, ?> c2 = mgmt.getCatalog().getCatalogItem(symName, version);
+        Preconditions.checkNotNull(c2, "Item "+type+":"+version+" was not found after adding it");
+        return c2;
+    }
+
+    static CatalogEntityItemDto newCatalogItemWithTypeAsName(String type, String version, String ...libraries) {
+        return newCatalogItemWithNameAndType(type, version, type, libraries);
+    }
+    static CatalogEntityItemDto newCatalogItemWithNameAndType(String symName, String version, String type, String ...libraries) {
+        @SuppressWarnings("deprecation")
+        CatalogEntityItemDto c1 = CatalogItemBuilder.newEntity(symName, version)
+                .javaType(type)
+                .libraries(CatalogItemDtoAbstract.parseLibraries(Arrays.asList(libraries)))
+                .build();
+        return c1;
+    }
+
+    protected Entity addItemFromCatalog(CatalogItem<?, ?> c2) {
+        return addItemFromCatalog(mgmt, app, c2);
+    }
+    
+    public static Entity addItemFromCatalog(ManagementContext mgmt, TestApplication parent, CatalogItem<?, ?> c2) {
+        return parent.createAndManageChild( CatalogTestUtils.createEssentialEntitySpec(mgmt, c2) );
+    }
+
+    public static void assertV1MethodCall(Entity me) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        Assert.assertEquals(doMethodCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V1);
+    }
+    public static void assertV2MethodCall(BrooklynObject me) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        Assert.assertEquals(doMethodCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2);
+    }
+    public static void assertV2EvilTwinMethodCall(Entity me) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        Assert.assertEquals(doMethodCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2_EVIL_TWIN);
+    }
+
+    public static Object doMethodCallBrooklyn(BrooklynObject me) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        return me.getClass().getMethod("sayHI", String.class).invoke(me, "Brooklyn");
+    }
+
+    public static void assertV1EffectorCall(Entity me) {
+        Assert.assertEquals(doEffectorCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V1);
+    }
+    public static void assertV2EffectorCall(Entity me) {
+        Assert.assertEquals(doEffectorCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2);
+    }
+    public static void assertV2EvilTwinEffectorCall(Entity me) {
+        Assert.assertEquals(doEffectorCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2_EVIL_TWIN);
+    }
+
+    public static String doEffectorCallBrooklyn(Entity me) {
+        return me.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), ImmutableMap.of("name", "brooklyn")).getUnchecked();
+    }
+
+    public static CatalogItem<?, ?> addMoreEntityV1(ManagementContext mgmt, String versionToRegister) {
+        TestResourceUnavailableException.throwIfResourceUnavailable(OsgiVersionMoreEntityTest.class, BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
+        return addCatalogItemWithNameAndType(mgmt,
+            OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+            versionToRegister,
+            OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+            BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
+    }
+    public static CatalogItem<?, ?> addMoreEntityV2(ManagementContext mgmt, String versionToRegister) {
+        TestResourceUnavailableException.throwIfResourceUnavailable(OsgiVersionMoreEntityTest.class, BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
+        return addCatalogItemWithNameAndType(mgmt,
+            OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+            versionToRegister,
+            OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+            BROOKLYN_TEST_MORE_ENTITIES_V2_URL,
+            BROOKLYN_TEST_OSGI_ENTITIES_URL);
+    }
+    
+    @Test
+    public void testMoreEntitiesV1() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
+
+        CatalogItem<?, ?> c2 = addMoreEntityV1(mgmt, TEST_VERSION);
+        
+        // test load and instantiate
+        Entity me = addItemFromCatalog(c2);
+        Assert.assertEquals(me.getCatalogItemId(), CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION));
+        
+        assertV1MethodCall(me);
+        assertV1EffectorCall(me);
+        
+        // test adding a child gets the right type; this time by entity parent hierarchy
+        BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, c2);
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        Entity me2 = me.addChild(EntitySpec.create( (Class)loader.loadClass(c2.getJavaType()) ));
+        Assert.assertEquals(me2.getCatalogItemId(), CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION));
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    protected PolicySpec<?> getPolicySpec(CatalogItem<?, ?> cp) {
+        BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, cp);
+        PolicySpec spec = PolicySpec.create( (Class)loader.loadClass(cp.getJavaType()) );
+        spec.catalogItemId(cp.getId());
+        return spec;
+    }
+
+    @Test
+    public void testMoreEntitiesV1Policy() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+
+        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
+        
+        // test load and instantiate
+        Entity me = addItemFromCatalog(c2);
+
+        CatalogItem<?, ?> cp = addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY,
+                TEST_VERSION,
+                BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        me.addPolicy(getPolicySpec(cp));
+        
+        Assert.assertEquals(me.getPolicies().size(), 1, "Wrong number of policies: "+me.getPolicies());
+        
+        String catalogItemId = Iterables.getOnlyElement( me.getPolicies() ).getCatalogItemId();
+        Assert.assertNotNull(catalogItemId);
+        // must be the actual source bundle
+        Assert.assertFalse(catalogItemId.equals(me.getCatalogItemId()), "catalog item id is: "+catalogItemId);
+        Assert.assertTrue(catalogItemId.equals(CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY, TEST_VERSION)), "catalog item id is: "+catalogItemId);
+    }
+
+    @Test
+    public void testMoreEntitiesV2FailsWithoutBasicTestOsgiEntitiesBundle() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_PATH);
+
+        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_URL);
+        
+        // test load and instantiate
+        try {
+            Entity me = addItemFromCatalog(c2);
+            Assert.fail("Should have failed, with unresolved dependency; instead got "+me);
+        } catch (Exception e) {
+            Assert.assertTrue(e.toString().toLowerCase().contains("unresolved constraint"), "Missing expected text in error: "+e);
+            Assert.assertTrue(e.toString().toLowerCase().contains("wiring.package"), "Missing expected text in error: "+e);
+            Assert.assertTrue(e.toString().toLowerCase().contains("brooklyn.osgi.tests"), "Missing expected text in error: "+e);
+        }
+    }
+    
+    // V2 works with dependency declared, and can load
+    // and has policy, with policy item catalog ID is reasonable
+    @Test
+    public void testMoreEntitiesV2() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_PATH);
+
+        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_URL,
+                BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        
+        // test load and instantiate
+        Entity me = addItemFromCatalog(c2);
+        Assert.assertEquals(me.getCatalogItemId(), CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION));
+        
+        assertV2MethodCall(me);
+        assertV2EffectorCall(me);
+        Assert.assertEquals(me.getPolicies().size(), 1, "Wrong number of policies: "+me.getPolicies());
+        
+        String catalogItemId = Iterables.getOnlyElement( me.getPolicies() ).getCatalogItemId();
+        Assert.assertNotNull(catalogItemId);
+        // allow either me's bundle (more) or the actual source bundle
+        Assert.assertTrue(catalogItemId.equals(me.getCatalogItemId()) || catalogItemId.startsWith("brooklyn-test-osgi-entities"));
+    }
+
+    @Test
+    public void testMoreEntitiesV1ThenV2GivesV2() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_PATH);
+
+        addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
+        addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        
+        // test load and instantiate
+        Entity me = addItemFromCatalog( mgmt.getCatalog().getCatalogItem(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION) );
+        
+        assertV2MethodCall(me);
+        assertV2EffectorCall(me);
+        Assert.assertEquals(me.getPolicies().size(), 1, "Wrong number of policies: "+me.getPolicies());
+    }
+
+    @Test
+    public void testMoreEntitiesV2ThenV1GivesV1() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_PATH);
+
+        addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
+        
+        // test load and instantiate
+        Entity me = addItemFromCatalog( mgmt.getCatalog().getCatalogItem(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION) );
+        /*
+         * WARNING - Weird maven-bundle-plugin and OSGi behaviour.  Some caveats:
+         * <p>
+         * (1) When "import-package" declares a version, that is the *minimum* version;
+         *     it may be that semantic versioning is applied, so 1.3.1 = [1.3.1,4.0.0);
+         *     or it may be just a minimum 1.3.1 = [1.3.1,) ... i can't find good docs
+         *     [but see http://www.christianposta.com/blog/?p=241]
+         * <p>
+         * (2) Different versions of maven-bundle-plugin do wildly different things.
+         *     * v1.4.0 attaches the version to import-package (so you get the minimum
+         *       which can cause this test to fail);
+         *     * v2.x does not seem to declare the exported package at all in import-package
+         *       (violating the so-called best practice, see
+         *       http://blog.osgi.org/2007/04/importance-of-exporting-nd-importing.html )
+         *     * v2.4.0 gives a huge list in import/export package, with semver ranges;
+         *       but the other versions seem not to list much and they do NOT have versions
+         * <p>
+         * The tests are all working with 2.5.3 but if version dependencies become any
+         * more intertwined maven-bundle-plugin will almost certainly NOT do the wrong
+         * thing because packages do not have versions. (Ironically, 1.4.0 seems the
+         * best behaved, but for the minimum/semver package version behaviour, and
+         * even that wouldn't be so bad if you're doing semver, or if you figure out
+         * how to override with a _versionpolicy tag!)
+         */
+        assertV1MethodCall(me);
+        assertV1EffectorCall(me);
+        Assert.assertEquals(me.getPolicies().size(), 0, "Wrong number of policies: "+me.getPolicies());
+    }
+
+    @Test
+    public void testUnfazedByMoreEntitiesV1AndV2AndV2EvilTwin() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_PATH);
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH);
+
+        addCatalogItemWithNameAndType("v1",
+                TEST_VERSION,
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
+        addCatalogItemWithNameAndType("v2",
+                TEST_VERSION,
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        addCatalogItemWithNameAndType("v2-evil", 
+                TEST_VERSION,
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
+
+        // test osgi finding
+        
+        List<Bundle> bundles = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework())
+            .symbolicName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL).findAll();
+        Assert.assertEquals(bundles.size(), 3, "Wrong list of bundles: "+bundles);
+        
+        Maybe<Bundle> preferredVersion = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework())
+            .symbolicName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL).find();
+        Assert.assertTrue(preferredVersion.isPresent());
+        Assert.assertEquals(preferredVersion.get().getVersion().toString(), "0.2.0");
+        
+        Maybe<Bundle> evilVersion = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework()).
+            preferringFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL).find();
+        Assert.assertTrue(evilVersion.isPresent());
+        Assert.assertEquals(evilVersion.get().getVersion().toString(), "0.2.0");
+
+        // test preferredUrl vs requiredUrl
+
+        Maybe<Bundle> versionIgnoresInvalidPreferredUrl = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework())
+            .symbolicName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL)
+            .version(TEST_VERSION)
+            .preferringFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL).find();
+        Assert.assertTrue(versionIgnoresInvalidPreferredUrl.isPresent());
+        Assert.assertEquals(versionIgnoresInvalidPreferredUrl.get().getVersion().toString(), TEST_VERSION);
+
+        Maybe<Bundle> versionRespectsInvalidRequiredUrl = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework())
+            .symbolicName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL)
+            .version(TEST_VERSION)
+            .requiringFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL).find();
+        Assert.assertFalse(versionRespectsInvalidRequiredUrl.isPresent());
+
+        // test entity resolution
+        
+        Entity v2 = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v2", TEST_VERSION) );
+        assertV2MethodCall(v2);
+        assertV2EffectorCall(v2);
+        Assert.assertEquals(v2.getPolicies().size(), 1, "Wrong number of policies: "+v2.getPolicies());
+
+        Entity v2_evil = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v2-evil", TEST_VERSION) );
+        assertV2EvilTwinMethodCall(v2_evil);
+        assertV2EvilTwinEffectorCall(v2_evil);
+        Assert.assertEquals(v2_evil.getPolicies().size(), 1, "Wrong number of policies: "+v2_evil.getPolicies());
+
+        Entity v1 = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v1", TEST_VERSION) );
+        assertV1MethodCall(v1);
+        assertV1EffectorCall(v1);
+        Assert.assertEquals(v1.getPolicies().size(), 0, "Wrong number of policies: "+v1.getPolicies());
+    }
+
+    // TODO versioning (WIP until #92), install both V1 and V2 with version number, and test that both work
+        
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/access/PortForwardManagerLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/access/PortForwardManagerLocationResolverTest.java b/core/src/test/java/org/apache/brooklyn/location/access/PortForwardManagerLocationResolverTest.java
index 541585f..dfda31e 100644
--- a/core/src/test/java/org/apache/brooklyn/location/access/PortForwardManagerLocationResolverTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/access/PortForwardManagerLocationResolverTest.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.location.access;
 
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
@@ -26,7 +27,6 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 
 public class PortForwardManagerLocationResolverTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/AggregatingMachineProvisioningLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/AggregatingMachineProvisioningLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/AggregatingMachineProvisioningLocationTest.java
index 3cb5ab9..f30665c 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/AggregatingMachineProvisioningLocationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/AggregatingMachineProvisioningLocationTest.java
@@ -27,6 +27,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.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -36,8 +37,6 @@ import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation.LocalhostMachine;
 
-import brooklyn.management.internal.LocalManagementContext;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/ByonLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/ByonLocationResolverTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/ByonLocationResolverTest.java
index 09eb142..c282fa2 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/ByonLocationResolverTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/ByonLocationResolverTest.java
@@ -33,6 +33,7 @@ import javax.annotation.Nullable;
 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.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,7 +45,6 @@ import org.testng.annotations.Test;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.net.Networking;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java
index 73ca57a..9ddc3bf 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java
@@ -31,6 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,7 +50,6 @@ import com.google.common.collect.Lists;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.net.Networking;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/HostLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/HostLocationResolverTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/HostLocationResolverTest.java
index 2ef938d..fff482c 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/HostLocationResolverTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/HostLocationResolverTest.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import java.util.NoSuchElementException;
 
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -33,7 +34,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostLocationResolverTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostLocationResolverTest.java
index 704f5ac..f04e6cd 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostLocationResolverTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostLocationResolverTest.java
@@ -28,6 +28,7 @@ import java.util.NoSuchElementException;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
@@ -36,7 +37,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.text.StringEscapes.JavaStringEscapes;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocationTest.java
index 2bd0d28..cd1df7a 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocationTest.java
@@ -39,9 +39,9 @@ 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.location.PortRange;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.location.geo.HostGeoInfo;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.net.Networking;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostProvisioningAndAccessTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostProvisioningAndAccessTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostProvisioningAndAccessTest.java
index e1dff5e..52aa281 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostProvisioningAndAccessTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostProvisioningAndAccessTest.java
@@ -24,13 +24,13 @@ import static org.testng.Assert.assertTrue;
 import java.util.Arrays;
 
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 
 public class LocalhostProvisioningAndAccessTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/LocationPredicatesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocationPredicatesTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocationPredicatesTest.java
index 219acd2..cfbc5ee 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/LocationPredicatesTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocationPredicatesTest.java
@@ -23,6 +23,7 @@ import static org.testng.Assert.assertTrue;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.testng.annotations.AfterMethod;
@@ -30,7 +31,6 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 
 public class LocationPredicatesTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/LocationRegistryTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocationRegistryTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocationRegistryTest.java
index b1e01ca..c887761 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/LocationRegistryTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocationRegistryTest.java
@@ -20,6 +20,7 @@ package org.apache.brooklyn.location.basic;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,7 +30,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 
 public class LocationRegistryTest {
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/MultiLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/MultiLocationResolverTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/MultiLocationResolverTest.java
index 8953397..8ccf2e4 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/MultiLocationResolverTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/MultiLocationResolverTest.java
@@ -35,6 +35,7 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.location.cloud.AvailabilityZoneExtension;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.Assert;
@@ -44,7 +45,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 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/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/MultiLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/MultiLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/MultiLocationTest.java
index 26423f5..314c81b 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/MultiLocationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/MultiLocationTest.java
@@ -32,9 +32,9 @@ import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.location.cloud.AvailabilityZoneExtension;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.net.Networking;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/SingleMachineLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/SingleMachineLocationResolverTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/SingleMachineLocationResolverTest.java
index a9da0dd..2639db9 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/SingleMachineLocationResolverTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/SingleMachineLocationResolverTest.java
@@ -25,6 +25,7 @@ import java.net.InetAddress;
 import java.util.Map;
 import java.util.NoSuchElementException;
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -32,7 +33,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/location/basic/SingleMachineProvisioningLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/SingleMachineProvisioningLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/SingleMachineProvisioningLocationTest.java
index 7d42e88..7c1e951 100644
--- a/core/src/test/java/org/apache/brooklyn/location/basic/SingleMachineProvisioningLocationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/SingleMachineProvisioningLocationTest.java
@@ -20,6 +20,7 @@ package org.apache.brooklyn.location.basic;
 
 import static org.testng.Assert.assertNotNull;
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -27,8 +28,6 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import brooklyn.management.internal.LocalManagementContext;
-
 public class SingleMachineProvisioningLocationTest {
     
     private static final Logger log = LoggerFactory.getLogger(SingleMachineProvisioningLocation.class);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 1fef80b..6ba27e7 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
@@ -34,8 +34,8 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.internal.ssh.SshTool;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/test/entity/LocalManagementContextForTests.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/test/entity/LocalManagementContextForTests.java b/core/src/test/java/org/apache/brooklyn/test/entity/LocalManagementContextForTests.java
index 775f1a3..808a054 100644
--- a/core/src/test/java/org/apache/brooklyn/test/entity/LocalManagementContextForTests.java
+++ b/core/src/test/java/org/apache/brooklyn/test/entity/LocalManagementContextForTests.java
@@ -18,11 +18,12 @@
  */
 package org.apache.brooklyn.test.entity;
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.ConfigKey;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
 
 /** management context which allows disabling common time-consuming tasks.
  * most instances have:

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BrooklynMementoPersisterJcloudsObjectStoreTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BrooklynMementoPersisterJcloudsObjectStoreTest.java b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BrooklynMementoPersisterJcloudsObjectStoreTest.java
index c720b41..0bf3330 100644
--- a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BrooklynMementoPersisterJcloudsObjectStoreTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BrooklynMementoPersisterJcloudsObjectStoreTest.java
@@ -19,13 +19,13 @@
 package brooklyn.entity.rebind.persister.jclouds;
 
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.rebind.RebindTestUtils;
 import brooklyn.entity.rebind.persister.BrooklynMementoPersisterTestFixture;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/EntityToBlobStorePersistencePerformanceTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/EntityToBlobStorePersistencePerformanceTest.java b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/EntityToBlobStorePersistencePerformanceTest.java
index 78c4545..1085b8b 100644
--- a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/EntityToBlobStorePersistencePerformanceTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/EntityToBlobStorePersistencePerformanceTest.java
@@ -18,11 +18,11 @@
 */
 package brooklyn.entity.rebind.persister.jclouds;
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.rebind.RebindTestUtils;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.qa.performance.EntityPersistencePerformanceTest;
 
 public class EntityToBlobStorePersistencePerformanceTest extends EntityPersistencePerformanceTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
index 92b7a44..3ac15df 100644
--- a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
@@ -18,14 +18,14 @@
  */
 package brooklyn.entity.rebind.persister.jclouds;
 
+import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerTestFixture;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-import brooklyn.management.ha.HighAvailabilityManagerTestFixture;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.text.Identifiers;
 
 @Test(groups={"Live", "Live-sanity"})

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/JcloudsBlobStoreBasedObjectStoreTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/JcloudsBlobStoreBasedObjectStoreTest.java b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/JcloudsBlobStoreBasedObjectStoreTest.java
index 647e514..0e0aa6c 100644
--- a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/JcloudsBlobStoreBasedObjectStoreTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/JcloudsBlobStoreBasedObjectStoreTest.java
@@ -21,6 +21,7 @@ package brooklyn.entity.rebind.persister.jclouds;
 
 import java.util.List;
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,7 +34,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessor;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsLiveTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsLiveTest.java
index b5f4dab..92d5f18 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsLiveTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsLiveTest.java
@@ -33,8 +33,10 @@ import org.testng.annotations.BeforeMethod;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
+
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.management.internal.LocalManagementContext;
+
 import brooklyn.util.exceptions.CompoundRuntimeException;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 7892c8e..c323b7f 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
@@ -26,6 +26,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.jclouds.compute.ComputeService;
 import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.Template;
@@ -39,7 +40,6 @@ import com.google.common.reflect.TypeToken;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.CompoundRuntimeException;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePoolLiveTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePoolLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePoolLiveTest.java
index 3fda145..e4286bc 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePoolLiveTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePoolLiveTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.brooklyn.location.jclouds;
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.location.jclouds.pool.MachineSet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -25,11 +26,9 @@ import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
-
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 import org.apache.brooklyn.location.jclouds.pool.MachinePoolPredicates;
 import org.apache.brooklyn.location.jclouds.pool.ReusableMachineTemplate;
-import brooklyn.management.internal.LocalManagementContext;
 
 public class BrooklynMachinePoolLiveTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverAwsLiveTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverAwsLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverAwsLiveTest.java
index 2f708f3..4112f61 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverAwsLiveTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverAwsLiveTest.java
@@ -28,9 +28,9 @@ import java.util.Set;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
-
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.location.basic.FixedListMachineProvisioningLocation;
-import brooklyn.management.internal.LocalManagementContext;
+
 import brooklyn.util.collections.MutableMap;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverSoftlayerLiveTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverSoftlayerLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverSoftlayerLiveTest.java
index 31831a8..1accbad 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverSoftlayerLiveTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverSoftlayerLiveTest.java
@@ -23,12 +23,12 @@ import static org.testng.Assert.assertTrue;
 
 import java.util.Set;
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.location.basic.FixedListMachineProvisioningLocation;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.MutableMap;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverTest.java
index 3f7c837..0b5d1c6 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverTest.java
@@ -22,6 +22,7 @@ import static org.testng.Assert.fail;
 
 import java.util.NoSuchElementException;
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.location.basic.FixedListMachineProvisioningLocation;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.annotations.AfterMethod;
@@ -29,7 +30,6 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 
 public class JcloudsByonLocationResolverTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationMetadataTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationMetadataTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationMetadataTest.java
index 3c3d4a5..186fa33 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationMetadataTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationMetadataTest.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.location.jclouds;
 import static org.testng.Assert.assertEquals;
 
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -31,8 +32,6 @@ import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.location.basic.LocationConfigKeys;
 
-import brooklyn.management.internal.LocalManagementContext;
-
 import com.google.common.collect.ImmutableSet;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java
index 8cd20b9..02537fe 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java
@@ -25,6 +25,7 @@ import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.location.basic.LocationInternal;
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
@@ -38,7 +39,6 @@ import org.testng.annotations.Test;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.event.basic.MapConfigKey;
 import brooklyn.event.basic.SetConfigKey;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 100f98a..83ffb78 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
@@ -31,6 +31,7 @@ import org.apache.brooklyn.api.location.LocationSpec;
 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.location.cloud.names.CustomMachineNamer;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.jclouds.scriptbuilder.domain.OsFamily;
@@ -58,7 +59,6 @@ import org.apache.brooklyn.location.basic.LocationConfigKeys;
 import org.apache.brooklyn.location.geo.HostGeoInfo;
 import org.apache.brooklyn.location.jclouds.JcloudsLocation.UserCreation;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 e3b749d..6043a09 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
@@ -26,6 +26,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.test.entity.TestApplication;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -34,7 +35,6 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.rebind.RebindTestUtils;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.config.ConfigBag;
 
 import com.google.common.base.Predicates;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/CarrenzaLocationLiveTest.groovy
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/CarrenzaLocationLiveTest.groovy b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/CarrenzaLocationLiveTest.groovy
index 3c23328..9e1e1e9 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/CarrenzaLocationLiveTest.groovy
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/provider/CarrenzaLocationLiveTest.groovy
@@ -30,7 +30,7 @@ import brooklyn.config.BrooklynProperties
 import org.apache.brooklyn.location.basic.SshMachineLocation
 import org.apache.brooklyn.location.jclouds.JcloudsLocation
 import org.apache.brooklyn.location.jclouds.JcloudsSshMachineLocation
-import brooklyn.management.internal.LocalManagementContext
+import org.apache.brooklyn.core.management.internal.LocalManagementContext
 import brooklyn.util.collections.MutableMap
 
 import com.google.common.collect.ImmutableList

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtensionTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtensionTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtensionTest.java
index 84c90ab..cf372d0 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtensionTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtensionTest.java
@@ -24,6 +24,7 @@ import static org.testng.Assert.assertTrue;
 import java.util.List;
 
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.location.jclouds.AbstractJcloudsLiveTest;
 import org.apache.brooklyn.location.jclouds.JcloudsLocation;
 import org.jclouds.domain.LocationScope;
@@ -35,8 +36,6 @@ import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.location.jclouds.JcloudsSshMachineLocation;
 
-import brooklyn.management.internal.LocalManagementContext;
-
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 f3de4dd..93ece4a 100644
--- a/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetricsImpl.java
+++ b/policy/src/main/java/brooklyn/entity/brooklyn/BrooklynMetricsImpl.java
@@ -24,10 +24,10 @@ import java.util.concurrent.ThreadFactory;
 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 brooklyn.entity.basic.AbstractEntity;
-import brooklyn.management.internal.LocalSubscriptionManager;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.task.BasicExecutionManager;
 
 import com.google.common.util.concurrent.ThreadFactoryBuilder;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/policy/src/test/java/brooklyn/enricher/DeltaEnrichersTests.groovy
----------------------------------------------------------------------
diff --git a/policy/src/test/java/brooklyn/enricher/DeltaEnrichersTests.groovy b/policy/src/test/java/brooklyn/enricher/DeltaEnrichersTests.groovy
index 406fc27..e0419f7 100644
--- a/policy/src/test/java/brooklyn/enricher/DeltaEnrichersTests.groovy
+++ b/policy/src/test/java/brooklyn/enricher/DeltaEnrichersTests.groovy
@@ -31,7 +31,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal
 import org.apache.brooklyn.api.event.Sensor
 import brooklyn.event.basic.BasicAttributeSensor
 import org.apache.brooklyn.api.management.SubscriptionContext
-import brooklyn.management.internal.LocalManagementContext
+import org.apache.brooklyn.core.management.internal.LocalManagementContext
 
 class DeltaEnrichersTests {
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/base/src/test/java/brooklyn/entity/AbstractGoogleComputeLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/AbstractGoogleComputeLiveTest.java b/software/base/src/test/java/brooklyn/entity/AbstractGoogleComputeLiveTest.java
index ecc4adc..b3d9c10 100644
--- a/software/base/src/test/java/brooklyn/entity/AbstractGoogleComputeLiveTest.java
+++ b/software/base/src/test/java/brooklyn/entity/AbstractGoogleComputeLiveTest.java
@@ -23,6 +23,7 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -31,7 +32,6 @@ import org.testng.annotations.Test;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.MutableMap;
 
 import com.google.common.base.CaseFormat;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/base/src/test/java/brooklyn/entity/AbstractSoftlayerLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/AbstractSoftlayerLiveTest.java b/software/base/src/test/java/brooklyn/entity/AbstractSoftlayerLiveTest.java
index a8f15f5..46f0d6a 100644
--- a/software/base/src/test/java/brooklyn/entity/AbstractSoftlayerLiveTest.java
+++ b/software/base/src/test/java/brooklyn/entity/AbstractSoftlayerLiveTest.java
@@ -23,6 +23,7 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -35,7 +36,6 @@ import com.google.common.collect.ImmutableMap;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.text.StringShortener;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 485b287..f528f7c 100644
--- a/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessSshDriverIntegrationTest.java
+++ b/software/base/src/test/java/brooklyn/entity/basic/SoftwareProcessSshDriverIntegrationTest.java
@@ -33,6 +33,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.location.LocationSpec;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
@@ -44,7 +45,6 @@ import brooklyn.entity.trait.Startable;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.BrooklynNetworkUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.os.Os;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 41ec5c5..dd2b9b0 100644
--- a/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppRebindTest.java
+++ b/software/base/src/test/java/brooklyn/entity/java/VanillaJavaAppRebindTest.java
@@ -24,6 +24,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.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
@@ -38,8 +39,9 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.java.JavaOptsTest.TestingJavaOptsVanillaJavaAppImpl;
 import brooklyn.entity.rebind.RebindTestUtils;
 import brooklyn.event.basic.Sensors;
+
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-import brooklyn.management.internal.LocalManagementContext;
+
 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/6602f694/software/base/src/test/java/brooklyn/entity/pool/ServerPoolLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/pool/ServerPoolLocationResolverTest.java b/software/base/src/test/java/brooklyn/entity/pool/ServerPoolLocationResolverTest.java
index e6c3ab6..55b1df7 100644
--- a/software/base/src/test/java/brooklyn/entity/pool/ServerPoolLocationResolverTest.java
+++ b/software/base/src/test/java/brooklyn/entity/pool/ServerPoolLocationResolverTest.java
@@ -26,6 +26,7 @@ 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.api.location.LocationSpec;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.Assert;
@@ -43,7 +44,6 @@ import brooklyn.entity.basic.Entities;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.dynamic.DynamicLocation;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.MutableMap;
 
 public class ServerPoolLocationResolverTest {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/base/src/test/java/brooklyn/entity/pool/ServerPoolRebindTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/pool/ServerPoolRebindTest.java b/software/base/src/test/java/brooklyn/entity/pool/ServerPoolRebindTest.java
index f24ae5f..d47a5bb 100644
--- a/software/base/src/test/java/brooklyn/entity/pool/ServerPoolRebindTest.java
+++ b/software/base/src/test/java/brooklyn/entity/pool/ServerPoolRebindTest.java
@@ -27,6 +27,7 @@ import java.util.Collection;
 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.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,7 +37,6 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.rebind.RebindOptions;
 import brooklyn.entity.rebind.RebindTestUtils;
-import brooklyn.management.internal.LocalManagementContext;
 
 import com.google.common.base.Optional;
 import com.google.common.base.Predicates;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/base/src/test/java/brooklyn/entity/software/AbstractDockerLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/software/AbstractDockerLiveTest.java b/software/base/src/test/java/brooklyn/entity/software/AbstractDockerLiveTest.java
index c0bf566..654597b 100644
--- a/software/base/src/test/java/brooklyn/entity/software/AbstractDockerLiveTest.java
+++ b/software/base/src/test/java/brooklyn/entity/software/AbstractDockerLiveTest.java
@@ -21,7 +21,6 @@ package brooklyn.entity.software;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.MutableMap;
 
 import com.google.common.base.CaseFormat;
@@ -30,6 +29,7 @@ import com.google.common.collect.ImmutableMap;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/base/src/test/java/brooklyn/management/usage/ApplicationUsageTrackingTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/management/usage/ApplicationUsageTrackingTest.java b/software/base/src/test/java/brooklyn/management/usage/ApplicationUsageTrackingTest.java
index 4d032df..6fbbb98 100644
--- a/software/base/src/test/java/brooklyn/management/usage/ApplicationUsageTrackingTest.java
+++ b/software/base/src/test/java/brooklyn/management/usage/ApplicationUsageTrackingTest.java
@@ -30,6 +30,10 @@ import java.util.Set;
 
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.management.internal.UsageListener.ApplicationMetadata;
+import org.apache.brooklyn.core.management.usage.ApplicationUsage;
+import org.apache.brooklyn.core.management.usage.ApplicationUsage.ApplicationEvent;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
@@ -41,9 +45,6 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.proxying.EntityProxy;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.management.internal.UsageListener.ApplicationMetadata;
-import brooklyn.management.usage.ApplicationUsage.ApplicationEvent;
 import brooklyn.test.Asserts;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/base/src/test/java/brooklyn/management/usage/LocationUsageTrackingTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/management/usage/LocationUsageTrackingTest.java b/software/base/src/test/java/brooklyn/management/usage/LocationUsageTrackingTest.java
index 61032c4..82ff588 100644
--- a/software/base/src/test/java/brooklyn/management/usage/LocationUsageTrackingTest.java
+++ b/software/base/src/test/java/brooklyn/management/usage/LocationUsageTrackingTest.java
@@ -31,6 +31,9 @@ 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.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.management.internal.UsageListener.LocationMetadata;
+import org.apache.brooklyn.core.management.usage.LocationUsage;
+import org.apache.brooklyn.core.management.usage.LocationUsage.LocationEvent;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
@@ -41,8 +44,6 @@ import brooklyn.entity.basic.SoftwareProcessEntityTest;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.management.internal.UsageListener.LocationMetadata;
-import brooklyn.management.usage.LocationUsage.LocationEvent;
 import brooklyn.test.Asserts;
 import brooklyn.util.time.Time;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/base/src/test/java/brooklyn/management/usage/RecordingLegacyUsageListener.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/management/usage/RecordingLegacyUsageListener.java b/software/base/src/test/java/brooklyn/management/usage/RecordingLegacyUsageListener.java
index c270ac5..4ac1631 100644
--- a/software/base/src/test/java/brooklyn/management/usage/RecordingLegacyUsageListener.java
+++ b/software/base/src/test/java/brooklyn/management/usage/RecordingLegacyUsageListener.java
@@ -21,15 +21,16 @@ package brooklyn.management.usage;
 import java.util.List;
 import java.util.Map;
 
-import brooklyn.management.usage.ApplicationUsage.ApplicationEvent;
-import brooklyn.management.usage.LocationUsage.LocationEvent;
+import org.apache.brooklyn.core.management.usage.ApplicationUsage.ApplicationEvent;
+import org.apache.brooklyn.core.management.usage.LocationUsage.LocationEvent;
+
 import brooklyn.util.collections.MutableList;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
 @Deprecated
-public class RecordingLegacyUsageListener implements brooklyn.management.internal.UsageManager.UsageListener {
+public class RecordingLegacyUsageListener implements org.apache.brooklyn.core.management.internal.UsageManager.UsageListener {
 
     private final List<List<?>> events = Lists.newCopyOnWriteArrayList();
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/base/src/test/java/brooklyn/management/usage/RecordingUsageListener.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/management/usage/RecordingUsageListener.java b/software/base/src/test/java/brooklyn/management/usage/RecordingUsageListener.java
index bf26c5c..e5868a8 100644
--- a/software/base/src/test/java/brooklyn/management/usage/RecordingUsageListener.java
+++ b/software/base/src/test/java/brooklyn/management/usage/RecordingUsageListener.java
@@ -20,14 +20,15 @@ package brooklyn.management.usage;
 
 import java.util.List;
 
-import brooklyn.management.usage.ApplicationUsage.ApplicationEvent;
-import brooklyn.management.usage.LocationUsage.LocationEvent;
+import org.apache.brooklyn.core.management.usage.ApplicationUsage.ApplicationEvent;
+import org.apache.brooklyn.core.management.usage.LocationUsage.LocationEvent;
+
 import brooklyn.util.collections.MutableList;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
-public class RecordingUsageListener implements brooklyn.management.internal.UsageListener {
+public class RecordingUsageListener implements org.apache.brooklyn.core.management.internal.UsageListener {
 
     private final List<List<?>> events = Lists.newCopyOnWriteArrayList();
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/base/src/test/java/brooklyn/management/usage/UsageListenerTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/management/usage/UsageListenerTest.java b/software/base/src/test/java/brooklyn/management/usage/UsageListenerTest.java
index 863f587..a143558 100644
--- a/software/base/src/test/java/brooklyn/management/usage/UsageListenerTest.java
+++ b/software/base/src/test/java/brooklyn/management/usage/UsageListenerTest.java
@@ -34,9 +34,9 @@ import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.management.internal.UsageManager;
 
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.management.internal.UsageManager;
 import brooklyn.test.Asserts;
 
 import com.google.common.collect.ImmutableList;
@@ -107,7 +107,7 @@ public class UsageListenerTest {
             }});
     }
     
-    public static class RecordingStaticLegacyUsageListener extends RecordingLegacyUsageListener implements brooklyn.management.internal.UsageManager.UsageListener {
+    public static class RecordingStaticLegacyUsageListener extends RecordingLegacyUsageListener implements org.apache.brooklyn.core.management.internal.UsageManager.UsageListener {
         private static final List<RecordingStaticLegacyUsageListener> STATIC_INSTANCES = Lists.newCopyOnWriteArrayList();
         
         public static RecordingStaticLegacyUsageListener getInstance() {
@@ -125,7 +125,7 @@ public class UsageListenerTest {
         }
     }
     
-    public static class RecordingStaticUsageListener extends RecordingUsageListener implements brooklyn.management.internal.UsageListener {
+    public static class RecordingStaticUsageListener extends RecordingUsageListener implements org.apache.brooklyn.core.management.internal.UsageListener {
         private static final List<RecordingStaticUsageListener> STATIC_INSTANCES = Lists.newCopyOnWriteArrayList();
         
         public static RecordingStaticUsageListener getInstance() {



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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/WarmStandbyTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/WarmStandbyTest.java b/core/src/test/java/brooklyn/management/ha/WarmStandbyTest.java
deleted file mode 100644
index 54f4b8b..0000000
--- a/core/src/test/java/brooklyn/management/ha/WarmStandbyTest.java
+++ /dev/null
@@ -1,153 +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.management.ha;
-
-import static org.testng.Assert.assertEquals;
-
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
-import org.apache.brooklyn.api.management.ha.ManagementNodeState;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-import org.apache.brooklyn.test.entity.TestApplication;
-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.entity.basic.Entities;
-import brooklyn.entity.rebind.PersistenceExceptionHandlerImpl;
-import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
-import brooklyn.entity.rebind.persister.InMemoryObjectStore;
-import brooklyn.entity.rebind.persister.ListeningObjectStore;
-import brooklyn.entity.rebind.persister.PersistMode;
-import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.time.Duration;
-
-@Test
-public class WarmStandbyTest {
-
-    private static final Logger log = LoggerFactory.getLogger(WarmStandbyTest.class);
-    
-    private List<HaMgmtNode> nodes = new MutableList<WarmStandbyTest.HaMgmtNode>();
-    Map<String,String> sharedBackingStore = MutableMap.of();
-    Map<String,Date> sharedBackingStoreDates = MutableMap.of();
-    private ClassLoader classLoader = getClass().getClassLoader();
-    
-    public class HaMgmtNode {
-        // TODO share with HotStandbyTest and SplitBrainTest and a few others (minor differences but worth it ultimately)
-        
-        private ManagementContextInternal mgmt;
-        private String ownNodeId;
-        private String nodeName;
-        private ListeningObjectStore objectStore;
-        private ManagementPlaneSyncRecordPersister persister;
-        private HighAvailabilityManagerImpl ha;
-
-        @BeforeMethod(alwaysRun=true)
-        public void setUp() throws Exception {
-            nodeName = "node "+nodes.size();
-            mgmt = newLocalManagementContext();
-            ownNodeId = mgmt.getManagementNodeId();
-            objectStore = new ListeningObjectStore(newPersistenceObjectStore());
-            objectStore.injectManagementContext(mgmt);
-            objectStore.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);
-            persister = new ManagementPlaneSyncRecordPersisterToObjectStore(mgmt, objectStore, classLoader);
-            ((ManagementPlaneSyncRecordPersisterToObjectStore)persister).preferRemoteTimestampInMemento();
-            BrooklynMementoPersisterToObjectStore persisterObj = new BrooklynMementoPersisterToObjectStore(objectStore, mgmt.getBrooklynProperties(), classLoader);
-            mgmt.getRebindManager().setPersister(persisterObj, PersistenceExceptionHandlerImpl.builder().build());
-            ha = ((HighAvailabilityManagerImpl)mgmt.getHighAvailabilityManager())
-                .setPollPeriod(Duration.PRACTICALLY_FOREVER)
-                .setHeartbeatTimeout(Duration.THIRTY_SECONDS)
-                .setPersister(persister);
-            log.info("Created "+nodeName+" "+ownNodeId);
-        }
-        
-        public void tearDown() throws Exception {
-            if (ha != null) ha.stop();
-            if (mgmt != null) Entities.destroyAll(mgmt);
-            if (objectStore != null) objectStore.deleteCompletely();
-        }
-        
-        @Override
-        public String toString() {
-            return nodeName+" "+ownNodeId;
-        }
-    }
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        nodes.clear();
-        sharedBackingStore.clear();
-    }
-    
-    public HaMgmtNode newNode() throws Exception {
-        HaMgmtNode node = new HaMgmtNode();
-        node.setUp();
-        nodes.add(node);
-        return node;
-    }
-
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        for (HaMgmtNode n: nodes)
-            n.tearDown();
-    }
-
-    protected ManagementContextInternal newLocalManagementContext() {
-        return new LocalManagementContextForTests();
-    }
-
-    protected PersistenceObjectStore newPersistenceObjectStore() {
-        return new InMemoryObjectStore(sharedBackingStore, sharedBackingStoreDates);
-    }
-
-    // TODO refactor above -- routines above this line are shared among HotStandbyTest and SplitBrainTest
-    
-    @Test
-    public void testWarmStandby() throws Exception {
-        HaMgmtNode n1 = newNode();
-        n1.ha.start(HighAvailabilityMode.AUTO);
-        assertEquals(n1.ha.getNodeState(), ManagementNodeState.MASTER);
-        
-        TestApplication app = TestApplication.Factory.newManagedInstanceForTests(n1.mgmt);
-        app.start(MutableList.<Location>of());
-        
-        n1.mgmt.getRebindManager().forcePersistNow(false, null);
-
-        HaMgmtNode n2 = newNode();
-        n2.ha.start(HighAvailabilityMode.STANDBY);
-        assertEquals(n2.ha.getNodeState(), ManagementNodeState.STANDBY);
-
-        assertEquals(n2.mgmt.getApplications().size(), 0);
-    }
-    
-    // TODO support forcible demotion, and check that a master forcibly demoted 
-    // to warm standby clears its apps, policies, and locations  
-
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/internal/AccessManagerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/internal/AccessManagerTest.java b/core/src/test/java/brooklyn/management/internal/AccessManagerTest.java
deleted file mode 100644
index 34f733c..0000000
--- a/core/src/test/java/brooklyn/management/internal/AccessManagerTest.java
+++ /dev/null
@@ -1,145 +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.management.internal;
-
-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 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.test.entity.LocalManagementContextForTests;
-import org.apache.brooklyn.test.entity.TestApplication;
-import org.apache.brooklyn.test.entity.TestEntity;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.basic.ApplicationBuilder;
-import brooklyn.entity.basic.Entities;
-
-import org.apache.brooklyn.location.basic.SimulatedLocation;
-
-import brooklyn.util.exceptions.Exceptions;
-
-import com.google.common.collect.ImmutableSet;
-
-public class AccessManagerTest {
-
-    private LocalManagementContext managementContext;
-    private TestApplication app;
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        managementContext = new LocalManagementContextForTests();
-        app = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext);
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (managementContext != null) Entities.destroyAll(managementContext);
-        app = null;
-    }
-
-    @Test
-    public void testEntityManagementAllowed() throws Exception {
-        // default is allowed
-        TestEntity e1 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-
-        // when forbidden, should give error trying to create+manage new entity
-        managementContext.getAccessManager().setEntityManagementAllowed(false);
-        try {
-            app.createAndManageChild(EntitySpec.create(TestEntity.class));
-            fail();
-        } catch (Exception e) {
-            // expect it to be forbidden
-            if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) {
-                throw e;
-            }
-        }
-
-        // when forbidden, should refuse to create new app
-        try {
-            ApplicationBuilder.newManagedApp(TestApplication.class, managementContext);
-            fail();
-        } catch (Exception e) {
-            // expect it to be forbidden
-            if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) {
-                throw e;
-            }
-        }
-
-        // but when forbidden, still allowed to create locations
-        managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
-        
-        // when re-enabled, can create entities again
-        managementContext.getAccessManager().setEntityManagementAllowed(true);
-        TestEntity e3 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        
-        assertEquals(ImmutableSet.copyOf(managementContext.getEntityManager().getEntities()), ImmutableSet.of(app, e1, e3));
-    }
-    
-    @Test
-    public void testLocationManagementAllowed() throws Exception {
-        // default is allowed
-        Location loc1 = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
-
-        // when forbidden, should give error
-        managementContext.getAccessManager().setLocationManagementAllowed(false);
-        try {
-            managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
-            fail();
-        } catch (Exception e) {
-            // expect it to be forbidden
-            if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) {
-                throw e;
-            }
-        }
-
-        // but when forbidden, still allowed to create entity
-        ApplicationBuilder.newManagedApp(TestApplication.class, managementContext);
-        
-        // when re-enabled, can create entities again
-        managementContext.getAccessManager().setLocationManagementAllowed(true);
-        Location loc3 = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
-        
-        assertEquals(ImmutableSet.copyOf(managementContext.getLocationManager().getLocations()), ImmutableSet.of(loc1, loc3));
-    }
-    
-    @Test
-    public void testLocationProvisioningAllowed() throws Exception {
-        SimulatedLocation loc = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
-        
-        // default is allowed
-        assertTrue(managementContext.getAccessController().canProvisionLocation(loc).isAllowed());
-
-        // when forbidden, should say so
-        managementContext.getAccessManager().setLocationProvisioningAllowed(false);
-        assertFalse(managementContext.getAccessController().canProvisionLocation(loc).isAllowed());
-
-        // but when forbidden, still allowed to create locations
-        managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
-        
-        // when re-enabled, can create entities again
-        managementContext.getAccessManager().setLocationProvisioningAllowed(true);
-        assertTrue(managementContext.getAccessController().canProvisionLocation(loc).isAllowed());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/internal/EntityExecutionManagerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/internal/EntityExecutionManagerTest.java b/core/src/test/java/brooklyn/management/internal/EntityExecutionManagerTest.java
deleted file mode 100644
index d8cb07a..0000000
--- a/core/src/test/java/brooklyn/management/internal/EntityExecutionManagerTest.java
+++ /dev/null
@@ -1,479 +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.management.internal;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Semaphore;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.proxying.EntitySpec;
-import org.apache.brooklyn.api.management.ExecutionManager;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-import org.apache.brooklyn.test.entity.TestApplication;
-import org.apache.brooklyn.test.entity.TestEntity;
-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.config.BrooklynProperties;
-import brooklyn.entity.basic.ApplicationBuilder;
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.basic.BrooklynTaskTags.WrappedEntity;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.event.basic.BasicAttributeSensor;
-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;
-
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.FluentIterable;
-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.collect.Lists;
-import com.google.common.util.concurrent.Callables;
-
-/** Includes many tests for {@link BrooklynGarbageCollector} */
-public class EntityExecutionManagerTest {
-    
-    private static final Logger LOG = LoggerFactory.getLogger(EntityExecutionManagerTest.class);
-    
-    private static final Duration TIMEOUT_MS = Duration.TEN_SECONDS;
-    
-    private ManagementContextInternal mgmt;
-    private TestApplication app;
-    private TestEntity e;
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (app != null) Entities.destroyAll(app.getManagementContext());
-        app = null;
-        if (mgmt != null) Entities.destroyAll(mgmt);
-    }
-
-    @Test
-    public void testOnDoneCallback() throws InterruptedException {
-        mgmt = LocalManagementContextForTests.newInstance();
-        ExecutionManager em = mgmt.getExecutionManager();
-        BasicExecutionManager bem = (BasicExecutionManager)em;
-        final Map<Task<?>,Duration> completedTasks = MutableMap.of();
-        final Semaphore sema4 = new Semaphore(-1);
-        bem.addListener(new ExecutionListener() {
-            @Override
-            public void onTaskDone(Task<?> task) {
-                Assert.assertTrue(task.isDone());
-                Assert.assertEquals(task.getUnchecked(), "foo");
-                completedTasks.put(task, Duration.sinceUtc(task.getEndTimeUtc()));
-                sema4.release();
-            }
-        });
-        Task<String> t1 = em.submit( Tasks.<String>builder().name("t1").dynamic(false).body(Callables.returning("foo")).build() );
-        t1.getUnchecked();
-        Task<String> t2 = em.submit( Tasks.<String>builder().name("t2").dynamic(false).body(Callables.returning("foo")).build() );
-        sema4.acquire();
-        Assert.assertEquals(completedTasks.size(), 2, "completed tasks are: "+completedTasks);
-        completedTasks.get(t1).isShorterThan(Duration.TEN_SECONDS);
-        completedTasks.get(t2).isShorterThan(Duration.TEN_SECONDS);
-    }
-    
-    protected void forceGc() {
-        ((LocalManagementContext)app.getManagementContext()).getGarbageCollector().gcIteration();
-    }
-
-    protected static Task<?> runEmptyTaskWithNameAndTags(Entity target, String name, Object ...tags) {
-        TaskBuilder<Object> tb = newEmptyTask(name);
-        for (Object tag: tags) tb.tag(tag);
-        Task<?> task = ((EntityInternal)target).getExecutionContext().submit(tb.build());
-        task.getUnchecked();
-        return task;
-    }
-
-    protected static TaskBuilder<Object> newEmptyTask(String name) {
-        return Tasks.builder().name(name).dynamic(false).body(Callables.returning(null));
-    }
-    
-    protected void assertTaskCountForEntitySoon(final Entity entity, final int expectedCount) {
-        // Dead task (and initialization task) should have been GC'd on completion.
-        // However, the GC'ing happens in a listener, executed in a different thread - the task.get()
-        // doesn't block for it. Therefore can't always guarantee it will be GC'ed by now.
-        Repeater.create().backoff(Duration.millis(10), 2, Duration.millis(500)).limitTimeTo(Duration.TEN_SECONDS).until(new Callable<Boolean>() {
-            @Override
-            public Boolean call() throws Exception {
-                forceGc();
-                Collection<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(((EntityInternal)entity).getManagementContext().getExecutionManager(), entity);
-                Assert.assertEquals(tasks.size(), expectedCount, "Tasks were "+tasks);
-                return true;
-            }
-        }).runRequiringTrue();
-    }
-
-    @Test
-    public void testGetTasksAndGcBoringTags() throws Exception {
-        app = TestApplication.Factory.newManagedInstanceForTests();
-        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        
-        final Task<?> task = runEmptyTaskWithNameAndTags(e, "should-be-kept", ManagementContextInternal.NON_TRANSIENT_TASK_TAG);
-        runEmptyTaskWithNameAndTags(e, "should-be-gcd", ManagementContextInternal.TRANSIENT_TASK_TAG);
-        
-        assertTaskCountForEntitySoon(e, 1);
-        Collection<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(app.getManagementContext().getExecutionManager(), e);
-        assertEquals(tasks, ImmutableList.of(task), "Mismatched tasks, got: "+tasks);
-    }
-
-    @Test
-    public void testGcTaskAtNormalTagLimit() throws Exception {
-        app = TestApplication.Factory.newManagedInstanceForTests();
-        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        
-        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
-            BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2);
-
-        for (int count=0; count<5; count++)
-            runEmptyTaskWithNameAndTags(e, "task"+count, ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
-
-        assertTaskCountForEntitySoon(e, 2);
-    }
-    
-    @Test
-    public void testGcTaskAtEntityLimit() throws Exception {
-        app = TestApplication.Factory.newManagedInstanceForTests();
-        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        
-        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
-            BrooklynGarbageCollector.MAX_TASKS_PER_ENTITY, 2);
-        
-        for (int count=0; count<5; count++)
-            runEmptyTaskWithNameAndTags(e, "task-e-"+count, ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
-        for (int count=0; count<5; count++)
-            runEmptyTaskWithNameAndTags(app, "task-app-"+count, ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
-        
-        assertTaskCountForEntitySoon(app, 2);
-        assertTaskCountForEntitySoon(e, 2);
-    }
-    
-    @Test
-    public void testGcTaskWithTagAndEntityLimit() throws Exception {
-        app = TestApplication.Factory.newManagedInstanceForTests();
-        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        
-        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
-            BrooklynGarbageCollector.MAX_TASKS_PER_ENTITY, 6);
-        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
-            BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2);
-
-        int count=0;
-        
-        runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
-        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
-        Time.sleep(Duration.ONE_MILLISECOND);
-        // should keep the 2 below, because all the other borings get grace, but delete the ones above
-        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
-        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
-        
-        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag", "another-tag-e");
-        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag", "another-tag-e");
-        // should keep both the above
-        
-        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag");
-        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag");
-        Time.sleep(Duration.ONE_MILLISECOND);
-        runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag");
-        // should keep the below since they have unique tags, but remove one of the e tasks above 
-        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag", "and-another-tag");
-        runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag-app", "another-tag");
-        runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag-app", "another-tag");
-        
-        assertTaskCountForEntitySoon(e, 6);
-        assertTaskCountForEntitySoon(app, 3);
-        
-        // now with a lowered limit, we should remove one more e
-        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
-            BrooklynGarbageCollector.MAX_TASKS_PER_ENTITY, 5);
-        assertTaskCountForEntitySoon(e, 5);
-    }
-    
-    @Test
-    public void testGcDynamicTaskAtNormalTagLimit() throws Exception {
-        app = TestApplication.Factory.newManagedInstanceForTests();
-        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        
-        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
-            BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2);
-
-        for (int count=0; count<5; count++) {
-            TaskBuilder<Object> tb = Tasks.builder().name("task-"+count).dynamic(true).body(new Runnable() { @Override public void run() {}})
-                .tag(ManagementContextInternal.NON_TRANSIENT_TASK_TAG).tag("foo");
-            ((EntityInternal)e).getExecutionContext().submit(tb.build()).getUnchecked();
-        }
-
-        // might need an eventually here, if the internal job completion and GC is done in the background
-        // (if there are no test failures for a few months, since Sept 2014, then we can remove this comment)
-        assertTaskCountForEntitySoon(e, 2);
-    }
-    
-    @Test
-    public void testUnmanagedEntityCanBeGcedEvenIfPreviouslyTagged() throws Exception {
-        app = TestApplication.Factory.newManagedInstanceForTests();
-        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        String eId = e.getId();
-        
-        e.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of()).get();
-        Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(app.getManagementContext().getExecutionManager(), e);
-        Task<?> task = Iterables.get(tasks, 0);
-        assertTrue(task.getTags().contains(BrooklynTaskTags.tagForContextEntity(e)));
-
-        Set<Object> tags = app.getManagementContext().getExecutionManager().getTaskTags();
-        assertTrue(tags.contains(BrooklynTaskTags.tagForContextEntity(e)), "tags="+tags);
-        
-        Entities.destroy(e);
-        forceGc();
-        
-        Set<Object> tags2 = app.getManagementContext().getExecutionManager().getTaskTags();
-        for (Object tag : tags2) {
-            if (tag instanceof Entity && ((Entity)tag).getId().equals(eId)) {
-                fail("tags contains unmanaged entity "+tag);
-            }
-            if ((tag instanceof WrappedEntity) && ((WrappedEntity)tag).entity.getId().equals(eId) 
-                    && ((WrappedEntity)tag).wrappingType.equals(BrooklynTaskTags.CONTEXT_ENTITY)) {
-                fail("tags contains unmanaged entity (wrapped) "+tag);
-            }
-        }
-        return;
-    }
-    
-    @Test(groups="Integration")
-    public void testSubscriptionAndEffectorTasksGced() throws Exception {
-        app = TestApplication.Factory.newManagedInstanceForTests();
-        BasicExecutionManager em = (BasicExecutionManager) app.getManagementContext().getExecutionManager();
-        // allow background enrichers to complete
-        Time.sleep(Duration.ONE_SECOND);
-        forceGc();
-        List<Task<?>> t1 = em.getAllTasks();
-        
-        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        entity.setAttribute(TestEntity.NAME, "bob");
-        entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of()).get();
-        Entities.destroy(entity);
-        Time.sleep(Duration.ONE_SECOND);
-        forceGc();
-        List<Task<?>> t2 = em.getAllTasks();
-        
-        Assert.assertEquals(t1.size(), t2.size(), "lists are different:\n"+t1+"\n"+t2+"\n");
-    }
-
-    /**
-     * Invoke effector many times, where each would claim 10MB because it stores the return value.
-     * If it didn't gc the tasks promptly, it would consume 10GB ram (so would OOME before that).
-     */
-    @Test(groups="Integration")
-    public void testEffectorTasksGcedSoNoOome() throws Exception {
-        
-        BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty();
-        brooklynProperties.put(BrooklynGarbageCollector.GC_PERIOD, Duration.ONE_MILLISECOND);
-        brooklynProperties.put(BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2);
-        
-        app = ApplicationBuilder.newManagedApp(TestApplication.class, LocalManagementContextForTests.newInstance(brooklynProperties));
-        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        
-        for (int i = 0; i < 1000; i++) {
-            if (i%100==0) LOG.info(JavaClassNames.niceClassAndMethod()+": iteration "+i);
-            try {
-                LOG.debug("testEffectorTasksGced: iteration="+i);
-                entity.invoke(TestEntity.IDENTITY_EFFECTOR, ImmutableMap.of("arg", new BigObject(10*1000*1000))).get();
-                
-                Time.sleep(Duration.ONE_MILLISECOND); // Give GC thread a chance to run
-                forceGc();
-            } catch (OutOfMemoryError e) {
-                LOG.warn(JavaClassNames.niceClassAndMethod()+": OOME at iteration="+i);
-                throw e;
-            }
-        }
-    }
-    
-    @Test(groups="Integration")
-    public void testUnmanagedEntityGcedOnUnmanageEvenIfEffectorInvoked() throws Exception {
-        app = TestApplication.Factory.newManagedInstanceForTests();
-        
-        BasicAttributeSensor<Object> byteArrayAttrib = new BasicAttributeSensor<Object>(Object.class, "test.byteArray", "");
-
-        for (int i = 0; i < 1000; i++) {
-            if (i<100 && i%10==0 || i%100==0) LOG.info(JavaClassNames.niceClassAndMethod()+": iteration "+i);
-            try {
-                LOG.debug(JavaClassNames.niceClassAndMethod()+": iteration="+i);
-                TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-                entity.setAttribute(byteArrayAttrib, new BigObject(10*1000*1000));
-                entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of()).get();
-                
-                // we get exceptions because tasks are still trying to publish after deployment;
-                // this should prevent them
-//                ((LocalEntityManager)app.getManagementContext().getEntityManager()).stopTasks(entity, Duration.ONE_SECOND);
-//                Entities.destroy(entity);
-                
-                // alternatively if we 'unmanage' instead of destroy, there are usually not errors
-                // (the errors come from the node transitioning to a 'stopping' state on destroy, 
-                // and publishing lots of info then)
-                Entities.unmanage(entity);
-                
-                forceGc();
-                // previously we did an extra GC but it was crazy slow, shouldn't be needed
-//                System.gc(); System.gc();
-            } catch (OutOfMemoryError e) {
-                LOG.warn(JavaClassNames.niceClassAndMethod()+": OOME at iteration="+i);
-                ExecutionManager em = app.getManagementContext().getExecutionManager();
-                Collection<Task<?>> tasks = ((BasicExecutionManager)em).getAllTasks();
-                LOG.info("TASKS count "+tasks.size()+": "+tasks);
-                throw e;
-            }
-        }
-    }
-
-    @Test(groups={"Integration"})
-    public void testEffectorTasksGcedForMaxPerTag() throws Exception {
-        int maxNumTasks = 2;
-        BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty();
-        brooklynProperties.put(BrooklynGarbageCollector.GC_PERIOD, Duration.ONE_SECOND);
-        brooklynProperties.put(BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2);
-        
-        app = ApplicationBuilder.newManagedApp(TestApplication.class, LocalManagementContextForTests.newInstance(brooklynProperties));
-        final TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        
-        List<Task<?>> tasks = Lists.newArrayList();
-        
-        for (int i = 0; i < (maxNumTasks+1); i++) {
-            Task<?> task = entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of());
-            task.get();
-            tasks.add(task);
-            
-            // TASKS_OLDEST_FIRST_COMPARATOR is based on comparing EndTimeUtc; but two tasks executed in
-            // rapid succession could finish in same millisecond
-            // (especially when using System.currentTimeMillis, which can return the same time for several millisconds).
-            Thread.sleep(10);
-        }
-        
-        // Should initially have all tasks
-        Set<Task<?>> storedTasks = app.getManagementContext().getExecutionManager().getTasksWithAllTags(
-                ImmutableList.of(BrooklynTaskTags.tagForContextEntity(entity), ManagementContextInternal.EFFECTOR_TAG));
-        assertEquals(storedTasks, ImmutableSet.copyOf(tasks), "storedTasks="+storedTasks+"; expected="+tasks);
-        
-        // Then oldest should be GC'ed to leave only maxNumTasks
-        final List<Task<?>> recentTasks = tasks.subList(tasks.size()-maxNumTasks, tasks.size());
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
-            @Override public void run() {
-                Set<Task<?>> storedTasks2 = app.getManagementContext().getExecutionManager().getTasksWithAllTags(
-                       ImmutableList.of(BrooklynTaskTags.tagForContextEntity(entity), ManagementContextInternal.EFFECTOR_TAG));
-                List<String> storedTasks2Str = FluentIterable
-                        .from(storedTasks2)
-                        .transform(new Function<Task<?>, String>() {
-                            @Override public String apply(Task<?> input) {
-                                return taskToVerboseString(input);
-                            }})
-                        .toList();
-                assertEquals(storedTasks2, ImmutableSet.copyOf(recentTasks), "storedTasks="+storedTasks2Str+"; expected="+recentTasks);
-            }});
-    }
-    
-    private String taskToVerboseString(Task t) {
-        return Objects.toStringHelper(t)
-                .add("id", t.getId())
-                .add("displayName", t.getDisplayName())
-                .add("submitTime", t.getSubmitTimeUtc())
-                .add("startTime", t.getStartTimeUtc())
-                .add("endTime", t.getEndTimeUtc())
-                .add("status", t.getStatusSummary())
-                .add("tags", t.getTags())
-                .toString();
-    }
-    //2014-10-22 17:48:32,359 INFO  TESTNG FAILED: "Surefire test" - brooklyn.management.internal.EntityExecutionManagerTest.testEffectorTasksGcedForMaxPerTag() finished in 10020 ms
-    //java.lang.AssertionError: storedTasks=[Task[myEffector:dmCJkiM9], Task[myEffector:R2hPTpNL]]; expected=[Task[myEffector:Z9wTQ8We], Task[myEffector:R2hPTpNL]] 
-    // expected [[Task[myEffector:Z9wTQ8We], Task[myEffector:R2hPTpNL]]] 
-    // but found [[Task[myEffector:dmCJkiM9], Task[myEffector:R2hPTpNL]]]
-            
-    @Test(groups="Integration")
-    public void testEffectorTasksGcedForAge() throws Exception {
-        Duration maxTaskAge = Duration.millis(100);
-        Duration maxOverhead = Duration.millis(250);
-        Duration earlyReturnGrace = Duration.millis(10);
-        BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty();
-        brooklynProperties.put(BrooklynGarbageCollector.GC_PERIOD, Duration.ONE_MILLISECOND);
-        brooklynProperties.put(BrooklynGarbageCollector.MAX_TASK_AGE, maxTaskAge);
-        
-        app = ApplicationBuilder.newManagedApp(TestApplication.class, LocalManagementContextForTests.newInstance(brooklynProperties));
-        final TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        
-        Stopwatch stopwatch = Stopwatch.createStarted();
-        Task<?> oldTask = entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of());
-        oldTask.get();
-        
-        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
-            @Override public void run() {
-                Set<Task<?>> storedTasks = app.getManagementContext().getExecutionManager().getTasksWithAllTags(ImmutableList.of(
-                        BrooklynTaskTags.tagForTargetEntity(entity), 
-                        ManagementContextInternal.EFFECTOR_TAG));
-                assertEquals(storedTasks, ImmutableSet.of(), "storedTasks="+storedTasks);
-            }});
-
-        Duration timeToGc = Duration.of(stopwatch);
-        assertTrue(timeToGc.isLongerThan(maxTaskAge.subtract(earlyReturnGrace)), "timeToGc="+timeToGc+"; maxTaskAge="+maxTaskAge);
-        assertTrue(timeToGc.isShorterThan(maxTaskAge.add(maxOverhead)), "timeToGc="+timeToGc+"; maxTaskAge="+maxTaskAge);
-    }
-    
-    private static class BigObject implements Serializable {
-        private static final long serialVersionUID = -4021304829674972215L;
-        private final int sizeBytes;
-        private final byte[] data;
-        
-        BigObject(int sizeBytes) {
-            this.sizeBytes = sizeBytes;
-            this.data = new byte[sizeBytes];
-        }
-        
-        @Override
-        public String toString() {
-            return "BigObject["+sizeBytes+"/"+data.length+"]";
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/internal/LocalManagementContextInstancesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/internal/LocalManagementContextInstancesTest.java b/core/src/test/java/brooklyn/management/internal/LocalManagementContextInstancesTest.java
deleted file mode 100644
index 0e07b94..0000000
--- a/core/src/test/java/brooklyn/management/internal/LocalManagementContextInstancesTest.java
+++ /dev/null
@@ -1,86 +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.management.internal;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.ImmutableSet;
-
-/**
- * Tests the {@link LocalManagementContext#terminateAll()} and {@link LocalManagementContext#getInstances()} behaviour.
- * Note this test must NEVER be run in parallel with other tests, as it will terminate the ManagementContext of those
- * other tests.
- * 
- * @author pveentjer
- */
-public class LocalManagementContextInstancesTest {
-
-    @AfterMethod(alwaysRun = true)
-    public void tearDown(){
-         LocalManagementContext.terminateAll();
-    }
-
-    /** WIP group because other threads may be running in background, 
-     * creating management contexts at the same time as us (slim chance, but observed once);
-     * they shouldn't be, but cleaning that up is another matter! */
-    @Test(groups="WIP")
-    public void testGetInstances(){
-        LocalManagementContext.terminateAll();
-        LocalManagementContext context1 = new LocalManagementContext();
-        LocalManagementContext context2 = new LocalManagementContext();
-        LocalManagementContext context3 = new LocalManagementContext();
-
-        assertEquals(LocalManagementContext.getInstances(), ImmutableSet.of(context1, context2, context3));
-    }
-
-    /** WIP group because other threads may be running in background;
-     * they shouldn't be, but cleaning that up is another matter! */
-    @Test
-    public void terminateAll(){
-        LocalManagementContext.terminateAll();
-        
-        LocalManagementContext context1 = new LocalManagementContext();
-        LocalManagementContext context2 = new LocalManagementContext();
-
-        LocalManagementContext.terminateAll();
-
-        assertTrue(LocalManagementContext.getInstances().isEmpty());
-        assertFalse(context1.isRunning());
-        assertFalse(context2.isRunning());
-    }
-
-    @Test
-    public void terminateExplicitContext(){
-        LocalManagementContext context1 = new LocalManagementContext();
-        LocalManagementContext context2 = new LocalManagementContext();
-        LocalManagementContext context3 = new LocalManagementContext();
-
-        context2.terminate();
-
-        Assert.assertFalse(LocalManagementContext.getInstances().contains(context2));
-        Assert.assertTrue(LocalManagementContext.getInstances().contains(context1));
-        Assert.assertTrue(LocalManagementContext.getInstances().contains(context3));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/internal/LocalManagementContextTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/internal/LocalManagementContextTest.java b/core/src/test/java/brooklyn/management/internal/LocalManagementContextTest.java
deleted file mode 100644
index d225cd7..0000000
--- a/core/src/test/java/brooklyn/management/internal/LocalManagementContextTest.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.management.internal;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotEquals;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.management.ManagementContext.PropertiesReloadListener;
-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.BrooklynProperties;
-import brooklyn.config.BrooklynProperties.Factory.Builder;
-import brooklyn.util.os.Os;
-
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-
-public class LocalManagementContextTest {
-    
-    private LocalManagementContext context; 
-    private File globalPropertiesFile;
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        context = null;
-        globalPropertiesFile = Os.newTempFile(getClass(), "global.properties");
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (context!=null) context.terminate();
-        if (globalPropertiesFile != null) globalPropertiesFile.delete();
-    }
-    
-    @Test
-    public void testReloadPropertiesFromBuilder() throws IOException {
-        String globalPropertiesContents = "brooklyn.location.localhost.displayName=myname";
-        Files.write(globalPropertiesContents, globalPropertiesFile, Charsets.UTF_8);
-        Builder propsBuilder = new BrooklynProperties.Factory.Builder()
-            .globalPropertiesFile(globalPropertiesFile.getAbsolutePath());
-        // no builder support in LocalManagementContextForTests (we are testing that the builder's files are reloaded so we need it here)
-        context = new LocalManagementContext(propsBuilder);
-        Location location = context.getLocationRegistry().resolve("localhost");
-        assertEquals(location.getDisplayName(), "myname");
-        String newGlobalPropertiesContents = "brooklyn.location.localhost.displayName=myname2";
-        Files.write(newGlobalPropertiesContents, globalPropertiesFile, Charsets.UTF_8);
-        context.reloadBrooklynProperties();
-        Location location2 = context.getLocationRegistry().resolve("localhost");
-        assertEquals(location.getDisplayName(), "myname");
-        assertEquals(location2.getDisplayName(), "myname2");
-    }
-    
-    @Test
-    public void testReloadPropertiesFromProperties() throws IOException {
-        String globalPropertiesContents = "brooklyn.location.localhost.displayName=myname";
-        Files.write(globalPropertiesContents, globalPropertiesFile, Charsets.UTF_8);
-        BrooklynProperties brooklynProperties = new BrooklynProperties.Factory.Builder()
-            .globalPropertiesFile(globalPropertiesFile.getAbsolutePath())
-            .build();
-        context = LocalManagementContextForTests.builder(true).useProperties(brooklynProperties).build();
-        Location location = context.getLocationRegistry().resolve("localhost");
-        assertEquals(location.getDisplayName(), "myname");
-        String newGlobalPropertiesContents = "brooklyn.location.localhost.displayName=myname2";
-        Files.write(newGlobalPropertiesContents, globalPropertiesFile, Charsets.UTF_8);
-        context.reloadBrooklynProperties();
-        Location location2 = context.getLocationRegistry().resolve("localhost");
-        assertEquals(location.getDisplayName(), "myname");
-        assertEquals(location2.getDisplayName(), "myname");
-    }
-    
-    @Test
-    public void testPropertiesModified() throws IOException {
-        BrooklynProperties properties = BrooklynProperties.Factory.newEmpty();
-        properties.put("myname", "myval");
-        context = LocalManagementContextForTests.builder(true).useProperties(properties).build();
-        assertEquals(context.getBrooklynProperties().get("myname"), "myval");
-        properties.put("myname", "newval");
-        assertEquals(properties.get("myname"), "newval");
-        // TODO: Should changes in the 'properties' collection be reflected in context.getBrooklynProperties()?
-        assertNotEquals(context.getBrooklynProperties().get("myname"), "newval");
-    }
-    
-    @Test
-    public void testAddAndRemoveReloadListener() {
-        final AtomicInteger reloadedCallbackCount = new AtomicInteger(0);
-        BrooklynProperties properties = BrooklynProperties.Factory.newEmpty();
-        properties.put("myname", "myval");
-        context = LocalManagementContextForTests.builder(true).useProperties(properties).build();
-        PropertiesReloadListener listener = new PropertiesReloadListener() {
-            public void reloaded() {
-                reloadedCallbackCount.incrementAndGet();
-            }
-        };
-        assertEquals(reloadedCallbackCount.get(), 0);
-        context.addPropertiesReloadListener(listener);
-        context.reloadBrooklynProperties();
-        assertEquals(reloadedCallbackCount.get(), 1);
-        context.removePropertiesReloadListener(listener);
-        context.reloadBrooklynProperties();
-        assertEquals(reloadedCallbackCount.get(), 1);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/internal/LocalSubscriptionManagerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/internal/LocalSubscriptionManagerTest.java b/core/src/test/java/brooklyn/management/internal/LocalSubscriptionManagerTest.java
deleted file mode 100644
index 7907d30..0000000
--- a/core/src/test/java/brooklyn/management/internal/LocalSubscriptionManagerTest.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.management.internal;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.fail;
-
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.brooklyn.api.entity.Entity;
-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.management.SubscriptionHandle;
-import org.apache.brooklyn.api.management.SubscriptionManager;
-import org.apache.brooklyn.test.entity.TestEntity;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.BrooklynAppUnitTestSupport;
-import brooklyn.entity.basic.BasicGroup;
-import brooklyn.entity.basic.Entities;
-
-/**
- * testing the {@link SubscriptionManager} and associated classes.
- */
-public class LocalSubscriptionManagerTest extends BrooklynAppUnitTestSupport {
-    
-    private static final int TIMEOUT_MS = 5000;
-    
-    private TestEntity entity;
-    
-    @BeforeMethod(alwaysRun=true)
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-    }
-
-    private void manage(Entity ...entities) {
-        for (Entity e: entities)
-            Entities.manage(e);
-    }
-
-    @Test
-    public void testSubscribeToEntityAttributeChange() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        app.subscribe(entity, TestEntity.SEQUENCE, new SensorEventListener<Object>() {
-                @Override public void onEvent(SensorEvent<Object> event) {
-                    latch.countDown();
-                }});
-        entity.setSequenceValue(1234);
-        if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-            fail("Timeout waiting for Event on TestEntity listener");
-        }
-    }
-    
-    @Test
-    public void testSubscribeToEntityWithAttributeWildcard() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        app.subscribe(entity, null, new SensorEventListener<Object>() {
-            @Override public void onEvent(SensorEvent<Object> event) {
-                latch.countDown();
-            }});
-        entity.setSequenceValue(1234);
-        if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-            fail("Timeout waiting for Event on TestEntity listener");
-        }
-    }
-    
-    @Test
-    public void testSubscribeToAttributeChangeWithEntityWildcard() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        app.subscribe(null, TestEntity.SEQUENCE, new SensorEventListener<Object>() {
-                @Override public void onEvent(SensorEvent<Object> event) {
-                    latch.countDown();
-                }});
-        entity.setSequenceValue(1234);
-        if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-            fail("Timeout waiting for Event on TestEntity listener");
-        }
-    }
-    
-    @Test
-    public void testSubscribeToChildAttributeChange() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        app.subscribeToChildren(app, TestEntity.SEQUENCE, new SensorEventListener<Object>() {
-            @Override public void onEvent(SensorEvent<Object> event) {
-                latch.countDown();
-            }});
-        entity.setSequenceValue(1234);
-        if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-            fail("Timeout waiting for Event on child TestEntity listener");
-        }
-    }
-    
-    @Test
-    public void testSubscribeToMemberAttributeChange() throws Exception {
-        BasicGroup group = app.createAndManageChild(EntitySpec.create(BasicGroup.class));
-        TestEntity member = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        manage(group, member);
-        
-        group.addMember(member);
-
-        final List<SensorEvent<Integer>> events = new CopyOnWriteArrayList<SensorEvent<Integer>>();
-        final CountDownLatch latch = new CountDownLatch(1);
-        app.subscribeToMembers(group, TestEntity.SEQUENCE, new SensorEventListener<Integer>() {
-            @Override public void onEvent(SensorEvent<Integer> event) {
-                events.add(event);
-                latch.countDown();
-            }});
-        member.setAttribute(TestEntity.SEQUENCE, 123);
-
-        if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-            fail("Timeout waiting for Event on parent TestEntity listener");
-        }
-        assertEquals(events.size(), 1);
-        assertEquals(events.get(0).getValue(), (Integer)123);
-        assertEquals(events.get(0).getSensor(), TestEntity.SEQUENCE);
-        assertEquals(events.get(0).getSource().getId(), member.getId());
-    }
-    
-    // Regression test for ConcurrentModificationException in issue #327
-    @Test(groups="Integration")
-    public void testConcurrentSubscribingAndPublishing() throws Exception {
-        final AtomicReference<Exception> threadException = new AtomicReference<Exception>();
-        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        
-        // Repeatedly subscribe and unsubscribe, so listener-set constantly changing while publishing to it.
-        // First create a stable listener so it is always the same listener-set object.
-        Thread thread = new Thread() {
-            public void run() {
-                try {
-                    SensorEventListener<Object> noopListener = new SensorEventListener<Object>() {
-                        @Override public void onEvent(SensorEvent<Object> event) {
-                        }
-                    };
-                    app.subscribe(null, TestEntity.SEQUENCE, noopListener);
-                    while (!Thread.currentThread().isInterrupted()) {
-                        SubscriptionHandle handle = app.subscribe(null, TestEntity.SEQUENCE, noopListener);
-                        app.unsubscribe(null, handle);
-                    }
-                } catch (Exception e) {
-                    threadException.set(e);
-                }
-            }
-        };
-        
-        try {
-            thread.start();
-            for (int i = 0; i < 10000; i++) {
-                entity.setAttribute(TestEntity.SEQUENCE, i);
-            }
-        } finally {
-            thread.interrupt();
-        }
-
-        if (threadException.get() != null) throw threadException.get();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/osgi/OsgiPathTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiPathTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiPathTest.java
deleted file mode 100644
index 829b281..0000000
--- a/core/src/test/java/brooklyn/management/osgi/OsgiPathTest.java
+++ /dev/null
@@ -1,105 +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.management.osgi;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-import org.osgi.framework.BundleException;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.config.BrooklynProperties;
-import brooklyn.config.BrooklynServerConfig;
-import brooklyn.config.BrooklynServerPaths;
-import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.util.os.Os;
-import brooklyn.util.text.Identifiers;
-
-
-/** 
- * Tests that OSGi entities load correctly and have the right catalog information set.
- * Note further tests done elsewhere using CAMP YAML (referring to methods in this class).
- */
-public class OsgiPathTest {
-   
-    protected LocalManagementContext mgmt = null;
-
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws BundleException, IOException, InterruptedException {
-        if (mgmt!=null) Entities.destroyAll(mgmt);
-    }
-    
-    @Test(groups="Integration") // integration only because OSGi takes ~200ms
-    public void testOsgiPathDefault() {
-        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
-        String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath();
-        Assert.assertTrue(path.startsWith(BrooklynServerPaths.getMgmtBaseDir(mgmt)), path);
-        Assert.assertTrue(path.contains(mgmt.getManagementNodeId()), path);
-        
-        assertExistsThenIsCleaned(path);
-    }
-
-    @Test(groups="Integration") // integration only because OSGi takes ~200ms
-    public void testOsgiPathCustom() {
-        BrooklynProperties bp = BrooklynProperties.Factory.newEmpty();
-        String randomSeg = "osgi-test-"+Identifiers.makeRandomId(4);
-        bp.put(BrooklynServerConfig.OSGI_CACHE_DIR, "${brooklyn.os.tmpdir}"+"/"+randomSeg+"/"+"${brooklyn.mgmt.node.id}");
-        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).useProperties(bp).build();
-        String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath();
-        Os.deleteOnExitRecursivelyAndEmptyParentsUpTo(new File(path), new File(Os.tmp()+"/"+randomSeg));
-        
-        Assert.assertTrue(path.startsWith(Os.tmp()), path);
-        Assert.assertTrue(path.contains(mgmt.getManagementNodeId()), path);
-        
-        assertExistsThenIsCleaned(path);
-    }
-
-    @Test(groups="Integration") // integration only because OSGi takes ~200ms
-    public void testOsgiPathCustomWithoutNodeIdNotCleaned() {
-        BrooklynProperties bp = BrooklynProperties.Factory.newEmpty();
-        String randomSeg = "osgi-test-"+Identifiers.makeRandomId(4);
-        bp.put(BrooklynServerConfig.OSGI_CACHE_DIR, "${brooklyn.os.tmpdir}"+"/"+randomSeg+"/"+"sample");
-        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).useProperties(bp).build();
-        String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath();
-        Os.deleteOnExitRecursivelyAndEmptyParentsUpTo(new File(path), new File(Os.tmp()+"/"+randomSeg));
-        
-        Assert.assertTrue(path.startsWith(Os.tmp()), path);
-        Assert.assertFalse(path.contains(mgmt.getManagementNodeId()), path);
-        
-        assertExistsThenCorrectCleanedBehaviour(path, false);
-    }
-
-    private void assertExistsThenIsCleaned(String path) {
-        assertExistsThenCorrectCleanedBehaviour(path, true);
-    }
-    private void assertExistsThenCorrectCleanedBehaviour(String path, boolean shouldBeCleanAfterwards) {
-        Assert.assertTrue(new File(path).exists(), "OSGi cache "+path+" should exist when in use");
-        Entities.destroyAll(mgmt);
-        mgmt = null;
-        if (shouldBeCleanAfterwards)
-            Assert.assertFalse(new File(path).exists(), "OSGi cache "+path+" should be cleaned after");
-        else
-            Assert.assertTrue(new File(path).exists(), "OSGi cache "+path+" should NOT be cleaned after");
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java
deleted file mode 100644
index 64306c6..0000000
--- a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java
+++ /dev/null
@@ -1,259 +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.management.osgi;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.jar.JarInputStream;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.test.TestResourceUnavailableException;
-
-import brooklyn.util.exceptions.Exceptions;
-
-import org.apache.commons.io.FileUtils;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.FrameworkEvent;
-import org.osgi.framework.launch.Framework;
-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.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;
-
-/** 
- * Tests some assumptions about OSGi behaviour, in standalone mode (not part of brooklyn).
- * See {@link OsgiTestResources} for description of test resources.
- */
-public class OsgiStandaloneTest {
-
-    private static final Logger log = LoggerFactory.getLogger(OsgiStandaloneTest.class);
-
-    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_PATH = OsgiTestResources.BROOKLYN_OSGI_TEST_A_0_1_0_PATH;
-    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = "classpath:"+BROOKLYN_OSGI_TEST_A_0_1_0_PATH;
-
-    public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
-    public static final String BROOKLYN_TEST_OSGI_ENTITIES_URL = "classpath:"+BROOKLYN_TEST_OSGI_ENTITIES_PATH;
-    public static final String BROOKLYN_TEST_OSGI_ENTITIES_NAME = "org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities";
-    public static final String BROOKLYN_TEST_OSGI_ENTITIES_VERSION = "0.1.0";
-
-    protected Framework framework = null;
-    private File storageTempDir;
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        storageTempDir = Os.newTempDir("osgi-standalone");
-        framework = Osgis.newFrameworkStarted(storageTempDir.getAbsolutePath(), true, null);
-    }
-
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws BundleException, IOException, InterruptedException {
-        tearDownOsgiFramework(framework, storageTempDir);
-    }
-
-    public static void tearDownOsgiFramework(Framework framework, File storageTempDir) throws BundleException, InterruptedException, IOException {
-        if (framework!=null) {
-            framework.stop();
-            Assert.assertEquals(framework.waitForStop(1000).getType(), FrameworkEvent.STOPPED);
-            framework = null;
-        }
-        if (storageTempDir!=null) {
-            FileUtils.deleteDirectory(storageTempDir);
-            storageTempDir = null;
-        }
-    }
-
-    protected Bundle install(String url) throws BundleException {
-        try {
-            return Osgis.install(framework, url);
-        } catch (Exception e) {
-            throw new IllegalStateException("test resources not available; may be an IDE issue, so try a mvn rebuild of this project", e);
-        }
-    }
-
-    protected Bundle installFromClasspath(String resourceName) throws BundleException {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), resourceName);
-        try {
-            return Osgis.install(framework, String.format("classpath:%s", resourceName));
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    @Test
-    public void testInstallBundle() throws Exception {
-        Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
-        checkMath(bundle, 3, 6);
-    }
-
-    @Test
-    public void testBootBundle() throws Exception {
-        Bundle bundle = installFromClasspath(BROOKLYN_TEST_OSGI_ENTITIES_PATH);
-        Class<?> bundleCls = bundle.loadClass("brooklyn.osgi.tests.SimpleEntity");
-        Assert.assertEquals(Entity.class,  bundle.loadClass(Entity.class.getName()));
-        Assert.assertEquals(Entity.class, bundleCls.getClassLoader().loadClass(Entity.class.getName()));
-    }
-
-    @Test
-    public void testDuplicateBundle() throws Exception {
-        MavenArtifact artifact = new MavenArtifact("org.apache.brooklyn", "brooklyn-api", "jar", "0.8.0-SNAPSHOT"); // BROOKLYN_VERSION
-        String localUrl = MavenRetriever.localUrl(artifact);
-        if ("file".equals(Urls.getProtocol(localUrl))) {
-            helperDuplicateBundle(localUrl);
-        } else {
-            log.warn("Skipping test OsgiStandaloneTest.testDuplicateBundle due to " + artifact + " not available in local repo.");
-        }
-    }
-
-    @Test(groups="Integration")
-    public void testRemoteDuplicateBundle() throws Exception {
-        helperDuplicateBundle(MavenRetriever.hostedUrl(new MavenArtifact("org.apache.brooklyn", "brooklyn-api", "jar", "0.8.0-SNAPSHOT"))); // BROOKLYN_VERSION
-    }
-
-    public void helperDuplicateBundle(String url) throws Exception {
-        //The bundle is already installed from the boot path.
-        //Make sure that we still get the initially loaded
-        //bundle after trying to install the same version.
-        Bundle bundle = install(url);
-        Assert.assertTrue(Osgis.isExtensionBundle(bundle));
-    }
-
-    @Test
-    public void testAMultiplier() throws Exception {
-        Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
-        checkMath(bundle, 3, 6);
-        setAMultiplier(bundle, 5);
-        checkMath(bundle, 3, 15);
-    }
-
-    /** run two multiplier tests to ensure that irrespective of order the tests run in, 
-     * on a fresh install the multiplier is reset */
-    @Test
-    public void testANOtherMultiple() throws Exception {
-        Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
-        checkMath(bundle, 3, 6);
-        setAMultiplier(bundle, 14);
-        checkMath(bundle, 3, 42);
-    }
-
-    @Test
-    public void testGetBundle() throws Exception {
-        Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
-        setAMultiplier(bundle, 3);
-
-        // can look it up based on the same location string (no other "location identifier" reference string seems to work here, however) 
-        Bundle bundle2 = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
-        checkMath(bundle2, 3, 9);
-    }
-
-    @Test
-    public void testUninstallAndReinstallBundle() throws Exception {
-        Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
-        checkMath(bundle, 3, 6);
-        setAMultiplier(bundle, 3);
-        checkMath(bundle, 3, 9);
-        bundle.uninstall();
-        
-        Bundle bundle2 = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
-        checkMath(bundle2, 3, 6);
-    }
-
-    protected void checkMath(Bundle bundle, int input, int output) throws Exception {
-        Assert.assertNotNull(bundle);
-        Class<?> aClass = bundle.loadClass("brooklyn.test.osgi.TestA");
-        Object aInst = aClass.newInstance();
-        Object result = aClass.getMethod("times", int.class).invoke(aInst, input);
-        Assert.assertEquals(result, output);
-    }
-
-    protected void setAMultiplier(Bundle bundle, int newMultiplier) throws Exception {
-        Assert.assertNotNull(bundle);
-        Class<?> aClass = bundle.loadClass("brooklyn.test.osgi.TestA");
-        aClass.getField("multiplier").set(null, newMultiplier);
-    }
-
-    @Test
-    public void testReadAManifest() throws Exception {
-        Enumeration<URL> manifests = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF");
-        log.info("Bundles and exported packages:");
-        MutableSet<String> allPackages = MutableSet.of();
-        while (manifests.hasMoreElements()) {
-            ManifestHelper mf = Osgis.ManifestHelper.forManifestContents(Streams.readFullyString( manifests.nextElement().openStream() ));
-            List<String> mfPackages = mf.getExportedPackages();
-            log.info("  "+mf.getSymbolicNameVersion()+": "+mfPackages);
-            allPackages.addAll(mfPackages);
-        }
-        log.info("Total export package count: "+allPackages.size());
-        Assert.assertTrue(allPackages.size()>20, "did not find enough packages"); // probably much larger
-        Assert.assertTrue(allPackages.contains(Osgis.class.getPackage().getName()));
-    }
-    
-    @Test
-    public void testReadKnownManifest() throws Exception {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_ENTITIES_PATH);
-        InputStream in = this.getClass().getResourceAsStream(BROOKLYN_TEST_OSGI_ENTITIES_PATH);
-        JarInputStream jarIn = new JarInputStream(in);
-        ManifestHelper helper = Osgis.ManifestHelper.forManifest(jarIn.getManifest());
-        jarIn.close();
-        Assert.assertEquals(helper.getVersion().toString(), "0.1.0");
-        Assert.assertTrue(helper.getExportedPackages().contains("brooklyn.osgi.tests"));
-    }
-    
-    @Test
-    public void testLoadOsgiBundleDependencies() throws Exception {
-        Bundle bundle = installFromClasspath(BROOKLYN_TEST_OSGI_ENTITIES_PATH);
-        Assert.assertNotNull(bundle);
-        Class<?> aClass = bundle.loadClass("brooklyn.osgi.tests.SimpleApplicationImpl");
-        Object aInst = aClass.newInstance();
-        Assert.assertNotNull(aInst);
-    }
-    
-    @Test
-    public void testLoadAbsoluteWindowsResourceWithInstalledOSGi() {
-        //Felix installs an additional URL to the system classloader
-        //which throws an IllegalArgumentException when passed a
-        //windows path. See ExtensionManager.java static initializer.
-        String context = "mycontext";
-        String dummyPath = "C:\\dummypath";
-        ResourceUtils utils = ResourceUtils.create(this, context);
-        try {
-            utils.getResourceFromUrl(dummyPath);
-            Assert.fail("Non-reachable, should throw an exception for non-existing resource.");
-        } catch (RuntimeException e) {
-            Assert.assertTrue(e.getMessage().startsWith("Error getting resource '"+dummyPath+"' for "+context));
-        }
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/osgi/OsgiTestResources.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiTestResources.java b/core/src/test/java/brooklyn/management/osgi/OsgiTestResources.java
deleted file mode 100644
index ef0321d..0000000
--- a/core/src/test/java/brooklyn/management/osgi/OsgiTestResources.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.management.osgi;
-
-/**
- * Many OSGi tests require OSGi bundles (of course). Test bundles have been collected here
- * for convenience and clarity. Available bundles (on the classpath, with source code
- * either embedded or in /src/dependencies) are described by the constants in this class.
- * <p>
- * Some of these bundles are also used in REST API tests, as that stretches catalog further
- * (using CAMP) and that is one area where OSGi is heavily used. 
- */
-public class OsgiTestResources {
-
-
-    /**
-     * brooklyn-osgi-test-a_0.1.0 -
-     * defines TestA which has a "times" method and a static multiplier field;
-     * we set the multiplier to determine when we are sharing versions and when not
-     */
-    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_PATH = "/brooklyn/osgi/brooklyn-osgi-test-a_0.1.0.jar";
-
-    /**
-     * brooklyn-test-osgi-entities (v 0.1.0) -
-     * defines an entity and an application, to confirm it can be read and used by brooklyn
-     */
-    public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = "/brooklyn/osgi/brooklyn-test-osgi-entities.jar";
-    public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_APPLICATION = "brooklyn.osgi.tests.SimpleApplication";
-    public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY = "brooklyn.osgi.tests.SimpleEntity";
-    public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY = "brooklyn.osgi.tests.SimplePolicy";
-
-    /**
-     * brooklyn-test-osgi-more-entities_0.1.0 -
-     * another bundle with a minimal sayHi effector, used to test versioning and dependencies
-     * (this one has no dependencies, but see also {@value #BROOKLYN_TEST_MORE_ENTITIES_V2_PATH})
-     */
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART = "brooklyn-test-osgi-more-entities";
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL = 
-        "org.apache.brooklyn.test.resources.osgi."+BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART;
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_V1_PATH = "/brooklyn/osgi/" + BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART + "_0.1.0.jar";
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY = "brooklyn.osgi.tests.more.MoreEntity";
-    
-    /**
-     * brooklyn-test-osgi-more-entities_0.2.0 -
-     * similar to {@link #BROOKLYN_TEST_MORE_ENTITIES_V1_PATH} but saying "HI NAME" rather than "Hi NAME",
-     * and declaring an explicit dependency on SimplePolicy from {@link #BROOKLYN_TEST_OSGI_ENTITIES_PATH}
-     */
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_PATH = "/brooklyn/osgi/" + BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART + "_0.2.0.jar";
-    
-    /**
-     * bundle with identical metadata (same symbolic name and version -- hence being an evil twin) 
-     * as {@link #BROOKLYN_TEST_MORE_ENTITIES_V2_PATH},
-     * but slightly different behaviour -- saying "HO NAME" -- in order to make sure we can differentiate two two
-     * at runtime.
-     */
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH = "/brooklyn/osgi/" + BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART + "_evil-twin_0.2.0.jar";
-    
-}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..8a02fcf
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerImpl.java
@@ -0,0 +1,1105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.rebind.RebindManager;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.ha.HighAvailabilityManager;
+import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
+import org.apache.brooklyn.api.management.ha.MementoCopyMode;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister.Delta;
+import org.apache.brooklyn.core.management.ha.BasicMasterChooser.AlphabeticMasterChooser;
+import org.apache.brooklyn.core.management.internal.BrooklynObjectManagementMode;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.BrooklynVersion;
+import brooklyn.catalog.internal.BasicBrooklynCatalog;
+import brooklyn.catalog.internal.CatalogDto;
+import brooklyn.config.BrooklynServerConfig;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.rebind.RebindManagerImpl;
+import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils;
+import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils.CreateBackupMode;
+import brooklyn.entity.rebind.persister.PersistenceActivityMetrics;
+import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord;
+import brooklyn.entity.rebind.plane.dto.ManagementPlaneSyncRecordImpl;
+import brooklyn.entity.rebind.plane.dto.ManagementPlaneSyncRecordImpl.Builder;
+import brooklyn.internal.BrooklynFeatureEnablement;
+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;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Ticker;
+import com.google.common.collect.Iterables;
+
+/**
+ * This is the guts of the high-availability solution in Brooklyn.
+ * <p>
+ * Multiple brooklyn nodes can be started to form a single management plane, where one node is 
+ * designated master and the others are "warm standbys". On termination or failure of the master,
+ * the standbys deterministically decide which standby should become master (see {@link MasterChooser}).
+ * That standby promotes itself.
+ * <p>
+ * The management nodes communicate their health/status via the {@link ManagementPlaneSyncRecordPersister}.
+ * For example, if using {@link ManagementPlaneSyncRecordPersisterToObjectStore} with a shared blobstore or 
+ * filesystem/NFS mount, then each management-node periodically writes its state. 
+ * This acts as a heartbeat, being read by the other management-nodes.
+ * <p>
+ * Promotion to master involves:
+ * <ol>
+ *   <li>notifying the other management-nodes that it is now master
+ *   <li>calling {@link RebindManager#rebind(ClassLoader, org.apache.brooklyn.api.entity.rebind.RebindExceptionHandler, ManagementNodeState)} to read all persisted entity state, and thus reconstitute the entities.
+ * </ol>
+ * <p>
+ * Future improvements in this area will include brooklyn-managing-brooklyn to decide + promote
+ * the standby.
+ * 
+ * @since 0.7.0
+ * 
+ * @author aled
+ */
+@Beta
+public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
+
+    public final ConfigKey<Duration> POLL_PERIOD = ConfigKeys.newConfigKey(Duration.class, "brooklyn.ha.pollPeriod",
+        "How often nodes should poll to detect whether master is healthy", Duration.seconds(1));
+    public final ConfigKey<Duration> HEARTBEAT_TIMEOUT = ConfigKeys.newConfigKey(Duration.class, "brooklyn.ha.heartbeatTimeout",
+        "Maximum allowable time for detection of a peer's heartbeat; if no sign of master after this time, "
+        + "another node may promote itself", Duration.THIRTY_SECONDS);
+    
+    @VisibleForTesting /* only used in tests currently */
+    public static interface PromotionListener {
+        public void promotingToMaster();
+    }
+    
+    private static final Logger LOG = LoggerFactory.getLogger(HighAvailabilityManagerImpl.class);
+
+    private final ManagementContextInternal managementContext;
+    private volatile String ownNodeId;
+    private volatile ManagementPlaneSyncRecordPersister persister;
+    private volatile PromotionListener promotionListener;
+    private volatile MasterChooser masterChooser = new AlphabeticMasterChooser();
+    private volatile Ticker localTickerUtc = new Ticker() {
+        // strictly not a ticker because returns millis UTC, but it works fine even so
+        @Override
+        public long read() {
+            return System.currentTimeMillis();
+        }
+    };
+    private volatile Ticker optionalRemoteTickerUtc = null;
+    
+    private volatile Task<?> pollingTask;
+    private volatile boolean disabled;
+    private volatile boolean running;
+    private volatile ManagementNodeState nodeState = ManagementNodeState.INITIALIZING;
+    private volatile boolean nodeStateTransitionComplete = false;
+    private volatile long priority = 0;
+    
+    private final static int MAX_NODE_STATE_HISTORY = 200;
+    private final List<Map<String,Object>> nodeStateHistory = MutableList.of();
+    
+    private volatile transient Duration pollPeriodLocalOverride;
+    private volatile transient Duration heartbeatTimeoutOverride;
+
+    private volatile ManagementPlaneSyncRecord lastSyncRecord;
+    
+    private volatile PersistenceActivityMetrics managementStateWritePersistenceMetrics = new PersistenceActivityMetrics();
+    private volatile PersistenceActivityMetrics managementStateReadPersistenceMetrics = new PersistenceActivityMetrics();
+    private final long startTimeUtc;
+
+    public HighAvailabilityManagerImpl(ManagementContextInternal managementContext) {
+        this.managementContext = managementContext;
+        startTimeUtc = localTickerUtc.read();
+    }
+
+    @Override
+    public HighAvailabilityManagerImpl setPersister(ManagementPlaneSyncRecordPersister persister) {
+        this.persister = checkNotNull(persister, "persister");
+        return this;
+    }
+    
+    @Override
+    public ManagementPlaneSyncRecordPersister getPersister() {
+        return persister;
+    }
+    
+    protected synchronized Duration getPollPeriod() {
+        if (pollPeriodLocalOverride!=null) return pollPeriodLocalOverride;
+        return managementContext.getBrooklynProperties().getConfig(POLL_PERIOD);
+    }
+    
+    /** Overrides {@link #POLL_PERIOD} from brooklyn config, 
+     * including e.g. {@link Duration#PRACTICALLY_FOREVER} to disable polling;
+     * or <code>null</code> to clear a local override */
+    public HighAvailabilityManagerImpl setPollPeriod(Duration val) {
+        this.pollPeriodLocalOverride = val;
+        if (running) {
+            registerPollTask();
+        }
+        return this;
+    }
+
+    public HighAvailabilityManagerImpl setMasterChooser(MasterChooser val) {
+        this.masterChooser = checkNotNull(val, "masterChooser");
+        return this;
+    }
+
+    public synchronized Duration getHeartbeatTimeout() {
+        if (heartbeatTimeoutOverride!=null) return heartbeatTimeoutOverride;
+        return managementContext.getBrooklynProperties().getConfig(HEARTBEAT_TIMEOUT);
+    }
+    
+    /** Overrides {@link #HEARTBEAT_TIMEOUT} from brooklyn config, 
+     * including e.g. {@link Duration#PRACTICALLY_FOREVER} to prevent failover due to heartbeat absence;
+     * or <code>null</code> to clear a local override */
+    public HighAvailabilityManagerImpl setHeartbeatTimeout(Duration val) {
+        this.heartbeatTimeoutOverride = val;
+        return this;
+    }
+
+    /** A ticker that reads in milliseconds, for populating local timestamps.
+     * Defaults to System.currentTimeMillis(); may be overridden e.g. for testing. */
+    public HighAvailabilityManagerImpl setLocalTicker(Ticker val) {
+        this.localTickerUtc = checkNotNull(val);
+        return this;
+    }
+
+    /** A ticker that reads in milliseconds, for overriding remote timestamps.
+     * Defaults to null which means to use the remote timestamp. 
+     * Only for testing as this records the remote timestamp in the object.
+     * <p>
+     * If this is supplied, one must also set {@link ManagementPlaneSyncRecordPersisterToObjectStore#useRemoteTimestampInMemento()}. */
+    @VisibleForTesting
+    public HighAvailabilityManagerImpl setRemoteTicker(Ticker val) {
+        this.optionalRemoteTickerUtc = val;
+        return this;
+    }
+
+    public HighAvailabilityManagerImpl setPromotionListener(PromotionListener val) {
+        this.promotionListener = checkNotNull(val, "promotionListener");
+        return this;
+    }
+    
+    @Override
+    public boolean isRunning() {
+        return running;
+    }
+
+    @Override
+    public void disabled() {
+        disabled = true;
+        ownNodeId = managementContext.getManagementNodeId();
+        // this is notionally the master, just not running; see javadoc for more info
+        stop(ManagementNodeState.MASTER);
+        
+    }
+
+    @Override
+    public void start(HighAvailabilityMode startMode) {
+        nodeStateTransitionComplete = true;
+        disabled = false;
+        running = true;
+        changeMode(startMode, true, true);
+    }
+    
+    @Override
+    public void changeMode(HighAvailabilityMode startMode) {
+        changeMode(startMode, false, false);
+    }
+    
+    @VisibleForTesting
+    @Beta
+    public void changeMode(HighAvailabilityMode startMode, boolean preventElectionOnExplicitStandbyMode, boolean failOnExplicitModesIfUnusual) {
+        if (!running) {
+            // if was not running then start as disabled mode, then proceed as normal
+            LOG.info("HA changing mode to "+startMode+" from "+getInternalNodeState()+" when not running, forcing an intermediate start as DISABLED then will convert to "+startMode);
+            start(HighAvailabilityMode.DISABLED);
+        }
+        if (getNodeState()==ManagementNodeState.FAILED || getNodeState()==ManagementNodeState.INITIALIZING) {
+            if (startMode!=HighAvailabilityMode.DISABLED) {
+                // if coming from FAILED (or INITIALIZING because we skipped start call) then treat as initializing
+                setInternalNodeState(ManagementNodeState.INITIALIZING);
+            }
+        }
+        
+        ownNodeId = managementContext.getManagementNodeId();
+        // TODO Small race in that we first check, and then we'll do checkMaster() on first poll,
+        // so another node could have already become master or terminated in that window.
+        ManagementNodeSyncRecord existingMaster = hasHealthyMaster();
+        boolean weAreRecognisedAsMaster = existingMaster!=null && ownNodeId.equals(existingMaster.getNodeId());
+        boolean weAreMasterLocally = getInternalNodeState()==ManagementNodeState.MASTER;
+        
+        // catch error in some tests where mgmt context has a different mgmt context
+        if (managementContext.getHighAvailabilityManager()!=this)
+            throw new IllegalStateException("Cannot start an HA manager on a management context with a different HA manager!");
+        
+        if (weAreMasterLocally) {
+            // demotion may be required; do this before triggering an election
+            switch (startMode) {
+            case MASTER:
+            case AUTO:
+            case DISABLED:
+                // no action needed, will do anything necessary below (or above)
+                break;
+            case HOT_STANDBY: 
+            case HOT_BACKUP: 
+            case STANDBY: 
+                demoteTo(ManagementNodeState.of(startMode).get()); break;
+            default:
+                throw new IllegalStateException("Unexpected high availability mode "+startMode+" requested for "+this);
+            }
+        }
+        
+        ManagementNodeState oldState = getInternalNodeState();
+        
+        // now do election
+        switch (startMode) {
+        case AUTO:
+            // don't care; let's start and see if we promote ourselves
+            if (getInternalNodeState()==ManagementNodeState.INITIALIZING) {
+                setInternalNodeState(ManagementNodeState.STANDBY);
+            }
+            publishAndCheck(true);
+            switch (getInternalNodeState()) {
+            case HOT_BACKUP:
+                if (!nodeStateTransitionComplete) throw new IllegalStateException("Cannot switch to AUTO when in the middle of a transition to "+getInternalNodeState());
+                // else change us to standby, desiring to go to hot standby, and continue to below
+                setInternalNodeState(ManagementNodeState.STANDBY);
+                startMode = HighAvailabilityMode.HOT_BACKUP;
+            case HOT_STANDBY:
+            case STANDBY:
+                if (getInternalNodeState()==ManagementNodeState.STANDBY && oldState==ManagementNodeState.INITIALIZING && startMode!=HighAvailabilityMode.HOT_BACKUP
+                        && BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY)) {
+                    // auto requested; not promoted; so it should become hot standby
+                    startMode = HighAvailabilityMode.HOT_STANDBY;
+                }
+                ManagementPlaneSyncRecord newState = loadManagementPlaneSyncRecord(true);
+                String masterNodeId = newState.getMasterNodeId();
+                ManagementNodeSyncRecord masterNodeDetails = newState.getManagementNodes().get(masterNodeId);
+                LOG.info("Management node "+ownNodeId+" running as HA " + getInternalNodeState() + " autodetected"
+                        + (startMode == HighAvailabilityMode.HOT_STANDBY || startMode == HighAvailabilityMode.HOT_BACKUP ? 
+                            " (will change to "+startMode+")" : "")
+                        + ", " +
+                    (Strings.isBlank(masterNodeId) ? "no master currently (other node should promote itself soon)" : "master "
+                        + (existingMaster==null ? "(new) " : "")
+                        + "is "+masterNodeId +
+                        (masterNodeDetails==null || masterNodeDetails.getUri()==null ? " (no url)" : " at "+masterNodeDetails.getUri())));
+                break;
+            case MASTER:
+                LOG.info("Management node "+ownNodeId+" running as HA MASTER autodetected");
+                break;
+            default:
+                throw new IllegalStateException("Management node "+ownNodeId+" set to HA AUTO, encountered unexpected mode "+getInternalNodeState());
+            }
+            break;
+        case MASTER:
+            if (!failOnExplicitModesIfUnusual || existingMaster==null) {
+                promoteToMaster();
+                if (existingMaster!=null) {
+                    LOG.info("Management node "+ownNodeId+" running as HA MASTER explicitly");
+                } else {
+                    LOG.info("Management node "+ownNodeId+" running as HA MASTER explicitly, stealing from "+existingMaster);
+                }
+            } else if (!weAreRecognisedAsMaster) {
+                throw new IllegalStateException("Master already exists; cannot run as master (master "+existingMaster.toVerboseString()+"); "
+                    + "to trigger a promotion, set a priority and demote the current master");
+            } else {
+                LOG.info("Management node "+ownNodeId+" already running as HA MASTER, when set explicitly");
+            }
+            break;
+        case HOT_BACKUP:
+            setInternalNodeState(ManagementNodeState.HOT_BACKUP);
+            // then continue into next block
+        case STANDBY:
+        case HOT_STANDBY:
+            if (startMode!=HighAvailabilityMode.HOT_BACKUP) {
+                if (ManagementNodeState.isHotProxy(getInternalNodeState()) && startMode==HighAvailabilityMode.HOT_STANDBY) {
+                    // if was hot_backup, we can immediately go hot_standby
+                    setInternalNodeState(ManagementNodeState.HOT_STANDBY);
+                } else {
+                    // from any other state, set standby, then perhaps switch to hot_standby later on (or might become master in the next block)
+                    setInternalNodeState(ManagementNodeState.STANDBY);
+                }
+            }
+            if (ManagementNodeState.isStandby(getInternalNodeState())) {
+                if (!preventElectionOnExplicitStandbyMode) {
+                    publishAndCheck(true);
+                }
+                if (failOnExplicitModesIfUnusual && existingMaster==null) {
+                    LOG.error("Management node "+ownNodeId+" detected no master when "+startMode+" requested and existing master required; failing.");
+                    throw new IllegalStateException("No existing master; cannot start as "+startMode);
+                }
+            }
+            String message = "Management node "+ownNodeId+" running as HA "+getNodeState()+" (";
+            if (getNodeState().toString().equals(startMode.toString()))
+                message += "explicitly requested";
+            else if (startMode==HighAvailabilityMode.HOT_STANDBY && getNodeState()==ManagementNodeState.STANDBY)
+                message += "caller requested "+startMode+", will attempt rebind for HOT_STANDBY next";
+            else
+                message += "caller requested "+startMode;
+            
+            if (getNodeState()==ManagementNodeState.MASTER) {
+                message += " but election re-promoted this node)";
+            } else {
+                ManagementPlaneSyncRecord newState = loadManagementPlaneSyncRecord(true);
+                if (Strings.isBlank(newState.getMasterNodeId())) {
+                    message += "); no master currently"; 
+                    if (startMode != HighAvailabilityMode.HOT_BACKUP) message += " (subsequent election may repair)";
+                } else {
+                    message += "); master "+newState.getMasterNodeId();
+                }
+            }
+            LOG.info(message);
+            break;
+        case DISABLED:
+            // safe just to run even if we weren't master
+            LOG.info("Management node "+ownNodeId+" HA DISABLED (was "+getInternalNodeState()+")");
+            demoteTo(ManagementNodeState.FAILED);
+            if (pollingTask!=null) pollingTask.cancel(true);
+            break;
+        default:
+            throw new IllegalStateException("Unexpected high availability mode "+startMode+" requested for "+this);
+        }
+        
+        if ((startMode==HighAvailabilityMode.HOT_STANDBY || startMode==HighAvailabilityMode.HOT_BACKUP)) {
+            if (!ManagementNodeState.isHotProxy(oldState)) {
+                // now transition to hot proxy
+                nodeStateTransitionComplete = false;
+                if (startMode==HighAvailabilityMode.HOT_STANDBY) {
+                    // if it should be hot standby, then we may need to promote
+                    // inform the world that we are transitioning (but not eligible for promotion while going in to hot standby)
+                    // (no harm in doing this twice)
+                    publishHealth();
+                }
+                try {
+                    activateHotProxy(ManagementNodeState.of(startMode).get()).get();
+                    // error above now throws
+                    nodeStateTransitionComplete = true;
+                    publishHealth();
+
+                    if (getNodeState()==ManagementNodeState.HOT_STANDBY || getNodeState()==ManagementNodeState.HOT_BACKUP) {
+                        LOG.info("Management node "+ownNodeId+" now running as HA "+getNodeState()+"; "
+                            + managementContext.getApplications().size()+" application"+Strings.s(managementContext.getApplications().size())+" loaded");
+                    } else {
+                        // shouldn't come here, we should have gotten an error above
+                        LOG.warn("Management node "+ownNodeId+" unable to promote to "+startMode+" (currently "+getNodeState()+"); "
+                            + "(see log for further details)");
+                    }
+                } catch (Exception e) {
+                    LOG.warn("Management node "+ownNodeId+" unable to promote to "+startMode+" (currently "+getNodeState()+"); rethrowing: "+Exceptions.collapseText(e));
+                    nodeStateTransitionComplete = true;
+                    throw Exceptions.propagate(e);
+                }
+            } else {
+                // transitioning among hot proxy states - tell the rebind manager
+                managementContext.getRebindManager().stopReadOnly();
+                managementContext.getRebindManager().startReadOnly(ManagementNodeState.of(startMode).get());
+                nodeStateTransitionComplete = true;
+            }
+        } else {
+            nodeStateTransitionComplete = true;
+        }
+        if (startMode!=HighAvailabilityMode.DISABLED)
+            registerPollTask();
+    }
+
+    @Override
+    public void setPriority(long priority) {
+        this.priority = priority;
+        if (persister!=null) publishHealth();
+    }
+    
+    @Override
+    public long getPriority() {
+        return priority;
+    }
+    
+    @Override
+    public void stop() {
+        LOG.debug("Stopping "+this);
+        stop(ManagementNodeState.TERMINATED);
+    }
+    
+    private void stop(ManagementNodeState newState) {
+        boolean wasRunning = running;
+        
+        running = false;
+        setInternalNodeState(newState);
+        if (pollingTask != null) pollingTask.cancel(true);
+        
+        if (wasRunning) {
+            try {
+                publishHealth();
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                LOG.error("Problem publishing manager-node health on termination (continuing)", e);
+            }
+        }
+    }
+    
+    /** returns the node state this node is trying to be in */
+    public ManagementNodeState getTransitionTargetNodeState() {
+        return getInternalNodeState();
+    }
+    
+    protected ManagementNodeState getInternalNodeState() {
+        return nodeState;
+    }
+    
+    protected void setInternalNodeState(ManagementNodeState newState) {
+        ManagementNodeState oldState = getInternalNodeState();
+        synchronized (nodeStateHistory) {
+            if (this.nodeState != newState) {
+                nodeStateHistory.add(0, MutableMap.<String,Object>of("state", newState, "timestamp", currentTimeMillis()));
+                while (nodeStateHistory.size()>MAX_NODE_STATE_HISTORY) {
+                    nodeStateHistory.remove(nodeStateHistory.size()-1);
+                }
+            }
+            ((RebindManagerImpl)managementContext.getRebindManager()).setAwaitingInitialRebind(running &&
+                (ManagementNodeState.isHotProxy(newState) || newState==ManagementNodeState.MASTER));
+            this.nodeState = newState;
+        }
+        
+        if (ManagementNodeState.isHotProxy(oldState) && !ManagementNodeState.isHotProxy(newState)) {
+            // could perhaps promote standby items on some transitions; but for now we stop the old read-only and re-load them
+            // TODO ideally there'd be an incremental rebind as well as an incremental persist
+            managementContext.getRebindManager().stopReadOnly();
+            clearManagedItems(ManagementTransitionMode.transitioning(BrooklynObjectManagementMode.LOADED_READ_ONLY, BrooklynObjectManagementMode.UNMANAGED_PERSISTED));
+        }
+    }
+
+    @Override
+    public ManagementNodeState getNodeState() {
+        ManagementNodeState myNodeState = getInternalNodeState();
+        if (myNodeState==ManagementNodeState.FAILED) return getInternalNodeState();
+        // if target is master then we claim already being master, to prevent other nodes from taking it
+        // (we may fail subsequently of course)
+        if (myNodeState==ManagementNodeState.MASTER) return myNodeState;
+        
+        if (!nodeStateTransitionComplete) return ManagementNodeState.INITIALIZING;
+        return myNodeState;
+    }
+
+    public ManagementPlaneSyncRecord getLastManagementPlaneSyncRecord() {
+        return lastSyncRecord;
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected void registerPollTask() {
+        final Runnable job = new Runnable() {
+            @Override public void run() {
+                try {
+                    publishAndCheck(false);
+                } catch (Exception e) {
+                    if (running) {
+                        LOG.error("Problem in HA-poller: "+e, e);
+                    } else {
+                        if (LOG.isDebugEnabled()) LOG.debug("Problem in HA-poller, but no longer running: "+e, e);
+                    }
+                } catch (Throwable t) {
+                    LOG.error("Problem in HA-poller: "+t, t);
+                    throw Exceptions.propagate(t);
+                }
+            }
+        };
+        Callable<Task<?>> taskFactory = new Callable<Task<?>>() {
+            @Override public Task<?> call() {
+                return Tasks.builder().dynamic(false).body(job).name("HA poller task").tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
+                    .description("polls HA status to see whether this node should promote").build();
+            }
+        };
+        
+        LOG.debug("Registering poll task for "+this+", period "+getPollPeriod());
+        if (getPollPeriod().equals(Duration.PRACTICALLY_FOREVER)) {
+            // don't schedule - used for tests
+            // (scheduling fires off one initial task in the background before the delay, 
+            // which affects tests that want to know exactly when publishing happens;
+            // TODO would be nice if scheduled task had a "no initial submission" flag )
+        } else {
+            if (pollingTask!=null) pollingTask.cancel(true);
+            
+            ScheduledTask task = new ScheduledTask(MutableMap.of("period", getPollPeriod(), "displayName", "scheduled:[HA poller task]"), taskFactory);
+            pollingTask = managementContext.getExecutionManager().submit(task);
+        }
+    }
+    
+    /** invoked manually when initializing, and periodically thereafter */
+    @VisibleForTesting
+    public synchronized void publishAndCheck(boolean initializing) {
+        publishHealth();
+        checkMaster(initializing);
+    }
+    
+    protected synchronized void publishHealth() {
+        if (persister == null) {
+            LOG.info("Cannot publish management-node health as no persister");
+            return;
+        }
+        
+        Stopwatch timer = Stopwatch.createStarted();
+        try {
+            ManagementNodeSyncRecord memento = createManagementNodeSyncRecord(false);
+            Delta delta = ManagementPlaneSyncRecordDeltaImpl.builder().node(memento).build();
+            persister.delta(delta);
+            managementStateWritePersistenceMetrics.noteSuccess(Duration.of(timer));
+            if (LOG.isTraceEnabled()) LOG.trace("Published management-node health: {}", memento);
+        } catch (Throwable t) {
+            managementStateWritePersistenceMetrics.noteFailure(Duration.of(timer));
+            managementStateWritePersistenceMetrics.noteError(t.toString());
+            LOG.debug("Error publishing management-node health (rethrowing): "+t);
+            throw Exceptions.propagate(t);
+        }
+    }
+    
+    public void publishClearNonMaster() {
+        ManagementPlaneSyncRecord plane = getLastManagementPlaneSyncRecord();
+        if (plane==null || persister==null) {
+            LOG.warn("Cannot clear HA node records; HA not active (or not yet loaded)");
+            return;
+        }
+        org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordDeltaImpl.Builder db = ManagementPlaneSyncRecordDeltaImpl.builder();
+        for (Map.Entry<String,ManagementNodeSyncRecord> node: plane.getManagementNodes().entrySet()) {
+            // only keep a node if it both claims master and is recognised as master;
+            // else ex-masters who died are kept around!
+            if (!ManagementNodeState.MASTER.equals(node.getValue().getStatus()) || 
+                    !Objects.equal(plane.getMasterNodeId(), node.getValue().getNodeId())) {
+                db.removedNodeId(node.getKey());
+            }
+        }
+        persister.delta(db.build());
+        // then get, so model is updated
+        loadManagementPlaneSyncRecord(true);
+    }
+    
+    protected synchronized void publishDemotion(boolean demotingFromMaster) {
+        checkState(getNodeState() != ManagementNodeState.MASTER, "node status must not be master when demoting", getNodeState());
+        
+        if (persister == null) {
+            LOG.info("Cannot publish management-node health as no persister");
+            return;
+        }
+        
+        ManagementNodeSyncRecord memento = createManagementNodeSyncRecord(false);
+        ManagementPlaneSyncRecordDeltaImpl.Builder deltaBuilder = ManagementPlaneSyncRecordDeltaImpl.builder()
+                .node(memento);
+        if (demotingFromMaster) {
+            deltaBuilder.clearMaster(ownNodeId);
+        }
+        
+        Delta delta = deltaBuilder.build();
+        persister.delta(delta);
+        if (LOG.isTraceEnabled()) LOG.trace("Published management-node health: {}", memento);
+    }
+    
+    /**
+     * Publishes (via {@link #persister}) the state of this management node with itself set to master.
+     */
+    protected synchronized void publishPromotionToMaster() {
+        checkState(getNodeState() == ManagementNodeState.MASTER, "node status must be master on publish, but is %s", getNodeState());
+        
+        if (persister == null) {
+            LOG.info("Cannot publish management-node health as no persister");
+            return;
+        }
+        
+        ManagementNodeSyncRecord memento = createManagementNodeSyncRecord(false);
+        Delta delta = ManagementPlaneSyncRecordDeltaImpl.builder()
+                .node(memento)
+                .setMaster(ownNodeId)
+                .build();
+        persister.delta(delta);
+        if (LOG.isTraceEnabled()) LOG.trace("Published management-node health: {}", memento);
+    }
+    
+    protected boolean isHeartbeatOk(ManagementNodeSyncRecord masterNode, ManagementNodeSyncRecord meNode) {
+        if (masterNode==null) return false;
+        if (meNode==null) {
+            // we can't confirm it's healthy, but it appears so as far as we can tell
+            return true;
+        }
+        Long timestampMaster = masterNode.getRemoteTimestamp();
+        Long timestampMe = meNode.getRemoteTimestamp();
+        if (timestampMaster==null || timestampMe==null) return false;
+        return (timestampMe - timestampMaster) <= getHeartbeatTimeout().toMilliseconds();
+    }
+    
+    protected ManagementNodeSyncRecord hasHealthyMaster() {
+        ManagementPlaneSyncRecord memento = loadManagementPlaneSyncRecord(false);
+        
+        String nodeId = memento.getMasterNodeId();
+        ManagementNodeSyncRecord masterMemento = (nodeId == null) ? null : memento.getManagementNodes().get(nodeId);
+        
+        ManagementNodeSyncRecord ourMemento = memento.getManagementNodes().get(ownNodeId);
+        boolean result = masterMemento != null && masterMemento.getStatus() == ManagementNodeState.MASTER
+                && isHeartbeatOk(masterMemento, ourMemento);
+        
+        if (LOG.isDebugEnabled()) LOG.debug("Healthy-master check result={}; masterId={}; masterMemento={}; ourMemento={}",
+                new Object[] {result, nodeId, (masterMemento == null ? "<none>" : masterMemento.toVerboseString()), (ourMemento == null ? "<none>" : ourMemento.toVerboseString())});
+        
+        return (result ? masterMemento : null);
+    }
+    
+    /**
+     * Looks up the state of all nodes in the management plane, and checks if the master is still ok.
+     * If it's not then determines which node should be promoted to master. If it is ourself, then promotes.
+     */
+    protected void checkMaster(boolean initializing) {
+        ManagementPlaneSyncRecord memento = loadManagementPlaneSyncRecord(false);
+        
+        if (getNodeState()==ManagementNodeState.FAILED || getNodeState()==ManagementNodeState.HOT_BACKUP) {
+            // if failed or hot backup then we can't promote ourselves, so no point in checking who is master
+            return;
+        }
+        
+        String currMasterNodeId = memento.getMasterNodeId();
+        ManagementNodeSyncRecord currMasterNodeRecord = memento.getManagementNodes().get(currMasterNodeId);
+        ManagementNodeSyncRecord ownNodeRecord = memento.getManagementNodes().get(ownNodeId);
+        
+        ManagementNodeSyncRecord newMasterNodeRecord = null;
+        boolean demotingSelfInFavourOfOtherMaster = false;
+        
+        if (currMasterNodeRecord != null && currMasterNodeRecord.getStatus() == ManagementNodeState.MASTER && isHeartbeatOk(currMasterNodeRecord, ownNodeRecord)) {
+            // master seems healthy
+            if (ownNodeId.equals(currMasterNodeId)) {
+                if (LOG.isTraceEnabled()) LOG.trace("Existing master healthy (us): master={}", currMasterNodeRecord.toVerboseString());
+                return;
+            } else {
+                if (ownNodeRecord!=null && ownNodeRecord.getStatus() == ManagementNodeState.MASTER) {
+                    LOG.error("Management node "+ownNodeId+" detected master change, stolen from us, deferring to "+currMasterNodeId);
+                    newMasterNodeRecord = currMasterNodeRecord;
+                    demotingSelfInFavourOfOtherMaster = true;
+                } else {
+                    if (LOG.isTraceEnabled()) LOG.trace("Existing master healthy (remote): master={}", currMasterNodeRecord.toVerboseString());
+                    return;
+                }
+            }
+        } else if (ownNodeRecord == null || !isHeartbeatOk(ownNodeRecord, ownNodeRecord)) {
+            // our heartbeats are also out-of-date! perhaps something wrong with persistence? just log, and don't over-react!
+            if (ownNodeRecord == null) {
+                LOG.error("No management node memento for self ("+ownNodeId+"); perhaps persister unwritable? "
+                        + "Master ("+currMasterNodeId+") reported failed but no-op as cannot tell conclusively");
+            } else {
+                LOG.error("This management node ("+ownNodeId+") memento heartbeats out-of-date; perhaps perister unwritable? "
+                        + "Master ("+currMasterNodeId+") reported failed but no-op as cannot tell conclusively"
+                        + ": self="+ownNodeRecord.toVerboseString());
+            }
+            return;
+        } else if (ownNodeId.equals(currMasterNodeId)) {
+            // we are supposed to be the master, but seem to be unhealthy!
+            LOG.warn("This management node ("+ownNodeId+") supposed to be master but reportedly unhealthy? "
+                    + "no-op as expect other node to fix: self="+ownNodeRecord.toVerboseString());
+            return;
+        }
+        
+        if (demotingSelfInFavourOfOtherMaster) {
+            LOG.debug("Master-change for this node only, demoting "+ownNodeRecord.toVerboseString()+" in favour of official master "+newMasterNodeRecord.toVerboseString());
+            demoteTo(
+                BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY) ?
+                    ManagementNodeState.HOT_STANDBY : ManagementNodeState.STANDBY);
+            return;
+        } else {
+            LOG.debug("Detected master heartbeat timeout. Initiating a new master election. Master was " + currMasterNodeRecord);
+        }
+        
+        // Need to choose a new master
+        newMasterNodeRecord = masterChooser.choose(memento, getHeartbeatTimeout(), ownNodeId);
+        
+        String newMasterNodeId = (newMasterNodeRecord == null) ? null : newMasterNodeRecord.getNodeId();
+        URI newMasterNodeUri = (newMasterNodeRecord == null) ? null : newMasterNodeRecord.getUri();
+        boolean weAreNewMaster = ownNodeId.equals(newMasterNodeId);
+        
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Management node master-change required: newMaster={}; oldMaster={}; plane={}, self={}; heartbeatTimeout={}", 
+                new Object[] {
+                    (newMasterNodeRecord == null ? "<none>" : newMasterNodeRecord.toVerboseString()),
+                    (currMasterNodeRecord == null ? currMasterNodeId+" (no memento)": currMasterNodeRecord.toVerboseString()),
+                    memento,
+                    ownNodeRecord.toVerboseString(), 
+                    getHeartbeatTimeout()
+                });
+        }
+        String message = "Management node "+ownNodeId+" detected ";
+        String currMasterSummary = currMasterNodeId + "(" + (currMasterNodeRecord==null ? "<none>" : timestampString(currMasterNodeRecord.getRemoteTimestamp())) + ")";
+        if (weAreNewMaster && (ownNodeRecord.getStatus() == ManagementNodeState.MASTER)) {
+            LOG.warn(message + "we must reassert master status, as was stolen and then failed at "+
+                (currMasterNodeRecord==null ? "a node which has gone away" : currMasterSummary));
+            publishPromotionToMaster();
+            publishHealth();
+            return;
+        }
+        
+        if (!initializing) {
+            if (weAreNewMaster) {
+                message += "we should be master, changing from ";
+            }
+            else if (currMasterNodeRecord==null && newMasterNodeId==null) message += "master change attempted but no candidates ";
+            else message += "master change, from ";
+            message += currMasterSummary + " to "
+                + (newMasterNodeId == null ? "<none>" :
+                    (weAreNewMaster ? "us " : "")
+                    + newMasterNodeId + " (" + timestampString(newMasterNodeRecord.getRemoteTimestamp()) + ")" 
+                    + (newMasterNodeUri!=null ? " "+newMasterNodeUri : "")  );
+            // always log, if you're looking at a standby node it's useful to see the new master's URL
+            LOG.info(message);
+        }
+
+        // New master is ourself: promote
+        if (weAreNewMaster) {
+            promoteToMaster();
+        }
+    }
+    
+    private static String timestampString(Long remoteTimestamp) {
+        if (remoteTimestamp==null) return null;
+        return remoteTimestamp+" / "+Time.makeTimeStringRounded( Duration.sinceUtc(remoteTimestamp))+" ago";
+    }
+
+    protected void promoteToMaster() {
+        if (!running) {
+            LOG.warn("Ignoring promote-to-master request, as HighAvailabilityManager is not running");
+            return;
+        }
+        
+        if (promotionListener != null) {
+            try {
+                promotionListener.promotingToMaster();
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                LOG.warn("Problem in promption-listener (continuing)", e);
+            }
+        }
+        setInternalNodeState(ManagementNodeState.MASTER);
+        publishPromotionToMaster();
+        try {
+            managementContext.getRebindManager().rebind(managementContext.getCatalogClassLoader(), null, getInternalNodeState());
+        } catch (Exception e) {
+            LOG.error("Management node "+managementContext.getManagementNodeId()+" enountered problem during rebind when promoting self to master; demoting to FAILED and rethrowing: "+e);
+            demoteTo(ManagementNodeState.FAILED);
+            throw Exceptions.propagate(e);
+        }
+        managementContext.getRebindManager().start();
+    }
+    
+    protected void backupOnDemotionIfNeeded() {
+        if (managementContext.getBrooklynProperties().getConfig(BrooklynServerConfig.PERSISTENCE_BACKUPS_REQUIRED_ON_DEMOTION)) {
+            BrooklynPersistenceUtils.createBackup(managementContext, CreateBackupMode.DEMOTION, MementoCopyMode.LOCAL);
+        }
+    }
+
+    /** @deprecated since 0.7.0, use {@link #demoteTo(ManagementNodeState)} */ @Deprecated
+    protected void demoteToFailed() {
+        demoteTo(ManagementNodeState.FAILED);
+    }
+    /** @deprecated since 0.7.0, use {@link #demoteTo(ManagementNodeState)} */ @Deprecated
+    protected void demoteToStandby(boolean hot) {
+        demoteTo(hot ? ManagementNodeState.HOT_STANDBY : ManagementNodeState.STANDBY);
+    }
+    
+    protected void demoteTo(ManagementNodeState toState) {
+        if (toState!=ManagementNodeState.FAILED && !running) {
+            LOG.warn("Ignoring demote-from-master request, as HighAvailabilityManager is no longer running");
+            return;
+        }
+        boolean wasMaster = (getInternalNodeState() == ManagementNodeState.MASTER);
+        if (wasMaster) backupOnDemotionIfNeeded();
+        // TODO target may be RO ?
+        ManagementTransitionMode mode = ManagementTransitionMode.transitioning(
+            wasMaster ? BrooklynObjectManagementMode.MANAGED_PRIMARY : BrooklynObjectManagementMode.LOADED_READ_ONLY,
+            BrooklynObjectManagementMode.UNMANAGED_PERSISTED);
+
+        nodeStateTransitionComplete = false;
+        
+        switch (toState) {
+        case FAILED: 
+        case HOT_BACKUP:
+        case STANDBY:
+            setInternalNodeState(toState); break;
+        case HOT_STANDBY:
+            setInternalNodeState(ManagementNodeState.STANDBY); break;
+        default:
+            throw new IllegalStateException("Illegal target state: "+toState);
+        }
+        onDemotionStopItems(mode);
+        nodeStateTransitionComplete = true;
+        publishDemotion(wasMaster);
+        
+        if (toState==ManagementNodeState.HOT_BACKUP || toState==ManagementNodeState.HOT_STANDBY) {
+            nodeStateTransitionComplete = false;
+            try {
+                activateHotProxy(toState).get();
+            } finally {
+                nodeStateTransitionComplete = true;
+            }
+            publishHealth();
+        }
+    }
+    
+    protected void onDemotionStopItems(ManagementTransitionMode mode) {
+        // stop persistence and remove all apps etc
+        managementContext.getRebindManager().stopPersistence();
+        managementContext.getRebindManager().stopReadOnly();
+        clearManagedItems(mode);
+        
+        // tasks are cleared as part of unmanaging entities above
+    }
+
+    /** clears all managed items from the management context; same items destroyed as in the course of a rebind cycle */
+    protected void clearManagedItems(ManagementTransitionMode mode) {
+        // start with the root applications
+        for (Application app: managementContext.getApplications()) {
+            if (((EntityInternal)app).getManagementSupport().isDeployed()) {
+                ((LocalEntityManager)((EntityInternal)app).getManagementContext().getEntityManager()).unmanage(app, mode);
+            }
+        }
+        // for active management, call above will remove recursively at present,
+        // but for read-only, and if we stop recursively, go through them all
+        for (Entity entity: managementContext.getEntityManager().getEntities()) {
+            ((LocalEntityManager)managementContext.getEntityManager()).unmanage(entity, mode);
+        }
+    
+        // again, for locations, call unmanage on parents first
+        for (Location loc: managementContext.getLocationManager().getLocations()) {
+            if (loc.getParent()==null)
+                ((LocationManagerInternal)managementContext.getLocationManager()).unmanage(loc, mode);
+        }
+        for (Location loc: managementContext.getLocationManager().getLocations()) {
+            ((LocationManagerInternal)managementContext.getLocationManager()).unmanage(loc, mode);
+        }
+        
+        ((BasicBrooklynCatalog)managementContext.getCatalog()).reset(CatalogDto.newEmptyInstance("<reset-by-ha-status-change>"));
+    }
+    
+    /** @deprecated since 0.7.0, use {@link #activateHotProxy(ManagementNodeState)} */ @Deprecated
+    protected boolean attemptHotStandby() {
+        return activateHotProxy(ManagementNodeState.HOT_STANDBY).getWithoutError();
+    }
+    
+    /** Starts hot standby or hot backup, in foreground
+     * <p>
+     * In the case of the former, the caller is responsible for publishing health afterwards,
+     * but if it fails, this method will {@link #demoteTo(ManagementNodeState)} {@link ManagementNodeState#FAILED}.
+     * <p>
+     * @return whether the requested {@link ManagementNodeState} was possible;
+     * (if not, errors should be stored elsewhere), callers may want to rethrow */
+    protected ReferenceWithError<Boolean> activateHotProxy(ManagementNodeState toState) {
+        try {
+            Preconditions.checkState(nodeStateTransitionComplete==false, "Must be in transitioning state to go into "+toState);
+            setInternalNodeState(toState);
+            managementContext.getRebindManager().startReadOnly(toState);
+            
+            return ReferenceWithError.newInstanceWithoutError(true);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            LOG.warn("Unable to change "+ownNodeId+" to "+toState+", switching to FAILED: "+e, e);
+            demoteTo(ManagementNodeState.FAILED);
+            return ReferenceWithError.newInstanceThrowingError(false, e);
+        }
+    }
+    
+    @Override
+    public ManagementPlaneSyncRecord loadManagementPlaneSyncRecord(boolean useLocalKnowledgeForThisNode) {
+        ManagementPlaneSyncRecord record = loadManagementPlaneSyncRecordInternal(useLocalKnowledgeForThisNode);
+        lastSyncRecord = record;
+        return record; 
+    }
+    
+    private ManagementPlaneSyncRecord loadManagementPlaneSyncRecordInternal(boolean useLocalKnowledgeForThisNode) {
+        if (disabled) {
+            // if HA is disabled, then we are the only node - no persistence; just load a memento to describe this node
+            Builder builder = ManagementPlaneSyncRecordImpl.builder()
+                .node(createManagementNodeSyncRecord(true));
+            if (getTransitionTargetNodeState() == ManagementNodeState.MASTER) {
+                builder.masterNodeId(ownNodeId);
+            }
+            return builder.build();
+        }
+        if (persister == null) {
+            // e.g. web-console may be polling before we've started up
+            LOG.debug("High availablity manager has no persister; returning empty record");
+            return ManagementPlaneSyncRecordImpl.builder().build();
+        }
+        
+        int maxLoadAttempts = 5;
+        Exception lastException = null;
+        Stopwatch timer = Stopwatch.createStarted();
+
+        for (int i = 0; i < maxLoadAttempts; i++) {
+            try {
+                ManagementPlaneSyncRecord result = persister.loadSyncRecord();
+                
+                if (useLocalKnowledgeForThisNode) {
+                    // Report this node's most recent state, and detect AWOL nodes
+                    ManagementNodeSyncRecord me = BasicManagementNodeSyncRecord.builder()
+                        .from(result.getManagementNodes().get(ownNodeId), true)
+                        .from(createManagementNodeSyncRecord(false), true)
+                        .build();
+                    Iterable<ManagementNodeSyncRecord> allNodes = result.getManagementNodes().values();
+                    if (me.getRemoteTimestamp()!=null)
+                        allNodes = Iterables.transform(allNodes, new MarkAwolNodes(me));
+                    Builder builder = ManagementPlaneSyncRecordImpl.builder()
+                        .masterNodeId(result.getMasterNodeId())
+                        .nodes(allNodes);
+                    builder.node(me);
+                    if (getTransitionTargetNodeState() == ManagementNodeState.MASTER) {
+                        builder.masterNodeId(ownNodeId);
+                    }
+                    result = builder.build();
+                }
+                
+                if (i>0) {
+                    managementStateReadPersistenceMetrics.noteError("Succeeded only on attempt "+(i+1)+": "+lastException);
+                }
+                managementStateReadPersistenceMetrics.noteSuccess(Duration.of(timer));
+                return result;
+            } catch (IOException e) {
+                if (i < (maxLoadAttempts - 1)) {
+                    if (LOG.isDebugEnabled()) LOG.debug("Problem loading mangement-plane memento attempt "+(i+1)+"/"+maxLoadAttempts+"; retrying", e);
+                }
+                lastException = e;
+            }
+        }
+        String message = "Failed to load mangement-plane memento "+maxLoadAttempts+" consecutive times";
+        managementStateReadPersistenceMetrics.noteError(message+": "+lastException);
+        managementStateReadPersistenceMetrics.noteFailure(Duration.of(timer));
+
+        throw new IllegalStateException(message, lastException);
+    }
+
+    protected ManagementNodeSyncRecord createManagementNodeSyncRecord(boolean useLocalTimestampAsRemoteTimestamp) {
+        long timestamp = currentTimeMillis();
+        brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord.Builder builder = BasicManagementNodeSyncRecord.builder()
+                .brooklynVersion(BrooklynVersion.get())
+                .nodeId(ownNodeId)
+                .status(getNodeState())
+                .priority(getPriority())
+                .localTimestamp(timestamp)
+                .uri(managementContext.getManagementNodeUri().orNull());
+        if (useLocalTimestampAsRemoteTimestamp)
+            builder.remoteTimestamp(timestamp);
+        else if (optionalRemoteTickerUtc!=null) {
+            builder.remoteTimestamp(optionalRemoteTickerUtc.read());
+        }
+        return builder.build();
+    }
+    
+    /**
+     * Gets the current time, using the {@link #localTickerUtc}. Normally this is equivalent of {@link System#currentTimeMillis()},
+     * but in test environments a custom {@link Ticker} can be injected via {@link #setLocalTicker(Ticker)} to allow testing of
+     * specific timing scenarios.
+     */
+    protected long currentTimeMillis() {
+        return localTickerUtc.read();
+    }
+
+    /**
+     * Infers the health of a node - if it last reported itself as healthy (standby or master), but we haven't heard 
+     * from it in a long time then report that node as failed; otherwise report its health as-is.
+     */
+    private class MarkAwolNodes implements Function<ManagementNodeSyncRecord, ManagementNodeSyncRecord> {
+        private final ManagementNodeSyncRecord referenceNode;
+        private MarkAwolNodes(ManagementNodeSyncRecord referenceNode) {
+            this.referenceNode = referenceNode;
+        }
+        @Nullable
+        @Override
+        public ManagementNodeSyncRecord apply(@Nullable ManagementNodeSyncRecord input) {
+            if (input == null) return null;
+            if (!(input.getStatus() == ManagementNodeState.STANDBY || input.getStatus() == ManagementNodeState.HOT_STANDBY || input.getStatus() == ManagementNodeState.MASTER || input.getStatus() == ManagementNodeState.HOT_BACKUP)) return input;
+            if (isHeartbeatOk(input, referenceNode)) return input;
+            return BasicManagementNodeSyncRecord.builder()
+                    .from(input)
+                    .status(ManagementNodeState.FAILED)
+                    .build();
+        }
+    }
+    
+    @Override
+    public String toString() {
+        return super.toString()+"[node:"+ownNodeId+";running="+running+"]";
+    }
+    
+    @Override
+    public Map<String,Object> getMetrics() {
+        Map<String,Object> result = MutableMap.of();
+        
+        result.put("state", getNodeState());
+        result.put("uptime", Time.makeTimeStringRounded(Duration.millis(currentTimeMillis()-startTimeUtc)));
+        result.put("currentTimeUtc", currentTimeMillis());
+        result.put("startTimeUtc", startTimeUtc);
+        result.put("highAvailability", MutableMap.<String,Object>of(
+            "priority", getPriority(),
+            "pollPeriod", getPollPeriod().toMilliseconds(),
+            "heartbeatTimeout", getHeartbeatTimeout().toMilliseconds(),
+            "history", nodeStateHistory));
+        
+        result.putAll(managementContext.getRebindManager().getMetrics());
+        result.put("managementStatePersistence", 
+            MutableMap.of("read", managementStateReadPersistenceMetrics, "write", managementStateWritePersistenceMetrics));
+        
+        return result;
+    }
+    
+    @Override
+    public long getLastStateChange() {
+        if (nodeStateHistory.size() > 0) {
+            return (Long)nodeStateHistory.get(0).get("timestamp");
+        } else {
+            return 0;
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordDeltaImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordDeltaImpl.java b/core/src/main/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordDeltaImpl.java
new file mode 100644
index 0000000..1ae4ea7
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordDeltaImpl.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.management.ha;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Collection;
+
+import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister.Delta;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Sets;
+
+/**
+ * @since 0.7.0
+ * 
+ * @author aled
+ */
+@Beta
+public class ManagementPlaneSyncRecordDeltaImpl implements Delta {
+    
+    public static Builder builder() {
+        return new Builder();
+    }
+    
+    public static class Builder {
+        private Collection<ManagementNodeSyncRecord> nodes = Sets.newLinkedHashSet();
+        private Collection <String> removedNodeIds = Sets.newLinkedHashSet();
+        private MasterChange masterChange = MasterChange.NO_CHANGE;
+        private String master;
+        private String expectedOldMaster;
+        
+        public Builder node(ManagementNodeSyncRecord node) {
+            nodes.add(checkNotNull(node, "node")); return this;
+        }
+        public Builder removedNodeId(String id) {
+            removedNodeIds.add(checkNotNull(id, "id")); return this;
+        }
+        public Builder setMaster(String nodeId) {
+            masterChange = MasterChange.SET_MASTER;
+            master = checkNotNull(nodeId, "masterId");
+            return this;
+        }
+        public Builder clearMaster(String optionalExpectedNodeId) {
+            masterChange = MasterChange.CLEAR_MASTER;
+            this.expectedOldMaster = optionalExpectedNodeId;
+            return this;
+        }
+        public Delta build() {
+            return new ManagementPlaneSyncRecordDeltaImpl(this);
+        }
+    }
+    
+    private final Collection<ManagementNodeSyncRecord> nodes;
+    private final Collection <String> removedNodeIds;
+    private final MasterChange masterChange;
+    private String masterId;
+    private String expectedOldMaster;
+    
+    ManagementPlaneSyncRecordDeltaImpl(Builder builder) {
+        nodes = builder.nodes;
+        removedNodeIds = builder.removedNodeIds;
+        masterChange = builder.masterChange;
+        masterId = builder.master;
+        this.expectedOldMaster = builder.expectedOldMaster;
+        checkState((masterChange == MasterChange.SET_MASTER) ? (masterId != null) : (masterId == null), 
+                "invalid combination: change=%s; masterId=%s", masterChange, masterId);
+    }
+    
+    @Override
+    public Collection<ManagementNodeSyncRecord> getNodes() {
+        return nodes;
+    }
+
+    @Override
+    public Collection<String> getRemovedNodeIds() {
+        return removedNodeIds;
+    }
+
+    @Override
+    public MasterChange getMasterChange() {
+        return masterChange;
+    }
+
+    @Override
+    public String getNewMasterOrNull() {
+        return masterId;
+    }
+    
+    @Override
+    public String getExpectedMasterToClear() {
+        return expectedOldMaster;
+    }
+    
+    @Override
+    public String toString() {
+        return getClass().getCanonicalName()+"["+
+            (masterChange!=null && masterChange != MasterChange.NO_CHANGE ? 
+                masterChange+": "+expectedOldMaster+"->"+masterId+"; " : "")+
+            "nodes: "+nodes+
+            (removedNodeIds!=null && !removedNodeIds.isEmpty() ? "; removing: "+removedNodeIds : "")
+            +"]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java b/core/src/main/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java
new file mode 100644
index 0000000..7f188be
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java
@@ -0,0 +1,355 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+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.api.management.ha.ManagementNodeSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.rebind.persister.MementoSerializer;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessorWithLock;
+import brooklyn.entity.rebind.persister.RetryingMementoSerializer;
+import brooklyn.entity.rebind.persister.StoreObjectAccessorLocking;
+import brooklyn.entity.rebind.persister.XmlMementoSerializer;
+import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord;
+import brooklyn.entity.rebind.plane.dto.ManagementPlaneSyncRecordImpl;
+import brooklyn.util.exceptions.Exceptions;
+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.annotations.VisibleForTesting;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.Maps;
+
+/**
+ * Structure of files is:
+ * <ul>
+ *   <li>{@code plane/} - top-level directory
+ *     <ul>
+ *       <li>{@code master} - contains the id of the management-node that is currently master
+ *       <li>{@code change.log} - log of changes made
+ *       <li>{@code nodes/} - sub-directory, containing one file per management-node
+ *         <ul>
+ *           <li>{@code a9WiuVKp} - file named after the management-node's id, containing the management node's current state
+ *           <li>{@code E1eDXQF3}
+ *         </ul>
+ *     </ul>
+ * </ul>
+ * 
+ * All writes are done synchronously.
+ * 
+ * @since 0.7.0
+ * 
+ * @author aled
+ */
+@Beta
+public class ManagementPlaneSyncRecordPersisterToObjectStore implements ManagementPlaneSyncRecordPersister {
+
+    // TODO Multiple node appending to change.log could cause strange interleaving, or perhaps even data loss?
+    // But this file is not critical to functionality.
+
+    // TODO Should ManagementPlaneSyncRecordPersister.Delta be different so can tell what is a significant event,
+    // and thus log it in change.log - currently only subset of significant things being logged.
+
+    private static final Logger LOG = LoggerFactory.getLogger(ManagementPlaneSyncRecordPersisterToObjectStore.class);
+
+    private static final Duration SHUTDOWN_TIMEOUT = Duration.TEN_SECONDS;
+    private static final Duration SYNC_WRITE_TIMEOUT = Duration.TEN_SECONDS;
+    public static final String NODES_SUB_PATH = "nodes";
+
+    // TODO Leak if we go through lots of managers; but tiny!
+    private final ConcurrentMap<String, StoreObjectAccessorWithLock> nodeWriters = Maps.newConcurrentMap();
+
+    private StoreObjectAccessorWithLock masterWriter;
+    private StoreObjectAccessorWithLock changeLogWriter;
+
+    private ManagementContext mgmt;
+    private final PersistenceObjectStore objectStore;
+    private final MementoSerializer<Object> serializer;
+
+    private static final int MAX_SERIALIZATION_ATTEMPTS = 5;
+
+    private boolean started = false;
+    private volatile boolean running = true;
+    
+    @VisibleForTesting
+    /** allows, when testing, to be able to override file times / blobstore times with time from the ticker */
+    private boolean preferRemoteTimestampInMemento = false;
+
+    /**
+     * @param mgmt not used much at present but handy to ensure we know it so that obj store is prepared
+     * @param objectStore the objectStore use to read/write management-plane data;
+     *   this must have been {@link PersistenceObjectStore#prepareForSharedUse(brooklyn.entity.rebind.persister.PersistMode, HighAvailabilityMode)}
+     * @param classLoader ClassLoader to use when deserializing data
+     */
+    public ManagementPlaneSyncRecordPersisterToObjectStore(ManagementContext mgmt, PersistenceObjectStore objectStore, ClassLoader classLoader) {
+        this.mgmt = mgmt;
+        this.objectStore = checkNotNull(objectStore, "objectStore");
+
+        MementoSerializer<Object> rawSerializer = new XmlMementoSerializer<Object>(checkNotNull(classLoader, "classLoader"));
+        this.serializer = new RetryingMementoSerializer<Object>(rawSerializer, MAX_SERIALIZATION_ATTEMPTS);
+
+        objectStore.createSubPath(NODES_SUB_PATH);
+
+        LOG.debug("ManagementPlaneMemento-persister will use store "+objectStore);
+    }
+
+    protected synchronized void init() {
+        if (!started) {
+            started = true;
+            //Leading slash causes problems in SL, it's not a correct file name so remove it.
+            //But once removed we can't load the master file from existing persistence stores.
+            //Try to detect if the old file exists, if so use old-style names, otherwise use the correct names.
+            masterWriter = new StoreObjectAccessorLocking(objectStore.newAccessor("/master"));
+            if (masterWriter.get() != null) {
+                changeLogWriter = new StoreObjectAccessorLocking(objectStore.newAccessor("/change.log"));
+            } else {
+                masterWriter = new StoreObjectAccessorLocking(objectStore.newAccessor("master"));
+                changeLogWriter = new StoreObjectAccessorLocking(objectStore.newAccessor("change.log"));
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public void preferRemoteTimestampInMemento() {
+        preferRemoteTimestampInMemento = true;
+    }
+    
+    @Override
+    public void stop() {
+        running = false;
+        try {
+            for (StoreObjectAccessorWithLock writer : nodeWriters.values()) {
+                try {
+                    writer.waitForCurrentWrites(SHUTDOWN_TIMEOUT);
+                } catch (TimeoutException e) {
+                    LOG.warn("Timeout during shutdown, waiting for write of "+writer+"; continuing");
+                }
+            }
+            try {
+                masterWriter.waitForCurrentWrites(SHUTDOWN_TIMEOUT);
+            } catch (TimeoutException e) {
+                LOG.warn("Timeout during shutdown, waiting for write of "+masterWriter+"; continuing");
+            }
+        } catch (InterruptedException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    @Override
+    public ManagementPlaneSyncRecord loadSyncRecord() throws IOException {
+        if (!running) {
+            throw new IllegalStateException("Persister not running; cannot load memento from "+ objectStore.getSummaryName());
+        }
+        init();
+        
+        // Note this is called a lot - every time we check the heartbeats
+        if (LOG.isTraceEnabled()) LOG.trace("Loading management-plane memento from {}", objectStore.getSummaryName());
+
+        Stopwatch stopwatch = Stopwatch.createStarted();
+
+        ManagementPlaneSyncRecordImpl.Builder builder = ManagementPlaneSyncRecordImpl.builder();
+
+        // Be careful about order: if the master-file says nodeX then nodeX's file must have an up-to-date timestamp.
+        // Therefore read master file first, followed by the other node-files.
+        String masterNodeId = masterWriter.get();
+        if (masterNodeId == null) {
+            LOG.debug("No master-memento deserialized from file "+masterWriter+"; ignoring and continuing (normal on startup, should cause an error later in live operation)");
+        } else {
+            builder.masterNodeId(masterNodeId);
+        }
+
+        // Load node-files
+        List<String> nodeFiles = objectStore.listContentsWithSubPath(NODES_SUB_PATH);
+        LOG.trace("Loading nodes from {}; {} nodes.",
+                new Object[]{objectStore.getSummaryName(), nodeFiles.size()});
+
+        for (String nodeFile : nodeFiles) {
+            PersistenceObjectStore.StoreObjectAccessor objectAccessor = objectStore.newAccessor(nodeFile);
+            String nodeContents = null;
+            Exception problem = null;
+            try {
+                nodeContents = objectAccessor.get();
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                problem = e;
+            }
+            if (problem!=null || Strings.isBlank(nodeContents)) {
+                // happens if node has gone away, or if FileBasedObjectStore.moveFile is not atomic, 
+                // i.e. it has deleted but not updated it yet
+                if (objectAccessor.exists()) {
+                    throw Exceptions.propagate(new IllegalStateException("Node record "+nodeFile+" could not be read when "+mgmt.getManagementNodeId()+" was scanning", problem));
+                } else {
+                    LOG.warn("Node record "+nodeFile+" went away while "+mgmt.getManagementNodeId()+" was scanning, ignoring (it has probably been terminated)");
+                    // if file was deleted, silently ignore
+                    continue;
+                }
+            }
+            ManagementNodeSyncRecord memento = (ManagementNodeSyncRecord) serializer.fromString(nodeContents);
+            if (memento == null) {
+                // shouldn't happen
+                throw Exceptions.propagate(new IllegalStateException("Node record "+nodeFile+" could not be deserialized when "+mgmt.getManagementNodeId()+" was scanning: "+nodeContents, problem));
+            } else {
+                if (memento.getRemoteTimestamp()!=null && preferRemoteTimestampInMemento) {
+                    // in test mode, the remote timestamp is stored in the file
+                } else {
+                    if (memento.getRemoteTimestamp()!=null) {
+                        LOG.debug("Ignoring remote timestamp in memento file ("+memento+"); looks like this data has been manually copied in");
+                    }
+                    Date lastModifiedDate = objectAccessor.getLastModifiedDate();
+                    ((BasicManagementNodeSyncRecord)memento).setRemoteTimestamp(lastModifiedDate!=null ? lastModifiedDate.getTime() : null);
+                }
+                builder.node(memento);
+            }
+        }
+
+        if (LOG.isDebugEnabled()) LOG.trace("Loaded management-plane memento; {} nodes, took {}",
+            nodeFiles.size(),
+            Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)));
+        return builder.build();
+    }
+    
+    @Override
+    public void delta(Delta delta) {
+        if (!running) {
+            if (LOG.isDebugEnabled()) LOG.debug("Persister not running; ignoring checkpointed delta of manager-memento");
+            return;
+        }
+        init();
+        
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        if (LOG.isTraceEnabled()) LOG.trace("Checkpointing delta of manager-memento; updating {}", delta);
+        
+        for (ManagementNodeSyncRecord m : delta.getNodes()) {
+            persist(m);
+        }
+        for (String id : delta.getRemovedNodeIds()) {
+            deleteNode(id);
+        }
+        switch (delta.getMasterChange()) {
+        case NO_CHANGE:
+            break; // no-op
+        case SET_MASTER:
+            persistMaster(checkNotNull(delta.getNewMasterOrNull()), null);
+            break;
+        case CLEAR_MASTER:
+            persistMaster("", delta.getExpectedMasterToClear());
+            break; // no-op
+        default:
+            throw new IllegalStateException("Unknown state for master-change: "+delta.getMasterChange());
+        }
+        if (LOG.isDebugEnabled()) LOG.debug("Checkpointed delta of manager-memento in "+Time.makeTimeStringRounded(stopwatch)+": "+delta);
+    }
+
+    private void persistMaster(String nodeId, String optionalExpectedId) {
+        if (optionalExpectedId!=null) {
+            String currentRemoteMaster = masterWriter.get();
+            if (currentRemoteMaster==null) {
+                // okay to have nothing at remote
+            } else if (!currentRemoteMaster.trim().equals(optionalExpectedId.trim())) {
+                LOG.warn("Master at server is "+(Strings.isBlank(currentRemoteMaster) ? "<none>" : currentRemoteMaster)+"; expected "+optionalExpectedId+" "
+                    + (Strings.isNonBlank(nodeId) ? "and would set as "+nodeId : "and would clear") 
+                    + ", so not applying (yet)");
+                return;
+            }
+        }
+        masterWriter.put(nodeId);
+        try {
+            masterWriter.waitForCurrentWrites(SYNC_WRITE_TIMEOUT);
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+        changeLogWriter.append(Time.makeDateString() + ": set master to " + nodeId + "\n");
+        try {
+            changeLogWriter.waitForCurrentWrites(SYNC_WRITE_TIMEOUT);
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    @Override
+    @VisibleForTesting
+    public void waitForWritesCompleted(Duration timeout) throws InterruptedException, TimeoutException {
+        for (StoreObjectAccessorWithLock writer : nodeWriters.values()) {
+            writer.waitForCurrentWrites(timeout);
+        }
+        masterWriter.waitForCurrentWrites(timeout);
+    }
+
+    public void checkpoint(ManagementPlaneSyncRecord record) {
+        init();
+        for (ManagementNodeSyncRecord node : record.getManagementNodes().values()) {
+            // Check included in case the node in the memento is the one being initialised by
+            // BrooklynLauncher in the copy state command.
+            if (!ManagementNodeState.INITIALIZING.equals(node.getStatus()) && node.getNodeId() != null) {
+                persist(node);
+            }
+        }
+    }
+
+    private void persist(ManagementNodeSyncRecord node) {
+        StoreObjectAccessorWithLock writer = getOrCreateNodeWriter(node.getNodeId());
+        boolean fileExists = writer.exists();
+        writer.put(serializer.toString(node));
+        try {
+            writer.waitForCurrentWrites(SYNC_WRITE_TIMEOUT);
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+        if (!fileExists) {
+            changeLogWriter.append(Time.makeDateString()+": created node "+node.getNodeId()+"\n");
+        }
+        if (node.getStatus() == ManagementNodeState.TERMINATED || node.getStatus() == ManagementNodeState.FAILED) {
+            changeLogWriter.append(Time.makeDateString()+": set node "+node.getNodeId()+" status to "+node.getStatus()+"\n");
+        }
+    }
+    
+    private void deleteNode(String nodeId) {
+        getOrCreateNodeWriter(nodeId).delete();
+        changeLogWriter.append(Time.makeDateString()+": deleted node "+nodeId+"\n");
+    }
+
+    private StoreObjectAccessorWithLock getOrCreateNodeWriter(String nodeId) {
+        PersistenceObjectStore.StoreObjectAccessorWithLock writer = nodeWriters.get(nodeId);
+        if (writer == null) {
+            nodeWriters.putIfAbsent(nodeId, 
+                new StoreObjectAccessorLocking(objectStore.newAccessor(NODES_SUB_PATH+"/"+nodeId)));
+            writer = nodeWriters.get(nodeId);
+        }
+        return writer;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/ha/MasterChooser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/ha/MasterChooser.java b/core/src/main/java/org/apache/brooklyn/core/management/ha/MasterChooser.java
new file mode 100644
index 0000000..2a2bd09
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/ha/MasterChooser.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.management.ha;
+
+import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
+
+import brooklyn.util.time.Duration;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * For choosing which management node to promote, when master detected as failed or stopped.
+ * 
+ * @since 0.7.0
+ * 
+ * @author aled
+ */
+@Beta
+public interface MasterChooser {
+
+    ManagementNodeSyncRecord choose(ManagementPlaneSyncRecord memento, Duration heartbeatTimeout, String ownNodeId);
+    
+}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..99b79ec
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManager.java
@@ -0,0 +1,292 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static brooklyn.util.JavaGroovyEquivalents.elvis;
+import static brooklyn.util.JavaGroovyEquivalents.groovyTruth;
+import static brooklyn.util.JavaGroovyEquivalents.join;
+import static brooklyn.util.JavaGroovyEquivalents.mapOf;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.brooklyn.api.entity.Entity;
+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.management.ExecutionManager;
+import org.apache.brooklyn.api.management.SubscriptionHandle;
+import org.apache.brooklyn.api.management.SubscriptionManager;
+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;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimaps;
+
+/**
+ * A {@link SubscriptionManager} that stores subscription details locally.
+ */
+public class LocalSubscriptionManager extends AbstractSubscriptionManager {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(LocalSubscriptionManager.class);
+
+    protected final ExecutionManager em;
+    
+    private final String tostring = "SubscriptionContext("+Identifiers.getBase64IdFromValue(System.identityHashCode(this), 5)+")";
+
+    private final AtomicLong totalEventsPublishedCount = new AtomicLong();
+    private final AtomicLong totalEventsDeliveredCount = new AtomicLong();
+    
+    @SuppressWarnings("rawtypes")
+    protected final ConcurrentMap<String, Subscription> allSubscriptions = new ConcurrentHashMap<String, Subscription>();
+    @SuppressWarnings("rawtypes")
+    protected final ConcurrentMap<Object, Set<Subscription>> subscriptionsBySubscriber = new ConcurrentHashMap<Object, Set<Subscription>>();
+    @SuppressWarnings("rawtypes")
+    protected final ConcurrentMap<Object, Set<Subscription>> subscriptionsByToken = new ConcurrentHashMap<Object, Set<Subscription>>();
+    
+    public LocalSubscriptionManager(ExecutionManager m) {
+        this.em = m;
+    }
+        
+    public long getNumSubscriptions() {
+        return allSubscriptions.size();
+    }
+
+    public long getTotalEventsPublished() {
+        return totalEventsPublishedCount.get();
+    }
+    
+    public long getTotalEventsDelivered() {
+        return totalEventsDeliveredCount.get();
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected synchronized <T> SubscriptionHandle subscribe(Map<String, Object> flags, Subscription<T> s) {
+        Entity producer = s.producer;
+        Sensor<T> sensor= s.sensor;
+        s.subscriber = getSubscriber(flags, s);
+        if (flags.containsKey("subscriberExecutionManagerTag")) {
+            s.subscriberExecutionManagerTag = flags.remove("subscriberExecutionManagerTag");
+            s.subscriberExecutionManagerTagSupplied = true;
+        } else {
+            s.subscriberExecutionManagerTag = 
+                s.subscriber instanceof Entity ? "subscription-delivery-entity-"+((Entity)s.subscriber).getId()+"["+s.subscriber+"]" : 
+                s.subscriber instanceof String ? "subscription-delivery-string["+s.subscriber+"]" : 
+                "subscription-delivery-object["+s.subscriber+"]";
+            s.subscriberExecutionManagerTagSupplied = false;
+        }
+        s.eventFilter = (Predicate<SensorEvent<T>>) flags.remove("eventFilter");
+        s.flags = flags;
+        
+        if (LOG.isDebugEnabled()) LOG.debug("Creating subscription {} for {} on {} {} in {}", new Object[] {s.id, s.subscriber, producer, sensor, this});
+        allSubscriptions.put(s.id, s);
+        addToMapOfSets(subscriptionsByToken, makeEntitySensorToken(s.producer, s.sensor), s);
+        if (s.subscriber!=null) {
+            addToMapOfSets(subscriptionsBySubscriber, s.subscriber, s);
+        }
+        if (!s.subscriberExecutionManagerTagSupplied && s.subscriberExecutionManagerTag!=null) {
+            ((BasicExecutionManager) em).setTaskSchedulerForTag(s.subscriberExecutionManagerTag, SingleThreadedScheduler.class);
+        }
+        return s;
+    }
+
+    @SuppressWarnings("unchecked")
+    public Set<SubscriptionHandle> getSubscriptionsForSubscriber(Object subscriber) {
+        return (Set<SubscriptionHandle>) ((Set<?>) elvis(subscriptionsBySubscriber.get(subscriber), Collections.emptySet()));
+    }
+
+    public synchronized Set<SubscriptionHandle> getSubscriptionsForEntitySensor(Entity source, Sensor<?> sensor) {
+        Set<SubscriptionHandle> subscriptions = new LinkedHashSet<SubscriptionHandle>();
+        subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(source, sensor)), Collections.emptySet()));
+        subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(null, sensor)), Collections.emptySet()));
+        subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(source, null)), Collections.emptySet()));
+        subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(null, null)), Collections.emptySet()));
+        return subscriptions;
+    }
+
+    /**
+     * Unsubscribe the given subscription id.
+     *
+     * @see #subscribe(Map, Entity, Sensor, SensorEventListener)
+     */
+    @SuppressWarnings("rawtypes")
+    public synchronized boolean unsubscribe(SubscriptionHandle sh) {
+        if (!(sh instanceof Subscription)) throw new IllegalArgumentException("Only subscription handles of type Subscription supported: sh="+sh+"; type="+(sh != null ? sh.getClass().getCanonicalName() : null));
+        Subscription s = (Subscription) sh;
+        boolean result = allSubscriptions.remove(s.id) != null;
+        boolean b2 = removeFromMapOfCollections(subscriptionsByToken, makeEntitySensorToken(s.producer, s.sensor), s);
+        assert result==b2;
+        if (s.subscriber!=null) {
+            boolean b3 = removeFromMapOfCollections(subscriptionsBySubscriber, s.subscriber, s);
+            assert b3 == b2;
+        }
+
+        // FIXME ALEX - this seems wrong
+        ((BasicExecutionManager) em).setTaskSchedulerForTag(s.subscriberExecutionManagerTag, SingleThreadedScheduler.class);
+        return result;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T> void publish(final SensorEvent<T> event) {
+        // REVIEW 1459 - execution
+        
+        // delivery in parallel/background, using execution manager
+        
+        // subscriptions, should define SingleThreadedScheduler for any subscriber ID tag
+        // in order to ensure callbacks are invoked in the order they are submitted
+        // (recommend exactly one per subscription to prevent deadlock)
+        // this is done with:
+        // em.setTaskSchedulerForTag(subscriberId, SingleThreadedScheduler.class);
+        
+        //note, generating the notifications must be done in the calling thread to preserve order
+        //e.g. emit(A); emit(B); should cause onEvent(A); onEvent(B) in that order
+        if (LOG.isTraceEnabled()) LOG.trace("{} got event {}", this, event);
+        totalEventsPublishedCount.incrementAndGet();
+        
+        Set<Subscription> subs = (Set<Subscription>) ((Set<?>) getSubscriptionsForEntitySensor(event.getSource(), event.getSensor()));
+        if (groovyTruth(subs)) {
+            if (LOG.isTraceEnabled()) LOG.trace("sending {}, {} to {}", new Object[] {event.getSensor().getName(), event, join(subs, ",")});
+            for (Subscription s : subs) {
+                if (s.eventFilter!=null && !s.eventFilter.apply(event))
+                    continue;
+                final Subscription sAtClosureCreation = s;
+                
+//                Set<Object> tags = MutableSet.of();
+//                if (s.subscriberExecutionManagerTag!=null) tags.add(s.subscriberExecutionManagerTag);
+//                if (event.getSource()!=null) tags.add(BrooklynTaskTags.tagForContextEntity(event.getSource()));
+//                Map<String, Object> tagsMap = mapOf("tags", (Object)tags);
+                // use code above, instead of line below, if we want subscription deliveries associated with the entity;
+                // that will cause them to be cancelled when the entity is unmanaged
+                // (not sure that is useful, and likely NOT worth the expense, but it might be...) -Alex Oct 2014
+                Map<String, Object> tagsMap = mapOf("tag", s.subscriberExecutionManagerTag);
+                
+                em.submit(tagsMap, new Runnable() {
+                    @Override
+                    public String toString() {
+                        return "LSM.publish("+event+")";
+                    }
+                    public void run() {
+                        try {
+                            sAtClosureCreation.listener.onEvent(event);
+                        } catch (Throwable t) {
+                            if (event!=null && event.getSource()!=null && Entities.isNoLongerManaged(event.getSource())) {
+                                LOG.debug("Error processing subscriptions to "+this+", after entity unmanaged: "+t, t);
+                            } else {
+                                LOG.warn("Error processing subscriptions to "+this+": "+t, t);
+                            }
+                        }
+                    }});
+                totalEventsDeliveredCount.incrementAndGet();
+            }
+        }
+    }
+    
+    @Override
+    public String toString() {
+        return tostring;
+    }
+    
+    /**
+     * Copied from LanguageUtils.groovy, to remove dependency.
+     * 
+     * Adds the given value to a collection in the map under the key.
+     * 
+     * A collection (as {@link LinkedHashMap}) will be created if necessary,
+     * synchronized on map for map access/change and set for addition there
+     *
+     * @return the updated set (instance, not copy)
+     * 
+     * @deprecated since 0.5; use {@link HashMultimap}, and {@link Multimaps#synchronizedSetMultimap(com.google.common.collect.SetMultimap)}
+     */
+    @Deprecated
+    private static <K,V> Set<V> addToMapOfSets(Map<K,Set<V>> map, K key, V valueInCollection) {
+        Set<V> coll;
+        synchronized (map) {
+            coll = map.get(key);
+            if (coll==null) {
+                coll = new LinkedHashSet<V>();
+                map.put(key, coll);
+            }
+            if (coll.isEmpty()) {
+                synchronized (coll) {
+                    coll.add(valueInCollection);
+                }
+                //if collection was empty then add to the collection while holding the map lock, to prevent removal
+                return coll;
+            }
+        }
+        synchronized (coll) {
+            if (!coll.isEmpty()) {
+                coll.add(valueInCollection);
+                return coll;
+            }
+        }
+        //if was empty, recurse, because someone else might be removing the collection
+        return addToMapOfSets(map, key, valueInCollection);
+    }
+
+    /**
+     * Copied from LanguageUtils.groovy, to remove dependency.
+     * 
+     * Removes the given value from a collection in the map under the key.
+     *
+     * @return the updated set (instance, not copy)
+     * 
+     * @deprecated since 0.5; use {@link ArrayListMultimap} or {@link HashMultimap}, and {@link Multimaps#synchronizedListMultimap(com.google.common.collect.ListMultimap)} etc
+     */
+    @Deprecated
+    private static <K,V> boolean removeFromMapOfCollections(Map<K,? extends Collection<V>> map, K key, V valueInCollection) {
+        Collection<V> coll;
+        synchronized (map) {
+            coll = map.get(key);
+            if (coll==null) return false;
+        }
+        boolean result;
+        synchronized (coll) {
+            result = coll.remove(valueInCollection);
+        }
+        if (coll.isEmpty()) {
+            synchronized (map) {
+                synchronized (coll) {
+                    if (coll.isEmpty()) {
+                        //only remove from the map if no one is adding to the collection or to the map, and the collection is still in the map
+                        if (map.get(key)==coll) {
+                            map.remove(key);
+                        }
+                    }
+                }
+            }
+        }
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..dc95d11
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalUsageManager.java
@@ -0,0 +1,430 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.internal.storage.BrooklynStorage;
+
+import org.apache.brooklyn.location.basic.AbstractLocation;
+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;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+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;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+public class LocalUsageManager implements UsageManager {
+
+    // TODO Threading model needs revisited.
+    // Synchronizes on updates to storage; but if two Brooklyn nodes were both writing to the same
+    // ApplicationUsage or LocationUsage record there'd be a race. That currently won't happen
+    // (at least for ApplicationUsage?) because the app is mastered in just one node at a time,
+    // and because location events are just manage/unmanage which should be happening in just 
+    // one place at a time for a given location.
+    
+    private static final Logger log = LoggerFactory.getLogger(LocalUsageManager.class);
+
+    private static class ApplicationMetadataImpl implements org.apache.brooklyn.core.management.internal.UsageListener.ApplicationMetadata {
+        private final Application app;
+        private String applicationId;
+        private String applicationName;
+        private String entityType;
+        private String catalogItemId;
+        private Map<String, String> metadata;
+
+        ApplicationMetadataImpl(Application app) {
+            this.app = checkNotNull(app, "app");
+            applicationId = app.getId();
+            applicationName = app.getDisplayName();
+            entityType = app.getEntityType().getName();
+            catalogItemId = app.getCatalogItemId();
+            metadata = ((EntityInternal)app).toMetadataRecord();
+        }
+        @Override public Application getApplication() {
+            return app;
+        }
+        @Override public String getApplicationId() {
+            return applicationId;
+        }
+        @Override public String getApplicationName() {
+            return applicationName;
+        }
+        @Override public String getEntityType() {
+            return entityType;
+        }
+        @Override public String getCatalogItemId() {
+            return catalogItemId;
+        }
+        @Override public Map<String, String> getMetadata() {
+            return metadata;
+        }
+    }
+    
+    private static class LocationMetadataImpl implements org.apache.brooklyn.core.management.internal.UsageListener.LocationMetadata {
+        private final Location loc;
+        private String locationId;
+        private Map<String, String> metadata;
+
+        LocationMetadataImpl(Location loc) {
+            this.loc = checkNotNull(loc, "loc");
+            locationId = loc.getId();
+            metadata = ((LocationInternal)loc).toMetadataRecord();
+        }
+        @Override public Location getLocation() {
+            return loc;
+        }
+        @Override public String getLocationId() {
+            return locationId;
+        }
+        @Override public Map<String, String> getMetadata() {
+            return metadata;
+        }
+    }
+    
+    // Register a coercion from String->UsageListener, so that USAGE_LISTENERS defined in brooklyn.properties
+    // will be instantiated, given their class names.
+    static {
+        TypeCoercions.registerAdapter(String.class, org.apache.brooklyn.core.management.internal.UsageListener.class, new Function<String, org.apache.brooklyn.core.management.internal.UsageListener>() {
+            @Override public org.apache.brooklyn.core.management.internal.UsageListener apply(String input) {
+                // TODO Want to use classLoader = mgmt.getCatalog().getRootClassLoader();
+                ClassLoader classLoader = LocalUsageManager.class.getClassLoader();
+                Optional<Object> result = Reflections.invokeConstructorWithArgs(classLoader, input);
+                if (result.isPresent()) {
+                    if (result.get() instanceof org.apache.brooklyn.core.management.internal.UsageManager.UsageListener) {
+                        return new org.apache.brooklyn.core.management.internal.UsageManager.UsageListener.UsageListenerAdapter((org.apache.brooklyn.core.management.internal.UsageManager.UsageListener) result.get());
+                    } else {
+                        return (org.apache.brooklyn.core.management.internal.UsageListener) result.get();
+                    }
+                } else {
+                    throw new IllegalStateException("Failed to create UsageListener from class name '"+input+"' using no-arg constructor");
+                }
+            }
+        });
+    }
+    
+    @VisibleForTesting
+    public static final String APPLICATION_USAGE_KEY = "usage-application";
+    
+    @VisibleForTesting
+    public static final String LOCATION_USAGE_KEY = "usage-location";
+
+    private final LocalManagementContext managementContext;
+    
+    private final Object mutex = new Object();
+
+    private final List<org.apache.brooklyn.core.management.internal.UsageListener> listeners = Lists.newCopyOnWriteArrayList();
+    
+    private final AtomicInteger listenerQueueSize = new AtomicInteger();
+    
+    private ListeningExecutorService listenerExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
+            .setNameFormat("brooklyn-usagemanager-listener-%d")
+            .build()));
+
+    public LocalUsageManager(LocalManagementContext managementContext) {
+        this.managementContext = checkNotNull(managementContext, "managementContext");
+        
+        // TODO Once org.apache.brooklyn.core.management.internal.UsageManager.UsageListener is deleted, restore this
+        // to normal generics!
+        Collection<?> listeners = managementContext.getBrooklynProperties().getConfig(UsageManager.USAGE_LISTENERS);
+        if (listeners != null) {
+            for (Object listener : listeners) {
+                if (listener instanceof ManagementContextInjectable) {
+                    ((ManagementContextInjectable)listener).injectManagementContext(managementContext);
+                }
+                if (listener instanceof org.apache.brooklyn.core.management.internal.UsageManager.UsageListener) {
+                    addUsageListener((org.apache.brooklyn.core.management.internal.UsageManager.UsageListener)listener);
+                } else if (listener instanceof org.apache.brooklyn.core.management.internal.UsageListener) {
+                    addUsageListener((org.apache.brooklyn.core.management.internal.UsageListener)listener);
+                } else if (listener == null) {
+                    throw new NullPointerException("null listener in config "+UsageManager.USAGE_LISTENERS);
+                } else {
+                    throw new ClassCastException("listener "+listener+" of type "+listener.getClass()+" is not of type "+org.apache.brooklyn.core.management.internal.UsageListener.class.getName());
+                }
+            }
+        }
+    }
+
+    public void terminate() {
+        // Wait for the listeners to finish + close the listeners
+        Duration timeout = managementContext.getBrooklynProperties().getConfig(UsageManager.USAGE_LISTENER_TERMINATION_TIMEOUT);
+        if (listenerQueueSize.get() > 0) {
+            log.info("Usage manager waiting for "+listenerQueueSize+" listener events for up to "+timeout);
+        }
+        List<ListenableFuture<?>> futures = Lists.newArrayList();
+        for (final org.apache.brooklyn.core.management.internal.UsageListener listener : listeners) {
+            ListenableFuture<?> future = listenerExecutor.submit(new Runnable() {
+                public void run() {
+                    if (listener instanceof Closeable) {
+                        try {
+                            ((Closeable)listener).close();
+                        } catch (IOException e) {
+                            log.warn("Problem closing usage listener "+listener+" (continuing)", e);
+                        }
+                    }
+                }});
+            futures.add(future);
+        }
+        try {
+            Futures.successfulAsList(futures).get(timeout.toMilliseconds(), TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            log.warn("Problem terminiating usage listeners (continuing)", e);
+        } finally {
+            listenerExecutor.shutdownNow();
+        }
+    }
+
+    private void execOnListeners(final Function<org.apache.brooklyn.core.management.internal.UsageListener, Void> job) {
+        for (final org.apache.brooklyn.core.management.internal.UsageListener listener : listeners) {
+            listenerQueueSize.incrementAndGet();
+            listenerExecutor.execute(new Runnable() {
+                public void run() {
+                    try {
+                        job.apply(listener);
+                    } catch (RuntimeException e) {
+                        log.error("Problem notifying listener "+listener+" of "+job, e);
+                        Exceptions.propagateIfFatal(e);
+                    } finally {
+                        listenerQueueSize.decrementAndGet();
+                    }
+                }});
+        }
+    }
+    
+    @Override
+    public void recordApplicationEvent(final Application app, final Lifecycle state) {
+        log.debug("Storing application lifecycle usage event: application {} in state {}", new Object[] {app, state});
+        ConcurrentMap<String, ApplicationUsage> eventMap = managementContext.getStorage().getMap(APPLICATION_USAGE_KEY);
+        synchronized (mutex) {
+            ApplicationUsage usage = eventMap.get(app.getId());
+            if (usage == null) {
+                usage = new ApplicationUsage(app.getId(), app.getDisplayName(), app.getEntityType().getName(), ((EntityInternal)app).toMetadataRecord());
+            }
+            final ApplicationUsage.ApplicationEvent event = new ApplicationUsage.ApplicationEvent(state, getUser());
+            usage.addEvent(event);        
+            eventMap.put(app.getId(), usage);
+
+            execOnListeners(new Function<org.apache.brooklyn.core.management.internal.UsageListener, Void>() {
+                    public Void apply(org.apache.brooklyn.core.management.internal.UsageListener listener) {
+                        listener.onApplicationEvent(new ApplicationMetadataImpl(Entities.proxy(app)), event);
+                        return null;
+                    }
+                    public String toString() {
+                        return "applicationEvent("+app+", "+state+")";
+                    }});
+        }
+    }
+    
+    /**
+     * Adds this location event to the usage record for the given location (creating the usage 
+     * record if one does not already exist).
+     */
+    @Override
+    public void recordLocationEvent(final Location loc, final Lifecycle state) {
+        // TODO This approach (i.e. recording events on manage/unmanage would not work for
+        // locations that are reused. For example, in a FixedListMachineProvisioningLocation
+        // the ssh machine location is returned to the pool and handed back out again.
+        // But maybe the solution there is to hand out different instances so that one user
+        // can't change the config of the SshMachineLocation to subsequently affect the next 
+        // user.
+        //
+        // TODO Should perhaps extract the location storage methods into their own class,
+        // but no strong enough feelings yet...
+        
+        checkNotNull(loc, "location");
+        if (loc.getConfig(AbstractLocation.TEMPORARY_LOCATION)) {
+            log.info("Ignoring location lifecycle usage event for {} (state {}), because location is a temporary location", loc, state);
+            return;
+        }
+        checkNotNull(state, "state of location %s", loc);
+        if (loc.getId() == null) {
+            log.error("Ignoring location lifecycle usage event for {} (state {}), because location has no id", loc, state);
+            return;
+        }
+        if (managementContext.getStorage() == null) {
+            log.warn("Cannot store location lifecycle usage event for {} (state {}), because storage not available", loc, state);
+            return;
+        }
+        
+        Object callerContext = loc.getConfig(LocationConfigKeys.CALLER_CONTEXT);
+        
+        if (callerContext != null && callerContext instanceof Entity) {
+            log.debug("Storing location lifecycle usage event: location {} in state {}; caller context {}", new Object[] {loc, state, callerContext});
+            
+            Entity caller = (Entity) callerContext;
+            String entityTypeName = caller.getEntityType().getName();
+            String appId = caller.getApplicationId();
+
+            final LocationUsage.LocationEvent event = new LocationUsage.LocationEvent(state, caller.getId(), entityTypeName, appId, getUser());
+            
+            ConcurrentMap<String, LocationUsage> usageMap = managementContext.getStorage().<String, LocationUsage>getMap(LOCATION_USAGE_KEY);
+            synchronized (mutex) {
+                LocationUsage usage = usageMap.get(loc.getId());
+                if (usage == null) {
+                    usage = new LocationUsage(loc.getId(), ((LocationInternal)loc).toMetadataRecord());
+                }
+                usage.addEvent(event);
+                usageMap.put(loc.getId(), usage);
+                
+                execOnListeners(new Function<org.apache.brooklyn.core.management.internal.UsageListener, Void>() {
+                        public Void apply(org.apache.brooklyn.core.management.internal.UsageListener listener) {
+                            listener.onLocationEvent(new LocationMetadataImpl(loc), event);
+                            return null;
+                        }
+                        public String toString() {
+                            return "locationEvent("+loc+", "+state+")";
+                        }});
+            }
+        } else {
+            // normal for high-level locations
+            log.trace("Not recording location lifecycle usage event for {} in state {}, because no caller context", new Object[] {loc, state});
+        }
+    }
+
+    /**
+     * Returns the usage info for the location with the given id, or null if unknown.
+     */
+    @Override
+    public LocationUsage getLocationUsage(String locationId) {
+        BrooklynStorage storage = managementContext.getStorage();
+
+        Map<String, LocationUsage> usageMap = storage.getMap(LOCATION_USAGE_KEY);
+        return usageMap.get(locationId);
+    }
+    
+    /**
+     * Returns the usage info that matches the given predicate.
+     * For example, could be used to find locations used within a given time period.
+     */
+    @Override
+    public Set<LocationUsage> getLocationUsage(Predicate<? super LocationUsage> filter) {
+        // TODO could do more efficient indexing, to more easily find locations in use during a given period.
+        // But this is good enough for first-pass.
+
+        Map<String, LocationUsage> usageMap = managementContext.getStorage().getMap(LOCATION_USAGE_KEY);
+        Set<LocationUsage> result = Sets.newLinkedHashSet();
+        
+        for (LocationUsage usage : usageMap.values()) {
+            if (filter.apply(usage)) {
+                result.add(usage);
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * Returns the usage info for the location with the given id, or null if unknown.
+     */
+    @Override
+    public ApplicationUsage getApplicationUsage(String appId) {
+        BrooklynStorage storage = managementContext.getStorage();
+
+        Map<String, ApplicationUsage> usageMap = storage.getMap(APPLICATION_USAGE_KEY);
+        return usageMap.get(appId);
+    }
+    
+    /**
+     * Returns the usage info that matches the given predicate.
+     * For example, could be used to find applications used within a given time period.
+     */
+    @Override
+    public Set<ApplicationUsage> getApplicationUsage(Predicate<? super ApplicationUsage> filter) {
+        // TODO could do more efficient indexing, to more easily find locations in use during a given period.
+        // But this is good enough for first-pass.
+
+        Map<String, ApplicationUsage> usageMap = managementContext.getStorage().getMap(APPLICATION_USAGE_KEY);
+        Set<ApplicationUsage> result = Sets.newLinkedHashSet();
+        
+        for (ApplicationUsage usage : usageMap.values()) {
+            if (filter.apply(usage)) {
+                result.add(usage);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    @Deprecated
+    public void addUsageListener(org.apache.brooklyn.core.management.internal.UsageManager.UsageListener listener) {
+        addUsageListener(new org.apache.brooklyn.core.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener));
+    }
+
+    @Override
+    @Deprecated
+    public void removeUsageListener(org.apache.brooklyn.core.management.internal.UsageManager.UsageListener listener) {
+        removeUsageListener(new org.apache.brooklyn.core.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener));
+    }
+    
+    @Override
+    public void addUsageListener(org.apache.brooklyn.core.management.internal.UsageListener listener) {
+        listeners.add(listener);
+    }
+
+    @Override
+    public void removeUsageListener(org.apache.brooklyn.core.management.internal.UsageListener listener) {
+        listeners.remove(listener);
+    }
+
+    private String getUser() {
+        EntitlementContext entitlementContext = Entitlements.getEntitlementContext();
+        if (entitlementContext != null) {
+            return entitlementContext.user();
+        }
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/LocationManagerInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocationManagerInternal.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocationManagerInternal.java
new file mode 100644
index 0000000..ba426ec
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocationManagerInternal.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.management.LocationManager;
+
+public interface LocationManagerInternal extends LocationManager, BrooklynObjectManagerInternal<Location> {
+
+    public Iterable<String> getLocationIds();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..97a3d73
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementContextInternal.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.management.internal;
+
+import java.net.URI;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+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.ManagementContext;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.management.ha.OsgiManager;
+
+import brooklyn.catalog.internal.CatalogInitialization;
+import brooklyn.config.BrooklynProperties;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.proxying.InternalEntityFactory;
+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;
+
+public interface ManagementContextInternal extends ManagementContext {
+
+    public static final String SUB_TASK_TAG = TaskTags.SUB_TASK_TAG;
+    
+    public static final String EFFECTOR_TAG = BrooklynTaskTags.EFFECTOR_TAG;
+    public static final String NON_TRANSIENT_TASK_TAG = BrooklynTaskTags.NON_TRANSIENT_TASK_TAG;
+    public static final String TRANSIENT_TASK_TAG = BrooklynTaskTags.TRANSIENT_TASK_TAG;
+
+    public static final String EMPTY_CATALOG_URL = "classpath://brooklyn/empty.catalog.bom";
+    
+    ClassLoader getBaseClassLoader();
+
+    Iterable<URL> getBaseClassPathForScanning();
+
+    void setBaseClassPathForScanning(Iterable<URL> urls);
+
+    void setManagementNodeUri(URI uri);
+
+    void addEntitySetListener(CollectionChangeListener<Entity> listener);
+
+    void removeEntitySetListener(CollectionChangeListener<Entity> listener);
+
+    void terminate();
+    
+    long getTotalEffectorInvocations();
+
+    <T> T invokeEffectorMethodSync(final Entity entity, final Effector<T> eff, final Object args) throws ExecutionException;
+    
+    <T> Task<T> invokeEffector(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters);
+
+    BrooklynStorage getStorage();
+    
+    BrooklynProperties getBrooklynProperties();
+    
+    AccessManager getAccessManager();
+
+    UsageManager getUsageManager();
+    
+    /**
+     * @return The OSGi manager, if available; may be absent if OSGi is not supported,
+     * e.g. in test contexts (but will be supported in all major contexts).
+     */
+    Maybe<OsgiManager> getOsgiManager();
+
+    InternalEntityFactory getEntityFactory();
+    
+    InternalLocationFactory getLocationFactory();
+    
+    InternalPolicyFactory getPolicyFactory();
+    
+    /**
+     * Registers an entity that has been created, but that has not yet begun to be managed.
+     * <p>
+     * This differs from the idea of "preManaged" where the entities are in the process of being
+     * managed, but where management is not yet complete.
+     */
+    // TODO would benefit from better naming! The name has percolated up from LocalEntityManager.
+    //      should we just rename here as register or preManage?
+    void prePreManage(Entity entity);
+
+    /**
+     * Registers a location that has been created, but that has not yet begun to be managed.
+     */
+    void prePreManage(Location location);
+
+    /** Object which allows adding, removing, and clearing errors.
+     * TODO In future this will change to a custom interface with a unique identifier for each error. */
+    @Beta
+    List<Throwable> errors();
+
+    @Beta
+    CatalogInitialization getCatalogInitialization();
+
+    @Beta
+    void setCatalogInitialization(CatalogInitialization catalogInitialization);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionInfo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionInfo.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionInfo.java
new file mode 100644
index 0000000..2d944d9
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionInfo.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.management.internal;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+
+/** Stores a management transition mode, and the management context. */
+// TODO does this class really pull its weight?
+public class ManagementTransitionInfo {
+
+    final ManagementContext mgmtContext;
+    final ManagementTransitionMode mode;
+    
+    public ManagementTransitionInfo(ManagementContext mgmtContext, ManagementTransitionMode mode) {
+        this.mgmtContext = mgmtContext;
+        this.mode = mode;
+    }
+    
+    
+    public ManagementContext getManagementContext() {
+        return mgmtContext;
+    }
+
+    public ManagementTransitionMode getMode() {
+        return mode;
+    }
+    
+    @Override
+    public String toString() {
+        return super.toString()+"["+mgmtContext+";"+mode+"]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionMode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionMode.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionMode.java
new file mode 100644
index 0000000..3adc625
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionMode.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Records details of a management transition, specifically the {@link BrooklynObjectManagementMode} before and after,
+ * and allows easy checking of various aspects of that.
+ * <p>
+ * This helps make code readable and keep correct logic if we expand/change the management modes.
+ */
+public class ManagementTransitionMode {
+
+    private static final Logger log = LoggerFactory.getLogger(ManagementTransitionMode.class);
+    
+    private final BrooklynObjectManagementMode modeBefore, modeAfter;
+
+    private ManagementTransitionMode(BrooklynObjectManagementMode modeBefore, BrooklynObjectManagementMode modeAfter) {
+        this.modeBefore = modeBefore;
+        this.modeAfter = modeAfter;
+    }
+    
+    public static ManagementTransitionMode transitioning(BrooklynObjectManagementMode modeBefore, BrooklynObjectManagementMode modeAfter) {
+        return new ManagementTransitionMode(Preconditions.checkNotNull(modeBefore, "modeBefore"), Preconditions.checkNotNull(modeAfter, "modeAfter"));
+    }
+
+    @Deprecated /** @deprecated marking places where we aren't sure */
+    public static ManagementTransitionMode guessing(BrooklynObjectManagementMode modeBefore, BrooklynObjectManagementMode modeAfter) {
+        return transitioning(modeBefore, modeAfter);
+    }
+
+    /** @return the mode this object was previously managed as */
+    public BrooklynObjectManagementMode getModeBefore() {
+        return modeBefore;
+    }
+    
+    /** @return the mode this object is now being managed as */
+    public BrooklynObjectManagementMode getModeAfter() {
+        return modeAfter;
+    }
+    
+    /** This management node was previously not loaded here, 
+     * either it did not exist (and is just being created) or it was in persisted state but
+     * not loaded at this node. */
+    public boolean wasNotLoaded() {
+        return getModeBefore()==BrooklynObjectManagementMode.NONEXISTENT || getModeBefore()==BrooklynObjectManagementMode.UNMANAGED_PERSISTED;
+    }
+
+    /** This management node is now not going to be loaded here, either it is being destroyed
+     * (not known anywhere, not even persisted) or simply forgotten here */
+    public boolean isNoLongerLoaded() {
+        return getModeAfter()==BrooklynObjectManagementMode.NONEXISTENT || getModeAfter()==BrooklynObjectManagementMode.UNMANAGED_PERSISTED;
+    }
+
+    /** This management node was the master for the given object */
+    public boolean wasPrimary() {
+        return getModeBefore()==BrooklynObjectManagementMode.MANAGED_PRIMARY;
+    }
+
+    /** This management node is now the master for the given object */
+    public boolean isPrimary() {
+        return getModeAfter()==BrooklynObjectManagementMode.MANAGED_PRIMARY;
+    }
+
+    /** Object was previously loaded as read-only at this management node;
+     * active management was occurring elsewhere (or not at all)
+     */
+    public boolean wasReadOnly() {
+        return getModeBefore()==BrooklynObjectManagementMode.LOADED_READ_ONLY;
+    }
+    
+    /** Object is now being loaded as read-only at this management node;
+     * expect active management to be occurring elsewhere
+     */
+    public boolean isReadOnly() {
+        return getModeAfter()==BrooklynObjectManagementMode.LOADED_READ_ONLY;
+    }
+    
+    /** Object is being created:
+     * previously did not exist (not even in persisted state);
+     * implies that we are the active manager creating it,
+     * i.e. {@link #getModeAfter()} should indicate {@link BrooklynObjectManagementMode#MANAGED_PRIMARY}.
+     * (if we're read-only and the manager has just created it, 
+     * {@link #getModeBefore()} should indicate {@link BrooklynObjectManagementMode#UNMANAGED_PERSISTED})
+     */
+    public boolean isCreating() {
+        if (getModeBefore()!=BrooklynObjectManagementMode.NONEXISTENT)
+            return false;
+        
+        if (getModeAfter()==BrooklynObjectManagementMode.LOADED_READ_ONLY) {
+            log.warn("isCreating set on RO object; highly irregular!");
+        }
+        return true;
+    }
+
+    /** Object is being destroyed:
+     * either destroyed elsewhere and we're catching up (in read-only mode),
+     * or we've been the active manager and are destroying it */
+    public boolean isDestroying() {
+        return getModeAfter()==BrooklynObjectManagementMode.NONEXISTENT;
+    }
+    
+    @Override
+    public String toString() {
+        return ManagementTransitionMode.class.getSimpleName()+"["+getModeBefore()+"->"+getModeAfter()+"]";
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentAccessManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentAccessManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentAccessManager.java
new file mode 100644
index 0000000..1d94f02
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentAccessManager.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.management.internal;
+
+import org.apache.brooklyn.api.management.AccessController;
+
+
+public class NonDeploymentAccessManager implements AccessManager {
+
+    private final ManagementContextInternal initialManagementContext;
+    
+    public NonDeploymentAccessManager(ManagementContextInternal initialManagementContext) {
+        this.initialManagementContext = initialManagementContext;
+    }
+    
+    private boolean isInitialManagementContextReal() {
+        return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext));
+    }
+
+    @Override
+    public AccessController getAccessController() {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getAccessManager().getAccessController();
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public boolean isLocationProvisioningAllowed() {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getAccessManager().isLocationProvisioningAllowed();
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public boolean isLocationManagementAllowed() {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getAccessManager().isLocationManagementAllowed();
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public boolean isEntityManagementAllowed() {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getAccessManager().isEntityManagementAllowed();
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public void setLocationProvisioningAllowed(boolean allowed) {
+        if (isInitialManagementContextReal()) {
+            initialManagementContext.getAccessManager().setLocationProvisioningAllowed(allowed);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public void setLocationManagementAllowed(boolean allowed) {
+        if (isInitialManagementContextReal()) {
+            initialManagementContext.getAccessManager().setLocationManagementAllowed(allowed);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public void setEntityManagementAllowed(boolean allowed) {
+        if (isInitialManagementContextReal()) {
+            initialManagementContext.getAccessManager().setEntityManagementAllowed(allowed);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentEntityManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentEntityManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentEntityManager.java
new file mode 100644
index 0000000..658bbf7
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentEntityManager.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.api.entity.proxying.EntityTypeRegistry;
+import org.apache.brooklyn.api.management.ManagementContext;
+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 com.google.common.base.Predicate;
+
+public class NonDeploymentEntityManager implements EntityManagerInternal {
+
+    private final ManagementContext initialManagementContext;
+    
+    public NonDeploymentEntityManager(ManagementContext initialManagementContext) {
+        this.initialManagementContext = initialManagementContext;
+    }
+    
+    @Override
+    public EntityTypeRegistry getEntityTypeRegistry() {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getEntityManager().getEntityTypeRegistry();
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation.");
+        }
+    }
+    
+    @Override
+    public <T extends Entity> T createEntity(EntitySpec<T> spec) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getEntityManager().createEntity(spec);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation.");
+        }
+    }
+    
+    @Override
+    public <T extends Entity> T createEntity(Map<?,?> config, Class<T> type) {
+        return createEntity(EntitySpec.create(type).configure(config));
+    }
+
+    @Override
+    public <T extends Policy> T createPolicy(PolicySpec<T> spec) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getEntityManager().createPolicy(spec);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation.");
+        }
+    }
+    
+    @Override
+    public <T extends Enricher> T createEnricher(EnricherSpec<T> spec) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getEntityManager().createEnricher(spec);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation.");
+        }
+    }
+    
+    @Override
+    public Collection<Entity> getEntities() {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getEntityManager().getEntities();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    @Override
+    public Collection<Entity> getEntitiesInApplication(Application application) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getEntityManager().getEntitiesInApplication(application);
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    @Override
+    public Collection<Entity> findEntities(Predicate<? super Entity> filter) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getEntityManager().findEntities(filter);
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    @Override
+    public Collection<Entity> findEntitiesInApplication(Application application, Predicate<? super Entity> filter) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getEntityManager().findEntitiesInApplication(application, filter);
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    @Override
+    public Entity getEntity(String id) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getEntityManager().getEntity(id);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public Iterable<String> getEntityIds() {
+        if (isInitialManagementContextReal()) {
+            return ((EntityManagerInternal)initialManagementContext.getEntityManager()).getEntityIds();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    @Override
+    public ManagementTransitionMode getLastManagementTransitionMode(String itemId) {
+        if (isInitialManagementContextReal()) {
+            return ((EntityManagerInternal)initialManagementContext.getEntityManager()).getLastManagementTransitionMode(itemId);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void setManagementTransitionMode(Entity item, ManagementTransitionMode mode) {
+        if (isInitialManagementContextReal()) {
+            ((EntityManagerInternal)initialManagementContext.getEntityManager()).setManagementTransitionMode(item, mode);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+    
+    @Override
+    public boolean isManaged(Entity entity) {
+        return false;
+    }
+
+    @Override
+    public void manage(Entity e) {
+        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot manage "+e);
+    }
+
+    @Override
+    public void unmanage(Entity e, ManagementTransitionMode info) {
+        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+    }
+    
+    @Override
+    public void manageRebindedRoot(Entity item) {
+        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+    }
+
+    @Override
+    public void unmanage(Entity e) {
+        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot unmanage "+e);
+    }
+    
+    private boolean isInitialManagementContextReal() {
+        return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext));
+    }
+
+    @Override
+    public Iterable<Entity> getAllEntitiesInApplication(Application application) {
+        if (isInitialManagementContextReal()) {
+            return ((EntityManagerInternal)initialManagementContext.getEntityManager()).getAllEntitiesInApplication(application);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation.");
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentLocationManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentLocationManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentLocationManager.java
new file mode 100644
index 0000000..5c81648
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentLocationManager.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import java.util.Collection;
+import java.util.Collections;
+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;
+
+public class NonDeploymentLocationManager implements LocationManagerInternal {
+
+    private final ManagementContext initialManagementContext;
+    
+    public NonDeploymentLocationManager(ManagementContext initialManagementContext) {
+        this.initialManagementContext = initialManagementContext;
+    }
+    
+    @Override
+    public <T extends Location> T createLocation(LocationSpec<T> spec) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getLocationManager().createLocation(spec);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot create "+spec);
+        }
+    }
+
+    @Override
+    public <T extends Location> T createLocation(Map<?, ?> config, Class<T> type) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getLocationManager().createLocation(config, type);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot create "+type);
+        }
+    }
+    
+    @Override
+    public Collection<Location> getLocations() {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getLocationManager().getLocations();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    @Override
+    public Location getLocation(String id) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getLocationManager().getLocation(id);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public Iterable<String> getLocationIds() {
+        if (isInitialManagementContextReal()) {
+            return ((LocationManagerInternal)initialManagementContext.getLocationManager()).getLocationIds();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+    
+    @Override
+    public boolean isManaged(Location loc) {
+        return false;
+    }
+
+    @Override
+    public void manageRebindedRoot(Location loc) {
+        if (isInitialManagementContextReal()) {
+            ((LocationManagerInternal)initialManagementContext.getLocationManager()).manageRebindedRoot(loc);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot manage "+loc);
+        }
+    }
+
+    @Override
+    @Deprecated
+    public Location manage(Location loc) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getLocationManager().manage(loc);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot manage "+loc);
+        }
+    }
+
+
+    @Override
+    public ManagementTransitionMode getLastManagementTransitionMode(String itemId) {
+        if (isInitialManagementContextReal()) {
+            return ((LocationManagerInternal)initialManagementContext.getLocationManager()).getLastManagementTransitionMode(itemId);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public void setManagementTransitionMode(Location item, ManagementTransitionMode mode) {
+        if (isInitialManagementContextReal()) {
+            ((LocationManagerInternal)initialManagementContext.getLocationManager()).setManagementTransitionMode(item, mode);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public void unmanage(Location item, ManagementTransitionMode info) {
+        if (isInitialManagementContextReal()) {
+            ((LocationManagerInternal)initialManagementContext.getLocationManager()).unmanage(item, info);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public void unmanage(Location loc) {
+        if (isInitialManagementContextReal()) {
+            initialManagementContext.getLocationManager().unmanage(loc);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot unmanage "+loc);
+        }
+    }
+    
+    private boolean isInitialManagementContextReal() {
+        return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext));
+    }
+}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
deleted file mode 100644
index 1fb5bf3..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
+++ /dev/null
@@ -1,93 +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.catalog.internal;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
-
-public class CatalogBundleDto implements CatalogBundle {
-    private String symbolicName;
-    private String version;
-    private String url;
-
-    public CatalogBundleDto() {}
-
-    public CatalogBundleDto(String name, String version, String url) {
-        if (name == null && version == null) {
-            Preconditions.checkNotNull(url, "url to an OSGi bundle is required");
-        } else {
-            Preconditions.checkNotNull(name, "both name and version are required");
-            Preconditions.checkNotNull(version, "both name and version are required");
-        }
-
-        this.symbolicName = name;
-        this.version = version;
-        this.url = url;
-    }
-
-    @Override
-    public boolean isNamed() {
-        return symbolicName != null && version != null;
-    }
-
-    @Override
-    public String getSymbolicName() {
-        return symbolicName;
-    }
-
-    @Override
-    public String getVersion() {
-        return version;
-    }
-
-    @Override
-    public String getUrl() {
-        return url;
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(this)
-                .add("symbolicName", symbolicName)
-                .add("version", version)
-                .add("url", url)
-                .toString();
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(symbolicName, version, url);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        CatalogBundleDto other = (CatalogBundleDto) obj;
-        if (!Objects.equal(symbolicName, other.symbolicName)) return false;
-        if (!Objects.equal(version, other.version)) return false;
-        if (!Objects.equal(url, other.url)) return false;
-        return true;
-    }
-    
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java
deleted file mode 100644
index b81b439..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java
+++ /dev/null
@@ -1,334 +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.catalog.internal;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.lang.reflect.Modifier;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Set;
-
-import javax.annotation.Nullable;
-
-import org.reflections.util.ClasspathHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.api.catalog.Catalog;
-import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.Entity;
-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 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;
-import brooklyn.util.time.Time;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.Iterables;
-
-public class CatalogClasspathDo {
-
-    public static enum CatalogScanningModes {
-        /** the classpath is not scanned; 
-         * for any catalog which is presented over the internet this is recommended (to prevent loading) and is the default; 
-         * (you should explicitly list the items to include; it may be useful to autogenerate it by using a local catalog
-         * scanning with ANNOTATIONS, viwing that by running mgmt.getCatalog().toXmlString(),
-         * then editing the resulting XML (e.g. setting the classpath and removing the scan attribute) */
-        NONE, 
-        
-        /** types in the classpath are scanned for annotations indicating inclusion in the catalog ({@link Catalog});
-         * this is the default if no catalog is supplied, scanning the local classpath */
-        ANNOTATIONS,
-        
-        @Beta
-        /** all catalog-friendly types are included, 
-         * even if not annotated for inclusion in the catalog; useful for quick hacking,
-         * or a classpath (and possibly in future a regex, if added) which is known to have only good things in it;
-         * however the precise semantics of what is included is subject to change,
-         * and it is strongly recommended to use the {@link Catalog} annotation and scan for annotations 
-         * <p>
-         * a catalog-friendly type is currently defined as:
-         * any concrete non-anonymous (and not a non-static inner) class implementing Entity or Policy;
-         * and additionally for entities and applications, an interface with the {@link ImplementedBy} annotation;
-         * note that this means classes done "properly" with both an interface and an implementation
-         * will be included twice, once as interface and once as implementation;
-         * this guarantees inclusion of anything previously included (implementations; 
-         * and this will be removed from catalog in future likely),
-         * plus things now done properly (which will become the only way in the future)
-         **/
-        TYPES
-    }
-    
-    private static final Logger log = LoggerFactory.getLogger(CatalogClasspathDo.class);
-    
-    private final CatalogDo catalog;
-    private final CatalogClasspathDto classpath;
-    private final CatalogScanningModes scanMode;
-    
-    boolean isLoaded = false;
-    private URL[] urls;
-    
-    private final AggregateClassLoader classloader = AggregateClassLoader.newInstanceWithNoLoaders();
-    private volatile boolean classloaderLoaded = false;
-
-    public CatalogClasspathDo(CatalogDo catalog) {
-        this.catalog = Preconditions.checkNotNull(catalog, "catalog");
-        this.classpath = catalog.dto.classpath;
-        this.scanMode = (classpath != null) ? classpath.scan : null;
-    }
-    
-    /** causes all scanning-based classpaths to scan the classpaths
-    * (but does _not_ load all JARs) */
-    // TODO this does a Java scan; we also need an OSGi scan which uses the OSGi classloaders when loading for scanning and resolving dependencies 
-    synchronized void load() {
-        if (classpath == null || isLoaded) return;
-
-        if (classpath.getEntries() == null) {
-            urls = new URL[0];
-        } else {
-            urls = new URL[classpath.getEntries().size()];
-            for (int i=0; i<urls.length; i++) {
-                try {
-                    String u = classpath.getEntries().get(i);
-                    if (u.startsWith("classpath:")) {
-                        // special support for classpath: url's
-                        // TODO put convenience in ResourceUtils for extracting to a normal url
-                        // (or see below)
-                        InputStream uin = ResourceUtils.create(this).getResourceFromUrl(u);
-                        File f = Os.newTempFile("brooklyn-catalog-"+u, null);
-                        FileOutputStream fout = new FileOutputStream(f);
-                        try {
-                            Streams.copy(uin, fout);
-                        } finally {
-                            Streams.closeQuietly(fout);
-                            Streams.closeQuietly(uin);
-                        }
-                        u = f.toURI().toString();
-                    }
-                    urls[i] = new URL(u);
-                    
-                    // TODO potential disk leak above as we have no way to know when the temp file can be removed earlier than server shutdown;
-                    // a better way to handle this is to supply a stream handler (but URLConnection is a little bit hard to work with):
-//                    urls[i] = new URL(null, classpath.getEntries().get(i)   // (handy construtor for reparsing urls, without splitting into uri first)
-//                        , new URLStreamHandler() {
-//                            @Override
-//                            protected URLConnection openConnection(URL u) throws IOException {
-//                                new ResourceUtils(null). ???
-//                            }
-//                        });
-                } catch (Exception e) {
-                    Exceptions.propagateIfFatal(e);
-                    log.error("Error loading URL "+classpath.getEntries().get(i)+" in definition of catalog "+catalog+"; skipping definition");
-                    throw Exceptions.propagate(e);
-                }
-            }
-        }
-        
-        // prefix is supported (but not really used yet) --
-        // seems to have _better_ URL-discovery with prefixes 
-        // (might also offer regex ? but that is post-load filter as opposed to native optimisation)
-        String prefix = null;
-
-        if (scanMode==null || scanMode==CatalogScanningModes.NONE)
-            return;
-        
-        Stopwatch timer = Stopwatch.createStarted();
-        ReflectionScanner scanner = null;
-        if (!catalog.isLocal()) {
-            log.warn("Scanning not supported for remote catalogs; ignoring scan request in "+catalog);
-        } else if (classpath.getEntries() == null || classpath.getEntries().isEmpty()) {
-            // scan default classpath:
-            ClassLoader baseCL = null;
-            Iterable<URL> baseCP = null;
-            if (catalog.mgmt instanceof ManagementContextInternal) {
-                baseCL = ((ManagementContextInternal)catalog.mgmt).getBaseClassLoader();
-                baseCP = ((ManagementContextInternal)catalog.mgmt).getBaseClassPathForScanning();
-            }
-            scanner = new ReflectionScanner(baseCP, prefix, baseCL, catalog.getRootClassLoader());
-            if (scanner.getSubTypesOf(Entity.class).isEmpty()) {
-                try {
-                    ((ManagementContextInternal)catalog.mgmt).setBaseClassPathForScanning(ClasspathHelper.forJavaClassPath());
-                    log.debug("Catalog scan of default classloader returned nothing; reverting to java.class.path");
-                    baseCP = ((ManagementContextInternal)catalog.mgmt).getBaseClassPathForScanning();
-                    scanner = new ReflectionScanner(baseCP, prefix, baseCL, catalog.getRootClassLoader());
-                } catch (Exception e) {
-                    log.info("Catalog scan is empty, and unable to use java.class.path (base classpath is "+baseCP+"): "+e);
-                    Exceptions.propagateIfFatal(e);
-                }
-            }
-        } else {
-            // scan specified jars:
-            scanner = new ReflectionScanner(urls==null || urls.length==0 ? null : Arrays.asList(urls), prefix, getLocalClassLoader());
-        }
-        
-        if (scanner!=null) {
-            int count = 0, countApps = 0;
-            if (scanMode==CatalogScanningModes.ANNOTATIONS) {
-                Set<Class<?>> catalogClasses = scanner.getTypesAnnotatedWith(Catalog.class);
-                for (Class<?> c: catalogClasses) {
-                    try {
-                        CatalogItem<?,?> item = addCatalogEntry(c);
-                        count++;
-                        if (CatalogTemplateItemDto.class.isInstance(item)) countApps++;
-                    } catch (Exception e) {
-                        log.warn("Failed to add catalog entry for "+c+"; continuing scan...", e);
-                    }
-                }
-            } else if (scanMode==CatalogScanningModes.TYPES) {
-                Iterable<Class<?>> entities = this.excludeInvalidClasses(
-                        Iterables.concat(scanner.getSubTypesOf(Entity.class),
-                                // not sure why we have to look for sub-types of Application, 
-                                // they should be picked up as sub-types of Entity, but in maven builds (only!)
-                                // they are not -- i presume a bug in scanner
-                                scanner.getSubTypesOf(Application.class), 
-                                scanner.getSubTypesOf(ApplicationBuilder.class)));
-                for (Class<?> c: entities) {
-                    if (Application.class.isAssignableFrom(c) || ApplicationBuilder.class.isAssignableFrom(c)) {
-                        addCatalogEntry(new CatalogTemplateItemDto(), c);
-                        countApps++;
-                    } else {
-                        addCatalogEntry(new CatalogEntityItemDto(), c);
-                    }
-                    count++;
-                }
-                Iterable<Class<? extends Policy>> policies = this.excludeInvalidClasses(scanner.getSubTypesOf(Policy.class));
-                for (Class<?> c: policies) {
-                    addCatalogEntry(new CatalogPolicyItemDto(), c);
-                    count++;
-                }
-                
-                Iterable<Class<? extends Location>> locations = this.excludeInvalidClasses(scanner.getSubTypesOf(Location.class));
-                for (Class<?> c: locations) {
-                    addCatalogEntry(new CatalogLocationItemDto(), c);
-                    count++;
-                }
-            } else {
-                throw new IllegalStateException("Unsupported catalog scan mode "+scanMode+" for "+this);
-            }
-            log.debug("Catalog '"+catalog.dto.name+"' classpath scan completed: loaded "+
-                    count+" item"+Strings.s(count)+" ("+countApps+" app"+Strings.s(countApps)+") in "+Time.makeTimeStringRounded(timer));
-        }
-        
-        isLoaded = true;
-    }
-
-    /** removes inner classes (non-static nesteds) and others; 
-     * bear in mind named ones will be hard to instantiate without the outer class instance) */
-    private <T> Iterable<Class<? extends T>> excludeInvalidClasses(Iterable<Class<? extends T>> input) {
-        Predicate<Class<? extends T>> f = new Predicate<Class<? extends T>>() {
-            @Override
-            public boolean apply(@Nullable Class<? extends T> input) {
-                if (input==null) return false;
-                if (input.isLocalClass() || input.isAnonymousClass()) return false;
-                if (Modifier.isAbstract(input.getModifiers())) {
-                    if (input.getAnnotation(ImplementedBy.class)==null)
-                        return false;
-                }
-                // non-abstract top-level classes are okay
-                if (!input.isMemberClass()) return true;
-                if (!Modifier.isStatic(input.getModifiers())) return false;
-                // nested classes only okay if static
-                return true;
-            }
-        };
-        return Iterables.filter(input, f);
-    }
-
-    /** augments the given item with annotations and class data for the given class, then adds to catalog
-     * @deprecated since 0.7.0 the classpath DO is replaced by libraries */
-    @Deprecated
-    public CatalogItem<?,?> addCatalogEntry(Class<?> c) {
-        if (Application.class.isAssignableFrom(c)) return addCatalogEntry(new CatalogTemplateItemDto(), c);
-        if (ApplicationBuilder.class.isAssignableFrom(c)) return addCatalogEntry(new CatalogTemplateItemDto(), c);
-        if (Entity.class.isAssignableFrom(c)) return addCatalogEntry(new CatalogEntityItemDto(), c);
-        if (Policy.class.isAssignableFrom(c)) return addCatalogEntry(new CatalogPolicyItemDto(), c);
-        if (Location.class.isAssignableFrom(c)) return addCatalogEntry(new CatalogLocationItemDto(), c);
-        throw new IllegalStateException("Cannot add "+c+" to catalog: unsupported type "+c.getName());
-    }
-    
-    /** augments the given item with annotations and class data for the given class, then adds to catalog 
-     * @deprecated since 0.7.0 the classpath DO is replaced by libraries */
-    @Deprecated
-    public CatalogItem<?,?> addCatalogEntry(CatalogItemDtoAbstract<?,?> item, Class<?> c) {
-        Catalog annotations = c.getAnnotation(Catalog.class);
-        item.setSymbolicName(c.getName());
-        item.setJavaType(c.getName());
-        item.setDisplayName(firstNonEmpty(c.getSimpleName(), c.getName()));
-        if (annotations!=null) {
-            item.setDisplayName(firstNonEmpty(annotations.name(), item.getDisplayName()));
-            item.setDescription(firstNonEmpty(annotations.description()));
-            item.setIconUrl(firstNonEmpty(annotations.iconUrl()));
-        }
-        if (log.isTraceEnabled())
-            log.trace("adding to catalog: "+c+" (from catalog "+catalog+")");
-        catalog.addEntry(item);
-        return item;
-    }
-
-    private static String firstNonEmpty(String ...candidates) {
-        for (String c: candidates)
-            if (c!=null && !c.isEmpty()) return c;
-        return null;
-    }
-
-    /** returns classloader for the entries specified here */
-    public ClassLoader getLocalClassLoader() {
-        if (!classloaderLoaded) loadLocalClassLoader();
-        return classloader;
-    }
-
-    protected synchronized void loadLocalClassLoader() {
-        if (classloaderLoaded) return;
-        if (urls==null) return;
-        classloader.addFirst(new UrlClassLoader(urls));
-        classloaderLoaded = true;
-        return;
-    }
-
-    /** adds the given URL as something this classloader will load
-     * (however no scanning is done) */
-    public void addToClasspath(URL u, boolean updateDto) {
-        if (updateDto) classpath.getEntries().add(u.toExternalForm());
-        addToClasspath(new UrlClassLoader(u));
-    }
-
-    /** adds the given URL as something this classloader will load
-     * (however no scanning is done).
-     * <p>
-     * the DTO will _not_ be updated. */
-    public void addToClasspath(ClassLoader loader) {
-        classloader.addFirst(loader);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDto.java
deleted file mode 100644
index c9e97f7..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDto.java
+++ /dev/null
@@ -1,43 +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.catalog.internal;
-
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import brooklyn.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
-
-public class CatalogClasspathDto {
-    
-    /** whether/what to scan; defaults to 'none' */
-    CatalogScanningModes scan;
-    private List<String> entries;
-    
-    public synchronized void addEntry(String url) {
-        if (entries==null)
-            entries = new CopyOnWriteArrayList<String>();
-        
-        entries.add(url);
-    }
-
-    public synchronized List<String> getEntries() {
-        return entries;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
deleted file mode 100644
index 25fabff..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.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.catalog.internal;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-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.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.javalang.AggregateClassLoader;
-import brooklyn.util.net.Urls;
-import brooklyn.util.time.CountdownTimer;
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-
-public class CatalogDo {
-
-    private static final Logger log = LoggerFactory.getLogger(CatalogDo.class);
-    
-    volatile boolean isLoaded = false;
-    final CatalogDto dto;
-    ManagementContext mgmt = null;
-    CatalogDo parent = null;
-    
-    List<CatalogDo> childrenCatalogs = new ArrayList<CatalogDo>();
-    CatalogClasspathDo classpath;
-    private Map<String, CatalogItemDo<?,?>> cacheById;
-
-    AggregateClassLoader childrenClassLoader = AggregateClassLoader.newInstanceWithNoLoaders();
-    ClassLoader recursiveClassLoader;
-
-    protected CatalogDo(CatalogDto dto) {
-        this.dto = Preconditions.checkNotNull(dto);
-    }
-    
-    public CatalogDo(ManagementContext mgmt, CatalogDto dto) {
-        this(dto);
-        this.mgmt = mgmt;
-    }
-
-    boolean isLoaded() {
-        return isLoaded;
-    }
-
-    /** Calls {@link #load(CatalogDo)} with a null parent. */
-    public CatalogDo load() {
-        return load(null);
-    }
-
-    /** Calls {@link #load(ManagementContext, CatalogDo)} with the catalog's existing management context. */
-    public CatalogDo load(CatalogDo parent) {
-        return load(mgmt, parent);
-    }
-
-    /** Calls {@link #load(ManagementContext, CatalogDo, boolean)} failing on load errors. */
-    public synchronized CatalogDo load(ManagementContext mgmt, CatalogDo parent) {
-        return load(mgmt, parent, true);
-    }
-
-    /** causes all URL-based catalogs to have their manifests loaded,
-     * and all scanning-based classpaths to scan the classpaths
-     * (but does not load all JARs)
-     */
-    public synchronized CatalogDo load(ManagementContext mgmt, CatalogDo parent, boolean failOnLoadError) {
-        if (isLoaded()) {
-            if (mgmt!=null && !Objects.equal(mgmt, this.mgmt)) {
-                throw new IllegalStateException("Cannot set mgmt "+mgmt+" on "+this+" after catalog is loaded");
-            }
-            log.debug("Catalog "+this+" is already loaded");
-            return this;
-        }
-        loadThisCatalog(mgmt, parent, failOnLoadError);
-        loadChildrenCatalogs(failOnLoadError);
-        buildCaches();
-        return this;
-    }
-
-    protected synchronized void loadThisCatalog(ManagementContext mgmt, CatalogDo parent, boolean failOnLoadError) {
-        if (isLoaded()) return;
-        CatalogUtils.logDebugOrTraceIfRebinding(log, "Loading catalog {} into {}", this, parent);
-        if (this.parent!=null && !this.parent.equals(parent))
-            log.warn("Catalog "+this+" being initialised with different parent "+parent+" when already parented by "+this.parent, new Throwable("source of reparented "+this));
-        if (this.mgmt!=null && !this.mgmt.equals(mgmt))
-            log.warn("Catalog "+this+" being initialised with different mgmt "+mgmt+" when already managed by "+this.mgmt, new Throwable("source of reparented "+this));
-        this.parent = parent;
-        this.mgmt = mgmt;
-        dto.populate();
-        loadCatalogClasspath();
-        loadCatalogItems(failOnLoadError);
-        isLoaded = true;
-        synchronized (this) {
-            notifyAll();
-        }
-    }
-
-    private void loadCatalogClasspath() {
-        try {
-            classpath = new CatalogClasspathDo(this);
-            classpath.load();
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            log.error("Unable to load catalog "+this+" (ignoring): "+e);
-            log.info("Trace for failure to load "+this+": "+e, e);
-        }
-    }
-
-    private void loadCatalogItems(boolean failOnLoadError) {
-        Iterable<CatalogItemDtoAbstract<?, ?>> entries = dto.getUniqueEntries();
-        if (entries!=null) {
-            for (CatalogItemDtoAbstract<?,?> entry : entries) {
-                try {
-                    CatalogUtils.installLibraries(mgmt, entry.getLibraries());
-                } catch (Exception e) {
-                    Exceptions.propagateIfFatal(e);
-                    if (failOnLoadError) {
-                        Exceptions.propagate(e);
-                    } else {
-                        log.error("Loading bundles for catalog item " + entry + " failed: " + e.getMessage(), e);
-                    }
-                }
-            }
-        }
-    }
-
-    public boolean blockIfNotLoaded(Duration timeout) throws InterruptedException {
-        if (isLoaded()) return true;
-        synchronized (this) {
-            if (isLoaded()) return true;
-            CountdownTimer timer = CountdownTimer.newInstanceStarted(timeout);
-            while (!isLoaded())
-                if (!timer.waitOnForExpiry(this))
-                    return false;
-            return true;
-        }
-    }
-    
-    protected void loadChildrenCatalogs(boolean failOnLoadError) {
-        if (dto.catalogs!=null) {
-            for (CatalogDto child: dto.catalogs) {
-                loadCatalog(child, failOnLoadError);
-            }
-        }
-    }
-    
-    CatalogDo loadCatalog(CatalogDto child, boolean failOnLoadError) {
-        CatalogDo childL = new CatalogDo(child);
-        childrenCatalogs.add(childL);
-        childL.load(mgmt, this, failOnLoadError);
-        childrenClassLoader.addFirst(childL.getRecursiveClassLoader());
-        clearCache(false);
-        return childL;
-    }
-
-    protected Map<String, CatalogItemDo<?,?>> getIdCache() {
-        Map<String, CatalogItemDo<?,?>> cache = this.cacheById;
-        if (cache==null) cache = buildCaches();
-        return cache;
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    protected synchronized Map<String, CatalogItemDo<?,?>> buildCaches() {
-        if (cacheById != null) return cacheById;
-        CatalogUtils.logDebugOrTraceIfRebinding(log, "Building cache for {}", this);
-        if (!isLoaded()) 
-            log.debug("Catalog not fully loaded when loading cache of "+this);
-        
-        Map<String, CatalogItemDo<?,?>> cache = new LinkedHashMap<String, CatalogItemDo<?,?>>();
-        
-        // build the cache; first from children catalogs, then from local entities
-        // so that root and near-root takes precedence over deeper items;
-        // and go through in reverse order so that things at the top of the file take precedence
-        // (both in the cache and in the aggregate class loader);
-        // however anything added _subsequently_ will take precedence (again in both)
-        if (dto.catalogs!=null) { 
-            List<CatalogDo> catalogsReversed = new ArrayList<CatalogDo>(childrenCatalogs);
-            Collections.reverse(catalogsReversed);
-            for (CatalogDo child: catalogsReversed) {
-                cache.putAll(child.getIdCache());
-            }
-        }
-        if (dto.getUniqueEntries()!=null) {
-            List<CatalogItemDtoAbstract<?,?>> entriesReversed = MutableList.copyOf(dto.getUniqueEntries());
-            Collections.reverse(entriesReversed);
-            for (CatalogItemDtoAbstract<?,?> entry: entriesReversed)
-                cache.put(entry.getId(), new CatalogItemDo(this, entry));
-        }
-        this.cacheById = cache;
-        return cache;
-    }
-    
-    protected synchronized void clearCache(boolean deep) {
-        this.cacheById = null;
-        if (deep) {
-            for (CatalogDo child : childrenCatalogs) {
-                child.clearCache(true);
-            }
-        }
-        clearParentCache();
-    }
-    protected void clearParentCache() {
-        if (this.parent!=null)
-            this.parent.clearCache(false);
-    }
-    
-    /**
-     * Adds the given entry to the catalog, with no enrichment.
-     * Callers may prefer {@link CatalogClasspathDo#addCatalogEntry(CatalogItemDtoAbstract, Class)}
-     */
-    public synchronized void addEntry(CatalogItemDtoAbstract<?,?> entry) {
-        dto.addEntry(entry);
-        
-        // could do clearCache(false); but this is slightly more efficient...
-        if (cacheById != null) {
-            @SuppressWarnings({ "unchecked", "rawtypes" })
-            CatalogItemDo<?, ?> cdo = new CatalogItemDo(this, entry);
-            cacheById.put(entry.getId(), cdo);
-        }        
-        clearParentCache();
-        
-        if (mgmt != null) {
-            mgmt.getRebindManager().getChangeListener().onManaged(entry);
-        }
-   }
-    
-    /**
-     * Removes the given entry from the catalog.
-     */
-    public synchronized void deleteEntry(CatalogItemDtoAbstract<?, ?> entry) {
-        dto.removeEntry(entry);
-        
-        // could do clearCache(false); but this is slightly more efficient...
-        if (cacheById != null) {
-            cacheById.remove(entry.getId());
-        }
-        clearParentCache();
-        
-        if (mgmt != null) {
-            // TODO: Can the entry be in more than one catalogue? The management context has no notion of
-            // catalogue hierarchy so this will effectively remove it from all catalogues.
-            // (YES- we're assuming ID's are unique across all catalogues; if not, things get out of sync;
-            // however see note at top of BasicBrooklynCatalog --
-            // manualCatalog and OSGi is used for everything now except legacy XML trees)
-            mgmt.getRebindManager().getChangeListener().onUnmanaged(entry);
-        }
-    }
-
-    /** returns loaded catalog, if this has been loaded */
-    CatalogDo addCatalog(CatalogDto child) {
-        if (dto.catalogs == null)
-            dto.catalogs = new ArrayList<CatalogDto>();
-        dto.catalogs.add(child);
-        if (!isLoaded())
-            return null;
-        return loadCatalog(child, true);
-    }
-    
-    /** adds the given urls; filters out any nulls supplied */
-    public synchronized void addToClasspath(String ...urls) {
-        if (dto.classpath == null)
-            dto.classpath = new CatalogClasspathDto();
-        for (String url: urls) {
-            if (url!=null)
-                dto.classpath.addEntry(url);
-        }
-        if (isLoaded())
-            throw new IllegalStateException("dynamic classpath entry value update not supported");
-        // easy enough to add, just support unload+reload (and can also allow dynamic setScan below)
-        // but more predictable if we don't; the one exception is in the manualAdditionsCatalog
-        // where BasicBrooklynCatalog reaches in and updates the DTO and/or CompositeClassLoader directly, if necessary
-//            for (String url: urls)
-//                loadedClasspath.addEntry(url);
-    }
-
-    public synchronized void setClasspathScanForEntities(CatalogScanningModes value) {
-        if (dto.classpath == null)
-            dto.classpath = new CatalogClasspathDto();
-        dto.classpath.scan = value;
-        if (isLoaded()) 
-            throw new IllegalStateException("dynamic classpath scan value update not supported");
-        // easy enough to add, see above
-    }
-
-    @Override
-    public String toString() {
-        String size = cacheById == null ? "not yet loaded" : "size " + cacheById.size();
-        return "Loaded:" + dto + "(" + size + ")";
-    }
-
-    /** is "local" if it and all ancestors are not based on any remote urls */ 
-    public boolean isLocal() {
-        if (dto.url != null) {
-            String proto = Urls.getProtocol(dto.url);
-            if (proto != null) {
-                // 'file' is the only protocol accepted as "local"
-                if (!"file".equals(proto)) return false;
-            }
-        }
-        return parent == null || parent.isLocal();
-    }
-
-    /** classloader for only the entries in this catalog's classpath */ 
-    public ClassLoader getLocalClassLoader() {
-        if (classpath != null) return classpath.getLocalClassLoader();
-        return null;
-    }
-
-    /** recursive classloader is the local classloader plus all children catalog's classloader */
-    public ClassLoader getRecursiveClassLoader() {
-        if (recursiveClassLoader == null) loadRecursiveClassLoader();
-        return recursiveClassLoader;
-    }
-    
-    protected synchronized void loadRecursiveClassLoader() {
-        if (recursiveClassLoader!=null) return;
-        AggregateClassLoader cl = AggregateClassLoader.newInstanceWithNoLoaders();
-        cl.addFirst(childrenClassLoader);
-        ClassLoader local = getLocalClassLoader();
-        if (local != null) cl.addFirst(local);
-        if (parent == null) {
-            // we are root.  include the mgmt base classloader and/or standard class loaders 
-            ClassLoader base = mgmt != null ? ((ManagementContextInternal)mgmt).getBaseClassLoader() : null;
-            if (base != null) cl.addFirst(base);
-            else {
-                cl.addFirst(getClass().getClassLoader());
-                cl.addFirst(Object.class.getClassLoader());
-            }
-        }
-        recursiveClassLoader = cl;
-    }
-    
-    /** the root classloader is the recursive CL from the outermost catalog
-     * (which includes the base classloader from the mgmt context, if set) */
-    public ClassLoader getRootClassLoader() {
-        if (parent != null) return parent.getRootClassLoader();
-        return getRecursiveClassLoader();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java
deleted file mode 100644
index 535ce33..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java
+++ /dev/null
@@ -1,230 +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.catalog.internal;
-
-import java.io.InputStream;
-import java.io.StringReader;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.api.catalog.CatalogItem;
-
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.exceptions.PropagatedRuntimeException;
-import brooklyn.util.stream.Streams;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Objects;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Lists;
-
-@Beta
-public class CatalogDto {
-
-    private static final Logger LOG = LoggerFactory.getLogger(CatalogDto.class);
-
-    String id;
-    String url;
-    String contents;
-    String contentsDescription;
-    String name;
-    String description;
-    CatalogClasspathDto classpath;
-    private List<CatalogItemDtoAbstract<?,?>> entries = null;
-    
-    // for thread-safety, any dynamic additions to this should be handled by a method 
-    // in this class which does copy-on-write
-    List<CatalogDto> catalogs = null;
-
-    public static CatalogDto newDefaultLocalScanningDto(CatalogClasspathDo.CatalogScanningModes scanMode) {
-        CatalogDo result = new CatalogDo(
-                newNamedInstance("Local Scanned Catalog", "All annotated Brooklyn entities detected in the default classpath", "scanning-local-classpath") );
-        result.setClasspathScanForEntities(scanMode);
-        return result.dto;
-    }
-
-    /** @deprecated since 0.7.0 use {@link #newDtoFromXmlUrl(String)} if you must, but note the xml format itself is deprecated */
-    @Deprecated
-    public static CatalogDto newDtoFromUrl(String url) {
-        return newDtoFromXmlUrl(url);
-    }
-    
-    /** @deprecated since 0.7.0 the xml format is deprecated; use YAML parse routines on BasicBrooklynCatalog */
-    @Deprecated
-    public static CatalogDto newDtoFromXmlUrl(String url) {
-        if (LOG.isDebugEnabled()) LOG.debug("Retrieving catalog from: {}", url);
-        try {
-            InputStream source = ResourceUtils.create().getResourceFromUrl(url);
-            String contents = Streams.readFullyString(source);
-            return newDtoFromXmlContents(contents, url);
-        } catch (Throwable t) {
-            Exceptions.propagateIfFatal(t);
-            throw new PropagatedRuntimeException("Unable to retrieve catalog from " + url + ": " + t, t);
-        }
-    }
-
-    /** @deprecated since 0.7.0 the xml format is deprecated; use YAML parse routines on BasicBrooklynCatalog */
-    @Deprecated
-    public static CatalogDto newDtoFromXmlContents(String xmlContents, String originDescription) {
-        CatalogDto result = (CatalogDto) new CatalogXmlSerializer().deserialize(new StringReader(xmlContents));
-        result.contentsDescription = originDescription;
-
-        if (LOG.isDebugEnabled()) LOG.debug("Retrieved catalog from: {}", originDescription);
-        return result;
-    }
-
-    /**
-     * Creates a DTO.
-     * <p>
-     * The way contents is treated may change; thus this (and much of catalog) should be treated as beta.
-     * 
-     * @param name
-     * @param description
-     * @param optionalContentsDescription optional description of contents; if null, we normally expect source 'contents' to be set later;
-     *   if the DTO has no 'contents' (ie XML source) then a description should be supplied so we know who is populating it
-     *   (e.g. manual additions); without this, warnings may be generated
-     *   
-     * @return a new Catalog DTO
-     */
-    public static CatalogDto newNamedInstance(String name, String description, String optionalContentsDescription) {
-        CatalogDto result = new CatalogDto();
-        result.name = name;
-        result.description = description;
-        if (optionalContentsDescription!=null) result.contentsDescription = optionalContentsDescription;
-        return result;
-    }
-
-    /** Used when caller wishes to create an explicitly empty catalog */
-    public static CatalogDto newEmptyInstance(String optionalContentsDescription) {
-        CatalogDto result = new CatalogDto();
-        if (optionalContentsDescription!=null) result.contentsDescription = optionalContentsDescription;
-        return result;
-    }
-
-    public static CatalogDto newLinkedInstance(String url) {
-        CatalogDto result = new CatalogDto();
-        result.contentsDescription = url;
-        result.contents = ResourceUtils.create().getResourceAsString(url);
-        return result;
-    }
-
-    /** @deprecated since 0.7.0 use {@link #newDtoFromCatalogItems(Collection, String)}, supplying a description for tracking */
-    @Deprecated
-    public static CatalogDto newDtoFromCatalogItems(Collection<CatalogItem<?, ?>> entries) {
-        return newDtoFromCatalogItems(entries, null);
-    }
-    
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static CatalogDto newDtoFromCatalogItems(Collection<CatalogItem<?, ?>> entries, String description) {
-        CatalogDto result = new CatalogDto();
-        result.contentsDescription = description;
-        // Weird casts because compiler does not seem to like
-        // .copyInto(Lists.<CatalogItemDtoAbstract<?, ?>>newArrayListWithExpectedSize(entries.size()));
-        result.entries = (List<CatalogItemDtoAbstract<?, ?>>) (List) FluentIterable.from(entries)
-                .filter(CatalogItemDtoAbstract.class)
-                .copyInto(Lists.newArrayListWithExpectedSize(entries.size()));
-        return result;
-    }
-    
-    void populate() {
-        if (contents==null) {
-            if (url != null) {
-                contents = ResourceUtils.create().getResourceAsString(url);
-                contentsDescription = url;
-            } else if (contentsDescription==null) {
-                LOG.debug("Catalog DTO has no contents and no description; ignoring call to populate it. Description should be set to suppress this message.");
-                return;
-            } else {
-                LOG.trace("Nothing needs doing (no contents or URL) for catalog with contents described as "+contentsDescription+".");
-                return;
-            }
-        }
-        
-        CatalogDto remoteDto = newDtoFromXmlContents(contents, contentsDescription);
-        try {
-            copyFrom(remoteDto, true);
-        } catch (Exception e) {
-            Exceptions.propagate(e);
-        }
-    }        
-
-    /**
-     * @throws NullPointerException If source is null (and !skipNulls)
-     */
-    void copyFrom(CatalogDto source, boolean skipNulls) throws IllegalAccessException {
-        if (source==null) {
-            if (skipNulls) return;
-            throw new NullPointerException("source DTO is null, when copying to "+this);
-        }
-        
-        if (!skipNulls || source.id != null) id = source.id;
-        if (!skipNulls || source.contentsDescription != null) contentsDescription = source.contentsDescription;
-        if (!skipNulls || source.contents != null) contents = source.contents;
-        if (!skipNulls || source.name != null) name = source.name;
-        if (!skipNulls || source.description != null) description = source.description;
-        if (!skipNulls || source.classpath != null) classpath = source.classpath;
-        if (!skipNulls || source.entries != null) entries = source.entries;
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(this)
-                .omitNullValues()
-                .add("name", name)
-                .add("id", id)
-                .add("contentsDescription", contentsDescription)
-                .toString();
-    }
-
-    // temporary fix for issue where entries might not be unique
-    Iterable<CatalogItemDtoAbstract<?, ?>> getUniqueEntries() {
-        if (entries==null) return null;
-        Map<String, CatalogItemDtoAbstract<?, ?>> result = getEntriesMap();
-        return result.values();
-    }
-
-    private Map<String, CatalogItemDtoAbstract<?, ?>> getEntriesMap() {
-        if (entries==null) return null;
-        Map<String,CatalogItemDtoAbstract<?, ?>> result = MutableMap.of();
-        for (CatalogItemDtoAbstract<?,?> entry: entries) {
-            result.put(entry.getId(), entry);
-        }
-        return result;
-    }
-
-    void removeEntry(CatalogItemDtoAbstract<?, ?> entry) {
-        if (entries!=null)
-            entries.remove(entry);
-    }
-
-    void addEntry(CatalogItemDtoAbstract<?, ?> entry) {
-        if (entries == null) entries = MutableList.of();
-        CatalogItemDtoAbstract<?, ?> oldEntry = getEntriesMap().get(entry.getId());
-        entries.add(entry);
-        if (oldEntry!=null)
-            removeEntry(oldEntry);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogDtoUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDtoUtils.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDtoUtils.java
deleted file mode 100644
index cb44cb0..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogDtoUtils.java
+++ /dev/null
@@ -1,67 +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.catalog.internal;
-
-import java.io.InputStream;
-import java.io.InputStreamReader;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.exceptions.Exceptions;
-
-public class CatalogDtoUtils {
-
-    private static final Logger log = LoggerFactory.getLogger(CatalogDtoUtils.class);
-    
-    public static CatalogDto newDefaultLocalScanningDto(CatalogScanningModes scanMode) {
-        return CatalogDto.newDefaultLocalScanningDto(scanMode);
-    }
-
-    /** throws if there are any problems in retrieving or copying */
-    public static void populateFromUrl(CatalogDto dto, String url) {
-        CatalogDto remoteDto = newDtoFromUrl(url);
-        try {
-            copyDto(remoteDto, dto, true);
-        } catch (Exception e) {
-            Exceptions.propagate(e);
-        }
-    }
-
-    /** does a shallow copy.
-     * "skipNulls" means not to copy any fields from the source which are null */ 
-    static void copyDto(CatalogDto source, CatalogDto target, boolean skipNulls) throws IllegalArgumentException, IllegalAccessException {
-        target.copyFrom(source, skipNulls);
-    }
-
-    public static CatalogDto newDtoFromUrl(String url) {
-        if (log.isDebugEnabled()) log.debug("Retrieving catalog from: {}", url);
-        try {
-            InputStream source = ResourceUtils.create().getResourceFromUrl(url);
-            CatalogDto result = (CatalogDto) new CatalogXmlSerializer().deserialize(new InputStreamReader(source));
-            if (log.isDebugEnabled()) log.debug("Retrieved catalog from: {}", url);
-            return result;
-        } catch (Throwable t) {
-            log.debug("Unable to retrieve catalog from: "+url+" ("+t+")");
-            throw Exceptions.propagate(t);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogEntityItemDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogEntityItemDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogEntityItemDto.java
deleted file mode 100644
index 577eff5..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogEntityItemDto.java
+++ /dev/null
@@ -1,43 +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.catalog.internal;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.proxying.EntitySpec;
-
-
-public class CatalogEntityItemDto extends CatalogItemDtoAbstract<Entity,EntitySpec<?>> {
-    
-    @Override
-    public CatalogItemType getCatalogItemType() {
-        return CatalogItemType.ENTITY;
-    }
-
-    @Override
-    public Class<Entity> getCatalogItemJavaType() {
-        return Entity.class;
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    @Override
-    public Class<EntitySpec<?>> getSpecType() {
-        return (Class)EntitySpec.class;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
deleted file mode 100644
index 07eac9b..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
+++ /dev/null
@@ -1,449 +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.catalog.internal;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.api.catalog.CatalogItem;
-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 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;
-import brooklyn.util.text.Strings;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-
-@Beta
-public class CatalogInitialization implements ManagementContextInjectable {
-
-    /*
-
-    A1) if not persisting, go to B1
-    A2) if --catalog-reset, delete persisted catalog items
-    A3) if there is a persisted catalog (and it wasn't not deleted by A2), read it and go to C1
-    A4) go to B1
-
-    B1) look for --catalog-initial, if so read it, then go to C1
-    B2) look for BrooklynServerConfig.BROOKLYN_CATALOG_URL, if so, read it, supporting YAML or XML (warning if XML), then go to C1
-    B3) look for ~/.brooklyn/catalog.bom, if exists, read it then go to C1
-    B4) look for ~/.brooklyn/brooklyn.xml, if exists, warn, read it then go to C1
-    B5) read all classpath://brooklyn/default.catalog.bom items, if they exist (and for now they will)
-    B6) go to C1
-
-    C1) if --catalog-add, read and add those items
-
-    D1) if persisting, read the rest of the persisted items (entities etc)
-
-     */
-
-    private static final Logger log = LoggerFactory.getLogger(CatalogInitialization.class);
-    
-    private String initialUri;
-    private boolean reset;
-    private String additionsUri;
-    private boolean force;
-
-    private boolean disallowLocal = false;
-    private List<Function<CatalogInitialization, Void>> callbacks = MutableList.of();
-    private boolean 
-        /** has run an unofficial initialization (i.e. an early load, triggered by an early read of the catalog) */
-        hasRunUnofficialInitialization = false, 
-        /** has run an official initialization, but it is not a permanent one (e.g. during a hot standby mode, or a run failed) */
-        hasRunTransientOfficialInitialization = false, 
-        /** has run an official initialization which is permanent (node is master, and the new catalog is now set) */
-        hasRunFinalInitialization = false;
-    /** is running a populate method; used to prevent recursive loops */
-    private boolean isPopulating = false;
-    
-    private ManagementContext managementContext;
-    private boolean isStartingUp = false;
-    private boolean failOnStartupErrors = false;
-    
-    private Object populatingCatalogMutex = new Object();
-    
-    public CatalogInitialization(String initialUri, boolean reset, String additionUri, boolean force) {
-        this.initialUri = initialUri;
-        this.reset = reset;
-        this.additionsUri = additionUri;
-        this.force = force;
-    }
-    
-    public CatalogInitialization() {
-        this(null, false, null, false);
-    }
-
-    @Override
-    public void injectManagementContext(ManagementContext managementContext) {
-        Preconditions.checkNotNull(managementContext, "management context");
-        if (this.managementContext!=null && managementContext!=this.managementContext)
-            throw new IllegalStateException("Cannot switch management context, from "+this.managementContext+" to "+managementContext);
-        this.managementContext = managementContext;
-    }
-    
-    /** Called by the framework to set true while starting up, and false afterwards,
-     * in order to assist in appropriate logging and error handling. */
-    public void setStartingUp(boolean isStartingUp) {
-        this.isStartingUp = isStartingUp;
-    }
-
-    public void setFailOnStartupErrors(boolean startupFailOnCatalogErrors) {
-        this.failOnStartupErrors = startupFailOnCatalogErrors;
-    }
-
-    public CatalogInitialization addPopulationCallback(Function<CatalogInitialization, Void> callback) {
-        callbacks.add(callback);
-        return this;
-    }
-    
-    public ManagementContext getManagementContext() {
-        return Preconditions.checkNotNull(managementContext, "management context has not been injected into "+this);
-    }
-
-    public boolean isInitialResetRequested() {
-        return reset;
-    }
-
-    /** Returns true if the canonical initialization has completed, 
-     * that is, an initialization which is done when a node is rebinded as master
-     * (or an initialization done by the startup routines when not running persistence);
-     * see also {@link #hasRunAnyInitialization()}. */
-    public boolean hasRunFinalInitialization() { return hasRunFinalInitialization; }
-    /** Returns true if an official initialization has run,
-     * even if it was a transient run, e.g. so that the launch sequence can tell whether rebind has triggered initialization */
-    public boolean hasRunOfficialInitialization() { return hasRunFinalInitialization || hasRunTransientOfficialInitialization; }
-    /** Returns true if the initializer has run at all,
-     * including transient initializations which might be needed before a canonical becoming-master rebind,
-     * for instance because the catalog is being accessed before loading rebind information
-     * (done by {@link #populateUnofficial(BasicBrooklynCatalog)}) */
-    public boolean hasRunAnyInitialization() { return hasRunFinalInitialization || hasRunTransientOfficialInitialization || hasRunUnofficialInitialization; }
-
-    /** makes or updates the mgmt catalog, based on the settings in this class 
-     * @param nodeState the management node for which this is being read; if master, then we expect this run to be the last one,
-     *   and so subsequent applications should ignore any initialization data (e.g. on a subsequent promotion to master, 
-     *   after a master -> standby -> master cycle)
-     * @param needsInitialItemsLoaded whether the catalog needs the initial items loaded
-     * @param needsAdditionalItemsLoaded whether the catalog needs the additions loaded
-     * @param optionalExcplicitItemsForResettingCatalog
-     *   if supplied, the catalog is reset to contain only these items, before calling any other initialization
-     *   for use primarily when rebinding
-     */
-    public void populateCatalog(ManagementNodeState nodeState, boolean needsInitialItemsLoaded, boolean needsAdditionsLoaded, Collection<CatalogItem<?, ?>> optionalExcplicitItemsForResettingCatalog) {
-        if (log.isDebugEnabled()) {
-            String message = "Populating catalog for "+nodeState+", needsInitial="+needsInitialItemsLoaded+", needsAdditional="+needsAdditionsLoaded+", explicitItems="+(optionalExcplicitItemsForResettingCatalog==null ? "null" : optionalExcplicitItemsForResettingCatalog.size())+"; from "+JavaClassNames.callerNiceClassAndMethod(1);
-            if (!ManagementNodeState.isHotProxy(nodeState)) {
-                log.debug(message);
-            } else {
-                // in hot modes, make this message trace so we don't get too much output then
-                log.trace(message);
-            }
-        }
-        synchronized (populatingCatalogMutex) {
-            try {
-                if (hasRunFinalInitialization() && (needsInitialItemsLoaded || needsAdditionsLoaded)) {
-                    // if we have already run "final" then we should only ever be used to reset the catalog, 
-                    // not to initialize or add; e.g. we are being given a fixed list on a subsequent master rebind after the initial master rebind 
-                    log.warn("Catalog initialization called to populate initial, even though it has already run the final official initialization");
-                }
-                isPopulating = true;
-                BasicBrooklynCatalog catalog = (BasicBrooklynCatalog) managementContext.getCatalog();
-                if (!catalog.getCatalog().isLoaded()) {
-                    catalog.load();
-                } else {
-                    if (needsInitialItemsLoaded && hasRunAnyInitialization()) {
-                        // an indication that something caused it to load early; not severe, but unusual
-                        if (hasRunTransientOfficialInitialization) {
-                            log.debug("Catalog initialization now populating, but has noted a previous official run which was not final (probalby loaded while in a standby mode, or a previous run failed); overwriting any items installed earlier");
-                        } else {
-                            log.warn("Catalog initialization now populating, but has noted a previous unofficial run (it may have been an early web request); overwriting any items installed earlier");
-                        }
-                        catalog.reset(ImmutableList.<CatalogItem<?,?>>of());
-                    }
-                }
-
-                populateCatalogImpl(catalog, needsInitialItemsLoaded, needsAdditionsLoaded, optionalExcplicitItemsForResettingCatalog);
-                if (nodeState == ManagementNodeState.MASTER) {
-                    // TODO ideally this would remain false until it has *persisted* the changed catalog;
-                    // if there is a subsequent startup failure the forced additions will not be persisted,
-                    // but nor will they be loaded on a subsequent run.
-                    // callers will have to restart a brooklyn, or reach into this class to change this field,
-                    // or (recommended) manually adjust the catalog.
-                    // TODO also, if a node comes up in standby, the addition might not take effector for a while
-                    //
-                    // however since these options are mainly for use on the very first brooklyn run, it's not such a big deal; 
-                    // once up and running the typical way to add items is via the REST API
-                    hasRunFinalInitialization = true;
-                }
-            } finally {
-                if (!hasRunFinalInitialization) {
-                    hasRunTransientOfficialInitialization = true;
-                }
-                isPopulating = false;
-            }
-        }
-    }
-
-    private void populateCatalogImpl(BasicBrooklynCatalog catalog, boolean needsInitialItemsLoaded, boolean needsAdditionsLoaded, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) {
-        applyCatalogLoadMode();
-        
-        if (optionalItemsForResettingCatalog!=null) {
-            catalog.reset(optionalItemsForResettingCatalog);
-        }
-        
-        if (needsInitialItemsLoaded) {
-            populateInitial(catalog);
-        }
-
-        if (needsAdditionsLoaded) {
-            populateAdditions(catalog);
-            populateViaCallbacks(catalog);
-        }
-    }
-
-    private enum PopulateMode { YAML, XML, AUTODETECT }
-    
-    protected void populateInitial(BasicBrooklynCatalog catalog) {
-        if (disallowLocal) {
-            if (!hasRunFinalInitialization()) {
-                log.debug("CLI initial catalog not being read when local catalog load mode is disallowed.");
-            }
-            return;
-        }
-
-//        B1) look for --catalog-initial, if so read it, then go to C1
-//        B2) look for BrooklynServerConfig.BROOKLYN_CATALOG_URL, if so, read it, supporting YAML or XML (warning if XML), then go to C1
-//        B3) look for ~/.brooklyn/catalog.bom, if exists, read it then go to C1
-//        B4) look for ~/.brooklyn/brooklyn.xml, if exists, warn, read it then go to C1
-//        B5) read all classpath://brooklyn/default.catalog.bom items, if they exist (and for now they will)
-//        B6) go to C1
-
-        if (initialUri!=null) {
-            populateInitialFromUri(catalog, initialUri, PopulateMode.AUTODETECT);
-            return;
-        }
-        
-        String catalogUrl = managementContext.getConfig().getConfig(BrooklynServerConfig.BROOKLYN_CATALOG_URL);
-        if (Strings.isNonBlank(catalogUrl)) {
-            populateInitialFromUri(catalog, catalogUrl, PopulateMode.AUTODETECT);
-            return;
-        }
-        
-        catalogUrl = Os.mergePaths(BrooklynServerConfig.getMgmtBaseDir( managementContext.getConfig() ), "catalog.bom");
-        if (new File(catalogUrl).exists()) {
-            populateInitialFromUri(catalog, new File(catalogUrl).toURI().toString(), PopulateMode.YAML);
-            return;
-        }
-        
-        catalogUrl = Os.mergePaths(BrooklynServerConfig.getMgmtBaseDir( managementContext.getConfig() ), "catalog.xml");
-        if (new File(catalogUrl).exists()) {
-            populateInitialFromUri(catalog, new File(catalogUrl).toURI().toString(), PopulateMode.XML);
-            return;
-        }
-
-        // otherwise look for for classpath:/brooklyn/default.catalog.bom --
-        // there is one on the classpath which says to scan, and provides a few templates;
-        // if one is supplied by user in the conf/ dir that will override the item from the classpath
-        // (TBD - we might want to scan for all such bom's?)
-        
-        catalogUrl = "classpath:/brooklyn/default.catalog.bom";
-        if (new ResourceUtils(this).doesUrlExist(catalogUrl)) {
-            populateInitialFromUri(catalog, catalogUrl, PopulateMode.YAML);
-            return;
-        }
-        
-        log.info("No catalog found on classpath or specified; catalog will not be initialized.");
-        return;
-    }
-    
-    private void populateInitialFromUri(BasicBrooklynCatalog catalog, String catalogUrl, PopulateMode mode) {
-        log.debug("Loading initial catalog from {}", catalogUrl);
-
-        Exception problem = null;
-        Object result = null;
-        
-        String contents = null;
-        try {
-            contents = new ResourceUtils(this).getResourceAsString(catalogUrl);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            if (problem==null) problem = e;
-        }
-
-        if (contents!=null && (mode==PopulateMode.YAML || mode==PopulateMode.AUTODETECT)) {
-            // try YAML first
-            try {
-                catalog.reset(MutableList.<CatalogItem<?,?>>of());
-                result = catalog.addItems(contents);
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                if (problem==null) problem = e;
-            }
-        }
-        
-        if (result==null && contents!=null && (mode==PopulateMode.XML || mode==PopulateMode.AUTODETECT)) {
-            // then try XML
-            try {
-                populateInitialFromUriXml(catalog, catalogUrl, contents);
-                // clear YAML problem
-                problem = null;
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                if (problem==null) problem = e;
-            }
-        }
-        
-        if (result!=null) {
-            log.debug("Loaded initial catalog from {}: {}", catalogUrl, result);
-        }
-        if (problem!=null) {
-            log.warn("Error importing catalog from " + catalogUrl + ": " + problem, problem);
-            // TODO inform mgmt of error
-        }
-
-    }
-
-    // deprecated XML format
-    @SuppressWarnings("deprecation")
-    private void populateInitialFromUriXml(BasicBrooklynCatalog catalog, String catalogUrl, String contents) {
-        CatalogDto dto = CatalogDto.newDtoFromXmlContents(contents, catalogUrl);
-        if (dto!=null) {
-            catalog.reset(dto);
-        }
-    }
-
-    boolean hasRunAdditions = false;
-    protected void populateAdditions(BasicBrooklynCatalog catalog) {
-        if (Strings.isNonBlank(additionsUri)) {
-            if (disallowLocal) {
-                if (!hasRunAdditions) {
-                    log.warn("CLI additions supplied but not supported when catalog load mode disallows local loads; ignoring.");
-                }
-                return;
-            }   
-            if (!hasRunAdditions) {
-                log.debug("Adding to catalog from CLI: "+additionsUri+" (force: "+force+")");
-            }
-            Iterable<? extends CatalogItem<?, ?>> items = catalog.addItems(
-                new ResourceUtils(this).getResourceAsString(additionsUri), force);
-            
-            if (!hasRunAdditions)
-                log.debug("Added to catalog from CLI: "+items);
-            else
-                log.debug("Added to catalog from CLI: count "+Iterables.size(items));
-            
-            hasRunAdditions = true;
-        }
-    }
-
-    protected void populateViaCallbacks(BasicBrooklynCatalog catalog) {
-        for (Function<CatalogInitialization, Void> callback: callbacks)
-            callback.apply(this);
-    }
-
-    private Object setFromCLMMutex = new Object();
-    private boolean setFromCatalogLoadMode = false;
-
-    /** @deprecated since introduced in 0.7.0, only for legacy compatibility with 
-     * {@link CatalogLoadMode} {@link BrooklynServerConfig#CATALOG_LOAD_MODE},
-     * allowing control of catalog loading from a brooklyn property */
-    @Deprecated
-    public void applyCatalogLoadMode() {
-        synchronized (setFromCLMMutex) {
-            if (setFromCatalogLoadMode) return;
-            setFromCatalogLoadMode = true;
-            Maybe<Object> clmm = ((ManagementContextInternal)managementContext).getConfig().getConfigRaw(BrooklynServerConfig.CATALOG_LOAD_MODE, false);
-            if (clmm.isAbsent()) return;
-            brooklyn.catalog.CatalogLoadMode clm = TypeCoercions.coerce(clmm.get(), brooklyn.catalog.CatalogLoadMode.class);
-            log.warn("Legacy CatalogLoadMode "+clm+" set: applying, but this should be changed to use new CLI --catalogXxx commands");
-            switch (clm) {
-            case LOAD_BROOKLYN_CATALOG_URL:
-                reset = true;
-                break;
-            case LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE:
-                // now the default
-                break;
-            case LOAD_PERSISTED_STATE:
-                disallowLocal = true;
-                break;
-            }
-        }
-    }
-
-    /** Creates the catalog based on parameters set here, if not yet loaded,
-     * but ignoring persisted state and warning if persistence is on and we are starting up
-     * (because the official persistence is preferred and the catalog will be subsequently replaced);
-     * for use when the catalog is accessed before persistence is completed. 
-     * <p>
-     * This method is primarily used during testing, which in many cases does not enforce the full startup order
-     * and which wants a local catalog in any case. It may also be invoked if a client requests the catalog
-     * while the server is starting up. */
-    public void populateUnofficial(BasicBrooklynCatalog catalog) {
-        synchronized (populatingCatalogMutex) {
-            // check isPopulating in case this method gets called from inside another populate call
-            if (hasRunAnyInitialization() || isPopulating) return;
-            log.debug("Populating catalog unofficially ("+catalog+")");
-            isPopulating = true;
-            try {
-                if (isStartingUp) {
-                    log.warn("Catalog access requested when not yet initialized; populating best effort rather than through recommended pathway. Catalog data may be replaced subsequently.");
-                }
-                populateCatalogImpl(catalog, true, true, null);
-            } finally {
-                hasRunUnofficialInitialization = true;
-                isPopulating = false;
-            }
-        }
-    }
-
-    public void handleException(Throwable throwable, Object details) {
-        if (throwable instanceof InterruptedException)
-            throw new RuntimeInterruptedException((InterruptedException) throwable);
-        if (throwable instanceof RuntimeInterruptedException)
-            throw (RuntimeInterruptedException) throwable;
-
-        log.error("Error loading catalog item '"+details+"': "+throwable);
-        log.debug("Trace for error loading catalog item '"+details+"': "+throwable, throwable);
-
-        // TODO give more detail when adding
-        ((ManagementContextInternal)getManagementContext()).errors().add(throwable);
-        
-        if (isStartingUp && failOnStartupErrors) {
-            throw new FatalRuntimeException("Unable to load catalog item '"+details+"': "+throwable, throwable);
-        }
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
deleted file mode 100644
index 0001ac9..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
+++ /dev/null
@@ -1,128 +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.catalog.internal;
-
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
-
-import com.google.common.base.Preconditions;
-
-public class CatalogItemBuilder<CatalogItemType extends CatalogItemDtoAbstract<?, ?>> {
-    private CatalogItemType dto;
-
-    public static CatalogItemBuilder<CatalogEntityItemDto> newEntity(String symbolicName, String version) {
-        return new CatalogItemBuilder<CatalogEntityItemDto>(new CatalogEntityItemDto())
-                .symbolicName(symbolicName)
-                .version(version);
-    }
-
-    public static CatalogItemBuilder<CatalogTemplateItemDto> newTemplate(String symbolicName, String version) {
-        return new CatalogItemBuilder<CatalogTemplateItemDto>(new CatalogTemplateItemDto())
-                .symbolicName(symbolicName)
-                .version(version);
-    }
-
-    public static CatalogItemBuilder<CatalogPolicyItemDto> newPolicy(String symbolicName, String version) {
-        return new CatalogItemBuilder<CatalogPolicyItemDto>(new CatalogPolicyItemDto())
-                .symbolicName(symbolicName)
-                .version(version);
-    }
-
-    public static CatalogItemBuilder<CatalogLocationItemDto> newLocation(String symbolicName, String version) {
-        return new CatalogItemBuilder<CatalogLocationItemDto>(new CatalogLocationItemDto())
-                .symbolicName(symbolicName)
-                .version(version);
-    }
-
-    public CatalogItemBuilder(CatalogItemType dto) {
-        this.dto = dto;
-        this.dto.setLibraries(Collections.<CatalogBundle>emptyList());
-    }
-
-    public CatalogItemBuilder<CatalogItemType> symbolicName(String symbolicName) {
-        dto.setSymbolicName(symbolicName);
-        return this;
-    }
-
-    @Deprecated
-    public CatalogItemBuilder<CatalogItemType> javaType(String javaType) {
-        dto.setJavaType(javaType);
-        return this;
-    }
-
-    /** @deprecated since 0.7.0 use {@link #displayName}*/
-    @Deprecated
-    public CatalogItemBuilder<CatalogItemType> name(String name) {
-        return displayName(name);
-    }
-
-    public CatalogItemBuilder<CatalogItemType> displayName(String displayName) {
-        dto.setDisplayName(displayName);
-        return this;
-    }
-
-    public CatalogItemBuilder<CatalogItemType> description(String description) {
-        dto.setDescription(description);
-        return this;
-    }
-
-    public CatalogItemBuilder<CatalogItemType> iconUrl(String iconUrl) {
-        dto.setIconUrl(iconUrl);
-        return this;
-    }
-
-    public CatalogItemBuilder<CatalogItemType> version(String version) {
-        dto.setVersion(version);
-        return this;
-    }
-
-    public CatalogItemBuilder<CatalogItemType> deprecated(boolean deprecated) {
-        dto.setDeprecated(deprecated);
-        return this;
-    }
-
-    public CatalogItemBuilder<CatalogItemType> libraries(Collection<CatalogBundle> libraries) {
-        dto.setLibraries(libraries);
-        return this;
-    }
-
-    public CatalogItemBuilder<CatalogItemType> plan(String yaml) {
-        dto.setPlanYaml(yaml);
-        return this;
-    }
-
-    public CatalogItemType build() {
-        Preconditions.checkNotNull(dto.getSymbolicName());
-        Preconditions.checkNotNull(dto.getVersion());
-
-        if (dto.getLibraries() == null) {
-            dto.setLibraries(Collections.<CatalogBundle>emptyList());
-        }
-
-        CatalogItemType ret = dto;
-
-        //prevent mutations through the builder
-        dto = null;
-
-        return ret;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
deleted file mode 100644
index 8b868cc..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
+++ /dev/null
@@ -1,143 +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.catalog.internal;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-
-import org.apache.brooklyn.api.catalog.CatalogItem;
-
-import brooklyn.util.text.NaturalOrderComparator;
-
-/**
- * Largest version first order.
- * 
- * When using the comparator to sort - first using symbolicName
- * and if equal puts larger versions first, snapshots at the back.
- */
-public class CatalogItemComparator<T,SpecT> implements Comparator<CatalogItem<T, SpecT>> {
-    private static final String SNAPSHOT = "SNAPSHOT";
-
-    public static final CatalogItemComparator<?, ?> INSTANCE = new CatalogItemComparator<Object, Object>();
-
-    @SuppressWarnings("unchecked")
-    public static <T,SpecT> CatalogItemComparator<T,SpecT> getInstance() {
-        return (CatalogItemComparator<T, SpecT>) INSTANCE;
-    }
-
-    @Override
-    public int compare(CatalogItem<T, SpecT> o1, CatalogItem<T, SpecT> o2) {
-        int symbolicNameComparison = o1.getSymbolicName().compareTo(o2.getSymbolicName());
-        if (symbolicNameComparison != 0) {
-            return symbolicNameComparison;
-        } else {
-            String v1 = o1.getVersion();
-            String v2 = o2.getVersion();
-
-            boolean isV1Snapshot = v1.toUpperCase().contains(SNAPSHOT);
-            boolean isV2Snapshot = v2.toUpperCase().contains(SNAPSHOT);
-            if (isV1Snapshot == isV2Snapshot) {
-                String[] v1Parts = split(v1);
-                String[] v2Parts = split(v2);
-                return -compare(v1Parts, v2Parts);
-            } else if (isV1Snapshot) {
-                return 1;
-            } else {
-                return -1;
-            }
-        }
-    }
-
-    private String[] split(String v) {
-        Collection<String> parts = new ArrayList<String>();
-        int startPos = 0;
-        int delimPos;
-        while ((delimPos = v.indexOf('.', startPos)) != -1) {
-            String part = v.substring(startPos, delimPos);
-            if (parse(part) != -1) {
-                parts.add(part);
-            } else {
-                break;
-            }
-            startPos = delimPos+1;
-        }
-        String remaining = v.substring(startPos);
-        parts.addAll(Arrays.asList(remaining.split("[^\\d]", 2)));
-        return parts.toArray(new String[parts.size()]);
-    }
-
-    private int compare(String[] v1Parts, String[] v2Parts) {
-        int len = Math.max(v1Parts.length, v2Parts.length);
-        for (int i = 0; i < len; i++) {
-            if (i == v1Parts.length) {
-                return isNumber(v2Parts[i]) ? -1 : 1;
-            }
-            if (i == v2Parts.length) {
-                return isNumber(v1Parts[i]) ? 1 : -1;
-            }
-
-            String p1 = v1Parts[i];
-            String p2 = v2Parts[i];
-            int n1 = parse(p1);
-            int n2 = parse(p2);
-            if (n1 != -1 && n2 != -1) {
-                if (n1 != n2) {
-                    return compare(n1, n2);
-                }
-            } else if (n1 == -1 && n2 != -1) {
-                return -1;
-            } else if (n1 != -1 && n2 == -1) {
-                return 1;
-            } else {
-                int cmp = NaturalOrderComparator.INSTANCE.compare(p1, p2);
-                if (cmp < 0) {
-                    return -1;
-                } else if (cmp > 0) {
-                    return 1;
-                }
-            }
-        }
-        return 0;
-    }
-
-    private boolean isNumber(String v) {
-        return parse(v) != -1;
-    }
-
-    //Replace with Integer.compare in J7
-    private int compare(int n1, int n2) {
-        if (n1 == n2) {
-            return 0;
-        } else if (n1 < n2) {
-            return -1;
-        } else {
-            return 1;
-        }
-    }
-
-    private int parse(String p) {
-        try {
-            return Integer.parseInt(p);
-        } catch (NumberFormatException e) {
-            return -1;
-        }
-    }
-}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java
deleted file mode 100644
index 6e9f963..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.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.catalog.internal;
-
-import java.util.Collection;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import brooklyn.basic.BrooklynObjectInternal;
-
-import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.api.entity.rebind.RebindSupport;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.mementos.CatalogItemMemento;
-
-import brooklyn.config.ConfigKey;
-
-import com.google.common.base.Preconditions;
-
-public class CatalogItemDo<T,SpecT> implements CatalogItem<T,SpecT>, BrooklynObjectInternal {
-
-    protected final CatalogDo catalog;
-    protected final CatalogItemDtoAbstract<T,SpecT> itemDto;
-
-    protected volatile Class<T> javaClass; 
-    
-    public CatalogItemDo(CatalogDo catalog, CatalogItem<T,SpecT> itemDto) {
-        this.catalog = Preconditions.checkNotNull(catalog, "catalog");
-        this.itemDto = (CatalogItemDtoAbstract<T, SpecT>) Preconditions.checkNotNull(itemDto, "itemDto");
-    }
-
-    public CatalogItem<T,SpecT> getDto() {
-        return itemDto;
-    }
-    
-    /**
-     * Config not supported for catalog item. See {@link #getPlanYaml()}.
-     */
-    @Override
-    public ConfigurationSupportInternal config() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public <U> U setConfig(ConfigKey<U> key, U val) {
-        return config().set(key, val);
-    }
-
-    @Override
-    public CatalogItemType getCatalogItemType() {
-        return itemDto.getCatalogItemType();
-    }
-
-    @Override
-    public Class<T> getCatalogItemJavaType() {
-        return itemDto.getCatalogItemJavaType();
-    }
-
-    @Override
-    public String getId() {
-        return itemDto.getId();
-    }
-
-    @Override
-    public String getCatalogItemId() {
-        return itemDto.getCatalogItemId();
-    }
-
-    @Override
-    public void setDeprecated(boolean deprecated) {
-        itemDto.setDeprecated(deprecated);
-    }
-
-    @Override
-    public boolean isDeprecated() {
-        return itemDto.isDeprecated();
-    }
-
-    @Override
-    public void setCatalogItemId(String id) {
-        itemDto.setCatalogItemId(id);
-    }
-
-    @Override
-    public String getJavaType() {
-        return itemDto.getJavaType();
-    }
-
-    @Deprecated
-    @Override
-    public String getName() {
-        return getDisplayName();
-    }
-
-    @Deprecated
-    @Override
-    public String getRegisteredTypeName() {
-        return getSymbolicName();
-    }
-
-    @Override
-    public String getDisplayName() {
-        return itemDto.getDisplayName();
-    }
-
-    @Override
-    public TagSupport tags() {
-        return itemDto.tags();
-    }
-
-    @Override
-    public String getDescription() {
-        return itemDto.getDescription();
-    }
-
-    @Override
-    public String getIconUrl() {
-        return itemDto.getIconUrl();
-    }
-    
-    @Override
-    public String getSymbolicName() {
-        return itemDto.getSymbolicName();
-    }
-
-    @Override
-    public String getVersion() {
-        return itemDto.getVersion();
-    }
-
-    @Nonnull  // but it is still null sometimes, see in CatalogDo.loadJavaClass
-    @Override
-    public Collection<CatalogBundle> getLibraries() {
-        return itemDto.getLibraries();
-    }
-
-    /** @deprecated since 0.7.0 this is the legacy mechanism; still needed for policies and apps, but being phased out.
-     * new items should use {@link #getPlanYaml} and {@link #newClassLoadingContext} */
-    @Deprecated
-    public Class<T> getJavaClass() {
-        if (javaClass==null) loadJavaClass(null);
-        return javaClass;
-    }
-    
-    @SuppressWarnings("unchecked")
-    Class<? extends T> loadJavaClass(final ManagementContext mgmt) {
-        if (javaClass!=null) return javaClass;
-        javaClass = (Class<T>)CatalogUtils.newClassLoadingContext(mgmt, getId(), getLibraries(), catalog.getRootClassLoader()).loadClass(getJavaType());
-        return javaClass;
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getCanonicalName()+"["+itemDto+"]";
-    }
-
-    @Override
-    public String toXmlString() {
-        return itemDto.toXmlString();
-    }
-
-    @Override
-    public Class<SpecT> getSpecType() {
-        return itemDto.getSpecType();
-    }
-
-    @Nullable @Override
-    public String getPlanYaml() {
-        return itemDto.getPlanYaml();
-    }
-
-    @Override
-    public RebindSupport<CatalogItemMemento> getRebindSupport() {
-        return itemDto.getRebindSupport();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
deleted file mode 100644
index b72f55f..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
+++ /dev/null
@@ -1,404 +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.catalog.internal;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-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 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;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-
-public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynObject implements CatalogItem<T, SpecT> {
-
-    private static Logger LOG = LoggerFactory.getLogger(CatalogItemDtoAbstract.class);
-
-    private @SetFromFlag String symbolicName;
-    private @SetFromFlag String version = BasicBrooklynCatalog.NO_VERSION;
-
-    private @SetFromFlag String displayName;
-    private @SetFromFlag String description;
-    private @SetFromFlag String iconUrl;
-
-    private @SetFromFlag String javaType;
-    /**@deprecated since 0.7.0, left for deserialization backwards compatibility */
-    private @Deprecated @SetFromFlag String type;
-    private @SetFromFlag String planYaml;
-
-    private @SetFromFlag Collection<CatalogBundle> libraries;
-    private @SetFromFlag Set<Object> tags = Sets.newLinkedHashSet();
-    private @SetFromFlag boolean deprecated;
-
-    /**
-     * Config not supported for catalog item. See {@link #getPlanYaml()}.
-     */
-    @Override
-    public ConfigurationSupportInternal config() {
-        throw new UnsupportedOperationException();
-    }
-    
-    @Override
-    public <U> U setConfig(ConfigKey<U> key, U val) {
-        return config().set(key, val);
-    }
-    
-    @Override
-    public String getId() {
-        return getCatalogItemId();
-    }
-
-    @Override
-    public String getCatalogItemId() {
-        return CatalogUtils.getVersionedId(getSymbolicName(), getVersion());
-    }
-
-    @Override
-    public String getJavaType() {
-        if (javaType != null) return javaType;
-        return type;
-    }
-
-    @Deprecated
-    public String getName() {
-        return getDisplayName();
-    }
-
-    @Deprecated
-    public String getRegisteredTypeName() {
-        return getSymbolicName();
-    }
-
-    @Override
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    @Override
-    public String getDescription() {
-        return description;
-    }
-
-    @Override
-    public String getIconUrl() {
-        return iconUrl;
-    }
-
-    @Override
-    public String getSymbolicName() {
-        if (symbolicName != null) return symbolicName;
-        return getJavaType();
-    }
-
-    @Override
-    public String getVersion() {
-        // The property is set to NO_VERSION when the object is initialized so it's not supposed to be null ever.
-        // But xstream doesn't call constructors when reading from the catalog.xml file which results in null value
-        // for the version property. That's why we have to fix it in the getter.
-        if (version != null) {
-            return version;
-        } else {
-            return BasicBrooklynCatalog.NO_VERSION;
-        }
-    }
-
-    @Override
-    public boolean isDeprecated() {
-        return deprecated;
-    }
-
-    @Override
-    public void setDeprecated(boolean deprecated) {
-        this.deprecated = deprecated;
-    }
-
-    @Nonnull
-    @Override
-    public Collection<CatalogBundle> getLibraries() {
-        if (libraries != null) {
-            return ImmutableList.copyOf(libraries);
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    @Nullable @Override
-    public String getPlanYaml() {
-        return planYaml;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(symbolicName, planYaml, javaType, nullIfEmpty(libraries), version, getCatalogItemId());
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        CatalogItemDtoAbstract<?,?> other = (CatalogItemDtoAbstract<?,?>) obj;
-        if (!Objects.equal(symbolicName, other.symbolicName)) return false;
-        if (!Objects.equal(planYaml, other.planYaml)) return false;
-        if (!Objects.equal(javaType, other.javaType)) return false;
-        if (!Objects.equal(nullIfEmpty(libraries), nullIfEmpty(other.libraries))) return false;
-        if (!Objects.equal(getCatalogItemId(), other.getCatalogItemId())) return false;
-        if (!Objects.equal(version, other.version)) return false;
-        if (!Objects.equal(deprecated, other.deprecated)) return false;
-        if (!Objects.equal(description, other.description)) return false;
-        if (!Objects.equal(displayName, other.displayName)) return false;
-        if (!Objects.equal(iconUrl, other.iconUrl)) return false;
-        if (!Objects.equal(tags, other.tags)) return false;
-        // 'type' not checked, because deprecated, 
-        // and in future we might want to allow it to be removed/blanked in some impls without affecting equality
-        // (in most cases it is the same as symbolicName so doesn't matter)
-        return true;
-    }
-
-    private static <T> Collection<T> nullIfEmpty(Collection<T> coll) {
-        if (coll==null || coll.isEmpty()) return null;
-        return coll;
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName()+"["+getId()+"/"+getDisplayName()+"]";
-    }
-
-    public abstract Class<SpecT> getSpecType();
-
-    transient CatalogXmlSerializer serializer;
-
-    @Override
-    public String toXmlString() {
-        if (serializer==null) loadSerializer();
-        return serializer.toString(this);
-    }
-
-    private synchronized void loadSerializer() {
-        if (serializer == null) {
-            serializer = new CatalogXmlSerializer();
-        }
-    }
-
-    @Override
-    public RebindSupport<CatalogItemMemento> getRebindSupport() {
-        return new BasicCatalogItemRebindSupport(this);
-    }
-
-    @Override
-    public void setDisplayName(String newName) {
-        this.displayName = newName;
-    }
-
-    @Override
-    protected AbstractBrooklynObject configure(Map<?, ?> flags) {
-        FlagUtils.setFieldsFromFlags(flags, this);
-        return this;
-    }
-
-    @Override
-    public TagSupport tags() {
-        return new BasicTagSupport();
-    }
-
-    /*
-     * Using a custom tag support class rather than the one in AbstractBrooklynObject because
-     * when XStream unmarshals a catalog item with no tags (e.g. from any catalog.xml file)
-     * super.tags will be null, and any call to getTags throws a NullPointerException on the
-     * synchronized (tags) statement. It can't just be initialised here because super.tags is
-     * final.
-     */
-    private class BasicTagSupport implements TagSupport {
-
-        private void setTagsIfNull() {
-            // Possible if the class was unmarshalled by Xstream with no tags
-            synchronized (CatalogItemDtoAbstract.this) {
-                if (tags == null) {
-                    tags = Sets.newLinkedHashSet();
-                }
-            }
-        }
-
-        @Nonnull
-        @Override
-        public Set<Object> getTags() {
-            synchronized (CatalogItemDtoAbstract.this) {
-                setTagsIfNull();
-                return ImmutableSet.copyOf(tags);
-            }
-        }
-
-        @Override
-        public boolean containsTag(Object tag) {
-            synchronized (CatalogItemDtoAbstract.this) {
-                setTagsIfNull();
-                return tags.contains(tag);
-            }
-        }
-
-        @Override
-        public boolean addTag(Object tag) {
-            boolean result;
-            synchronized (CatalogItemDtoAbstract.this) {
-                setTagsIfNull();
-                result = tags.add(tag);
-            }
-            onTagsChanged();
-            return result;
-        }
-
-        @Override
-        public boolean addTags(Iterable<?> newTags) {
-            boolean result;
-            synchronized (CatalogItemDtoAbstract.this) {
-                setTagsIfNull();
-                result = Iterables.addAll(tags, newTags);
-            }
-            onTagsChanged();
-            return result;
-        }
-
-        @Override
-        public boolean removeTag(Object tag) {
-            boolean result;
-            synchronized (CatalogItemDtoAbstract.this) {
-                setTagsIfNull();
-                result = tags.remove(tag);
-            }
-            onTagsChanged();
-            return result;
-        }
-    }
-
-    @Override
-    @Deprecated
-    public void setCatalogItemId(String id) {
-        //no op, should be used by rebind code only
-    }
-
-    protected void setSymbolicName(String symbolicName) {
-        this.symbolicName = symbolicName;
-    }
-
-    protected void setVersion(String version) {
-        this.version = version;
-    }
-
-    protected void setDescription(String description) {
-        this.description = description;
-    }
-
-    protected void setIconUrl(String iconUrl) {
-        this.iconUrl = iconUrl;
-    }
-
-    protected void setJavaType(String javaType) {
-        this.javaType = javaType;
-        this.type = null;
-    }
-
-    protected void setPlanYaml(String planYaml) {
-        this.planYaml = planYaml;
-    }
-
-    protected void setLibraries(Collection<CatalogBundle> libraries) {
-        this.libraries = libraries;
-    }
-
-    protected void setTags(Set<Object> tags) {
-        this.tags = tags;
-    }
-
-    protected void setSerializer(CatalogXmlSerializer serializer) {
-        this.serializer = serializer;
-    }
-
-    /**
-     * Parses an instance of CatalogLibrariesDto from the given List. Expects the list entries
-     * to be either Strings or Maps of String -> String. Will skip items that are not.
-     */
-    public static Collection<CatalogBundle> parseLibraries(Collection<?> possibleLibraries) {
-        Collection<CatalogBundle> dto = MutableList.of();
-        for (Object object : possibleLibraries) {
-            if (object instanceof Map) {
-                Map<?, ?> entry = (Map<?, ?>) object;
-                String name = stringValOrNull(entry, "name");
-                String version = stringValOrNull(entry, "version");
-                String url = stringValOrNull(entry, "url");
-                dto.add(new CatalogBundleDto(name, version, url));
-            } else if (object instanceof String) {
-                String inlineRef = (String) object;
-
-                final String name;
-                final String version;
-                final String url;
-
-                //Infer reference type (heuristically)
-                if (inlineRef.contains("/") || inlineRef.contains("\\")) {
-                    //looks like an url/file path
-                    name = null;
-                    version = null;
-                    url = inlineRef;
-                } else if (CatalogUtils.looksLikeVersionedId(inlineRef)) {
-                    //looks like a name+version ref
-                    name = CatalogUtils.getIdFromVersionedId(inlineRef);
-                    version = CatalogUtils.getVersionFromVersionedId(inlineRef);
-                    url = null;
-                } else {
-                    //assume it to be relative url
-                    name = null;
-                    version = null;
-                    url = inlineRef;
-                }
-
-                dto.add(new CatalogBundleDto(name, version, url));
-            } else {
-                LOG.debug("Unexpected entry in libraries list neither string nor map: " + object);
-            }
-        }
-        return dto;
-    }
-
-    private static String stringValOrNull(Map<?, ?> map, String key) {
-        Object val = map.get(key);
-        return val != null ? String.valueOf(val) : null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
deleted file mode 100644
index 2adc851..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
+++ /dev/null
@@ -1,42 +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.catalog.internal;
-
-import java.util.Collection;
-
-import org.apache.brooklyn.api.catalog.CatalogItem;
-
-import com.google.common.base.Preconditions;
-
-@Deprecated
-public class CatalogLibrariesDo implements CatalogItem.CatalogItemLibraries {
-
-    private final CatalogLibrariesDto librariesDto;
-
-
-    public CatalogLibrariesDo(CatalogLibrariesDto librariesDto) {
-        this.librariesDto = Preconditions.checkNotNull(librariesDto, "librariesDto");
-    }
-
-    @Override
-    public Collection<String> getBundles() {
-        return librariesDto.getBundles();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
deleted file mode 100644
index 47dcdd5..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
+++ /dev/null
@@ -1,53 +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.catalog.internal;
-
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.brooklyn.api.catalog.CatalogItem;
-
-import com.google.common.collect.ImmutableList;
-
-@Deprecated
-public class CatalogLibrariesDto implements CatalogItem.CatalogItemLibraries {
-
-    private final Collection<String> bundles;
-
-    public CatalogLibrariesDto() {
-        this.bundles = Collections.emptyList();
-    }
-
-    public CatalogLibrariesDto(Collection<String> bundles) {
-        this.bundles = bundles;
-    }
-
-    /**
-     * @return An immutable copy of the bundle URLs referenced by this object
-     */
-    @Override
-    public Collection<String> getBundles() {
-        if (bundles == null) {
-            // can be null on deserialization
-            return Collections.emptyList();
-        }
-        return ImmutableList.copyOf(bundles);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogLocationItemDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogLocationItemDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogLocationItemDto.java
deleted file mode 100644
index da185f2..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogLocationItemDto.java
+++ /dev/null
@@ -1,43 +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.catalog.internal;
-
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.location.LocationSpec;
-
-
-public class CatalogLocationItemDto extends CatalogItemDtoAbstract<Location,LocationSpec<?>> {
-    
-    @Override
-    public CatalogItemType getCatalogItemType() {
-        return CatalogItemType.LOCATION;
-    }
-
-    @Override
-    public Class<Location> getCatalogItemJavaType() {
-        return Location.class;
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    @Override
-    public Class<LocationSpec<?>> getSpecType() {
-        return (Class)LocationSpec.class;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogPolicyItemDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogPolicyItemDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogPolicyItemDto.java
deleted file mode 100644
index 0475876..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogPolicyItemDto.java
+++ /dev/null
@@ -1,43 +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.catalog.internal;
-
-import org.apache.brooklyn.api.policy.Policy;
-import org.apache.brooklyn.api.policy.PolicySpec;
-
-
-public class CatalogPolicyItemDto extends CatalogItemDtoAbstract<Policy,PolicySpec<?>> {
-    
-    @Override
-    public CatalogItemType getCatalogItemType() {
-        return CatalogItemType.POLICY;
-    }
-
-    @Override
-    public Class<Policy> getCatalogItemJavaType() {
-        return Policy.class;
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    @Override
-    public Class<PolicySpec<?>> getSpecType() {
-        return (Class)PolicySpec.class;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogTemplateItemDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogTemplateItemDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogTemplateItemDto.java
deleted file mode 100644
index 1003a4c..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogTemplateItemDto.java
+++ /dev/null
@@ -1,42 +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.catalog.internal;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.proxying.EntitySpec;
-
-public class CatalogTemplateItemDto extends CatalogItemDtoAbstract<Application,EntitySpec<? extends Application>> {
-
-    @Override
-    public CatalogItemType getCatalogItemType() {
-        return CatalogItemType.TEMPLATE;
-    }
-
-    @Override
-    public Class<Application> getCatalogItemJavaType() {
-        return Application.class;
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    @Override
-    public Class<EntitySpec<? extends Application>> getSpecType() {
-        return (Class)EntitySpec.class;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
deleted file mode 100644
index 302e17c..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
+++ /dev/null
@@ -1,254 +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.catalog.internal;
-
-import java.util.Collection;
-
-import javax.annotation.Nullable;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.basic.BrooklynObjectInternal;
-
-import org.apache.brooklyn.api.basic.BrooklynObject;
-import org.apache.brooklyn.api.catalog.BrooklynCatalog;
-import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
-import org.apache.brooklyn.core.management.classloading.BrooklynClassLoadingContextSequential;
-import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
-import org.apache.brooklyn.core.management.classloading.OsgiBrooklynClassLoadingContext;
-import org.apache.brooklyn.core.management.ha.OsgiManager;
-import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
-
-import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
-import brooklyn.config.BrooklynLogging;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.entity.rebind.RebindManagerImpl.RebindTracker;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.text.Strings;
-import brooklyn.util.time.Time;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Joiner;
-import com.google.common.base.Stopwatch;
-
-public class CatalogUtils {
-    private static final Logger log = LoggerFactory.getLogger(CatalogUtils.class);
-
-    public static final char VERSION_DELIMITER = ':';
-
-    public static BrooklynClassLoadingContext newClassLoadingContext(ManagementContext mgmt, CatalogItem<?, ?> item) {
-        // TODO getLibraries() should never be null but sometimes it is still
-        // e.g. run CatalogResourceTest without the above check
-        if (item.getLibraries() == null) {
-            log.debug("CatalogItemDtoAbstract.getLibraries() is null.", new Exception("Trace for null CatalogItemDtoAbstract.getLibraries()"));
-        }
-        return newClassLoadingContext(mgmt, item.getId(), item.getLibraries());
-    }
-    
-    public static BrooklynClassLoadingContext getClassLoadingContext(Entity entity) {
-        ManagementContext mgmt = ((EntityInternal)entity).getManagementContext();
-        String catId = entity.getCatalogItemId();
-        if (Strings.isBlank(catId)) return JavaBrooklynClassLoadingContext.create(mgmt);
-        CatalogItem<?, ?> cat = getCatalogItemOptionalVersion(mgmt, catId);
-        if (cat==null) {
-            log.warn("Cannot load "+catId+" to get classloader for "+entity+"; will try with standard loader, but might fail subsequently");
-            return JavaBrooklynClassLoadingContext.create(mgmt);
-        }
-        return newClassLoadingContext(mgmt, cat);
-    }
-
-    public static BrooklynClassLoadingContext newClassLoadingContext(@Nullable ManagementContext mgmt, String catalogItemId, Collection<CatalogBundle> libraries) {
-        BrooklynClassLoadingContextSequential result = new BrooklynClassLoadingContextSequential(mgmt);
-
-        if (libraries!=null && !libraries.isEmpty()) {
-            result.add(new OsgiBrooklynClassLoadingContext(mgmt, catalogItemId, libraries));
-        }
-
-        BrooklynClassLoadingContext loader = BrooklynLoaderTracker.getLoader();
-        if (loader != null) {
-            result.add(loader);
-        }
-
-        result.addSecondary(JavaBrooklynClassLoadingContext.create(mgmt));
-        return result;
-    }
-
-    /**
-     * @deprecated since 0.7.0 only for legacy catalog items which provide a non-osgi loader; see {@link #newDefault(ManagementContext)}
-     */ @Deprecated
-    public static BrooklynClassLoadingContext newClassLoadingContext(@Nullable ManagementContext mgmt, String catalogItemId, Collection<CatalogBundle> libraries, ClassLoader customClassLoader) {
-        BrooklynClassLoadingContextSequential result = new BrooklynClassLoadingContextSequential(mgmt);
-
-        if (libraries!=null && !libraries.isEmpty()) {
-            result.add(new OsgiBrooklynClassLoadingContext(mgmt, catalogItemId, libraries));
-        }
-
-        BrooklynClassLoadingContext loader = BrooklynLoaderTracker.getLoader();
-        if (loader != null) {
-            result.add(loader);
-        }
-
-        result.addSecondary(JavaBrooklynClassLoadingContext.create(mgmt, customClassLoader));
-        return result;
-    }
-
-    /**
-     * Registers all bundles with the management context's OSGi framework.
-     */
-    public static void installLibraries(ManagementContext managementContext, @Nullable Collection<CatalogBundle> libraries) {
-        if (libraries == null) return;
-
-        ManagementContextInternal mgmt = (ManagementContextInternal) managementContext;
-        if (!libraries.isEmpty()) {
-            Maybe<OsgiManager> osgi = mgmt.getOsgiManager();
-            if (osgi.isAbsent()) {
-                throw new IllegalStateException("Unable to load bundles "+libraries+" because OSGi is not running.");
-            }
-            if (log.isDebugEnabled()) 
-                logDebugOrTraceIfRebinding(log, 
-                    "Loading bundles in {}: {}", 
-                    new Object[] {managementContext, Joiner.on(", ").join(libraries)});
-            Stopwatch timer = Stopwatch.createStarted();
-            for (CatalogBundle bundleUrl : libraries) {
-                osgi.get().registerBundle(bundleUrl);
-            }
-            if (log.isDebugEnabled()) 
-                logDebugOrTraceIfRebinding(log, 
-                    "Registered {} bundles in {}",
-                    new Object[]{libraries.size(), Time.makeTimeStringRounded(timer)});
-        }
-    }
-
-    /** Scans the given {@link BrooklynClassLoadingContext} to detect what catalog item id is in effect. */
-    public static String getCatalogItemIdFromLoader(BrooklynClassLoadingContext loader) {
-        if (loader instanceof OsgiBrooklynClassLoadingContext) {
-            return ((OsgiBrooklynClassLoadingContext)loader).getCatalogItemId();
-        } else {
-            return null;
-        }
-    }
-
-    public static void setCatalogItemIdOnAddition(Entity entity, BrooklynObject itemBeingAdded) {
-        if (entity.getCatalogItemId()!=null) {
-            if (itemBeingAdded.getCatalogItemId()==null) {
-                if (log.isDebugEnabled())
-                    BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(entity),
-                        "Catalog item addition: "+entity+" from "+entity.getCatalogItemId()+" applying its catalog item ID to "+itemBeingAdded);
-                ((BrooklynObjectInternal)itemBeingAdded).setCatalogItemId(entity.getCatalogItemId());
-            } else {
-                if (!itemBeingAdded.getCatalogItemId().equals(entity.getCatalogItemId())) {
-                    // not a problem, but something to watch out for
-                    log.debug("Cross-catalog item detected: "+entity+" from "+entity.getCatalogItemId()+" has "+itemBeingAdded+" from "+itemBeingAdded.getCatalogItemId());
-                }
-            }
-        } else if (itemBeingAdded.getCatalogItemId()!=null) {
-            if (log.isDebugEnabled())
-                BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(entity),
-                    "Catalog item addition: "+entity+" without catalog item ID has "+itemBeingAdded+" from "+itemBeingAdded.getCatalogItemId());
-        }
-    }
-
-    @Beta
-    public static void logDebugOrTraceIfRebinding(Logger log, String message, Object ...args) {
-        if (RebindTracker.isRebinding())
-            log.trace(message, args);
-        else
-            log.debug(message, args);
-    }
-
-    public static boolean looksLikeVersionedId(String versionedId) {
-        if (versionedId==null) return false;
-        int fi = versionedId.indexOf(VERSION_DELIMITER);
-        if (fi<0) return false;
-        int li = versionedId.lastIndexOf(VERSION_DELIMITER);
-        if (li!=fi) {
-            // if multiple colons, we say it isn't a versioned reference; the prefix in that case must understand any embedded versioning scheme
-            // this fixes the case of:  http://localhost:8080
-            return false;
-        }
-        String candidateVersion = versionedId.substring(li+1);
-        if (!candidateVersion.matches("[0-9]+(|(\\.|_).*)")) {
-            // version must start with a number, followed if by anything with full stop or underscore before any other characters
-            // e.g.  foo:1  or foo:1.1  or foo:1_SNAPSHOT all supported, but not e.g. foo:bar (or chef:cookbook or docker:my/image)
-            return false;
-        }
-        return true;
-    }
-
-    public static String getIdFromVersionedId(String versionedId) {
-        if (versionedId == null) return null;
-        int versionDelimiterPos = versionedId.lastIndexOf(VERSION_DELIMITER);
-        if (versionDelimiterPos != -1) {
-            return versionedId.substring(0, versionDelimiterPos);
-        } else {
-            return null;
-        }
-    }
-
-    public static String getVersionFromVersionedId(String versionedId) {
-        if (versionedId == null) return null;
-        int versionDelimiterPos = versionedId.lastIndexOf(VERSION_DELIMITER);
-        if (versionDelimiterPos != -1) {
-            return versionedId.substring(versionDelimiterPos+1);
-        } else {
-            return null;
-        }
-    }
-
-    public static String getVersionedId(String id, String version) {
-        // TODO null checks
-        return id + VERSION_DELIMITER + version;
-    }
-
-    //TODO Don't really like this, but it's better to have it here than on the interface to keep the API's 
-    //surface minimal. Could instead have the interface methods accept VerionedId object and have the helpers
-    //construct it as needed.
-    public static CatalogItem<?, ?> getCatalogItemOptionalVersion(ManagementContext mgmt, String versionedId) {
-        if (versionedId == null) return null;
-        if (looksLikeVersionedId(versionedId)) {
-            String id = getIdFromVersionedId(versionedId);
-            String version = getVersionFromVersionedId(versionedId);
-            return mgmt.getCatalog().getCatalogItem(id, version);
-        } else {
-            return mgmt.getCatalog().getCatalogItem(versionedId, BrooklynCatalog.DEFAULT_VERSION);
-        }
-    }
-
-    public static boolean isBestVersion(ManagementContext mgmt, CatalogItem<?,?> item) {
-        CatalogItem<?, ?> bestVersion = getCatalogItemOptionalVersion(mgmt, item.getSymbolicName());
-        if (bestVersion==null) return false;
-        return (bestVersion.getVersion().equals(item.getVersion()));
-    }
-
-    public static <T,SpecT> CatalogItem<T, SpecT> getCatalogItemOptionalVersion(ManagementContext mgmt, Class<T> type, String versionedId) {
-        if (looksLikeVersionedId(versionedId)) {
-            String id = getIdFromVersionedId(versionedId);
-            String version = getVersionFromVersionedId(versionedId);
-            return mgmt.getCatalog().getCatalogItem(type, id, version);
-        } else {
-            return mgmt.getCatalog().getCatalogItem(type, versionedId, BrooklynCatalog.DEFAULT_VERSION);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java b/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
deleted file mode 100644
index dbfd3fc..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.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.catalog.internal;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import brooklyn.basic.AbstractBrooklynObject;
-import brooklyn.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
-import brooklyn.util.xstream.EnumCaseForgivingSingleValueConverter;
-import brooklyn.util.xstream.XmlSerializer;
-
-public class CatalogXmlSerializer extends XmlSerializer<Object> {
-
-    @SuppressWarnings("deprecation")
-    public CatalogXmlSerializer() {
-        xstream.addDefaultImplementation(ArrayList.class, Collection.class);
-        
-        xstream.aliasType("list", List.class);
-        xstream.aliasType("map", Map.class);
-
-        xstream.useAttributeFor("id", String.class);
-
-        xstream.aliasType("catalog", CatalogDto.class);
-        xstream.useAttributeFor(CatalogDto.class, "url");
-        xstream.addImplicitCollection(CatalogDto.class, "catalogs", CatalogDto.class);
-        xstream.addImplicitCollection(CatalogDto.class, "entries", CatalogTemplateItemDto.class);
-        xstream.addImplicitCollection(CatalogDto.class, "entries", CatalogEntityItemDto.class);
-        xstream.addImplicitCollection(CatalogDto.class, "entries", CatalogPolicyItemDto.class);
-        xstream.addImplicitCollection(CatalogDto.class, "entries", CatalogLocationItemDto.class);
-
-        xstream.aliasType("template", CatalogTemplateItemDto.class);
-        xstream.aliasType("entity", CatalogEntityItemDto.class);
-        xstream.aliasType("policy", CatalogPolicyItemDto.class);
-        xstream.aliasType("location", CatalogPolicyItemDto.class);
-
-        xstream.aliasField("registeredType", CatalogItemDtoAbstract.class, "symbolicName");
-        xstream.aliasAttribute(CatalogItemDtoAbstract.class, "displayName", "name");
-        xstream.useAttributeFor(CatalogItemDtoAbstract.class, "type");
-        xstream.useAttributeFor(CatalogItemDtoAbstract.class, "version");
-        xstream.aliasType("bundle", CatalogBundleDto.class);
-        xstream.registerConverter(new CatalogBundleConverter(xstream.getMapper(), xstream.getReflectionProvider()));
-
-        xstream.useAttributeFor(CatalogClasspathDto.class, "scan");
-        xstream.addImplicitCollection(CatalogClasspathDto.class, "entries", "entry", String.class);
-        xstream.registerConverter(new EnumCaseForgivingSingleValueConverter(CatalogScanningModes.class));
-
-        // Note: the management context is being omitted because it is unnecessary for
-        // representations of catalogues generated with this serializer.
-        xstream.omitField(AbstractBrooklynObject.class, "managementContext");
-        xstream.omitField(AbstractBrooklynObject.class, "_legacyConstruction");
-        xstream.omitField(AbstractBrooklynObject.class, "hasWarnedOfNoManagementContextWhenPersistRequested");
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/config/BrooklynServerConfig.java b/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
index 2eae73e..e884bdd 100644
--- a/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
+++ b/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
@@ -26,10 +26,10 @@ import java.net.URI;
 import java.util.Map;
 
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.os.Os;
@@ -103,7 +103,7 @@ public class BrooklynServerConfig {
     /** @deprecated since 0.7.0 replaced by {@link CatalogInitialization}; also note, default removed 
      * (it was overridden anyway, and in almost all cases the new behaviour is still the default behaviour) */
     @Deprecated
-    public static final ConfigKey<brooklyn.catalog.CatalogLoadMode> CATALOG_LOAD_MODE = ConfigKeys.newConfigKey(brooklyn.catalog.CatalogLoadMode.class,
+    public static final ConfigKey<org.apache.brooklyn.core.catalog.CatalogLoadMode> CATALOG_LOAD_MODE = ConfigKeys.newConfigKey(org.apache.brooklyn.core.catalog.CatalogLoadMode.class,
             "brooklyn.catalog.mode",
             "The mode the management context should use to load the catalog when first starting");
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 2860b82..1d21a7e 100644
--- a/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
+++ b/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
@@ -53,6 +53,7 @@ import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.EntityAdjunct;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.internal.BrooklynInitialization;
 import org.apache.brooklyn.core.management.internal.EffectorUtils;
@@ -64,7 +65,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.basic.AbstractBrooklynObject;
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.BrooklynLogging;
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 0d4d92c..52e87a6 100644
--- a/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
@@ -20,10 +20,10 @@ 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.catalog.internal.CatalogItemDtoAbstract;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.flags.FlagUtils;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/entity/rebind/RebindContextLookupContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindContextLookupContext.java b/core/src/main/java/brooklyn/entity/rebind/RebindContextLookupContext.java
index 81b1321..e132a51 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindContextLookupContext.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindContextLookupContext.java
@@ -34,8 +34,7 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.mementos.BrooklynMementoPersister.LookupContext;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
-
-import brooklyn.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 
 /** Looks in {@link RebindContext} <i>and</i> {@link ManagementContext} to find entities, locations, etc. */
 public class RebindContextLookupContext implements LookupContext {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 8c6a402..1164fff 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
@@ -64,6 +64,8 @@ import org.apache.brooklyn.api.mementos.TreeNode;
 import org.apache.brooklyn.api.mementos.BrooklynMementoManifest.EntityMementoManifest;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.internal.BrooklynObjectManagementMode;
 import org.apache.brooklyn.core.management.internal.BrooklynObjectManagerInternal;
@@ -72,8 +74,6 @@ 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 brooklyn.catalog.internal.CatalogInitialization;
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.BrooklynLogging;
 import brooklyn.config.BrooklynLogging.LoggingLevel;
 import brooklyn.enricher.basic.AbstractEnricher;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java b/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
index b2a3068..e46a6af 100644
--- a/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
+++ b/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
@@ -26,9 +26,8 @@ import java.util.Map;
 import org.codehaus.jackson.annotate.JsonAutoDetect;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.mementos.CatalogItemMemento;
-
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
-import brooklyn.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 
 import com.google.common.base.Joiner;
 import com.google.common.base.Objects;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 3525d84..dbe6e26 100644
--- a/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
+++ b/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
@@ -49,8 +49,8 @@ import org.apache.brooklyn.api.mementos.PolicyMemento;
 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 brooklyn.catalog.internal.CatalogItemDo;
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.basic.EntityDynamicType;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 be51b44..b5ca9c1 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
@@ -48,9 +48,9 @@ import org.apache.brooklyn.api.mementos.BrooklynMementoPersister;
 import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
 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 brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.ConfigKey;
 import brooklyn.config.StringConfigMap;
 import brooklyn.entity.basic.ConfigKeys;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java b/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java
index 7a7481c..4cf6310 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java
@@ -23,8 +23,7 @@ import java.util.Collection;
 
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemLibraries;
-
-import brooklyn.catalog.internal.CatalogBundleDto;
+import org.apache.brooklyn.core.catalog.internal.CatalogBundleDto;
 
 import com.thoughtworks.xstream.converters.Converter;
 import com.thoughtworks.xstream.converters.MarshallingContext;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 fe5a677..99ee2e5 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
@@ -42,12 +42,12 @@ import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingConte
 import org.apache.brooklyn.api.mementos.BrooklynMementoPersister.LookupContext;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.catalog.internal.CatalogBundleDto;
+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 brooklyn.catalog.internal.CatalogBundleDto;
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.BasicParameterType;
 import brooklyn.entity.effector.EffectorAndBody;
 import brooklyn.entity.effector.EffectorTasks.EffectorBodyTaskFactory;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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
index 9b23b7a..338b223 100644
--- a/core/src/main/java/brooklyn/util/ResourceUtils.java
+++ b/core/src/main/java/brooklyn/util/ResourceUtils.java
@@ -40,6 +40,8 @@ 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;
@@ -51,10 +53,6 @@ import org.apache.http.client.methods.HttpGet;
 import org.apache.http.util.EntityUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
-import brooklyn.catalog.internal.CatalogUtils;
-
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
 import brooklyn.util.collections.MutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogLoadMode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogLoadMode.java b/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogLoadMode.java
new file mode 100644
index 0000000..8e9d6d5
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogLoadMode.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.catalog;
+
+import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.rebind.persister.PersistMode;
+
+/** @deprecated since 0.7.0 replaced by {@link CatalogInitialization} */
+@Deprecated
+public enum CatalogLoadMode {
+    /**
+     * The server will load its initial catalog from the URL configured in
+     * {@link brooklyn.config.BrooklynServerConfig#BROOKLYN_CATALOG_URL} and will
+     * disregard existing persisted state.
+     */
+    LOAD_BROOKLYN_CATALOG_URL,
+
+    /**
+     * The server will load its initial catalog from previously persisted state,
+     * and will behave as {@link #LOAD_BROOKLYN_CATALOG_URL} if no state exists.
+     */
+    LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE,
+
+    /**
+     * The server will load its initial catalog from previously persisted state.
+     * The catalog will be empty if no previous state exists.
+     */
+    LOAD_PERSISTED_STATE;
+
+    /**
+     * @return A catalog load mode suitable for the given persistence mode:
+     * <ul>
+     * <li>disabled: {@link #LOAD_BROOKLYN_CATALOG_URL}</li>
+     * <li>rebind: {@link #LOAD_PERSISTED_STATE}</li>
+     * <li>auto or clean: {@link #LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE}</li>
+     * </ul>
+     */
+    public static CatalogLoadMode forPersistMode(PersistMode m) {
+        // Clean case relies on the persistence directory being cleaned and rebind manager
+        // believing the store to be empty.
+        switch (m) {
+        case DISABLED:
+            return LOAD_BROOKLYN_CATALOG_URL;
+        case AUTO:
+        case CLEAN:
+            return LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE;
+        case REBIND:
+            return LOAD_PERSISTED_STATE;
+        default:
+            LoggerFactory.getLogger(CatalogLoadMode.class)
+                    .warn("Unhandled persistence mode {}. Catalog defaulting to {}", m.name(), LOAD_BROOKLYN_CATALOG_URL.name());
+            return LOAD_BROOKLYN_CATALOG_URL;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogPredicates.java b/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogPredicates.java
new file mode 100644
index 0000000..654fe56
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogPredicates.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
+import org.apache.brooklyn.api.entity.Application;
+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.api.location.LocationSpec;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+
+public class CatalogPredicates {
+
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> isCatalogItemType(final CatalogItemType ciType) {
+        return new Predicate<CatalogItem<T,SpecT>>() {
+            @Override
+            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
+                return (item != null) && item.getCatalogItemType()==ciType;
+            }
+        };
+    }
+
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> deprecated(final boolean deprecated) {
+        return new Predicate<CatalogItem<T,SpecT>>() {
+            @Override
+            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
+                return (item != null) && item.isDeprecated() == deprecated;
+            }
+        };
+    }
+
+    public static final Predicate<CatalogItem<Application,EntitySpec<? extends Application>>> IS_TEMPLATE = 
+            CatalogPredicates.<Application,EntitySpec<? extends Application>>isCatalogItemType(CatalogItemType.TEMPLATE);
+    public static final Predicate<CatalogItem<Entity,EntitySpec<?>>> IS_ENTITY = 
+            CatalogPredicates.<Entity,EntitySpec<?>>isCatalogItemType(CatalogItemType.ENTITY);
+    public static final Predicate<CatalogItem<Policy,PolicySpec<?>>> IS_POLICY = 
+            CatalogPredicates.<Policy,PolicySpec<?>>isCatalogItemType(CatalogItemType.POLICY);
+    public static final Predicate<CatalogItem<Location,LocationSpec<?>>> IS_LOCATION = 
+            CatalogPredicates.<Location,LocationSpec<?>>isCatalogItemType(CatalogItemType.LOCATION);
+    
+    public static final Function<CatalogItem<?,?>,String> ID_OF_ITEM_TRANSFORMER = new Function<CatalogItem<?,?>, String>() {
+        @Override @Nullable
+        public String apply(@Nullable CatalogItem<?,?> input) {
+            if (input==null) return null;
+            return input.getId();
+        }
+    };
+
+    /** @deprecated since 0.7.0 use {@link #displayName(Predicate)} */
+    @Deprecated
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> name(final Predicate<? super String> filter) {
+        return displayName(filter);
+    }
+
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> displayName(final Predicate<? super String> filter) {
+        return new Predicate<CatalogItem<T,SpecT>>() {
+            @Override
+            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
+                return (item != null) && filter.apply(item.getDisplayName());
+            }
+        };
+    }
+
+    @Deprecated
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> registeredTypeName(final Predicate<? super String> filter) {
+        return symbolicName(filter);
+    }
+
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> symbolicName(final Predicate<? super String> filter) {
+        return new Predicate<CatalogItem<T,SpecT>>() {
+            @Override
+            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
+                return (item != null) && filter.apply(item.getSymbolicName());
+            }
+        };
+    }
+
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> javaType(final Predicate<? super String> filter) {
+        return new Predicate<CatalogItem<T,SpecT>>() {
+            @Override
+            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
+                return (item != null) && filter.apply(item.getJavaType());
+            }
+        };
+    }
+
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> xml(final Predicate<? super String> filter) {
+        return new Predicate<CatalogItem<T,SpecT>>() {
+            @Override
+            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
+                return (item != null) && filter.apply(item.toXmlString());
+            }
+        };
+    }
+
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> entitledToSee(final ManagementContext mgmt) {
+        return new Predicate<CatalogItem<T,SpecT>>() {
+            @Override
+            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
+                return (item != null) && 
+                    Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, item.getCatalogItemId());
+            }
+        };
+    }
+ 
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> isBestVersion(final ManagementContext mgmt) {
+        return new Predicate<CatalogItem<T,SpecT>>() {
+            @Override
+            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
+                return CatalogUtils.isBestVersion(mgmt, item);
+            }
+        };
+    }
+
+}
\ No newline at end of file


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/ha/OsgiManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/OsgiManager.java b/core/src/main/java/brooklyn/management/ha/OsgiManager.java
deleted file mode 100644
index fd98ed2..0000000
--- a/core/src/main/java/brooklyn/management/ha/OsgiManager.java
+++ /dev/null
@@ -1,312 +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.management.ha;
-
-import java.io.File;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.launch.Framework;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.BrooklynVersion;
-
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
-import org.apache.brooklyn.api.management.ManagementContext;
-
-import brooklyn.config.BrooklynServerConfig;
-import brooklyn.config.BrooklynServerPaths;
-import brooklyn.config.ConfigKey;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.collections.MutableSet;
-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;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
-public class OsgiManager {
-
-    private static final Logger log = LoggerFactory.getLogger(OsgiManager.class);
-    
-    public static final ConfigKey<Boolean> USE_OSGI = BrooklynServerConfig.USE_OSGI;
-    
-    /* see Osgis for info on starting framework etc */
-    
-    protected ManagementContext mgmt;
-    protected Framework framework;
-    protected File osgiCacheDir;
-
-    public OsgiManager(ManagementContext mgmt) {
-        this.mgmt = mgmt;
-    }
-
-    public void start() {
-        try {
-            osgiCacheDir = BrooklynServerPaths.getOsgiCacheDirCleanedIfNeeded(mgmt);
-            
-            // any extra OSGi startup args could go here
-            framework = Osgis.newFrameworkStarted(osgiCacheDir.getAbsolutePath(), false, MutableMap.of());
-            
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    public void stop() {
-        try {
-            if (framework!=null) {
-                framework.stop();
-                framework.waitForStop(0); // 0 means indefinite
-            }
-        } catch (BundleException e) {
-            throw Exceptions.propagate(e);
-        } catch (InterruptedException e) {
-            throw Exceptions.propagate(e);
-        }
-        if (BrooklynServerPaths.isOsgiCacheForCleaning(mgmt, osgiCacheDir)) {
-            // See exception reported in https://issues.apache.org/jira/browse/BROOKLYN-72
-            // We almost always fail to delete he OSGi temp directory due to a concurrent modification.
-            // Therefore keep trying.
-            final AtomicReference<DeletionResult> deletionResult = new AtomicReference<DeletionResult>();
-            Repeater.create("Delete OSGi cache dir")
-                    .until(new Callable<Boolean>() {
-                        public Boolean call() {
-                            deletionResult.set(Os.deleteRecursively(osgiCacheDir));
-                            return deletionResult.get().wasSuccessful();
-                        }})
-                    .limitTimeTo(Duration.ONE_SECOND)
-                    .backoffTo(Duration.millis(50))
-                    .run();
-            if (deletionResult.get().getThrowable()!=null) {
-                log.debug("Unable to delete "+osgiCacheDir+" (possibly being modified concurrently?): "+deletionResult.get().getThrowable());
-            }
-        }
-        osgiCacheDir = null;
-        framework = null;
-    }
-
-    public synchronized void registerBundle(CatalogBundle bundle) {
-        try {
-            if (checkBundleInstalledThrowIfInconsistent(bundle)) {
-                return;
-            }
-
-            Bundle b = Osgis.install(framework, bundle.getUrl());
-
-            checkCorrectlyInstalled(bundle, b);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            throw new IllegalStateException("Bundle from "+bundle.getUrl()+" failed to install: " + e.getMessage(), e);
-        }
-    }
-
-    private void checkCorrectlyInstalled(CatalogBundle bundle, Bundle b) {
-        String nv = b.getSymbolicName()+":"+b.getVersion().toString();
-
-        if (!isBundleNameEqualOrAbsent(bundle, b)) {
-            throw new IllegalStateException("Bundle already installed as "+nv+" but user explicitly requested "+bundle);
-        }
-
-        List<Bundle> matches = Osgis.bundleFinder(framework)
-                .symbolicName(b.getSymbolicName())
-                .version(b.getVersion().toString())
-                .findAll();
-        if (matches.isEmpty()) {
-            log.error("OSGi could not find bundle "+nv+" in search after installing it from "+bundle.getUrl());
-        } else if (matches.size()==1) {
-            log.debug("Bundle from "+bundle.getUrl()+" successfully installed as " + nv + " ("+b+")");
-        } else {
-            log.warn("OSGi has multiple bundles matching "+nv+", when just installed from "+bundle.getUrl()+": "+matches+"; "
-                + "brooklyn will prefer the URL-based bundle for top-level references but any dependencies or "
-                + "import-packages will be at the mercy of OSGi. "
-                + "It is recommended to use distinct versions for different bundles, and the same URL for the same bundles.");
-        }
-    }
-
-    private boolean checkBundleInstalledThrowIfInconsistent(CatalogBundle bundle) {
-        String bundleUrl = bundle.getUrl();
-        if (bundleUrl != null) {
-            Maybe<Bundle> installedBundle = Osgis.bundleFinder(framework).requiringFromUrl(bundleUrl).find();
-            if (installedBundle.isPresent()) {
-                Bundle b = installedBundle.get();
-                String nv = b.getSymbolicName()+":"+b.getVersion().toString();
-                if (!isBundleNameEqualOrAbsent(bundle, b)) {
-                    throw new IllegalStateException("User requested bundle " + bundle + " but already installed as "+nv);
-                } else {
-                    log.trace("Bundle from "+bundleUrl+" already installed as "+nv+"; not re-registering");
-                }
-                return true;
-            }
-        } else {
-            Maybe<Bundle> installedBundle = Osgis.bundleFinder(framework).symbolicName(bundle.getSymbolicName()).version(bundle.getVersion()).find();
-            if (installedBundle.isPresent()) {
-                log.trace("Bundle "+bundle+" installed from "+installedBundle.get().getLocation());
-            } else {
-                throw new IllegalStateException("Bundle "+bundle+" not previously registered, but URL is empty.");
-            }
-            return true;
-        }
-        return false;
-    }
-
-    public static boolean isBundleNameEqualOrAbsent(CatalogBundle bundle, Bundle b) {
-        return !bundle.isNamed() ||
-                (bundle.getSymbolicName().equals(b.getSymbolicName()) &&
-                bundle.getVersion().equals(b.getVersion().toString()));
-    }
-
-    public <T> Maybe<Class<T>> tryResolveClass(String type, CatalogBundle... catalogBundles) {
-        return tryResolveClass(type, Arrays.asList(catalogBundles));
-    }
-    public <T> Maybe<Class<T>> tryResolveClass(String type, Iterable<CatalogBundle> catalogBundles) {
-        Map<CatalogBundle,Throwable> bundleProblems = MutableMap.of();
-        Set<String> extraMessages = MutableSet.of();
-        for (CatalogBundle catalogBundle: catalogBundles) {
-            try {
-                Maybe<Bundle> bundle = findBundle(catalogBundle);
-                if (bundle.isPresent()) {
-                    Bundle b = bundle.get();
-                    Class<T> clazz;
-                    //Extension bundles don't support loadClass.
-                    //Instead load from the app classpath.
-                    if (Osgis.isExtensionBundle(b)) {
-                        @SuppressWarnings("unchecked")
-                        Class<T> c = (Class<T>)Class.forName(type);
-                        clazz = c;
-                    } else {
-                        @SuppressWarnings("unchecked")
-                        Class<T> c = (Class<T>)b.loadClass(type);
-                        clazz = c;
-                    }
-                    return Maybe.of(clazz);
-                } else {
-                    bundleProblems.put(catalogBundle, ((Maybe.Absent<?>)bundle).getException());
-                }
-                
-            } catch (Exception e) {
-                // should come from classloading now; name formatting or missing bundle errors will be caught above 
-                Exceptions.propagateIfFatal(e);
-                bundleProblems.put(catalogBundle, e);
-
-                Throwable cause = e.getCause();
-                if (cause != null && cause.getMessage().contains("Unresolved constraint in bundle")) {
-                    if (BrooklynVersion.INSTANCE.getVersionFromOsgiManifest()==null) {
-                        extraMessages.add("No brooklyn-core OSGi manifest available. OSGi will not work.");
-                    }
-                    if (BrooklynVersion.isDevelopmentEnvironment()) {
-                        extraMessages.add("Your development environment may not have created necessary files. Doing a maven build then retrying may fix the issue.");
-                    }
-                    if (!extraMessages.isEmpty()) log.warn(Strings.join(extraMessages, " "));
-                    log.warn("Unresolved constraint resolving OSGi bundle "+catalogBundle+" to load "+type+": "+cause.getMessage());
-                    if (log.isDebugEnabled()) log.debug("Trace for OSGi resolution failure", e);
-                }
-            }
-        }
-        if (bundleProblems.size()==1) {
-            Throwable error = Iterables.getOnlyElement(bundleProblems.values());
-            if (error instanceof ClassNotFoundException && error.getCause()!=null && error.getCause().getMessage()!=null) {
-                error = Exceptions.collapseIncludingAllCausalMessages(error);
-            }
-            return Maybe.absent("Unable to resolve class "+type+" in "+Iterables.getOnlyElement(bundleProblems.keySet())
-                + (extraMessages.isEmpty() ? "" : " ("+Strings.join(extraMessages, " ")+")"), error);
-        } else {
-            return Maybe.absent(Exceptions.create("Unable to resolve class "+type+": "+bundleProblems
-                + (extraMessages.isEmpty() ? "" : " ("+Strings.join(extraMessages, " ")+")"), bundleProblems.values()));
-        }
-    }
-
-    public Maybe<Bundle> findBundle(CatalogBundle catalogBundle) {
-        //Either fail at install time when the user supplied name:version is different
-        //from the one reported from the bundle
-        //or
-        //Ignore user supplied name:version when URL is supplied to be able to find the
-        //bundle even if it's with a different version.
-        //
-        //For now we just log a warning if there's a version discrepancy at install time,
-        //so prefer URL if supplied.
-        BundleFinder bundleFinder = Osgis.bundleFinder(framework);
-        if (catalogBundle.getUrl() != null) {
-            bundleFinder.requiringFromUrl(catalogBundle.getUrl());
-        } else {
-            bundleFinder.symbolicName(catalogBundle.getSymbolicName()).version(catalogBundle.getVersion());
-        }
-        return bundleFinder.find();
-    }
-
-    /**
-     * Iterates through catalogBundles until one contains a resource with the given name.
-     */
-    public URL getResource(String name, Iterable<CatalogBundle> catalogBundles) {
-        for (CatalogBundle catalogBundle: catalogBundles) {
-            try {
-                Maybe<Bundle> bundle = findBundle(catalogBundle);
-                if (bundle.isPresent()) {
-                    URL result = bundle.get().getResource(name);
-                    if (result!=null) return result;
-                }
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @return An iterable of all resources matching name in catalogBundles.
-     */
-    public Iterable<URL> getResources(String name, Iterable<CatalogBundle> catalogBundles) {
-        List<URL> resources = Lists.newArrayList();
-        for (CatalogBundle catalogBundle : catalogBundles) {
-            try {
-                Maybe<Bundle> bundle = findBundle(catalogBundle);
-                if (bundle.isPresent()) {
-                    Enumeration<URL> result = bundle.get().getResources(name);
-                    resources.addAll(Collections.list(result));
-                }
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-            }
-        }
-        return resources;
-    }
-
-    public Framework getFramework() {
-        return framework;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
deleted file mode 100644
index 5e7f4a6..0000000
--- a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
+++ /dev/null
@@ -1,485 +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.management.internal;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static java.lang.String.format;
-
-import java.net.URI;
-import java.net.URL;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.annotation.Nullable;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.api.basic.BrooklynObject;
-import org.apache.brooklyn.api.catalog.BrooklynCatalog;
-import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.api.entity.Effector;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.drivers.EntityDriverManager;
-import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolverManager;
-import org.apache.brooklyn.api.entity.rebind.RebindManager;
-import org.apache.brooklyn.api.location.LocationRegistry;
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.SubscriptionContext;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
-import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
-import org.apache.brooklyn.api.management.ha.HighAvailabilityManager;
-
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
-import brooklyn.catalog.internal.CatalogInitialization;
-import brooklyn.catalog.internal.CatalogUtils;
-import brooklyn.config.BrooklynProperties;
-import brooklyn.config.StringConfigMap;
-import brooklyn.entity.basic.AbstractEntity;
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.entity.drivers.BasicEntityDriverManager;
-import brooklyn.entity.drivers.downloads.BasicDownloadsManager;
-import brooklyn.entity.rebind.RebindManagerImpl;
-import brooklyn.internal.storage.BrooklynStorage;
-import brooklyn.internal.storage.DataGrid;
-import brooklyn.internal.storage.DataGridFactory;
-import brooklyn.internal.storage.impl.BrooklynStorageImpl;
-import brooklyn.internal.storage.impl.inmemory.InMemoryDataGridFactory;
-
-import org.apache.brooklyn.location.basic.BasicLocationRegistry;
-
-import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.ha.HighAvailabilityManagerImpl;
-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;
-import com.google.common.base.Preconditions;
-
-public abstract class AbstractManagementContext implements ManagementContextInternal {
-    private static final Logger log = LoggerFactory.getLogger(AbstractManagementContext.class);
-
-    private static DataGridFactory loadDataGridFactory(BrooklynProperties properties) {
-        String clazzName = properties.getFirst(DataGridFactory.class.getName());
-        if(clazzName == null){
-            clazzName = InMemoryDataGridFactory.class.getName();
-        }
-
-        Class<?> clazz;
-        try{
-            //todo: which classloader should we use?
-            clazz = LocalManagementContext.class.getClassLoader().loadClass(clazzName);
-        }catch(ClassNotFoundException e){
-            throw new IllegalStateException(format("Could not load class [%s]",clazzName),e);
-        }
-
-        Object instance;
-        try {
-            instance = clazz.newInstance();
-        } catch (InstantiationException e) {
-            throw new IllegalStateException(format("Could not instantiate class [%s]",clazzName),e);
-        } catch (IllegalAccessException e) {
-            throw new IllegalStateException(format("Could not instantiate class [%s]",clazzName),e);
-        }
-
-        if(!(instance instanceof DataGridFactory)){
-            throw new IllegalStateException(format("Class [%s] not an instantiate of class [%s]",clazzName, DataGridFactory.class.getName()));
-        }
-
-        return (DataGridFactory)instance;
-    }
-
-    static {
-        ResourceUtils.addClassLoaderProvider(new Function<Object, BrooklynClassLoadingContext>() {
-            @Override
-            public BrooklynClassLoadingContext apply(@Nullable Object input) {
-                if (input instanceof EntityInternal) {
-                    EntityInternal internal = (EntityInternal)input;
-                    if (internal.getCatalogItemId() != null) {
-                        CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(internal.getManagementContext(), internal.getCatalogItemId());
-                        if (item != null) {
-                            return CatalogUtils.newClassLoadingContext(internal.getManagementContext(), item);
-                        } else {
-                            log.error("Can't find catalog item " + internal.getCatalogItemId() +
-                                    " used for instantiating entity " + internal +
-                                    ". Falling back to application classpath.");
-                        }
-                    }
-                    return apply(internal.getManagementSupport());
-                }
-                
-                if (input instanceof EntityManagementSupport)
-                    return apply(((EntityManagementSupport)input).getManagementContext());
-                if (input instanceof ManagementContext)
-                    return JavaBrooklynClassLoadingContext.create((ManagementContext) input);
-                return null;
-            }
-        });
-    }
-
-    private final AtomicLong totalEffectorInvocationCount = new AtomicLong();
-
-    protected BrooklynProperties configMap;
-    protected BasicLocationRegistry locationRegistry;
-    protected final BasicBrooklynCatalog catalog;
-    protected ClassLoader baseClassLoader;
-    protected Iterable<URL> baseClassPathForScanning;
-
-    private final RebindManager rebindManager;
-    private final HighAvailabilityManager highAvailabilityManager;
-    
-    protected volatile BrooklynGarbageCollector gc;
-
-    private final EntityDriverManager entityDriverManager;
-    protected DownloadResolverManager downloadsManager;
-
-    protected EntitlementManager entitlementManager;
-
-    private final BrooklynStorage storage;
-
-    private volatile boolean running = true;
-    protected boolean startupComplete = false;
-    protected final List<Throwable> errors = Collections.synchronizedList(MutableList.<Throwable>of()); 
-
-    protected Maybe<URI> uri = Maybe.absent();
-    protected CatalogInitialization catalogInitialization;
-
-    public AbstractManagementContext(BrooklynProperties brooklynProperties){
-        this(brooklynProperties, null);
-    }
-
-    public AbstractManagementContext(BrooklynProperties brooklynProperties, DataGridFactory datagridFactory) {
-        this.configMap = brooklynProperties;
-        this.entityDriverManager = new BasicEntityDriverManager();
-        this.downloadsManager = BasicDownloadsManager.newDefault(configMap);
-        if (datagridFactory == null) {
-            datagridFactory = loadDataGridFactory(brooklynProperties);
-        }
-        DataGrid datagrid = datagridFactory.newDataGrid(this);
-
-        this.catalog = new BasicBrooklynCatalog(this);
-        
-        this.storage = new BrooklynStorageImpl(datagrid);
-        this.rebindManager = new RebindManagerImpl(this); // TODO leaking "this" reference; yuck
-        this.highAvailabilityManager = new HighAvailabilityManagerImpl(this); // TODO leaking "this" reference; yuck
-        
-        this.entitlementManager = Entitlements.newManager(this, brooklynProperties);
-    }
-
-    @Override
-    public void terminate() {
-        highAvailabilityManager.stop();
-        running = false;
-        rebindManager.stop();
-        storage.terminate();
-        // Don't unmanage everything; different entities get given their events at different times 
-        // so can cause problems (e.g. a group finds out that a member is unmanaged, before the
-        // group itself has been told that it is unmanaged).
-    }
-    
-    @Override
-    public boolean isRunning() {
-        return running;
-    }
-    
-    @Override
-    public boolean isStartupComplete() {
-        return startupComplete;
-    }
-
-    @Override
-    public BrooklynStorage getStorage() {
-        return storage;
-    }
-    
-    @Override
-    public RebindManager getRebindManager() {
-        return rebindManager;
-    }
-
-    @Override
-    public HighAvailabilityManager getHighAvailabilityManager() {
-        return highAvailabilityManager;
-    }
-
-    @Override
-    public long getTotalEffectorInvocations() {
-        return totalEffectorInvocationCount.get();
-    }
-    
-    @Override
-    public ExecutionContext getExecutionContext(Entity e) {
-        // BEC is a thin wrapper around EM so fine to create a new one here; but make sure it gets the real entity
-        if (e instanceof AbstractEntity) {
-            return new BasicExecutionContext(MutableMap.of("tag", BrooklynTaskTags.tagForContextEntity(e)), getExecutionManager());
-        } else {
-            return ((EntityInternal)e).getManagementSupport().getExecutionContext();
-        }
-    }
-
-    @Override
-    public ExecutionContext getServerExecutionContext() {
-        // BEC is a thin wrapper around EM so fine to create a new one here
-        return new BasicExecutionContext(MutableMap.of("tag", BrooklynTaskTags.BROOKLYN_SERVER_TASK_TAG), getExecutionManager());
-    }
-
-    @Override
-    public SubscriptionContext getSubscriptionContext(Entity e) {
-        // BSC is a thin wrapper around SM so fine to create a new one here
-        return new BasicSubscriptionContext(getSubscriptionManager(), e);
-    }
-
-    @Override
-    public EntityDriverManager getEntityDriverManager() {
-        return entityDriverManager;
-    }
-
-    @Override
-    public DownloadResolverManager getEntityDownloadsManager() {
-        return downloadsManager;
-    }
-    
-    @Override
-    public EntitlementManager getEntitlementManager() {
-        return entitlementManager;
-    }
-    
-    protected abstract void manageIfNecessary(Entity entity, Object context);
-
-    @Override
-    public <T> Task<T> invokeEffector(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters) {
-        return runAtEntity(entity, eff, parameters);
-    }
-    
-    protected <T> T invokeEffectorMethodLocal(Entity entity, Effector<T> eff, Object args) {
-        assert isManagedLocally(entity) : "cannot invoke effector method at "+this+" because it is not managed here";
-        totalEffectorInvocationCount.incrementAndGet();
-        Object[] transformedArgs = EffectorUtils.prepareArgsForEffector(eff, args);
-        return GroovyJavaMethods.invokeMethodOnMetaClass(entity, eff.getName(), transformedArgs);
-    }
-
-    /**
-     * Method for entity to make effector happen with correct semantics (right place, right task context),
-     * when a method is called on that entity.
-     * @throws ExecutionException 
-     */
-    @Override
-    public <T> T invokeEffectorMethodSync(final Entity entity, final Effector<T> eff, final Object args) throws ExecutionException {
-        try {
-            Task<?> current = Tasks.current();
-            if (current == null || !entity.equals(BrooklynTaskTags.getContextEntity(current)) || !isManagedLocally(entity)) {
-                manageIfNecessary(entity, eff.getName());
-                // Wrap in a task if we aren't already in a task that is tagged with this entity
-                Task<T> task = runAtEntity( EffectorUtils.getTaskFlagsForEffectorInvocation(entity, eff, 
-                            ConfigBag.newInstance().configureStringKey("args", args)),
-                        entity, 
-                        new Callable<T>() {
-                            public T call() {
-                                return invokeEffectorMethodLocal(entity, eff, args);
-                            }});
-                return task.get();
-            } else {
-                return invokeEffectorMethodLocal(entity, eff, args);
-            }
-        } catch (Exception e) {
-            // don't need to attach any message or warning because the Effector impl hierarchy does that (see calls to EffectorUtils.handleException)
-            throw new ExecutionException(e);
-        }
-    }
-
-    /**
-     * Whether the master entity record is local, and sensors and effectors can be properly accessed locally.
-     */ 
-    public abstract boolean isManagedLocally(Entity e);
-    
-    /**
-     * Causes the indicated runnable to be run at the right location for the given entity.
-     *
-     * Returns the actual task (if it is local) or a proxy task (if it is remote);
-     * if management for the entity has not yet started this may start it.
-     * 
-     * @deprecated since 0.6.0 use effectors (or support {@code runAtEntity(Entity, Effector, Map)} if something else is needed);
-     * (Callable with Map flags is too open-ended, bothersome to support, and not used much) 
-     */
-    @Deprecated
-    public abstract <T> Task<T> runAtEntity(@SuppressWarnings("rawtypes") Map flags, Entity entity, Callable<T> c);
-
-    /** Runs the given effector in the right place for the given entity.
-     * The task is immediately submitted in the background, but also recorded in the queueing context (if present)
-     * so it appears as a child, but marked inessential so it does not fail the parent task, who will ordinarily
-     * call {@link Task#get()} on the object and may do their own failure handling. 
-     */
-    protected abstract <T> Task<T> runAtEntity(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters);
-
-    @Override
-    public StringConfigMap getConfig() {
-        return configMap;
-    }
-
-    @Override
-    public BrooklynProperties getBrooklynProperties() {
-        return configMap;
-    }
-
-    @Override
-    public synchronized LocationRegistry getLocationRegistry() {
-        if (locationRegistry==null) locationRegistry = new BasicLocationRegistry(this);
-        return locationRegistry;
-    }
-
-    @Override
-    public BrooklynCatalog getCatalog() {
-        if (!getCatalogInitialization().hasRunAnyInitialization()) {
-            // catalog init is needed; normally this will be done from start sequence,
-            // but if accessed early -- and in tests -- we will load it here
-            getCatalogInitialization().injectManagementContext(this);
-            getCatalogInitialization().populateUnofficial(catalog);
-        }
-        return catalog;
-    }
-    
-    @Override
-    public ClassLoader getCatalogClassLoader() {
-        // catalog does not have to be initialized
-        return catalog.getRootClassLoader();
-    }
-
-    /**
-     * Optional class-loader that this management context should use as its base,
-     * as the first-resort in the catalog, and for scanning (if scanning the default in the catalog).
-     * In most instances the default classloader (ManagementContext.class.getClassLoader(), assuming
-     * this was in the JARs used at boot time) is fine, and in those cases this method normally returns null.
-     * (Surefire does some weird stuff, but the default classloader is fine for loading;
-     * however it requires a custom base classpath to be set for scanning.)
-     */
-    @Override
-    public ClassLoader getBaseClassLoader() {
-        return baseClassLoader;
-    }
-    
-    /** See {@link #getBaseClassLoader()}.  Only settable once and must be invoked before catalog is loaded. */
-    public void setBaseClassLoader(ClassLoader cl) {
-        if (baseClassLoader==cl) return;
-        if (baseClassLoader!=null) throw new IllegalStateException("Cannot change base class loader (in "+this+")");
-        if (catalog!=null) throw new IllegalStateException("Cannot set base class after catalog has been loaded (in "+this+")");
-        this.baseClassLoader = cl;
-    }
-    
-    /** Optional mechanism for setting the classpath which should be scanned by the catalog, if the catalog
-     * is scanning the default classpath.  Usually it infers the right thing, but some classloaders
-     * (e.g. surefire) do funny things which the underlying org.reflections.Reflections library can't see in to.
-     * <p>
-     * This should normally be invoked early in the server startup.  Setting it after the catalog is loaded will not
-     * take effect without an explicit internal call to do so.  Once set, it can be changed prior to catalog loading
-     * but it cannot be <i>changed</i> once the catalog is loaded.
-     * <p>
-     * ClasspathHelper.forJavaClassPath() is often a good argument to pass, and is used internally in some places
-     * when no items are found on the catalog. */
-    @Override
-    public void setBaseClassPathForScanning(Iterable<URL> urls) {
-        if (Objects.equal(baseClassPathForScanning, urls)) return;
-        if (baseClassPathForScanning != null) {
-            if (catalog==null)
-                log.warn("Changing scan classpath to "+urls+" from "+baseClassPathForScanning);
-            else
-                throw new IllegalStateException("Cannot change base class path for scanning (in "+this+")");
-        }
-        this.baseClassPathForScanning = urls;
-    }
-    /** 
-     * @see #setBaseClassPathForScanning(Iterable)
-     */
-    @Override
-    public Iterable<URL> getBaseClassPathForScanning() {
-        return baseClassPathForScanning;
-    }
-
-    public BrooklynGarbageCollector getGarbageCollector() {
-        return gc;
-    }
-
-    @Override
-    public void setManagementNodeUri(URI uri) {
-        this.uri = Maybe.of(checkNotNull(uri, "uri"));
-    }
-
-    @Override
-    public Maybe<URI> getManagementNodeUri() {
-        return uri;
-    }
-    
-    private Object catalogInitMutex = new Object();
-    @Override
-    public CatalogInitialization getCatalogInitialization() {
-        synchronized (catalogInitMutex) {
-            if (catalogInitialization!=null) return catalogInitialization;
-            CatalogInitialization ci = new CatalogInitialization();
-            setCatalogInitialization(ci);
-            return ci;
-        }
-    }
-    
-    @Override
-    public void setCatalogInitialization(CatalogInitialization catalogInitialization) {
-        synchronized (catalogInitMutex) {
-            Preconditions.checkNotNull(catalogInitialization, "initialization must not be null");
-            if (this.catalogInitialization!=null && this.catalogInitialization != catalogInitialization)
-                throw new IllegalStateException("Changing catalog init from "+this.catalogInitialization+" to "+catalogInitialization+"; changes not permitted");
-            catalogInitialization.injectManagementContext(this);
-            this.catalogInitialization = catalogInitialization;
-        }
-    }
-    
-    public BrooklynObject lookup(String id) {
-        return lookup(id, BrooklynObject.class);
-    }
-    
-    @SuppressWarnings("unchecked")
-    public <T extends BrooklynObject> T lookup(String id, Class<T> type) {
-        Object result;
-        result = getEntityManager().getEntity(id);
-        if (result!=null && type.isInstance(result)) return (T)result;
-        
-        result = getLocationManager().getLocation(id);
-        if (result!=null && type.isInstance(result)) return (T)result;
-
-        // TODO policies, enrichers, feeds
-        return null;
-    }
-
-    @Override
-    public List<Throwable> errors() {
-        return errors;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/AbstractSubscriptionManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/AbstractSubscriptionManager.java b/core/src/main/java/brooklyn/management/internal/AbstractSubscriptionManager.java
deleted file mode 100644
index 7e8effc..0000000
--- a/core/src/main/java/brooklyn/management/internal/AbstractSubscriptionManager.java
+++ /dev/null
@@ -1,141 +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.management.internal;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Collections;
-import java.util.Map;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.Group;
-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.management.SubscriptionHandle;
-import org.apache.brooklyn.api.management.SubscriptionManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
-
-public abstract class AbstractSubscriptionManager implements SubscriptionManager {
-
-    // TODO Perhaps could use guava's SynchronizedSetMultimap? But need to check its synchronization guarantees.
-    //      That would replace the utils used for subscriptionsBySubscriber etc.
-    
-    @SuppressWarnings("unused")
-    private static final Logger LOG = LoggerFactory.getLogger(AbstractSubscriptionManager.class);
-
-    /** performs the actual subscription; should return the subscription parameter as the handle */
-    protected abstract <T> SubscriptionHandle subscribe(Map<String, Object> flags, Subscription<T> s);
-    /** performs the actual publishing -- ie distribution to subscriptions */
-    public abstract <T> void publish(final SensorEvent<T> event);
-
-    public static class EntitySensorToken {
-        Entity e;
-        Sensor<?> s;
-        String sName;
-        public EntitySensorToken(Entity e, Sensor<?> s) {
-            this.e = e;
-            this.s = s;
-            this.sName = (s == null) ? null : checkNotNull(s.getName(), "sensor must have non-null name: %s", s);
-        }
-        @Override
-        public int hashCode() {
-            return Objects.hashCode(e, sName);
-        }
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) return true;
-            if (!(obj instanceof EntitySensorToken)) return false;
-            if (!Objects.equal(e, ((EntitySensorToken)obj).e)) return false;
-            if (!Objects.equal(sName, ((EntitySensorToken)obj).sName)) return false;
-            return true;
-        }
-        @Override
-        public String toString() {
-            return (e != null ? e.getId() :  "*")+":"+(s != null ? sName : "*");
-        }
-    }
-    static Object makeEntitySensorToken(Entity e, Sensor<?> s) {
-        return new EntitySensorToken(e, s);
-    }
-    static Object makeEntitySensorToken(SensorEvent<?> se) {
-        return makeEntitySensorToken(se.getSource(), se.getSensor());
-    }
-    
-    /** @see SubscriptionManager#subscribe(Map, Entity, Sensor, SensorEventListener) */
-    public final <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        return subscribe(Collections.<String,Object>emptyMap(), producer, sensor, listener);
-    }
- 
-    /**
-     * This implementation handles the following flags, in addition to those described in the {@link SubscriptionManager}
-     * interface:
-     * <ul>
-     * <li>subscriberExecutionManagerTag - a tag to pass to execution manager (without setting any execution semantics / TaskPreprocessor);
-     *      if not supplied and there is a subscriber, this will be inferred from the subscriber and set up with SingleThreadedScheduler
-     * <li>eventFilter - a Predicate&lt;SensorEvent&gt; instance to filter what events are delivered
-     * </ul>
-     * 
-     * @see SubscriptionManager#subscribe(Map, Entity, Sensor, SensorEventListener)
-     */
-    public final <T> SubscriptionHandle subscribe(Map<String, Object> flags, Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        return subscribe(flags, new Subscription<T>(producer, sensor, listener));
-    }
-        
-    /** @see SubscriptionManager#subscribeToChildren(Map, Entity, Sensor, SensorEventListener) */
-    public final <T> SubscriptionHandle subscribeToChildren(Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        return subscribeToChildren(Collections.<String,Object>emptyMap(), parent, sensor, listener);
-    }
-
-    /** @see SubscriptionManager#subscribe(Map, Entity, Sensor, SensorEventListener) */
-    public final  <T> SubscriptionHandle subscribeToChildren(Map<String, Object> flags, final Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        Predicate<SensorEvent<T>> eventFilter = new Predicate<SensorEvent<T>>() {
-            public boolean apply(SensorEvent<T> input) {
-                return parent != null && input.getSource() != null && parent.equals(input.getSource().getParent());
-            }
-        };
-        flags.put("eventFilter", eventFilter);
-        return subscribe(flags, null, sensor, listener);
-    }
-
-    /** @see SubscriptionManager#subscribeToChildren(Map, Entity, Sensor, SensorEventListener) */
-    public final <T> SubscriptionHandle subscribeToMembers(Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        return subscribeToMembers(Collections.<String,Object>emptyMap(), parent, sensor, listener);
-    }
-
-    /** @see SubscriptionManager#subscribe(Map, Entity, Sensor, SensorEventListener) */
-    public final  <T> SubscriptionHandle subscribeToMembers(Map<String, Object> flags, final Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        Predicate<SensorEvent<T>> eventFilter = new Predicate<SensorEvent<T>>() {
-            public boolean apply(SensorEvent<T> input) {
-                return parent.getMembers().contains(input.getSource());
-            }
-        };
-        flags.put("eventFilter", eventFilter);
-        return subscribe(flags, null, sensor, listener);
-    }
-
-    protected <T> Object getSubscriber(Map<String, Object> flags, Subscription<T> s) {
-        return s.subscriber!=null ? s.subscriber : flags.containsKey("subscriber") ? flags.remove("subscriber") : s.listener;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/AccessManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/AccessManager.java b/core/src/main/java/brooklyn/management/internal/AccessManager.java
deleted file mode 100644
index baa805a..0000000
--- a/core/src/main/java/brooklyn/management/internal/AccessManager.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.management.internal;
-
-import org.apache.brooklyn.api.management.AccessController;
-
-import com.google.common.annotations.Beta;
-
-@Beta
-public interface AccessManager {
-
-    AccessController getAccessController();
-    
-    boolean isLocationProvisioningAllowed();
-
-    boolean isLocationManagementAllowed();
-
-    boolean isEntityManagementAllowed();
-
-    void setLocationProvisioningAllowed(boolean allowed);
-    
-    void setLocationManagementAllowed(boolean allowed);
-    
-    void setEntityManagementAllowed(boolean allowed);
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/AsyncCollectionChangeAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/AsyncCollectionChangeAdapter.java b/core/src/main/java/brooklyn/management/internal/AsyncCollectionChangeAdapter.java
deleted file mode 100644
index e3e5193..0000000
--- a/core/src/main/java/brooklyn/management/internal/AsyncCollectionChangeAdapter.java
+++ /dev/null
@@ -1,83 +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.management.internal;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import org.apache.brooklyn.api.management.ExecutionManager;
-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> {
-    
-    private static final Logger LOG = LoggerFactory.getLogger(AsyncCollectionChangeAdapter.class);
-
-    private final ExecutionManager executor;
-    private final CollectionChangeListener<Item> delegate;
-    
-    public AsyncCollectionChangeAdapter(ExecutionManager executor, CollectionChangeListener<Item> delegate) {
-        this.executor = checkNotNull(executor, "executor");
-        this.delegate = checkNotNull(delegate, "delegate");
-        ((BasicExecutionManager) executor).setTaskSchedulerForTag(delegate, SingleThreadedScheduler.class);
-    }
-
-    @Override
-    public void onItemAdded(final Item item) {
-        executor.submit(MutableMap.of("tag", delegate), new Runnable() {
-            public void run() {
-                try {
-                    delegate.onItemAdded(item);
-                } catch (Throwable t) {
-                    LOG.warn("Error notifying listener of itemAdded("+item+")", t);
-                    Exceptions.propagate(t);
-                }
-            }
-        });
-    }
-    
-    @Override
-    public void onItemRemoved(final Item item) {
-        executor.submit(MutableMap.of("tag", delegate), new Runnable() {
-            public void run() {
-                try {
-                    delegate.onItemRemoved(item);
-                } catch (Throwable t) {
-                    LOG.warn("Error notifying listener of itemAdded("+item+")", t);
-                    Exceptions.propagate(t);
-                }
-            }
-        });
-    }
-
-    @Override
-    public int hashCode() {
-        return delegate.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        return (other instanceof AsyncCollectionChangeAdapter) && 
-                delegate.equals(((AsyncCollectionChangeAdapter<?>) other).delegate);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/BasicSubscriptionContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/BasicSubscriptionContext.java b/core/src/main/java/brooklyn/management/internal/BasicSubscriptionContext.java
deleted file mode 100644
index cb60545..0000000
--- a/core/src/main/java/brooklyn/management/internal/BasicSubscriptionContext.java
+++ /dev/null
@@ -1,180 +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.management.internal;
-
-import static brooklyn.util.JavaGroovyEquivalents.mapOf;
-import groovy.lang.Closure;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.Group;
-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.management.SubscriptionContext;
-import org.apache.brooklyn.api.management.SubscriptionHandle;
-import org.apache.brooklyn.api.management.SubscriptionManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-
-/**
- * A {@link SubscriptionContext} for an entity or other user of a {@link SubscriptionManager}.
- */
-public class BasicSubscriptionContext implements SubscriptionContext {
-    
-    private static final Logger LOG = LoggerFactory.getLogger(BasicSubscriptionContext.class);
-
-    private final SubscriptionManager manager;
-    private final Object subscriber;
-    private final Map<String,Object> flags;
-
-    public BasicSubscriptionContext(SubscriptionManager manager, Object subscriber) {
-        this(Collections.<String,Object>emptyMap(), manager, subscriber);
-    }
-    
-    public BasicSubscriptionContext(Map<String, ?> flags, SubscriptionManager manager, Object subscriber) {
-        this.manager = manager;
-        this.subscriber = subscriber;
-        this.flags = mapOf("subscriber", subscriber);
-        if (flags!=null) this.flags.putAll(flags);
-    }
-
-    @SuppressWarnings("rawtypes")
-    public <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, Closure c) {
-        return subscribe(Collections.<String,Object>emptyMap(), producer, sensor, c);
-    }
-    
-    @SuppressWarnings("rawtypes")
-    public <T> SubscriptionHandle subscribe(Map<String, Object> newFlags, Entity producer, Sensor<T> sensor, Closure c) {
-        return subscribe(newFlags, producer, sensor, toSensorEventListener(c));        
-    }
-
-    @Override
-    public <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        return subscribe(Collections.<String,Object>emptyMap(), producer, sensor, listener);
-    }
-    
-    @Override
-    public <T> SubscriptionHandle subscribe(Map<String, Object> newFlags, Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        Map<String,Object> subscriptionFlags = Maps.newLinkedHashMap(flags);
-        if (newFlags != null) subscriptionFlags.putAll(newFlags);
-        return manager.subscribe(subscriptionFlags, producer, sensor, listener);
-    }
-
-    @SuppressWarnings("rawtypes")
-    public <T> SubscriptionHandle subscribeToChildren(Entity parent, Sensor<T> sensor, Closure c) {
-        return subscribeToChildren(Collections.<String,Object>emptyMap(), parent, sensor, c);
-    }
-    
-    @SuppressWarnings("rawtypes")
-    public <T> SubscriptionHandle subscribeToChildren(Map<String, Object> newFlags, Entity parent, Sensor<T> sensor, Closure c) {
-        return subscribeToChildren(newFlags, parent, sensor, toSensorEventListener(c));
-    }
-
-    @Override
-    public <T> SubscriptionHandle subscribeToChildren(Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        return subscribeToChildren(Collections.<String,Object>emptyMap(), parent, sensor, listener);
-    }
-    
-    @Override
-    public <T> SubscriptionHandle subscribeToChildren(Map<String, Object> newFlags, Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        Map<String,Object> subscriptionFlags = Maps.newLinkedHashMap(flags);
-        if (newFlags != null) subscriptionFlags.putAll(newFlags);
-        return manager.subscribeToChildren(subscriptionFlags, parent, sensor, listener);
-    }
-
-    @SuppressWarnings("rawtypes")
-    public <T> SubscriptionHandle subscribeToMembers(Group parent, Sensor<T> sensor, Closure c) {
-        return subscribeToMembers(Collections.<String,Object>emptyMap(), parent, sensor, c);
-    }
-
-    @SuppressWarnings("rawtypes")
-    public <T> SubscriptionHandle subscribeToMembers(Map<String, Object> newFlags, Group parent, Sensor<T> sensor, Closure c) {
-        return subscribeToMembers(newFlags, parent, sensor, toSensorEventListener(c));
-    }
-    
-    @Override
-    public <T> SubscriptionHandle subscribeToMembers(Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        return subscribeToMembers(Collections.<String,Object>emptyMap(), parent, sensor, listener);
-    }
-    
-    @Override
-    public <T> SubscriptionHandle subscribeToMembers(Map<String, Object> newFlags, Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        Map<String,Object> subscriptionFlags = Maps.newLinkedHashMap(flags);
-        if (newFlags != null) subscriptionFlags.putAll(newFlags);
-        return manager.subscribeToMembers(subscriptionFlags, parent, sensor, listener);
-    }
-
-    @SuppressWarnings("rawtypes")
-    @Override
-    public boolean unsubscribe(SubscriptionHandle subscriptionId) {
-        Preconditions.checkNotNull(subscriptionId, "subscriptionId must not be null");
-        Preconditions.checkArgument(Objects.equal(subscriber, ((Subscription) subscriptionId).subscriber), "The subscriptionId is for a different "+subscriber+"; expected "+((Subscription) subscriptionId).subscriber);
-        return manager.unsubscribe(subscriptionId);
-    }
-
-    /** @see SubscriptionManager#publish(SensorEvent) */
-    @Override
-    public <T> void publish(SensorEvent<T> event) {
-        manager.publish(event);
-    }
-
-    /** Return the subscriptions associated with this context */
-    @Override
-    public Set<SubscriptionHandle> getSubscriptions() {
-        return manager.getSubscriptionsForSubscriber(subscriber);
-    }
-
-    @Override
-    public int unsubscribeAll() {
-        int count = 0;
-        
-        // To avoid ConcurrentModificationException when copying subscriptions, need to synchronize on it
-        Set<SubscriptionHandle> subscriptions = getSubscriptions();
-        Collection<SubscriptionHandle> subscriptionsCopy;
-        synchronized (subscriptions) {
-            subscriptionsCopy = ImmutableList.copyOf(subscriptions);
-        }
-        
-        for (SubscriptionHandle s : subscriptionsCopy) {
-            count++; 
-            boolean result = unsubscribe(s); 
-            if (!result) LOG.warn("When unsubscribing from all of {}, unsubscribe of {} return false", subscriber, s);
-        }
-        return count;
-    }
-    
-    @SuppressWarnings("rawtypes")
-    private <T> SensorEventListener<T> toSensorEventListener(final Closure c) {
-        return new SensorEventListener<T>() {
-            @Override public void onEvent(SensorEvent<T> event) {
-                c.call(event);
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/BrooklynGarbageCollector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/BrooklynGarbageCollector.java b/core/src/main/java/brooklyn/management/internal/BrooklynGarbageCollector.java
deleted file mode 100644
index 0c54909..0000000
--- a/core/src/main/java/brooklyn/management/internal/BrooklynGarbageCollector.java
+++ /dev/null
@@ -1,626 +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.management.internal;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.ConcurrentModificationException;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.BrooklynProperties;
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.basic.BrooklynTaskTags.WrappedEntity;
-import brooklyn.entity.basic.BrooklynTaskTags.WrappedStream;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.internal.storage.BrooklynStorage;
-import brooklyn.util.collections.MutableList;
-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;
-
-import com.google.common.base.Objects;
-import com.google.common.annotations.Beta;
-import com.google.common.collect.Iterables;
-
-/**
- * Deletes record of old tasks, to prevent space leaks and the eating up of more and more memory.
- * 
- * The deletion policy is configurable:
- * <ul>
- *   <li>Period - how frequently to look at the existing tasks to delete some, if required
- *   <li>Max task age - the time after which a completed task will be automatically deleted
- *       (i.e. any root task completed more than maxTaskAge ago will be deleted)
- *   <li>Max tasks per <various categories> - the maximum number of tasks to be kept for a given tag,
- *       split into categories based on what is seeming to be useful
- * </ul>
- * 
- * The default is to check with a period of one minute, deleting tasks after 30 days, 
- * and keeping at most 100000 tasks in the system,
- * max 1000 tasks per entity, 50 per effector within that entity, and 50 per other non-effector tag
- * within that entity (or global if not attached to an entity).
- * 
- * @author aled
- */
-public class BrooklynGarbageCollector {
-
-    private static final Logger LOG = LoggerFactory.getLogger(BrooklynGarbageCollector.class);
-
-    public static final ConfigKey<Duration> GC_PERIOD = ConfigKeys.newDurationConfigKey(
-            "brooklyn.gc.period", "the period for checking if any tasks need to be deleted", 
-            Duration.minutes(1));
-    
-    public static final ConfigKey<Boolean> DO_SYSTEM_GC = ConfigKeys.newBooleanConfigKey(
-            "brooklyn.gc.doSystemGc", "whether to periodically call System.gc()", false);
-    
-    /** 
-     * should we check for tasks which are submitted by another but backgrounded, i.e. not a child of that task?
-     * default to yes, despite it can be some extra loops, to make sure we GC them promptly.
-     * @since 0.7.0 */
-    // work offender is {@link DynamicSequentialTask} internal job tracker, but it is marked 
-    // transient so it is destroyed prompty; there may be others, however;
-    // but OTOH it might be expensive to check for these all the time!
-    // TODO probably we can set this false (remove this and related code),
-    // and just rely on usual GC to pick up background tasks; the lifecycle of background task
-    // should normally be independent of the submitter. (DST was the exception, and marking 
-    // transient there fixes the main problem, which is when the submitter is GC'd but the submitted is not,
-    // and we don't want the submitted to show up at the root in the GUI, which it will if its
-    // submitter has been GC'd)
-    @Beta
-    public static final ConfigKey<Boolean> CHECK_SUBTASK_SUBMITTERS = ConfigKeys.newBooleanConfigKey(
-        "brooklyn.gc.checkSubtaskSubmitters", "whether for subtasks to check the submitters", true);
-
-    public static final ConfigKey<Integer> MAX_TASKS_PER_TAG = ConfigKeys.newIntegerConfigKey(
-        "brooklyn.gc.maxTasksPerTag", 
-        "the maximum number of tasks to be kept for a given tag "
-        + "within an execution context (e.g. entity); "
-        + "some broad-brush tags are excluded, and if an entity has multiple tags all tag counts must be full",
-        50);
-    
-    public static final ConfigKey<Integer> MAX_TASKS_PER_ENTITY = ConfigKeys.newIntegerConfigKey(
-        "brooklyn.gc.maxTasksPerEntity", 
-        "the maximum number of tasks to be kept for a given entity",
-        1000);
-
-    public static final ConfigKey<Integer> MAX_TASKS_GLOBAL = ConfigKeys.newIntegerConfigKey(
-        "brooklyn.gc.maxTasksGlobal", 
-        "the maximum number of tasks to be kept across the entire system",
-        100000);
-
-    public static final ConfigKey<Duration> MAX_TASK_AGE = ConfigKeys.newDurationConfigKey(
-            "brooklyn.gc.maxTaskAge", 
-            "the duration after which a completed task will be automatically deleted", 
-            Duration.days(30));
-    
-    protected final static Comparator<Task<?>> TASKS_OLDEST_FIRST_COMPARATOR = new Comparator<Task<?>>() {
-        @Override public int compare(Task<?> t1, Task<?> t2) {
-            long end1 = t1.getEndTimeUtc();
-            long end2 = t2.getEndTimeUtc();
-            return (end1 < end2) ? -1 : ((end1 == end2) ? 0 : 1);
-        }
-    };
-    
-    private final BasicExecutionManager executionManager;
-    private final BrooklynStorage storage;
-    private final BrooklynProperties brooklynProperties;
-    private final ScheduledExecutorService executor;
-    private ScheduledFuture<?> activeCollector;
-    private Map<Entity,Task<?>> unmanagedEntitiesNeedingGc = new LinkedHashMap<Entity, Task<?>>();
-    
-    private Duration gcPeriod;
-    private final boolean doSystemGc;
-    private volatile boolean running = true;
-    
-    public BrooklynGarbageCollector(BrooklynProperties brooklynProperties, BasicExecutionManager executionManager, BrooklynStorage storage) {
-        this.executionManager = executionManager;
-        this.storage = storage;
-        this.brooklynProperties = brooklynProperties;
-
-        doSystemGc = brooklynProperties.getConfig(DO_SYSTEM_GC);
-        
-        executor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
-                @Override public Thread newThread(Runnable r) {
-                    return new Thread(r, "brooklyn-gc");
-                }});
-        
-        executionManager.addListener(new ExecutionListener() {
-                @Override public void onTaskDone(Task<?> task) {
-                    BrooklynGarbageCollector.this.onTaskDone(task);
-                }});
-    
-        scheduleCollector(true);
-    }
-
-    protected synchronized void scheduleCollector(boolean canInterruptCurrent) {
-        if (activeCollector != null) activeCollector.cancel(canInterruptCurrent);
-        
-        gcPeriod = brooklynProperties.getConfig(GC_PERIOD);
-        if (gcPeriod!=null) {
-            activeCollector = executor.scheduleWithFixedDelay(
-                new Runnable() {
-                    @Override public void run() {
-                        gcIteration();
-                    }
-                }, 
-                gcPeriod.toMillisecondsRoundingUp(), 
-                gcPeriod.toMillisecondsRoundingUp(), 
-                TimeUnit.MILLISECONDS);
-        }
-    }
-
-    /** force a round of Brooklyn garbage collection */
-    public void gcIteration() {
-        try {
-            logUsage("brooklyn gc (before)");
-            gcTasks();
-            logUsage("brooklyn gc (after)");
-            
-            if (doSystemGc) {
-                // Can be very useful when tracking down OOMEs etc, where a lot of tasks are executing
-                // Empirically observed that (on OS X jvm at least) calling twice blocks - logs a significant
-                // amount of memory having been released, as though a full-gc had been run. But this is highly
-                // dependent on the JVM implementation.
-                System.gc(); System.gc();
-                logUsage("brooklyn gc (after system gc)");
-            }
-        } catch (Throwable t) {
-            Exceptions.propagateIfFatal(t);
-            LOG.warn("Error during management-context GC: "+t, t);
-            // previously we bailed on all errors, but I don't think we should do that -Alex
-        }
-    }
-
-    public void logUsage(String prefix) {
-        if (LOG.isDebugEnabled())
-            LOG.debug(prefix+" - using "+getUsageString());
-    }
-
-    public static String makeBasicUsageString() {
-        return Strings.makeSizeString(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())+" / "+
-            Strings.makeSizeString(Runtime.getRuntime().totalMemory()) + " memory" +
-            " ("+Strings.makeSizeString(MemoryUsageTracker.SOFT_REFERENCES.getBytesUsed()) + " soft); "+
-            Thread.activeCount()+" threads";
-    }
-    
-    public String getUsageString() {
-        return makeBasicUsageString()+"; "+
-            "storage: " + storage.getStorageMetrics() + "; " +
-            "tasks: " +
-            executionManager.getNumActiveTasks()+" active, "+
-            executionManager.getNumIncompleteTasks()+" unfinished; "+
-            executionManager.getNumInMemoryTasks()+" remembered, "+
-            executionManager.getTotalTasksSubmitted()+" total submitted)";
-    }
-    
-    public void shutdownNow() {
-        running = false;
-        if (activeCollector != null) activeCollector.cancel(true);
-        if (executor != null) executor.shutdownNow();
-    }
-    
-    public void onUnmanaged(Entity entity) {
-        // defer task deletions until the entity is completely unmanaged
-        // (this is usually invoked during the stop sequence)
-        synchronized (unmanagedEntitiesNeedingGc) {
-            unmanagedEntitiesNeedingGc.put(entity, Tasks.current());
-        }
-    }
-    
-    public void deleteTasksForEntity(Entity entity) {
-        // remove all references to this entity from tasks
-        executionManager.deleteTag(entity);
-        executionManager.deleteTag(BrooklynTaskTags.tagForContextEntity(entity));
-        executionManager.deleteTag(BrooklynTaskTags.tagForCallerEntity(entity));
-        executionManager.deleteTag(BrooklynTaskTags.tagForTargetEntity(entity));
-    }
-    
-    public void onUnmanaged(Location loc) {
-        // No-op currently; no tasks are tracked through their location
-    }
-    
-    public void onTaskDone(Task<?> task) {
-        if (shouldDeleteTaskImmediately(task)) {
-            executionManager.deleteTask(task);
-        }
-    }
-    
-    /** @deprecated since 0.7.0, method moved internal until semantics are clarified; see also {@link #shouldDeleteTaskImmediately(Task)} */
-    @Deprecated
-    public boolean shouldDeleteTask(Task<?> task) {
-        return shouldDeleteTaskImmediately(task);
-    }
-    /** whether this task should be deleted on completion,
-     * because it is transient, or because it is submitted background without much context information */
-    protected boolean shouldDeleteTaskImmediately(Task<?> task) {
-        if (!task.isDone()) return false;
-        
-        Set<Object> tags = task.getTags();
-        if (tags.contains(ManagementContextInternal.TRANSIENT_TASK_TAG))
-            return true;
-        if (tags.contains(ManagementContextInternal.EFFECTOR_TAG) || tags.contains(ManagementContextInternal.NON_TRANSIENT_TASK_TAG))
-            return false;
-        
-        if (task.getSubmittedByTask()!=null) {
-            Task<?> parent = task.getSubmittedByTask();
-            if (executionManager.getTask(parent.getId())==null) {
-                // parent is already cleaned up
-                return true;
-            }
-            if (parent instanceof HasTaskChildren && Iterables.contains(((HasTaskChildren)parent).getChildren(), task)) {
-                // it is a child, let the parent manage this task's death
-                return false;
-            }
-            Entity associatedEntity = BrooklynTaskTags.getTargetOrContextEntity(task);
-            if (associatedEntity!=null) {
-                // this is associated to an entity; destroy only if the entity is unmanaged
-                return !Entities.isManaged(associatedEntity);
-            }
-            // if not associated to an entity, then delete immediately
-            return true;
-        }
-        
-        // e.g. scheduled tasks, sensor events, etc
-        // TODO (in future may keep some of these with another limit, based on a new TagCategory)
-        // there may also be a server association for server-side tasks which should be kept
-        // (but be careful not to keep too many subscriptions!)
-        
-        return true;
-    }
-
-    /**
-     * Deletes old tasks. The age/number of tasks to keep is controlled by fields like 
-     * {@link #maxTasksPerTag} and {@link #maxTaskAge}.
-     */
-    protected synchronized int gcTasks() {
-        // TODO Must be careful with memory usage here: have seen OOME if we get crazy lots of tasks.
-        // hopefully the use new limits, filters, and use of live lists in some places (added Sep 2014) will help.
-        // 
-        // An option is for getTasksWithTag(tag) to return an ArrayList rather than a LinkedHashSet. That
-        // is a far more memory efficient data structure (e.g. 4 bytes overhead per object rather than 
-        // 32 bytes overhead per object for HashSet).
-        //
-        // More notes on optimization is in the history of this file.
-        
-        if (!running) return 0;
-        
-        Duration newPeriod = brooklynProperties.getConfig(GC_PERIOD);
-        if (!Objects.equal(gcPeriod, newPeriod)) {
-            // caller has changed period, reschedule on next run
-            scheduleCollector(false);
-        }
-    
-        expireUnmanagedEntityTasks();
-        expireAgedTasks();
-        expireTransientTasks();
-        
-        // now look at overcapacity tags, non-entity tags first
-        
-        Set<Object> taskTags = executionManager.getTaskTags();
-        
-        int maxTasksPerEntity = brooklynProperties.getConfig(MAX_TASKS_PER_ENTITY);
-        int maxTasksPerTag = brooklynProperties.getConfig(MAX_TASKS_PER_TAG);
-        
-        Map<Object,AtomicInteger> taskNonEntityTagsOverCapacity = MutableMap.of();
-        Map<Object,AtomicInteger> taskEntityTagsOverCapacity = MutableMap.of();
-        
-        Map<Object,AtomicInteger> taskAllTagsOverCapacity = MutableMap.of();
-        
-        for (Object tag : taskTags) {
-            if (isTagIgnoredForGc(tag)) continue;
-            
-            Set<Task<?>> tasksWithTag = executionManager.tasksWithTagLiveOrNull(tag);
-            if (tasksWithTag==null) continue;
-            AtomicInteger overA = null;
-            if (tag instanceof WrappedEntity) {
-                int over = tasksWithTag.size() - maxTasksPerEntity;
-                if (over>0) {
-                    overA = new AtomicInteger(over);
-                    taskEntityTagsOverCapacity.put(tag, overA);
-                }
-            } else {
-                int over = tasksWithTag.size() - maxTasksPerTag;
-                if (over>0) {
-                    overA = new AtomicInteger(over);
-                    taskNonEntityTagsOverCapacity.put(tag, overA);
-                }
-            }
-            if (overA!=null) {
-                taskAllTagsOverCapacity.put(tag, overA);
-            }
-        }
-        
-        int deletedCount = 0;
-        deletedCount += expireOverCapacityTagsInCategory(taskNonEntityTagsOverCapacity, taskAllTagsOverCapacity, TagCategory.NON_ENTITY_NORMAL, false);
-        deletedCount += expireOverCapacityTagsInCategory(taskEntityTagsOverCapacity, taskAllTagsOverCapacity, TagCategory.ENTITY, true);
-        deletedCount += expireSubTasksWhoseSubmitterIsExpired();
-        
-        int deletedGlobally = expireIfOverCapacityGlobally();
-        deletedCount += deletedGlobally;
-        if (deletedGlobally>0) deletedCount += expireSubTasksWhoseSubmitterIsExpired();
-        
-        return deletedCount;
-    }
-
-    protected static boolean isTagIgnoredForGc(Object tag) {
-        if (tag == null) return true;
-        if (tag.equals(ManagementContextInternal.EFFECTOR_TAG)) return true;
-        if (tag.equals(ManagementContextInternal.SUB_TASK_TAG)) return true;
-        if (tag.equals(ManagementContextInternal.NON_TRANSIENT_TASK_TAG)) return true;
-        if (tag.equals(ManagementContextInternal.TRANSIENT_TASK_TAG)) return true;
-        if (tag instanceof WrappedStream) {
-            return true;
-        }
-        
-        return false;
-    }
-    
-    protected void expireUnmanagedEntityTasks() {
-        Iterator<Entry<Entity, Task<?>>> ei;
-        synchronized (unmanagedEntitiesNeedingGc) {
-            ei = MutableSet.copyOf(unmanagedEntitiesNeedingGc.entrySet()).iterator();
-        }
-        while (ei.hasNext()) {
-            Entry<Entity, Task<?>> ee = ei.next();
-            if (Entities.isManaged(ee.getKey())) continue;
-            if (ee.getValue()!=null && !ee.getValue().isDone()) continue;
-            deleteTasksForEntity(ee.getKey());
-            synchronized (unmanagedEntitiesNeedingGc) {
-                unmanagedEntitiesNeedingGc.remove(ee.getKey());
-            }
-        }
-    }
-    
-    protected void expireAgedTasks() {
-        Duration maxTaskAge = brooklynProperties.getConfig(MAX_TASK_AGE);
-        
-        Collection<Task<?>> allTasks = executionManager.allTasksLive();
-        Collection<Task<?>> tasksToDelete = MutableList.of();
-
-        try {
-            for (Task<?> task: allTasks) {
-                if (!task.isDone()) continue;
-                if (BrooklynTaskTags.isSubTask(task)) continue;
-
-                if (maxTaskAge.isShorterThan(Duration.sinceUtc(task.getEndTimeUtc())))
-                    tasksToDelete.add(task);
-            }
-            
-        } catch (ConcurrentModificationException e) {
-            // delete what we've found so far
-            LOG.debug("Got CME inspecting aged tasks, with "+tasksToDelete.size()+" found for deletion: "+e);
-        }
-        
-        for (Task<?> task: tasksToDelete) {
-            executionManager.deleteTask(task);
-        }
-    }
-    
-    protected void expireTransientTasks() {
-        Set<Task<?>> transientTasks = executionManager.getTasksWithTag(BrooklynTaskTags.TRANSIENT_TASK_TAG);
-        for (Task<?> t: transientTasks) {
-            if (!t.isDone()) continue;
-            executionManager.deleteTask(t);
-        }
-    }
-    
-    protected int expireSubTasksWhoseSubmitterIsExpired() {
-        // ideally we wouldn't have this; see comments on CHECK_SUBTASK_SUBMITTERS
-        if (!brooklynProperties.getConfig(CHECK_SUBTASK_SUBMITTERS))
-            return 0;
-        
-        Collection<Task<?>> allTasks = executionManager.allTasksLive();
-        Collection<Task<?>> tasksToDelete = MutableList.of();
-        try {
-            for (Task<?> task: allTasks) {
-                if (!task.isDone()) continue;
-                Task<?> submitter = task.getSubmittedByTask();
-                // if we've leaked, ie a subtask which is not a child task, 
-                // and the submitter is GC'd, then delete this also
-                if (submitter!=null && submitter.isDone() && executionManager.getTask(submitter.getId())==null) {
-                    tasksToDelete.add(task);
-                }
-            }
-            
-        } catch (ConcurrentModificationException e) {
-            // delete what we've found so far
-            LOG.debug("Got CME inspecting aged tasks, with "+tasksToDelete.size()+" found for deletion: "+e);
-        }
-        
-        for (Task<?> task: tasksToDelete) {
-            executionManager.deleteTask(task);
-        }
-        return tasksToDelete.size();
-    }
-    
-    protected enum TagCategory { 
-        ENTITY, NON_ENTITY_NORMAL;
-        
-        public boolean acceptsTag(Object tag) {
-            if (isTagIgnoredForGc(tag)) return false;
-            if (tag instanceof WrappedEntity) return this==ENTITY;
-            if (this==ENTITY) return false;
-            return true;
-        }
-    } 
-
-
-    /** expires tasks which are over-capacity in all their non-entity tag categories, returned count */
-    protected int expireOverCapacityTagsInCategory(Map<Object, AtomicInteger> taskTagsInCategoryOverCapacity, Map<Object, AtomicInteger> taskAllTagsOverCapacity, TagCategory category, boolean emptyFilterNeeded) {
-        if (emptyFilterNeeded) {
-            // previous run may have decremented counts  
-            MutableList<Object> nowOkayTags = MutableList.of(); 
-            for (Map.Entry<Object,AtomicInteger> entry: taskTagsInCategoryOverCapacity.entrySet()) {
-                if (entry.getValue().get()<=0) nowOkayTags.add(entry.getKey());
-            }
-            for (Object tag: nowOkayTags) taskTagsInCategoryOverCapacity.remove(tag);
-        }
-        
-        if (taskTagsInCategoryOverCapacity.isEmpty())
-            return 0;
-        
-        Collection<Task<?>> tasks = executionManager.allTasksLive();
-        List<Task<?>> tasksToConsiderDeleting = MutableList.of();
-        try {
-            for (Task<?> task: tasks) {
-                if (!task.isDone()) continue;
-                
-                Set<Object> tags = task.getTags();
-
-                int categoryTags = 0, tooFullCategoryTags = 0;
-                for (Object tag: tags) {
-                    if (category.acceptsTag(tag)) {
-                        categoryTags++;
-                        if (taskTagsInCategoryOverCapacity.containsKey(tag))
-                            tooFullCategoryTags++;
-                    }
-                }
-                if (tooFullCategoryTags>0) {
-                    if (categoryTags==tooFullCategoryTags) {
-                        // all buckets are full, delete this one
-                        tasksToConsiderDeleting.add(task);
-                    } else {
-                        // if any bucket is under capacity, then give grace to the other buckets in this category
-                        for (Object tag: tags) {
-                            if (category.acceptsTag(tag)) {
-                                AtomicInteger over = taskTagsInCategoryOverCapacity.get(tag);
-                                if (over!=null) {
-                                    if (over.decrementAndGet()<=0) {
-                                        // and remove it from over-capacity if so
-                                        taskTagsInCategoryOverCapacity.remove(tag);
-                                        if (taskTagsInCategoryOverCapacity.isEmpty())
-                                            return 0;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-
-        } catch (ConcurrentModificationException e) {
-            // do CME's happen with these data structures?
-            // if so, let's just delete what we've found so far
-            LOG.debug("Got CME inspecting tasks, with "+tasksToConsiderDeleting.size()+" found for deletion: "+e);
-        }
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("brooklyn-gc detected "+taskTagsInCategoryOverCapacity.size()+" "+category+" "
-                    + "tags over capacity, expiring old tasks; "
-                    + tasksToConsiderDeleting.size()+" tasks under consideration; categories are: "
-                    + taskTagsInCategoryOverCapacity);
-
-        Collections.sort(tasksToConsiderDeleting, TASKS_OLDEST_FIRST_COMPARATOR);
-        // now try deleting tasks which are overcapacity for each (non-entity) tag
-        int deleted = 0;
-        for (Task<?> task: tasksToConsiderDeleting) {
-            boolean delete = true;
-            for (Object tag: task.getTags()) {
-                if (!category.acceptsTag(tag))
-                    continue;
-                if (taskTagsInCategoryOverCapacity.get(tag)==null) {
-                    // no longer over capacity in this tag
-                    delete = false;
-                    break;
-                }
-            }
-            if (delete) {
-                // delete this and update overcapacity info
-                deleted++;
-                executionManager.deleteTask(task);
-                for (Object tag: task.getTags()) {
-                    AtomicInteger counter = taskAllTagsOverCapacity.get(tag);
-                    if (counter!=null && counter.decrementAndGet()<=0)
-                        taskTagsInCategoryOverCapacity.remove(tag);
-                }
-                if (LOG.isTraceEnabled())
-                    LOG.trace("brooklyn-gc deleted "+task+", buckets now "+taskTagsInCategoryOverCapacity);
-                if (taskTagsInCategoryOverCapacity.isEmpty())
-                    break;
-            }
-        }
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("brooklyn-gc deleted "+deleted+" tasks in over-capacity " + category+" tag categories; "
-                    + "capacities now: " + taskTagsInCategoryOverCapacity);
-        return deleted;
-    }
-
-    protected int expireIfOverCapacityGlobally() {
-        Collection<Task<?>> tasksLive = executionManager.allTasksLive();
-        if (tasksLive.size() <= brooklynProperties.getConfig(MAX_TASKS_GLOBAL))
-            return 0;
-        LOG.debug("brooklyn-gc detected "+tasksLive.size()+" tasks in memory, over global limit, looking at deleting some");
-        
-        try {
-            tasksLive = MutableList.copyOf(tasksLive);
-        } catch (ConcurrentModificationException e) {
-            tasksLive = executionManager.getTasksWithAllTags(MutableList.of());
-        }
-
-        MutableList<Task<?>> tasks = MutableList.of();
-        for (Task<?> task: tasksLive) {
-            if (task.isDone()) {
-                tasks.add(task);
-            }
-        }
-        
-        int numToDelete = tasks.size() - brooklynProperties.getConfig(MAX_TASKS_GLOBAL);
-        if (numToDelete <= 0) {
-            LOG.debug("brooklyn-gc detected only "+tasks.size()+" completed tasks in memory, not over global limit, so not deleting any");
-            return 0;
-        }
-            
-        Collections.sort(tasks, TASKS_OLDEST_FIRST_COMPARATOR);
-        
-        int numDeleted = 0;
-        while (numDeleted < numToDelete && tasks.size()>numDeleted) {
-            executionManager.deleteTask( tasks.get(numDeleted++) );
-        }
-        if (LOG.isDebugEnabled())
-            LOG.debug("brooklyn-gc deleted "+numDeleted+" tasks as was over global limit, now have "+executionManager.allTasksLive().size());
-        return numDeleted;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/BrooklynObjectManagementMode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/BrooklynObjectManagementMode.java b/core/src/main/java/brooklyn/management/internal/BrooklynObjectManagementMode.java
deleted file mode 100644
index dd9d8bc..0000000
--- a/core/src/main/java/brooklyn/management/internal/BrooklynObjectManagementMode.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.management.internal;
-
-/** Indicates how an entity/location/adjunct is treated at a given {@link ManagementContext} */
-public enum BrooklynObjectManagementMode {
-    /** item does not exist, not in memory, nor persisted (e.g. creating for first time, or finally destroying) */
-    NONEXISTENT, 
-    /** item exists or existed elsewhere, i.e. there is persisted state, but is not loaded here */
-    UNMANAGED_PERSISTED, 
-    /** item is loaded but read-only (ie not actively managed here) */
-    LOADED_READ_ONLY, 
-    /** item is actively managed here */
-    MANAGED_PRIMARY 
-}
\ No newline at end of file



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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java b/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
deleted file mode 100644
index e8d2cfa..0000000
--- a/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.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.management.internal;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.net.URI;
-import java.net.URL;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.api.basic.BrooklynObject;
-import org.apache.brooklyn.api.catalog.BrooklynCatalog;
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.Effector;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.drivers.EntityDriverManager;
-import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolverManager;
-import org.apache.brooklyn.api.entity.rebind.ChangeListener;
-import org.apache.brooklyn.api.entity.rebind.PersistenceExceptionHandler;
-import org.apache.brooklyn.api.entity.rebind.RebindExceptionHandler;
-import org.apache.brooklyn.api.entity.rebind.RebindManager;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.location.LocationRegistry;
-import org.apache.brooklyn.api.management.AccessController;
-import org.apache.brooklyn.api.management.EntityManager;
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.ExecutionManager;
-import org.apache.brooklyn.api.management.LocationManager;
-import org.apache.brooklyn.api.management.SubscriptionContext;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
-import org.apache.brooklyn.api.management.ha.HighAvailabilityManager;
-import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
-import org.apache.brooklyn.api.management.ha.ManagementNodeState;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
-import org.apache.brooklyn.api.mementos.BrooklynMementoPersister;
-import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
-
-import brooklyn.catalog.internal.CatalogInitialization;
-import brooklyn.config.BrooklynProperties;
-import brooklyn.config.StringConfigMap;
-import brooklyn.entity.basic.AbstractEntity;
-import brooklyn.entity.proxying.InternalEntityFactory;
-import brooklyn.entity.proxying.InternalLocationFactory;
-import brooklyn.entity.proxying.InternalPolicyFactory;
-import brooklyn.internal.storage.BrooklynStorage;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.ha.OsgiManager;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Objects;
-
-public class NonDeploymentManagementContext implements ManagementContextInternal {
-
-    private static final Logger log = LoggerFactory.getLogger(NonDeploymentManagementContext.class);
-    
-    public enum NonDeploymentManagementContextMode {
-        PRE_MANAGEMENT,
-        MANAGEMENT_REBINDING,
-        MANAGEMENT_STARTING,
-        MANAGEMENT_STARTED,
-        MANAGEMENT_STOPPING,
-        MANAGEMENT_STOPPED;
-        
-        public boolean isPreManaged() {
-            return this == PRE_MANAGEMENT || this == MANAGEMENT_REBINDING;
-        }
-    }
-    
-    private final AbstractEntity entity;
-    private NonDeploymentManagementContextMode mode;
-    private ManagementContextInternal initialManagementContext;
-    
-    private final QueueingSubscriptionManager qsm;
-    private final BasicSubscriptionContext subscriptionContext;
-    private NonDeploymentEntityManager entityManager;
-    private NonDeploymentLocationManager locationManager;
-    private NonDeploymentAccessManager accessManager;
-    private NonDeploymentUsageManager usageManager;
-    private EntitlementManager entitlementManager;;
-
-    public NonDeploymentManagementContext(AbstractEntity entity, NonDeploymentManagementContextMode mode) {
-        this.entity = checkNotNull(entity, "entity");
-        this.mode = checkNotNull(mode, "mode");
-        qsm = new QueueingSubscriptionManager();
-        subscriptionContext = new BasicSubscriptionContext(qsm, entity);
-        entityManager = new NonDeploymentEntityManager(null);
-        locationManager = new NonDeploymentLocationManager(null);
-        accessManager = new NonDeploymentAccessManager(null);
-        usageManager = new NonDeploymentUsageManager(null);
-        
-        // TODO might need to be some kind of "system" which can see that the system is running at this point
-        // though quite possibly we are entirely behind the auth-wall at this point
-        entitlementManager = Entitlements.minimal();
-    }
-
-    @Override
-    public String getManagementPlaneId() {
-        return (initialManagementContext == null) ? null : initialManagementContext.getManagementPlaneId();
-    }
-    
-    @Override
-    public String getManagementNodeId() {
-        return (initialManagementContext == null) ? null : initialManagementContext.getManagementNodeId();
-    }
-
-    @Override
-    public Maybe<URI> getManagementNodeUri() {
-        return (initialManagementContext == null) ? Maybe.<URI>absent() : initialManagementContext.getManagementNodeUri();
-    }
-
-    public void setManagementContext(ManagementContextInternal val) {
-        this.initialManagementContext = checkNotNull(val, "initialManagementContext");
-        this.entityManager = new NonDeploymentEntityManager(val);
-        this.locationManager = new NonDeploymentLocationManager(val);
-        this.accessManager = new NonDeploymentAccessManager(val);
-        this.usageManager = new NonDeploymentUsageManager(val);
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(this).add("entity", entity.getId()).add("mode", mode).toString();
-    }
-    
-    public void setMode(NonDeploymentManagementContextMode mode) {
-        this.mode = checkNotNull(mode, "mode");
-    }
-    public NonDeploymentManagementContextMode getMode() {
-        return mode;
-    }
-    
-    @Override
-    public Collection<Application> getApplications() {
-        return Collections.emptyList();
-    }
-
-    @Override
-    public boolean isRunning() {
-        // Assume that the real management context has not been terminated, so always true
-        return true;
-    }
-    
-    @Override
-    public boolean isStartupComplete() {
-        // This mgmt context is only used by items who are not yet fully started.
-        // It's slightly misleading as this does not refer to the main mgmt context.
-        // OTOH it probably won't be used.  TBC.  -Alex, Apr 2015
-        return false;
-    }
-
-    @Override
-    public InternalEntityFactory getEntityFactory() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getEntityFactory();
-    }
-
-    @Override
-    public InternalLocationFactory getLocationFactory() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getLocationFactory();
-    }
-
-    @Override
-    public InternalPolicyFactory getPolicyFactory() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getPolicyFactory();
-    }
-
-    @Override
-    public EntityManager getEntityManager() {
-        return entityManager;
-    }
-    
-    @Override
-    public LocationManager getLocationManager() {
-        return locationManager;
-    }
-
-    @Override
-    public AccessManager getAccessManager() {
-        return accessManager;
-    }
-
-    @Override
-    public UsageManager getUsageManager() {
-        return usageManager;
-    }
-    
-    @Override
-    public Maybe<OsgiManager> getOsgiManager() {
-        return Maybe.absent();
-    }
-
-    @Override
-    public AccessController getAccessController() {
-        return getAccessManager().getAccessController();
-    }
-
-    @Override
-    public ExecutionManager getExecutionManager() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getExecutionManager();
-    }
-
-    @Override
-    public QueueingSubscriptionManager getSubscriptionManager() {
-        return qsm;
-    }
-
-    @Override
-    public synchronized SubscriptionContext getSubscriptionContext(Entity entity) {
-        if (!this.entity.equals(entity)) throw new IllegalStateException("Non-deployment context "+this+" can only use a single Entity: has "+this.entity+", but passed "+entity);
-        if (mode==NonDeploymentManagementContextMode.MANAGEMENT_STOPPED)
-            throw new IllegalStateException("Entity "+entity+" is no longer managed; subscription context not available");
-        return subscriptionContext;
-    }
-
-    @Override
-    public ExecutionContext getExecutionContext(Entity entity) {
-        if (!this.entity.equals(entity)) throw new IllegalStateException("Non-deployment context "+this+" can only use a single Entity: has "+this.entity+", but passed "+entity);
-        if (mode==NonDeploymentManagementContextMode.MANAGEMENT_STOPPED)
-            throw new IllegalStateException("Entity "+entity+" is no longer managed; execution context not available");
-        checkInitialManagementContextReal();
-        return initialManagementContext.getExecutionContext(entity);
-    }
-
-    @Override
-    public ExecutionContext getServerExecutionContext() {
-        return initialManagementContext.getServerExecutionContext();
-    }
-
-    // TODO the methods below should delegate to the application?
-    @Override
-    public EntityDriverManager getEntityDriverManager() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getEntityDriverManager();
-    }
-
-    @Override
-    public DownloadResolverManager getEntityDownloadsManager() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getEntityDownloadsManager();
-    }
-
-    @Override
-    public StringConfigMap getConfig() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getConfig();
-    }
-
-    @Override
-    public BrooklynProperties getBrooklynProperties() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getBrooklynProperties();
-    }
-
-    @Override
-    public BrooklynStorage getStorage() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getStorage();
-    }
-    
-    @Override
-    public RebindManager getRebindManager() {
-        // There was a race where EffectorUtils on invoking an effector calls:
-        //     mgmtSupport.getEntityChangeListener().onEffectorCompleted(eff);
-        // but where the entity/app may be being unmanaged concurrently (e.g. calling app.stop()).
-        // So now we allow the change-listener to be called.
-        
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getRebindManager();
-        } else {
-            return new NonDeploymentRebindManager();
-        }
-    }
-
-    @Override
-    public HighAvailabilityManager getHighAvailabilityManager() {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getHighAvailabilityManager();
-        } else {
-            return new NonDeploymentHighAvailabilityManager();
-        }
-    }
-
-    @Override
-    public LocationRegistry getLocationRegistry() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getLocationRegistry();
-    }
-
-    @Override
-    public BrooklynCatalog getCatalog() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getCatalog();
-    }
-    
-    @Override
-    public ClassLoader getCatalogClassLoader() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getCatalogClassLoader();
-    }
-    
-    @Override
-    public EntitlementManager getEntitlementManager() {
-        return entitlementManager;
-    }
-    
-    @Override
-    public <T> T invokeEffectorMethodSync(final Entity entity, final Effector<T> eff, final Object args) throws ExecutionException {
-        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot invoke effector "+eff+" on entity "+entity);
-    }
-    
-    @Override
-    public <T> Task<T> invokeEffector(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters) {
-        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot invoke effector "+eff+" on entity "+entity);
-    }
-
-    @Override
-    public ClassLoader getBaseClassLoader() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getBaseClassLoader();
-    }
-
-    @Override
-    public Iterable<URL> getBaseClassPathForScanning() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getBaseClassPathForScanning();
-    }
-
-    @Override
-    public void addEntitySetListener(CollectionChangeListener<Entity> listener) {
-        checkInitialManagementContextReal();
-        initialManagementContext.addEntitySetListener(listener);
-    }
-
-    @Override
-    public void removeEntitySetListener(CollectionChangeListener<Entity> listener) {
-        checkInitialManagementContextReal();
-        initialManagementContext.removeEntitySetListener(listener);
-    }
-
-    @Override
-    public void terminate() {
-        if (isInitialManagementContextReal()) {
-            initialManagementContext.terminate();
-        } else {
-            // no-op; the non-deployment management context has nothing needing terminated
-        }
-    }
-
-    @Override
-    public long getTotalEffectorInvocations() {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getTotalEffectorInvocations();
-        } else {
-            return 0;
-        }
-    }
-
-    @Override
-    public void setBaseClassPathForScanning(Iterable<URL> urls) {
-        checkInitialManagementContextReal();
-        initialManagementContext.setBaseClassPathForScanning(urls);
-    }
-
-    @Override
-    public void setManagementNodeUri(URI uri) {
-        checkInitialManagementContextReal();
-        initialManagementContext.setManagementNodeUri(uri);
-    }
-
-    @Override
-    public void prePreManage(Entity entity) {
-        // should throw?  but in 0.7.0-SNAPSHOT it was no-op
-        log.warn("Ignoring call to prePreManage("+entity+") on "+this);
-    }
-
-    @Override
-    public void prePreManage(Location location) {
-        // should throw?  but in 0.7.0-SNAPSHOT it was no-op
-        log.warn("Ignoring call to prePreManage("+location+") on "+this);
-    }
-
-    private boolean isInitialManagementContextReal() {
-        return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext));
-    }
-    
-    private void checkInitialManagementContextReal() {
-        if (!isInitialManagementContextReal()) {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation.");
-        }
-    }
-    
-    @Override
-    public void reloadBrooklynProperties() {
-        checkInitialManagementContextReal();
-        initialManagementContext.reloadBrooklynProperties();
-    }
-    
-    @Override
-    public void addPropertiesReloadListener(PropertiesReloadListener listener) {
-        checkInitialManagementContextReal();
-        initialManagementContext.addPropertiesReloadListener(listener);
-    }
-    
-    @Override
-    public void removePropertiesReloadListener(PropertiesReloadListener listener) {
-        checkInitialManagementContextReal();
-        initialManagementContext.removePropertiesReloadListener(listener);
-    }
-
-    @Override
-    public BrooklynObject lookup(String id) {
-        checkInitialManagementContextReal();
-        return initialManagementContext.lookup(id);
-    }
-
-    @Override
-    public <T extends BrooklynObject> T lookup(String id, Class<T> type) {
-        checkInitialManagementContextReal();
-        return initialManagementContext.lookup(id, type);
-    }
-
-    @Override
-    public List<Throwable> errors() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.errors();
-    }
-
-    @Override
-    public CatalogInitialization getCatalogInitialization() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getCatalogInitialization();
-    }
-    
-    @Override
-    public void setCatalogInitialization(CatalogInitialization catalogInitialization) {
-        checkInitialManagementContextReal();
-        initialManagementContext.setCatalogInitialization(catalogInitialization);
-    }
-
-    /**
-     * For when the initial management context is not "real"; the changeListener is a no-op, but everything else forbidden.
-     * 
-     * @author aled
-     */
-    private class NonDeploymentRebindManager implements RebindManager {
-
-        @Override
-        public ChangeListener getChangeListener() {
-            return ChangeListener.NOOP;
-        }
-
-        @Override
-        public void setPersister(BrooklynMementoPersister persister) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-
-        @Override
-        public void setPersister(BrooklynMementoPersister persister, PersistenceExceptionHandler exceptionHandler) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-
-        @Override
-        public BrooklynMementoPersister getPersister() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-
-        @Override
-        public List<Application> rebind() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-
-        @Override
-        public List<Application> rebind(ClassLoader classLoader) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-
-        @Override
-        public List<Application> rebind(ClassLoader classLoader, RebindExceptionHandler exceptionHandler) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        
-        @Override
-        public List<Application> rebind(ClassLoader classLoader, RebindExceptionHandler exceptionHandler, ManagementNodeState mode) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-
-        @Override
-        public void startPersistence() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        
-        @Override
-        public void stopPersistence() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-
-        @Override
-        public void startReadOnly(ManagementNodeState state) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        
-        @Override
-        public void stopReadOnly() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-
-        @Override
-        public void start() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        
-        @Override
-        public void stop() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-
-        @Override
-        public void waitForPendingComplete(Duration timeout, boolean canTrigger) throws InterruptedException, TimeoutException {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public void forcePersistNow() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public void forcePersistNow(boolean full, PersistenceExceptionHandler exceptionHandler) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public BrooklynMementoRawData retrieveMementoRawData() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public boolean isAwaitingInitialRebind() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        
-        @Override
-        public Map<String, Object> getMetrics() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-    }
-
-    /**
-     * For when the initial management context is not "real".
-     * 
-     * @author aled
-     */
-    private class NonDeploymentHighAvailabilityManager implements HighAvailabilityManager {
-        @Override
-        public ManagementNodeState getNodeState() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public boolean isRunning() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public HighAvailabilityManager setPersister(ManagementPlaneSyncRecordPersister persister) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public void disabled() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public void start(HighAvailabilityMode startMode) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public void stop() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public ManagementPlaneSyncRecordPersister getPersister() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public ManagementPlaneSyncRecord getLastManagementPlaneSyncRecord() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public ManagementPlaneSyncRecord loadManagementPlaneSyncRecord(boolean x) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public void changeMode(HighAvailabilityMode startMode) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public void setPriority(long priority) {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public long getPriority() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public Map<String, Object> getMetrics() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public void publishClearNonMaster() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-        @Override
-        public long getLastStateChange() {
-            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/NonDeploymentUsageManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/NonDeploymentUsageManager.java b/core/src/main/java/brooklyn/management/internal/NonDeploymentUsageManager.java
deleted file mode 100644
index ec2cd10..0000000
--- a/core/src/main/java/brooklyn/management/internal/NonDeploymentUsageManager.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.management.internal;
-
-import java.util.Set;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.location.Location;
-
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.management.usage.ApplicationUsage;
-import brooklyn.management.usage.LocationUsage;
-
-import com.google.common.base.Predicate;
-
-
-public class NonDeploymentUsageManager implements UsageManager {
-
-    // TODO All the `isInitialManagementContextReal()` code-checks is a code-smell.
-    // Expect we can delete a lot of this once we guarantee that all entities are 
-    // instantiated via EntitySpec / EntityManager. Until then, we'll live with this.
-    
-    private final ManagementContextInternal initialManagementContext;
-    
-    public NonDeploymentUsageManager(ManagementContextInternal initialManagementContext) {
-        this.initialManagementContext = initialManagementContext;
-    }
-    
-    private boolean isInitialManagementContextReal() {
-        return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext));
-    }
-
-    @Override
-    public void recordApplicationEvent(Application app, Lifecycle state) {
-        if (isInitialManagementContextReal()) {
-            initialManagementContext.getUsageManager().recordApplicationEvent(app, state);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public void recordLocationEvent(Location loc, Lifecycle state) {
-        if (isInitialManagementContextReal()) {
-            initialManagementContext.getUsageManager().recordLocationEvent(loc, state);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public LocationUsage getLocationUsage(String locationId) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getUsageManager().getLocationUsage(locationId);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public Set<LocationUsage> getLocationUsage(Predicate<? super LocationUsage> filter) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getUsageManager().getLocationUsage(filter);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public ApplicationUsage getApplicationUsage(String appId) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getUsageManager().getApplicationUsage(appId);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public Set<ApplicationUsage> getApplicationUsage(Predicate<? super ApplicationUsage> filter) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getUsageManager().getApplicationUsage(filter);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    @Deprecated
-    public void addUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener) {
-        addUsageListener(new brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener));
-    }
-
-    @Override
-    @Deprecated
-    public void removeUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener) {
-        removeUsageListener(new brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener));
-    }
-    
-    @Override
-    public void addUsageListener(brooklyn.management.internal.UsageListener listener) {
-        if (isInitialManagementContextReal()) {
-            initialManagementContext.getUsageManager().addUsageListener(listener);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public void removeUsageListener(brooklyn.management.internal.UsageListener listener) {
-        if (isInitialManagementContextReal()) {
-            initialManagementContext.getUsageManager().removeUsageListener(listener);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/QueueingSubscriptionManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/QueueingSubscriptionManager.java b/core/src/main/java/brooklyn/management/internal/QueueingSubscriptionManager.java
deleted file mode 100644
index fb232ad..0000000
--- a/core/src/main/java/brooklyn/management/internal/QueueingSubscriptionManager.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.management.internal;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.event.Sensor;
-import org.apache.brooklyn.api.event.SensorEvent;
-import org.apache.brooklyn.api.management.SubscriptionHandle;
-
-import com.google.common.base.Objects;
-
-@SuppressWarnings("rawtypes")
-public class QueueingSubscriptionManager extends AbstractSubscriptionManager {
-
-    static class QueuedSubscription<T> {
-        Map<String, Object> flags;
-        Subscription<T> s;
-    }
-
-    public AbstractSubscriptionManager delegate = null;
-    public boolean useDelegateForSubscribing = false;
-    public boolean useDelegateForPublishing = false;
-    
-    List<QueuedSubscription> queuedSubscriptions = new ArrayList<QueuedSubscription>();
-    List<SensorEvent> queuedSensorEvents = new ArrayList<SensorEvent>();
-    
-    @Override
-    protected synchronized <T> SubscriptionHandle subscribe(Map<String, Object> flags, Subscription<T> s) {
-        if (useDelegateForSubscribing)
-            return delegate.subscribe(flags, s);
-        
-        QueuedSubscription<T> qs = new QueuedSubscription<T>();
-        qs.flags = flags;
-        s.subscriber = getSubscriber(flags, s);
-        qs.s = s;
-        queuedSubscriptions.add(qs);
-        return s;
-    }
-
-    @Override
-    public synchronized <T> void publish(SensorEvent<T> event) {
-        if (useDelegateForPublishing) {
-            delegate.publish(event);
-            return;
-        }
-        
-        queuedSensorEvents.add(event);
-    }
-
-    public void setDelegate(AbstractSubscriptionManager delegate) {
-        this.delegate = delegate;
-    }
-    
-    @SuppressWarnings("unchecked")
-    public synchronized void startDelegatingForSubscribing() {
-        assert delegate!=null;
-        for (QueuedSubscription s: queuedSubscriptions) {
-            delegate.subscribe(s.flags, s.s);
-        }
-        queuedSubscriptions.clear();
-        useDelegateForSubscribing = true;
-    }
-    
-    @SuppressWarnings("unchecked")
-    public synchronized void startDelegatingForPublishing() {
-        assert delegate!=null;
-        for (SensorEvent evt: queuedSensorEvents) {
-            delegate.publish(evt);
-        }
-        queuedSensorEvents.clear();
-        useDelegateForPublishing = true;
-    }
-    
-    public synchronized void stopDelegatingForSubscribing() {
-        useDelegateForSubscribing = false;
-    }
-    
-    public synchronized void stopDelegatingForPublishing() {
-        useDelegateForPublishing = false;
-    }
-
-    @Override
-    public synchronized boolean unsubscribe(SubscriptionHandle subscriptionId) {
-        if (useDelegateForSubscribing)
-            return delegate.unsubscribe(subscriptionId);
-        
-        Iterator<QueuedSubscription> qi = queuedSubscriptions.iterator();
-        while (qi.hasNext()) {
-            QueuedSubscription q = qi.next();
-            if (Objects.equal(subscriptionId, q.s)) {
-                qi.remove();
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public synchronized Set<SubscriptionHandle> getSubscriptionsForSubscriber(Object subscriber) {
-        if (useDelegateForSubscribing)
-            return delegate.getSubscriptionsForSubscriber(subscriber);
-        
-        Set<SubscriptionHandle> result = new LinkedHashSet<SubscriptionHandle>();
-        for (QueuedSubscription q: queuedSubscriptions) {
-            if (Objects.equal(subscriber, getSubscriber(q.flags, q.s))) result.add(q.s);
-        }
-        return result;
-    }
-
-    @Override
-    public synchronized Set<SubscriptionHandle> getSubscriptionsForEntitySensor(Entity source, Sensor<?> sensor) {
-        if (useDelegateForSubscribing)
-            return delegate.getSubscriptionsForEntitySensor(source, sensor);
-        
-        Set<SubscriptionHandle> result = new LinkedHashSet<SubscriptionHandle>();
-        for (QueuedSubscription q: queuedSubscriptions) {
-            if ((q.s.sensor==null || Objects.equal(q.s.sensor, sensor)) &&
-                    (q.s.producer==null || Objects.equal(q.s.producer, sensor)))
-                result.add(q.s);
-        }
-        return result;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/Subscription.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/Subscription.java b/core/src/main/java/brooklyn/management/internal/Subscription.java
deleted file mode 100644
index f8409cb..0000000
--- a/core/src/main/java/brooklyn/management/internal/Subscription.java
+++ /dev/null
@@ -1,66 +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.management.internal;
-
-import java.util.Map;
-
-import org.apache.brooklyn.api.entity.Entity;
-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.management.SubscriptionHandle;
-
-import brooklyn.util.text.Identifiers;
-
-import com.google.common.base.Predicate;
-
-class Subscription<T> implements SubscriptionHandle {
-    public final String id = Identifiers.makeRandomId(8);
-    
-    public Object subscriber;
-    public Object subscriberExecutionManagerTag;
-    /** whether the tag was supplied by user, in which case we should not clear execution semantics */
-    public boolean subscriberExecutionManagerTagSupplied;
-    public final Entity producer;
-    public final Sensor<T> sensor;
-    public final SensorEventListener<? super T> listener;
-    public Map<String,Object> flags;
-    public Predicate<SensorEvent<T>> eventFilter;
-
-    public Subscription(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        this.producer = producer;
-        this.sensor = sensor;
-        this.listener = listener;
-    }
-    
-    @Override
-    public boolean equals(Object other) {
-        return (other instanceof Subscription && ((Subscription<?>)other).id==id);
-    }
-
-    @Override
-    public int hashCode() {
-        return id.hashCode();
-    }
-    
-    @Override
-    public String toString() {
-        return "Subscription["+id+";"+subscriber+"@"+LocalSubscriptionManager.makeEntitySensorToken(producer,sensor)+"]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/SubscriptionTracker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/SubscriptionTracker.java b/core/src/main/java/brooklyn/management/internal/SubscriptionTracker.java
deleted file mode 100644
index ec86fae..0000000
--- a/core/src/main/java/brooklyn/management/internal/SubscriptionTracker.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.management.internal;
-
-import java.util.Collection;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.Group;
-import org.apache.brooklyn.api.event.Sensor;
-import org.apache.brooklyn.api.event.SensorEventListener;
-import org.apache.brooklyn.api.management.SubscriptionContext;
-import org.apache.brooklyn.api.management.SubscriptionHandle;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.SetMultimap;
-
-/**
- * Tracks subscriptions associated that are registered with particular entities. Gives utilities for unsubscribing from all
- * subscriptions on a given entity, etc.
- */
-public class SubscriptionTracker {
-
-    // This class is thread-safe. All modifications to subscriptions are synchronized on the 
-    // "subscriptions" field. However, calls to alien code (i.e. context.subscribe etc) is
-    // done without holding the lock.
-    //
-    // If two threads do subscribe() and unsubscribeAll() concurrently, then it's non-derministic
-    // whether the subscription will be in place at the end (but that's unavoidable). However, it
-    // is guaranteed that the internal state of the SubscriptionTracker will be consistent: if
-    // the "subscriptions" includes the new subscription then that subscription will really exist,
-    // and vice versa.
-    
-    protected SubscriptionContext context;
-    
-    private final SetMultimap<Entity, SubscriptionHandle> subscriptions = HashMultimap.create();
-
-    public SubscriptionTracker(SubscriptionContext subscriptionContext) {
-        this.context = subscriptionContext;
-    }
-    
-    /** @see SubscriptionContext#subscribe(Entity, Sensor, SensorEventListener) */
-    public <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        SubscriptionHandle handle = context.subscribe(producer, sensor, listener);
-        synchronized (subscriptions) {
-            subscriptions.put(producer, handle);
-        }
-        return handle;
-    }
-    
-    /** @see SubscriptionContext#subscribeToChildren(Entity, Sensor, SensorEventListener) */
-    public <T> SubscriptionHandle subscribeToChildren(Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        SubscriptionHandle handle = context.subscribeToChildren(parent, sensor, listener);
-        synchronized (subscriptions) {
-            subscriptions.put(parent, handle);
-        }
-        return handle;
-    }
-
-    /**
-     * @see SubscriptionContext#subscribeToMembers(Group, Sensor, SensorEventListener)
-     */
-    public <T> SubscriptionHandle subscribeToMembers(Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
-        SubscriptionHandle handle = context.subscribeToMembers(parent, sensor, listener);
-        synchronized (subscriptions) {
-            subscriptions.put(parent, handle);
-        }
-        return handle;
-    }    
-
-    /**
-     * Unsubscribes the given producer.
-     *
-     * @see SubscriptionContext#unsubscribe(SubscriptionHandle)
-     */
-    public boolean unsubscribe(Entity producer) {
-        Collection<SubscriptionHandle> handles;
-        synchronized (subscriptions) {
-            handles = subscriptions.removeAll(producer);
-        }
-        if (handles != null) {
-            for (SubscriptionHandle handle : handles) {
-                context.unsubscribe(handle);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-    * Unsubscribes the given producer.
-    *
-    * @see SubscriptionContext#unsubscribe(SubscriptionHandle)
-    */
-   public boolean unsubscribe(Entity producer, SubscriptionHandle handle) {
-       synchronized (subscriptions) {
-           subscriptions.remove(producer, handle);
-       }
-       return context.unsubscribe(handle);
-   }
-
-    /**
-    * @return an ordered list of all subscription handles
-    */
-   public Collection<SubscriptionHandle> getAllSubscriptions() {
-       synchronized (subscriptions) {
-           return ImmutableList.copyOf(subscriptions.values());
-       }
-   }
-   
-   public void unsubscribeAll() {
-       Collection<SubscriptionHandle> subscriptionsSnapshot;
-       synchronized (subscriptions) {
-           subscriptionsSnapshot = ImmutableList.copyOf(subscriptions.values());
-           subscriptions.clear();
-       }
-       for (SubscriptionHandle s: subscriptionsSnapshot) {
-           context.unsubscribe(s);
-       }
-   }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/UsageListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/UsageListener.java b/core/src/main/java/brooklyn/management/internal/UsageListener.java
deleted file mode 100644
index 5a2af3a..0000000
--- a/core/src/main/java/brooklyn/management/internal/UsageListener.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.management.internal;
-
-import java.util.Map;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.location.Location;
-
-import brooklyn.management.usage.ApplicationUsage.ApplicationEvent;
-import brooklyn.management.usage.LocationUsage.LocationEvent;
-
-import com.google.common.annotations.Beta;
-
-@Beta
-public interface UsageListener {
-    
-    /**
-     * A no-op implementation of {@link UsageListener}, for users to extend.
-     * 
-     * Users are encouraged to extend this class, which will shield the user 
-     * from the addition of other usage event methods being added. If additional
-     * methods are added in a future release, a no-op implementation will be
-     * added to this class.
-     */
-    @Beta
-    public static class BasicUsageListener implements UsageListener {
-        @Override
-        public void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event) {
-        }
-        
-        @Override public void onLocationEvent(LocationMetadata loc, LocationEvent event) {
-        }
-    }
-    
-    /**
-     * Users should never implement this interface directly; methods may be added in future releases
-     * without notice.
-     */
-    @Beta
-    public interface ApplicationMetadata {
-        /**
-         * Access the application directly with caution: by the time the listener fires, 
-         * the application may no longer be managed.
-         */
-        @Beta
-        Application getApplication();
-        
-        String getApplicationId();
-        
-        String getApplicationName();
-        
-        String getEntityType();
-        
-        String getCatalogItemId();
-        
-        Map<String, String> getMetadata();
-    }
-    
-    /**
-     * Users should never implement this interface directly; methods may be added in future releases
-     * without notice.
-     */
-    @Beta
-    public interface LocationMetadata {
-        /**
-         * Access the location directly with caution: by the time the listener fires, 
-         * the location may no longer be managed.
-         */
-        @Beta
-        Location getLocation();
-        
-        String getLocationId();
-        
-        Map<String, String> getMetadata();
-    }
-    
-    public static final UsageListener NOOP = new UsageListener() {
-        @Override public void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event) {}
-        @Override public void onLocationEvent(LocationMetadata loc, LocationEvent event) {}
-    };
-    
-    @Beta
-    void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event);
-    
-    @Beta
-    void onLocationEvent(LocationMetadata loc, LocationEvent event);
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/UsageManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/UsageManager.java b/core/src/main/java/brooklyn/management/internal/UsageManager.java
deleted file mode 100644
index 5105faf..0000000
--- a/core/src/main/java/brooklyn/management/internal/UsageManager.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.management.internal;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.location.Location;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.management.usage.ApplicationUsage;
-import brooklyn.management.usage.ApplicationUsage.ApplicationEvent;
-import brooklyn.management.usage.LocationUsage;
-import brooklyn.management.usage.LocationUsage.LocationEvent;
-import brooklyn.util.time.Duration;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.reflect.TypeToken;
-
-@Beta
-public interface UsageManager {
-
-    @SuppressWarnings("serial")
-    public static final ConfigKey<List<brooklyn.management.internal.UsageListener>> USAGE_LISTENERS = ConfigKeys.newConfigKey(
-            new TypeToken<List<brooklyn.management.internal.UsageListener>>() {},
-            "brooklyn.usageManager.listeners", "Optional usage listeners (i.e. for metering)",
-            ImmutableList.<brooklyn.management.internal.UsageListener>of());
-    
-    public static final ConfigKey<Duration> USAGE_LISTENER_TERMINATION_TIMEOUT = ConfigKeys.newConfigKey(
-            Duration.class,
-            "brooklyn.usageManager.listeners.timeout",
-            "Timeout on termination, to wait for queue of usage listener events to be processed",
-            Duration.TEN_SECONDS);
-
-    /**
-     * @since 0.7.0
-     * @deprecated since 0.7.0; use {@link brooklyn.management.internal.UsageListener}; see {@link UsageListenerAdapter} 
-     */
-    public interface UsageListener {
-        public static final UsageListener NOOP = new UsageListener() {
-            @Override public void onApplicationEvent(String applicationId, String applicationName, String entityType, 
-                    String catalogItemId, Map<String, String> metadata, ApplicationEvent event) {}
-            @Override public void onLocationEvent(String locationId, Map<String, String> metadata, LocationEvent event) {}
-        };
-        
-        public static class UsageListenerAdapter implements brooklyn.management.internal.UsageListener {
-            private final UsageListener listener;
-
-            public UsageListenerAdapter(UsageListener listener) {
-                this.listener = checkNotNull(listener, "listener");
-            }
-            
-            @Override
-            public void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event) {
-                listener.onApplicationEvent(app.getApplicationId(), app.getApplicationName(), app.getEntityType(), app.getCatalogItemId(), app.getMetadata(), event);
-            }
-
-            @Override
-            public void onLocationEvent(LocationMetadata loc, LocationEvent event) {
-                listener.onLocationEvent(loc.getLocationId(), loc.getMetadata(), event);
-            }
-            
-            @Override
-            public boolean equals(Object obj) {
-                return (obj instanceof UsageListenerAdapter) && listener.equals(((UsageListenerAdapter)obj).listener);
-            }
-            
-            @Override
-            public int hashCode() {
-                return Objects.hashCode(listener);
-            }
-        }
-        
-        void onApplicationEvent(String applicationId, String applicationName, String entityType, String catalogItemId,
-                Map<String, String> metadata, ApplicationEvent event);
-        
-        void onLocationEvent(String locationId, Map<String, String> metadata, LocationEvent event);
-    }
-
-    /**
-     * Adds this application event to the usage record for the given app (creating the usage 
-     * record if one does not already exist).
-     */
-    void recordApplicationEvent(Application app, Lifecycle state);
-    
-    /**
-     * Adds this location event to the usage record for the given location (creating the usage 
-     * record if one does not already exist).
-     */
-    void recordLocationEvent(Location loc, Lifecycle state);
-
-    /**
-     * Returns the usage info for the location with the given id, or null if unknown.
-     */
-    LocationUsage getLocationUsage(String locationId);
-    
-    /**
-     * Returns the usage info that matches the given predicate.
-     * For example, could be used to find locations used within a given time period.
-     */
-    Set<LocationUsage> getLocationUsage(Predicate<? super LocationUsage> filter);
-    
-    /**
-     * Returns the usage info for the application with the given id, or null if unknown.
-     */
-    ApplicationUsage getApplicationUsage(String appId);
-    
-    /**
-     * Returns the usage info that matches the given predicate.
-     * For example, could be used to find applications used within a given time period.
-     */
-    Set<ApplicationUsage> getApplicationUsage(Predicate<? super ApplicationUsage> filter);
-
-    /**
-     * @since 0.7.0
-     * @deprecated since 0.7.0; use {@link #removeUsageListener(brooklyn.management.internal.UsageListener)};
-     *             see {@link brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter} 
-     */
-    void addUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener);
-
-    /**
-     * @since 0.7.0
-     * @deprecated since 0.7.0; use {@link #removeUsageListener(brooklyn.management.internal.UsageListener)}
-     */
-    @Deprecated
-    void removeUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener);
-    
-    /**
-     * Adds the given listener, to be notified on recording of application/location events.
-     * The listener notifications may be asynchronous.
-     * 
-     * As of 0.7.0, the listener is not persisted so will be lost on restart/rebind. This
-     * behaviour may change in a subsequent release. 
-     */
-    void addUsageListener(brooklyn.management.internal.UsageListener listener);
-
-    /**
-     * Removes the given listener.
-     */
-    void removeUsageListener(brooklyn.management.internal.UsageListener listener);
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/usage/ApplicationUsage.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/usage/ApplicationUsage.java b/core/src/main/java/brooklyn/management/usage/ApplicationUsage.java
deleted file mode 100644
index 7186783..0000000
--- a/core/src/main/java/brooklyn/management/usage/ApplicationUsage.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.management.usage;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-import brooklyn.entity.basic.Lifecycle;
-
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-/**
- */
-public class ApplicationUsage {
-    
-    public static class ApplicationEvent {
-        private final Date date;
-        private final Lifecycle state;
-        private final String user;
-
-        public ApplicationEvent(Lifecycle state, String user) {
-            this(new Date(), state, user);
-        }
-
-        public ApplicationEvent(Date date, Lifecycle state) {
-            this(date,state, null);
-        }
-
-        public ApplicationEvent(Date date, Lifecycle state, String user) {
-            this.date = checkNotNull(date, "date");
-            this.state = checkNotNull(state, "state");
-            this.user = user;
-        }
-
-        public Date getDate() {
-            return date;
-        }
-
-        public Lifecycle getState() {
-            return state;
-        }
-
-        public String getUser() {
-            return user;
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            if (!(other instanceof ApplicationEvent)) return false;
-            ApplicationEvent o = (ApplicationEvent) other;
-            return Objects.equal(date, o.date) && Objects.equal(state, o.state) && Objects.equal(user, o.user);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hashCode(date, state, user);
-        }
-        
-        @Override
-        public String toString() {
-            return Objects.toStringHelper(this).add("date", date).add("state", state).add("entitlementContext", user).toString();
-        }
-    }
-    
-    private final String applicationId;
-    private final String applicationName;
-    private final String entityType;
-    private final Map<String, String> metadata;
-    private final List<ApplicationEvent> events = Collections.synchronizedList(Lists.<ApplicationEvent>newArrayList());
-
-    public ApplicationUsage(String applicationId, String applicationName, String entityType, Map<String, String> metadata) {
-        this.applicationId = checkNotNull(applicationId, "applicationId");
-        // allow name to be null, happens in certain failed rebind cases
-        this.applicationName = applicationName;
-        this.entityType = checkNotNull(entityType, "entityType");
-        this.metadata = checkNotNull(metadata, "metadata");
-    }
-
-    public String getApplicationId() {
-        return applicationId;
-    }
-
-    public String getApplicationName() {
-        return applicationName;
-    }
-
-    public String getEntityType() {
-        return entityType;
-    }
-
-    public Map<String, String> getMetadata() {
-        return metadata;
-    }
-    
-    public List<ApplicationEvent> getEvents() {
-        synchronized (events) {
-            return ImmutableList.copyOf(events);
-        }
-    }
-
-    public void addEvent(ApplicationEvent event) {
-        events.add(checkNotNull(event, "event"));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/usage/LocationUsage.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/usage/LocationUsage.java b/core/src/main/java/brooklyn/management/usage/LocationUsage.java
deleted file mode 100644
index fbca540..0000000
--- a/core/src/main/java/brooklyn/management/usage/LocationUsage.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.management.usage;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-import brooklyn.entity.basic.Lifecycle;
-
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-/**
- */
-public class LocationUsage {
-    
-    public static class LocationEvent {
-        private final Date date;
-        private final Lifecycle state;
-        private final String entityId;
-        private final String entityType;
-        private final String applicationId;
-        private final String user;
-
-        public LocationEvent(Lifecycle state, String entityId, String entityType, String applicationId, String user) {
-            this(new Date(), state, entityId, entityType, applicationId, user);
-        }
-        
-        public LocationEvent(Date date, Lifecycle state, String entityId, String entityType, String applicationId, String user) {
-            this.date = checkNotNull(date, "date");
-            this.state = checkNotNull(state, "state");
-            this.entityId = checkNotNull(entityId, "entityId");
-            this.entityType = checkNotNull(entityType, "entityType");
-            this.applicationId = checkNotNull(applicationId, "applicationId (entity "+entityId+")");
-            this.user = user;
-        }
-
-        public Date getDate() {
-            return date;
-        }
-
-        public Lifecycle getState() {
-            return state;
-        }
-
-        public String getEntityId() {
-            return entityId;
-        }
-
-        public String getEntityType() {
-            return entityType;
-        }
-
-        public String getApplicationId() {
-            return applicationId;
-        }
-
-        public String getUser() {
-            return user;
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            if (!(other instanceof LocationEvent)) return false;
-            LocationEvent o = (LocationEvent) other;
-            return Objects.equal(date, o.date) && Objects.equal(state, o.state) 
-                    && Objects.equal(entityId, o.entityId) && Objects.equal(entityType, o.entityType)
-                    && Objects.equal(applicationId, o.applicationId) && Objects.equal(user, o.user);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hashCode(date, state, entityId, entityType, applicationId, user);
-        }
-        
-        @Override
-        public String toString() {
-            return Objects.toStringHelper(this)
-                    .add("date", date)
-                    .add("state", state)
-                    .add("entityId", entityId)
-                    .add("appId", applicationId)
-                    .add("user", user)
-                    .toString();
-        }
-    }
-    
-    private final String locationId;
-    private final Map<String, String> metadata;
-    private final List<LocationEvent> events = Collections.synchronizedList(Lists.<LocationEvent>newArrayList());
-
-    public LocationUsage(String locationId, Map<String, String> metadata) {
-        this.locationId = checkNotNull(locationId, "locationId");
-        this.metadata = checkNotNull(metadata, "metadata");
-    }
-
-    public String getLocationId() {
-        return locationId;
-    }
-
-    public Map<String, String> getMetadata() {
-        return metadata;
-    }
-    
-    public List<LocationEvent> getEvents() {
-        synchronized (events) {
-            return ImmutableList.copyOf(events);
-        }
-    }
-
-    public void addEvent(LocationEvent event) {
-        events.add(checkNotNull(event, "event"));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 0c2314c..ab303a0 100644
--- a/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java
+++ b/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java
@@ -40,6 +40,7 @@ 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -52,7 +53,6 @@ import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
-import brooklyn.management.internal.SubscriptionTracker;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.flags.FlagUtils;
 import brooklyn.util.flags.SetFromFlag;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
index cb350af..53264e3 100644
--- a/core/src/main/java/brooklyn/util/ResourceUtils.java
+++ b/core/src/main/java/brooklyn/util/ResourceUtils.java
@@ -40,6 +40,7 @@ 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.management.classloading.JavaBrooklynClassLoadingContext;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.auth.Credentials;
@@ -53,8 +54,9 @@ import org.slf4j.LoggerFactory;
 import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.internal.BrooklynInitialization;
+
 import org.apache.brooklyn.location.basic.SshMachineLocation;
-import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
+
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.http.HttpTool;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
index 3a41fb2..8942a18 100644
--- a/core/src/main/java/brooklyn/util/task/BasicExecutionContext.java
+++ b/core/src/main/java/brooklyn/util/task/BasicExecutionContext.java
@@ -36,13 +36,13 @@ 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 brooklyn.management.entitlement.Entitlements;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
index 81f865d..e33120c 100644
--- a/core/src/main/java/brooklyn/util/task/CompoundTask.java
+++ b/core/src/main/java/brooklyn/util/task/CompoundTask.java
@@ -31,11 +31,11 @@ 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.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.MutableMap;
 
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
index a58a05a..455a889 100644
--- a/core/src/main/java/brooklyn/util/task/DynamicSequentialTask.java
+++ b/core/src/main/java/brooklyn/util/task/DynamicSequentialTask.java
@@ -31,11 +31,11 @@ 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.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.time.CountdownTimer;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
index b5d8b2d..eb0c2ad 100644
--- a/core/src/main/java/brooklyn/util/text/TemplateProcessor.java
+++ b/core/src/main/java/brooklyn/util/text/TemplateProcessor.java
@@ -30,6 +30,7 @@ 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;
 
@@ -38,7 +39,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.event.basic.DependentConfiguration;
 import brooklyn.event.basic.Sensors;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/ManagementContextInjectable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/ManagementContextInjectable.java b/core/src/main/java/org/apache/brooklyn/core/management/ManagementContextInjectable.java
new file mode 100644
index 0000000..c92639e
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/ManagementContextInjectable.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+
+/**
+ * Provides a way for the {@link ManagementContext} to be injected directly.
+ */
+public interface ManagementContextInjectable {
+
+    /**
+     * Sets the {@link ManagementContext} reference.
+     */
+    public void injectManagementContext(ManagementContext managementContext);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/classloading/AbstractBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/classloading/AbstractBrooklynClassLoadingContext.java b/core/src/main/java/org/apache/brooklyn/core/management/classloading/AbstractBrooklynClassLoadingContext.java
new file mode 100644
index 0000000..a8366df
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/classloading/AbstractBrooklynClassLoadingContext.java
@@ -0,0 +1,82 @@
+package org.apache.brooklyn.core.management.classloading;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
+
+import brooklyn.util.guava.Maybe;
+
+import com.google.common.base.Objects;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+public abstract class AbstractBrooklynClassLoadingContext implements BrooklynClassLoadingContext {
+
+    protected final ManagementContext mgmt;
+
+    public AbstractBrooklynClassLoadingContext(ManagementContext mgmt) {
+        this.mgmt = mgmt;
+    }
+
+    @Override
+    public ManagementContext getManagementContext() {
+        return mgmt;
+    }
+    
+    @Override
+    public Class<?> loadClass(String className) {
+        return tryLoadClass(className).get();
+    }
+
+    @Override
+    // this is the only one left for subclasses
+    public abstract Maybe<Class<?>> tryLoadClass(String className);
+
+    @Override
+    public <T> Class<? extends T> loadClass(String className, @Nullable Class<T> supertype) {
+        return tryLoadClass(className, supertype).get();
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Override
+    public <T> Maybe<Class<? extends T>> tryLoadClass(String className, @Nullable Class<T> supertype) {
+        Maybe<Class<?>> result = tryLoadClass(className);
+        if (result.isAbsent()) return (Maybe)result;
+        Class<?> clazz = result.get();
+        if (supertype==null || supertype.isAssignableFrom(clazz)) return (Maybe)result;
+        throw new ClassCastException(className+" is not an instance of "+supertype);
+    }
+
+    @Override
+    public abstract String toString();
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mgmt);
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof BrooklynClassLoadingContext)) return false;
+        if (!Objects.equal(mgmt, ((BrooklynClassLoadingContext)obj).getManagementContext())) return false;
+        return true;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/classloading/BrooklynClassLoadingContextSequential.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/classloading/BrooklynClassLoadingContextSequential.java b/core/src/main/java/org/apache/brooklyn/core/management/classloading/BrooklynClassLoadingContextSequential.java
new file mode 100644
index 0000000..afc2232
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/classloading/BrooklynClassLoadingContextSequential.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.classloading;
+
+import java.net.URL;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableSet;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+public final class BrooklynClassLoadingContextSequential extends AbstractBrooklynClassLoadingContext {
+
+    private static final Logger log = LoggerFactory.getLogger(BrooklynClassLoadingContextSequential.class);
+    
+    private final List<BrooklynClassLoadingContext> primaries = MutableList.<BrooklynClassLoadingContext>of();
+    // secondaries used to put java classloader last
+    private final Set<BrooklynClassLoadingContext> secondaries = MutableSet.<BrooklynClassLoadingContext>of();
+
+    public BrooklynClassLoadingContextSequential(ManagementContext mgmt, BrooklynClassLoadingContext ...targets) {
+        super(mgmt);
+        for (BrooklynClassLoadingContext target: targets)
+            add(target);
+    }
+    
+    public void add(BrooklynClassLoadingContext target) {
+        if (target instanceof BrooklynClassLoadingContextSequential) {
+            for (BrooklynClassLoadingContext targetN: ((BrooklynClassLoadingContextSequential)target).primaries )
+                add(targetN);
+            for (BrooklynClassLoadingContext targetN: ((BrooklynClassLoadingContextSequential)target).secondaries )
+                addSecondary(targetN);
+        } else {
+            this.primaries.add( target );
+        }
+    }
+
+    public void addSecondary(BrooklynClassLoadingContext target) {
+        if (!(target instanceof JavaBrooklynClassLoadingContext)) {
+            // support for legacy catalog classloader only
+            log.warn("Only Java classloaders should be secondary");
+        }
+        this.secondaries.add( target );
+    }
+    
+    public Maybe<Class<?>> tryLoadClass(String className) {
+        List<Throwable> errors = MutableList.of();
+        for (BrooklynClassLoadingContext target: primaries) {
+            Maybe<Class<?>> clazz = target.tryLoadClass(className);
+            if (clazz.isPresent())
+                return clazz;
+            errors.add( ((Maybe.Absent<?>)clazz).getException() );
+        }
+        boolean noPrimaryErrors = errors.isEmpty();
+        for (BrooklynClassLoadingContext target: secondaries) {
+            Maybe<Class<?>> clazz = target.tryLoadClass(className);
+            if (clazz.isPresent())
+                return clazz;
+            if (noPrimaryErrors)
+                errors.add( ((Maybe.Absent<?>)clazz).getException() );
+        }
+
+        return Maybe.absent(Exceptions.create("Unable to load "+className+" from "+primaries, errors));
+    }
+
+    @Override
+    public URL getResource(String resourceInThatDir) {
+        for (BrooklynClassLoadingContext target: primaries) {
+            URL result = target.getResource(resourceInThatDir);
+            if (result!=null) return result;
+        }
+        for (BrooklynClassLoadingContext target: secondaries) {
+            URL result = target.getResource(resourceInThatDir);
+            if (result!=null) return result;
+        }
+        return null;
+    }
+
+    @Override
+    public Iterable<URL> getResources(String name) {
+        List<Iterable<URL>> resources = Lists.newArrayList();
+        for (BrooklynClassLoadingContext target : primaries) {
+            resources.add(target.getResources(name));
+        }
+        for (BrooklynClassLoadingContext target : secondaries) {
+            resources.add(target.getResources(name));
+        }
+        return Iterables.concat(resources);
+    }
+
+    @Override
+    public String toString() {
+        return "classload:"+primaries+";"+secondaries;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(super.hashCode(), primaries, secondaries);
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (!super.equals(obj)) return false;
+        if (!(obj instanceof BrooklynClassLoadingContextSequential)) return false;
+        if (!Objects.equal(primaries, ((BrooklynClassLoadingContextSequential)obj).primaries)) return false;
+        if (!Objects.equal(secondaries, ((BrooklynClassLoadingContextSequential)obj).secondaries)) return false;
+        return true;
+    }
+    
+}



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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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
new file mode 100644
index 0000000..0e8ec80
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -0,0 +1,1315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import io.brooklyn.camp.CampPlatform;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
+import io.brooklyn.camp.spi.pdp.DeploymentPlan;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.Yaml;
+
+import brooklyn.basic.BrooklynObjectInternal.ConfigurationSupportInternal;
+import brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator;
+
+import org.apache.brooklyn.api.basic.AbstractBrooklynObjectSpec;
+import org.apache.brooklyn.api.catalog.BrooklynCatalog;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.api.policy.Policy;
+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 brooklyn.config.BrooklynServerConfig;
+
+import org.apache.brooklyn.location.basic.BasicLocationRegistry;
+
+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;
+import brooklyn.util.javalang.Reflections;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+import brooklyn.util.yaml.Yamls;
+import brooklyn.util.yaml.Yamls.YamlExtract;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Iterables;
+
+/* TODO the complex tree-structured catalogs are only useful when we are relying on those separate catalog classloaders
+ * to isolate classpaths. with osgi everything is just put into the "manual additions" catalog. */
+public class BasicBrooklynCatalog implements BrooklynCatalog {
+    private static final String POLICIES_KEY = "brooklyn.policies";
+    private static final String LOCATIONS_KEY = "brooklyn.locations";
+    public static final String NO_VERSION = "0.0.0.SNAPSHOT";
+
+    private static final Logger log = LoggerFactory.getLogger(BasicBrooklynCatalog.class);
+
+    public static class BrooklynLoaderTracker {
+        public static final ThreadLocal<BrooklynClassLoadingContext> loader = new ThreadLocal<BrooklynClassLoadingContext>();
+        
+        public static void setLoader(BrooklynClassLoadingContext val) {
+            loader.set(val);
+        }
+        
+        // TODO Stack, for recursive calls?
+        public static void unsetLoader(BrooklynClassLoadingContext val) {
+            loader.set(null);
+        }
+        
+        public static BrooklynClassLoadingContext getLoader() {
+            return loader.get();
+        }
+    }
+
+    private final ManagementContext mgmt;
+    private CatalogDo catalog;
+    private volatile CatalogDo manualAdditionsCatalog;
+    private volatile LoadedClassLoader manualAdditionsClasses;
+    private final AggregateClassLoader rootClassLoader = AggregateClassLoader.newInstanceWithNoLoaders();
+
+    public BasicBrooklynCatalog(ManagementContext mgmt) {
+        this(mgmt, CatalogDto.newNamedInstance("empty catalog", "empty catalog", "empty catalog, expected to be reset later"));
+    }
+
+    public BasicBrooklynCatalog(ManagementContext mgmt, CatalogDto dto) {
+        this.mgmt = checkNotNull(mgmt, "managementContext");
+        this.catalog = new CatalogDo(mgmt, dto);
+    }
+
+    public boolean blockIfNotLoaded(Duration timeout) {
+        try {
+            return getCatalog().blockIfNotLoaded(timeout);
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+    public void reset(CatalogDto dto) {
+        reset(dto, true);
+    }
+
+    public void reset(CatalogDto dto, boolean failOnLoadError) {
+        // Unregister all existing persisted items.
+        for (CatalogItem<?, ?> toRemove : getCatalogItems()) {
+            if (log.isTraceEnabled()) {
+                log.trace("Scheduling item for persistence removal: {}", toRemove.getId());
+            }
+            mgmt.getRebindManager().getChangeListener().onUnmanaged(toRemove);
+        }
+        CatalogDo catalog = new CatalogDo(mgmt, dto);
+        CatalogUtils.logDebugOrTraceIfRebinding(log, "Resetting "+this+" catalog to "+dto);
+        catalog.load(mgmt, null, failOnLoadError);
+        CatalogUtils.logDebugOrTraceIfRebinding(log, "Reloaded catalog for "+this+", now switching");
+        this.catalog = catalog;
+        resetRootClassLoader();
+        this.manualAdditionsCatalog = null;
+
+        // Inject management context into and persist all the new entries.
+        for (CatalogItem<?, ?> entry : getCatalogItems()) {
+            boolean setManagementContext = false;
+            if (entry instanceof CatalogItemDo) {
+                CatalogItemDo<?, ?> cid = CatalogItemDo.class.cast(entry);
+                if (cid.getDto() instanceof CatalogItemDtoAbstract) {
+                    CatalogItemDtoAbstract<?, ?> cdto = CatalogItemDtoAbstract.class.cast(cid.getDto());
+                    if (cdto.getManagementContext() == null) {
+                        cdto.setManagementContext((ManagementContextInternal) mgmt);
+                    }
+                    setManagementContext = true;
+                }
+            }
+            if (!setManagementContext) {
+                log.warn("Can't set management context on entry with unexpected type in catalog. type={}, " +
+                        "expected={}", entry, CatalogItemDo.class);
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("Scheduling item for persistence addition: {}", entry.getId());
+            }
+            mgmt.getRebindManager().getChangeListener().onManaged(entry);
+        }
+
+    }
+
+    /**
+     * Resets the catalog to the given entries
+     */
+    @Override
+    public void reset(Collection<CatalogItem<?, ?>> entries) {
+        CatalogDto newDto = CatalogDto.newDtoFromCatalogItems(entries, "explicit-catalog-reset");
+        reset(newDto);
+    }
+    
+    public CatalogDo getCatalog() {
+        return catalog;
+    }
+
+    protected CatalogItemDo<?,?> getCatalogItemDo(String symbolicName, String version) {
+        String fixedVersionId = getFixedVersionId(symbolicName, version);
+        if (fixedVersionId == null) {
+            //no items with symbolicName exist
+            return null;
+        }
+
+        return catalog.getIdCache().get( CatalogUtils.getVersionedId(symbolicName, fixedVersionId) );
+    }
+    
+    private String getFixedVersionId(String symbolicName, String version) {
+        if (!DEFAULT_VERSION.equals(version)) {
+            return version;
+        } else {
+            return getDefaultVersion(symbolicName);
+        }
+    }
+
+    private String getDefaultVersion(String symbolicName) {
+        Iterable<CatalogItem<Object, Object>> versions = getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName)));
+        Collection<CatalogItem<Object, Object>> orderedVersions = sortVersionsDesc(versions);
+        if (!orderedVersions.isEmpty()) {
+            return orderedVersions.iterator().next().getVersion();
+        } else {
+            return null;
+        }
+    }
+
+    private <T,SpecT> Collection<CatalogItem<T,SpecT>> sortVersionsDesc(Iterable<CatalogItem<T,SpecT>> versions) {
+        return ImmutableSortedSet.orderedBy(CatalogItemComparator.<T,SpecT>getInstance()).addAll(versions).build();
+    }
+
+    @Override
+    @Deprecated
+    public CatalogItem<?,?> getCatalogItem(String symbolicName) {
+        return getCatalogItem(symbolicName, DEFAULT_VERSION);
+    }
+    
+    @Override
+    public CatalogItem<?,?> getCatalogItem(String symbolicName, String version) {
+        if (symbolicName == null) return null;
+        checkNotNull(version, "version");
+        CatalogItemDo<?, ?> itemDo = getCatalogItemDo(symbolicName, version);
+        if (itemDo == null) return null;
+        return itemDo.getDto();
+    }
+    
+    @Override
+    @Deprecated
+    public void deleteCatalogItem(String id) {
+        //Delete only if installed through the
+        //deprecated methods. Don't support DEFAULT_VERSION for delete.
+        deleteCatalogItem(id, NO_VERSION);
+    }
+
+    @Override
+    public void deleteCatalogItem(String symbolicName, String version) {
+        log.debug("Deleting manual catalog item from "+mgmt+": "+symbolicName + ":" + version);
+        checkNotNull(symbolicName, "id");
+        checkNotNull(version, "version");
+        if (DEFAULT_VERSION.equals(version)) {
+            throw new IllegalStateException("Deleting items with unspecified version (argument DEFAULT_VERSION) not supported.");
+        }
+        CatalogItem<?, ?> item = getCatalogItem(symbolicName, version);
+        CatalogItemDtoAbstract<?,?> itemDto = getAbstractCatalogItem(item);
+        if (itemDto == null) {
+            throw new NoSuchElementException("No catalog item found with id "+symbolicName);
+        }
+        if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
+        manualAdditionsCatalog.deleteEntry(itemDto);
+        
+        // Ensure the cache is de-populated
+        getCatalog().deleteEntry(itemDto);
+
+        // And indicate to the management context that it should be removed.
+        if (log.isTraceEnabled()) {
+            log.trace("Scheduling item for persistence removal: {}", itemDto.getId());
+        }
+        if (itemDto.getCatalogItemType() == CatalogItemType.LOCATION) {
+            @SuppressWarnings("unchecked")
+            CatalogItem<Location,LocationSpec<?>> locationItem = (CatalogItem<Location, LocationSpec<?>>) itemDto;
+            ((BasicLocationRegistry)mgmt.getLocationRegistry()).removeDefinedLocation(locationItem);
+        }
+        mgmt.getRebindManager().getChangeListener().onUnmanaged(itemDto);
+
+    }
+
+    @Override
+    @Deprecated
+    public <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String id) {
+        return getCatalogItem(type, id, DEFAULT_VERSION);
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String id, String version) {
+        if (id==null || version==null) return null;
+        CatalogItem<?,?> result = getCatalogItem(id, version);
+        if (result==null) return null;
+        if (type==null || type.isAssignableFrom(result.getCatalogItemJavaType())) 
+            return (CatalogItem<T,SpecT>)result;
+        return null;
+    }
+
+    @Override
+    public void persist(CatalogItem<?, ?> catalogItem) {
+        checkArgument(getCatalogItem(catalogItem.getSymbolicName(), catalogItem.getVersion()) != null, "Unknown catalog item %s", catalogItem);
+        mgmt.getRebindManager().getChangeListener().onChanged(catalogItem);
+    }
+    
+    @Override
+    public ClassLoader getRootClassLoader() {
+        if (rootClassLoader.isEmpty() && catalog!=null) {
+            resetRootClassLoader();
+        }
+        return rootClassLoader;
+    }
+
+    private void resetRootClassLoader() {
+        rootClassLoader.reset(ImmutableList.of(catalog.getRootClassLoader()));
+    }
+
+    /**
+     * Loads this catalog. No effect if already loaded.
+     */
+    public void load() {
+        log.debug("Loading catalog for " + mgmt);
+        getCatalog().load(mgmt, null);
+        if (log.isDebugEnabled()) {
+            log.debug("Loaded catalog for " + mgmt + ": " + catalog + "; search classpath is " + catalog.getRootClassLoader());
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T, SpecT> SpecT createSpec(CatalogItem<T, SpecT> item) {
+        if (item == null) return null;
+        CatalogItemDo<T,SpecT> loadedItem = (CatalogItemDo<T, SpecT>) getCatalogItemDo(item.getSymbolicName(), item.getVersion());
+        if (loadedItem == null) throw new RuntimeException(item+" not in catalog; cannot create spec");
+        Class<SpecT> specType = loadedItem.getSpecType();
+        if (specType==null) return null;
+
+        String yaml = loadedItem.getPlanYaml();
+
+        if (yaml!=null) {
+            // preferred way is to parse the yaml, to resolve references late;
+            // the parsing on load is to populate some fields, but it is optional.
+            // TODO messy for location and policy that we need brooklyn.{locations,policies} root of the yaml, but it works;
+            // see related comment when the yaml is set, in addAbstractCatalogItems
+            // (not sure if anywhere else relies on that syntax; if not, it should be easy to fix!)
+            DeploymentPlan plan = makePlanFromYaml(yaml);
+            BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, item);
+            SpecT spec;
+            switch (item.getCatalogItemType()) {
+                case TEMPLATE:
+                case ENTITY:
+                    spec = createEntitySpec(loadedItem.getSymbolicName(), plan, loader);
+                    break;
+                case POLICY:
+                    spec = createPolicySpec(loadedItem.getSymbolicName(), plan, loader);
+                    break;
+                case LOCATION:
+                    spec = createLocationSpec(plan, loader);
+                    break;
+                default: throw new RuntimeException("Only entity, policy & location catalog items are supported. Unsupported catalog item type " + item.getCatalogItemType());
+            }
+            ((AbstractBrooklynObjectSpec<?, ?>)spec).catalogItemId(item.getId());
+            
+            if (Strings.isBlank( ((AbstractBrooklynObjectSpec<?, ?>)spec).getDisplayName() ))
+                ((AbstractBrooklynObjectSpec<?, ?>)spec).displayName(item.getDisplayName());
+            
+            return spec;
+        }
+
+        // revert to legacy mechanism
+        SpecT spec = null;
+        Method method;
+        try {
+            method = Reflections.findMethod(specType, "create", Class.class);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            throw new IllegalStateException("Unsupported creation of spec type "+specType+"; it must have a public static create(Class) method", e);            
+        }
+        try {
+            if (loadedItem.getJavaType()!=null) {
+                SpecT specT = (SpecT) method.invoke(null, loadedItem.loadJavaClass(mgmt));
+                spec = specT;
+            }
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            throw new IllegalStateException("Error creating "+specType+" "+loadedItem.getJavaType()+": "+e, e);
+        }
+
+        if (spec==null) 
+            throw new IllegalStateException("Unknown how to create instance of "+this);
+
+        return spec;
+    }
+
+    private <T, SpecT> SpecT createSpec(String optionalId, CatalogItemType ciType, DeploymentPlan plan, BrooklynClassLoadingContext loader) {
+        Preconditions.checkNotNull(ciType, "catalog item type for "+plan); 
+        switch (ciType) {
+        case TEMPLATE:
+        case ENTITY: 
+            return createEntitySpec(optionalId, plan, loader);
+        case LOCATION: return createLocationSpec(plan, loader);
+        case POLICY: return createPolicySpec(optionalId, plan, loader);
+        }
+        throw new IllegalStateException("Unknown CI Type "+ciType+" for "+plan);
+    }
+    
+    @SuppressWarnings("unchecked")
+    private <T, SpecT> SpecT createEntitySpec(String symbolicName, DeploymentPlan plan, BrooklynClassLoadingContext loader) {
+        CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get();
+
+        // TODO should not register new AT each time we instantiate from the same plan; use some kind of cache
+        AssemblyTemplate at;
+        BrooklynLoaderTracker.setLoader(loader);
+        try {
+            at = camp.pdp().registerDeploymentPlan(plan);
+        } finally {
+            BrooklynLoaderTracker.unsetLoader(loader);
+        }
+
+        try {
+            AssemblyTemplateInstantiator instantiator = at.getInstantiator().newInstance();
+            if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
+                return (SpecT) ((AssemblyTemplateSpecInstantiator)instantiator).createNestedSpec(at, camp, loader, 
+                    getInitialEncounteredSymbol(symbolicName));
+            }
+            throw new IllegalStateException("Unable to instantiate YAML; incompatible instantiator "+instantiator+" for "+at);
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    private MutableSet<String> getInitialEncounteredSymbol(String symbolicName) {
+        return symbolicName==null ? MutableSet.<String>of() : MutableSet.of(symbolicName);
+    }
+
+    private <T, SpecT> SpecT createPolicySpec(String symbolicName, DeploymentPlan plan, BrooklynClassLoadingContext loader) {
+        return createPolicySpec(plan, loader, getInitialEncounteredSymbol(symbolicName));
+    }
+
+    private <T, SpecT> SpecT createPolicySpec(DeploymentPlan plan, BrooklynClassLoadingContext loader, Set<String> encounteredCatalogTypes) {
+        //Would ideally re-use io.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityDecorationResolver.PolicySpecResolver
+        //but it is CAMP specific and there is no easy way to get hold of it.
+        Object policies = checkNotNull(plan.getCustomAttributes().get(POLICIES_KEY), "policy config");
+        if (!(policies instanceof Iterable<?>)) {
+            throw new IllegalStateException("The value of " + POLICIES_KEY + " must be an Iterable.");
+        }
+
+        Object policy = Iterables.getOnlyElement((Iterable<?>)policies);
+
+        return createPolicySpec(loader, policy, encounteredCatalogTypes);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T, SpecT> SpecT createPolicySpec(BrooklynClassLoadingContext loader, Object policy, Set<String> encounteredCatalogTypes) {
+        Map<String, Object> itemMap;
+        if (policy instanceof String) {
+            itemMap = ImmutableMap.<String, Object>of("type", policy);
+        } else if (policy instanceof Map) {
+            itemMap = (Map<String, Object>) policy;
+        } else {
+            throw new IllegalStateException("Policy expected to be string or map. Unsupported object type " + policy.getClass().getName() + " (" + policy.toString() + ")");
+        }
+
+        String versionedId = (String) checkNotNull(Yamls.getMultinameAttribute(itemMap, "policy_type", "policyType", "type"), "policy type");
+        PolicySpec<? extends Policy> spec;
+        CatalogItem<?, ?> policyItem = CatalogUtils.getCatalogItemOptionalVersion(mgmt, versionedId);
+        if (policyItem != null && !encounteredCatalogTypes.contains(policyItem.getSymbolicName())) {
+            if (policyItem.getCatalogItemType() != CatalogItemType.POLICY) {
+                throw new IllegalStateException("Non-policy catalog item in policy context: " + policyItem);
+            }
+            //TODO re-use createSpec
+            BrooklynClassLoadingContext itemLoader = CatalogUtils.newClassLoadingContext(mgmt, policyItem);
+            if (policyItem.getPlanYaml() != null) {
+                DeploymentPlan plan = makePlanFromYaml(policyItem.getPlanYaml());
+                encounteredCatalogTypes.add(policyItem.getSymbolicName());
+                return createPolicySpec(plan, itemLoader, encounteredCatalogTypes);
+            } else if (policyItem.getJavaType() != null) {
+                spec = PolicySpec.create((Class<Policy>)itemLoader.loadClass(policyItem.getJavaType()));
+            } else {
+                throw new IllegalStateException("Invalid policy item - neither yaml nor javaType: " + policyItem);
+            }
+        } else {
+            spec = PolicySpec.create(loader.loadClass(versionedId, Policy.class));
+        }
+        Map<String, Object> brooklynConfig = (Map<String, Object>) itemMap.get("brooklyn.config");
+        if (brooklynConfig != null) {
+            spec.configure(brooklynConfig);
+        }
+        return (SpecT) spec;
+    }
+    
+    private <T, SpecT> SpecT createLocationSpec(DeploymentPlan plan, BrooklynClassLoadingContext loader) {
+        // See #createPolicySpec; this impl is modeled on that.
+        // spec.catalogItemId is set by caller
+        Object locations = checkNotNull(plan.getCustomAttributes().get(LOCATIONS_KEY), "location config");
+        if (!(locations instanceof Iterable<?>)) {
+            throw new IllegalStateException("The value of " + LOCATIONS_KEY + " must be an Iterable.");
+        }
+
+        Object location = Iterables.getOnlyElement((Iterable<?>)locations);
+
+        return createLocationSpec(loader, location); 
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T, SpecT> SpecT createLocationSpec(BrooklynClassLoadingContext loader, Object location) {
+        Map<String, Object> itemMap;
+        if (location instanceof String) {
+            itemMap = ImmutableMap.<String, Object>of("type", location);
+        } else if (location instanceof Map) {
+            itemMap = (Map<String, Object>) location;
+        } else {
+            throw new IllegalStateException("Location expected to be string or map. Unsupported object type " + location.getClass().getName() + " (" + location.toString() + ")");
+        }
+
+        String type = (String) checkNotNull(Yamls.getMultinameAttribute(itemMap, "location_type", "locationType", "type"), "location type");
+        Map<String, Object> brooklynConfig = (Map<String, Object>) itemMap.get("brooklyn.config");
+        Maybe<Class<? extends Location>> javaClass = loader.tryLoadClass(type, Location.class);
+        if (javaClass.isPresent()) {
+            LocationSpec<?> spec = LocationSpec.create(javaClass.get());
+            if (brooklynConfig != null) {
+                spec.configure(brooklynConfig);
+            }
+            return (SpecT) spec;
+        } else {
+            Maybe<Location> loc = mgmt.getLocationRegistry().resolve(type, false, brooklynConfig);
+            if (loc.isPresent()) {
+                // TODO extensions?
+                Map<String, Object> locConfig = ((ConfigurationSupportInternal)loc.get().config()).getBag().getAllConfig();
+                Class<? extends Location> locType = loc.get().getClass();
+                Set<Object> locTags = loc.get().tags().getTags();
+                String locDisplayName = loc.get().getDisplayName();
+                return (SpecT) LocationSpec.create(locType)
+                        .configure(locConfig)
+                        .displayName(locDisplayName)
+                        .tags(locTags);
+            } else {
+                throw new IllegalStateException("No class or resolver found for location type "+type);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    /** @deprecated since 0.7.0 use {@link #createSpec(CatalogItem)} */
+    @Deprecated
+    public <T,SpecT> Class<? extends T> loadClass(CatalogItem<T,SpecT> item) {
+        if (log.isDebugEnabled())
+            log.debug("Loading class for catalog item " + item);
+        checkNotNull(item);
+        CatalogItemDo<?,?> loadedItem = getCatalogItemDo(item.getSymbolicName(), item.getVersion());
+        if (loadedItem==null) throw new NoSuchElementException("Unable to load '"+item.getId()+"' to instantiate it");
+        return (Class<? extends T>) loadedItem.getJavaClass();
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    /** @deprecated since 0.7.0 use {@link #createSpec(CatalogItem)} */
+    @Deprecated
+    public <T> Class<? extends T> loadClassByType(String typeName, Class<T> typeClass) {
+        final CatalogItem<?,?> resultI = getCatalogItemForType(typeName);
+
+        if (resultI == null) {
+            throw new NoSuchElementException("Unable to find catalog item for type "+typeName);
+        }
+
+        return (Class<? extends T>) loadClass(resultI);
+    }
+
+    @Deprecated /** @deprecated since 0.7.0 only used by other deprecated items */ 
+    private <T,SpecT> CatalogItemDtoAbstract<T,SpecT> getAbstractCatalogItem(CatalogItem<T,SpecT> item) {
+        while (item instanceof CatalogItemDo) item = ((CatalogItemDo<T,SpecT>)item).itemDto;
+        if (item==null) return null;
+        if (item instanceof CatalogItemDtoAbstract) return (CatalogItemDtoAbstract<T,SpecT>) item;
+        throw new IllegalStateException("Cannot unwrap catalog item '"+item+"' (type "+item.getClass()+") to restore DTO");
+    }
+    
+    @SuppressWarnings("unchecked")
+    private static <T> Maybe<T> getFirstAs(Map<?,?> map, Class<T> type, String firstKey, String ...otherKeys) {
+        if (map==null) return Maybe.absent("No map available");
+        String foundKey = null;
+        Object value = null;
+        if (map.containsKey(firstKey)) foundKey = firstKey;
+        else for (String key: otherKeys) {
+            if (map.containsKey(key)) {
+                foundKey = key;
+                break;
+            }
+        }
+        if (foundKey==null) return Maybe.absent("Missing entry '"+firstKey+"'");
+        value = map.get(foundKey);
+        if (type.equals(String.class) && Number.class.isInstance(value)) value = value.toString();
+        if (!type.isInstance(value)) 
+            throw new IllegalArgumentException("Entry for '"+firstKey+"' should be of type "+type+", not "+value.getClass());
+        return Maybe.of((T)value);
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private Maybe<Map<?,?>> getFirstAsMap(Map<?,?> map, String firstKey, String ...otherKeys) {
+        return (Maybe<Map<?,?>>)(Maybe) getFirstAs(map, Map.class, firstKey, otherKeys);
+    }
+
+    private List<CatalogItemDtoAbstract<?,?>> collectCatalogItems(String yaml) {
+        Map<?,?> itemDef = Yamls.getAs(Yamls.parseAll(yaml), Map.class);
+        Map<?,?> catalogMetadata = getFirstAsMap(itemDef, "brooklyn.catalog").orNull();
+        if (catalogMetadata==null)
+            log.warn("No `brooklyn.catalog` supplied in catalog request; using legacy mode for "+itemDef);
+        catalogMetadata = MutableMap.copyOf(catalogMetadata);
+
+        List<CatalogItemDtoAbstract<?, ?>> result = MutableList.of();
+        
+        collectCatalogItems(Yamls.getTextOfYamlAtPath(yaml, "brooklyn.catalog").getMatchedYamlTextOrWarn(), 
+            catalogMetadata, result, null);
+        
+        itemDef.remove("brooklyn.catalog");
+        catalogMetadata.remove("item");
+        catalogMetadata.remove("items");
+        if (!itemDef.isEmpty()) {
+            log.debug("Reading brooklyn.catalog peer keys as item ('top-level syntax')");
+            Map<String,?> rootItem = MutableMap.of("item", itemDef);
+            String rootItemYaml = yaml;
+            YamlExtract yamlExtract = Yamls.getTextOfYamlAtPath(rootItemYaml, "brooklyn.catalog");
+            String match = yamlExtract.withOriginalIndentation(true).withKeyIncluded(true).getMatchedYamlTextOrWarn();
+            if (match!=null) {
+                if (rootItemYaml.startsWith(match)) rootItemYaml = Strings.removeFromStart(rootItemYaml, match);
+                else rootItemYaml = Strings.replaceAllNonRegex(rootItemYaml, "\n"+match, "");
+            }
+            collectCatalogItems("item:\n"+makeAsIndentedObject(rootItemYaml), rootItem, result, catalogMetadata);
+        }
+        
+        return result;
+    }
+
+    @SuppressWarnings("unchecked")
+    private void collectCatalogItems(String sourceYaml, Map<?,?> itemMetadata, List<CatalogItemDtoAbstract<?, ?>> result, Map<?,?> parentMetadata) {
+
+        if (sourceYaml==null) sourceYaml = new Yaml().dump(itemMetadata);
+
+        Map<Object,Object> catalogMetadata = MutableMap.builder().putAll(parentMetadata).putAll(itemMetadata).build();
+        
+        // brooklyn.libraries we treat specially, to append the list, with the child's list preferred in classloading order
+        // `libraries` is supported in some places as a legacy syntax; it should always be `brooklyn.libraries` for new apps
+        // TODO in 0.8.0 require brooklyn.libraries, don't allow "libraries" on its own
+        List<?> librariesNew = MutableList.copyOf(getFirstAs(itemMetadata, List.class, "brooklyn.libraries", "libraries").orNull());
+        Collection<CatalogBundle> libraryBundlesNew = CatalogItemDtoAbstract.parseLibraries(librariesNew);
+        
+        List<?> librariesCombined = MutableList.copyOf(librariesNew)
+            .appendAll(getFirstAs(parentMetadata, List.class, "brooklyn.libraries", "libraries").orNull());
+        if (!librariesCombined.isEmpty())
+            catalogMetadata.put("brooklyn.libraries", librariesCombined);
+        Collection<CatalogBundle> libraryBundles = CatalogItemDtoAbstract.parseLibraries(librariesCombined);
+
+        // TODO as this may take a while if downloading, the REST call should be async
+        // (this load is required for the scan below and I think also for yaml resolution)
+        CatalogUtils.installLibraries(mgmt, libraryBundlesNew);
+
+        Boolean scanJavaAnnotations = getFirstAs(itemMetadata, Boolean.class, "scanJavaAnnotations", "scan_java_annotations").orNull();
+        if (scanJavaAnnotations==null || !scanJavaAnnotations) {
+            // don't scan
+        } else {
+            // scan for annotations: if libraries here, scan them; if inherited libraries error; else scan classpath
+            if (!libraryBundlesNew.isEmpty()) {
+                result.addAll(scanAnnotationsFromBundles(mgmt, libraryBundlesNew, catalogMetadata));
+            } else if (libraryBundles.isEmpty()) {
+                result.addAll(scanAnnotationsFromLocal(mgmt, catalogMetadata));
+            } else {
+                throw new IllegalStateException("Cannot scan catalog node no local bundles, and with inherited bundles we will not scan the classpath");
+            }
+        }
+        
+        Object items = catalogMetadata.remove("items");
+        Object item = catalogMetadata.remove("item");
+
+        if (items!=null) {
+            int count = 0;
+            for (Map<?,?> i: ((List<Map<?,?>>)items)) {
+                collectCatalogItems(Yamls.getTextOfYamlAtPath(sourceYaml, "items", count).getMatchedYamlTextOrWarn(), 
+                    i, result, catalogMetadata);
+                count++;
+            }
+        }
+        
+        if (item==null) return;
+
+        // now look at the actual item, first correcting the sourceYaml and interpreting the catalog metadata
+        String itemYaml = Yamls.getTextOfYamlAtPath(sourceYaml, "item").getMatchedYamlTextOrWarn();
+        if (itemYaml!=null) sourceYaml = itemYaml;
+        else sourceYaml = new Yaml().dump(item);
+        
+        CatalogItemType itemType = TypeCoercions.coerce(getFirstAs(catalogMetadata, Object.class, "itemType", "item_type").orNull(), CatalogItemType.class);
+        BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, "<load>:0", libraryBundles);
+
+        String id = getFirstAs(catalogMetadata, String.class, "id").orNull();
+        String version = getFirstAs(catalogMetadata, String.class, "version").orNull();
+        String symbolicName = getFirstAs(catalogMetadata, String.class, "symbolicName").orNull();
+        String displayName = getFirstAs(catalogMetadata, String.class, "displayName").orNull();
+        String name = getFirstAs(catalogMetadata, String.class, "name").orNull();
+
+        if ((Strings.isNonBlank(id) || Strings.isNonBlank(symbolicName)) && 
+                Strings.isNonBlank(displayName) &&
+                Strings.isNonBlank(name) && !name.equals(displayName)) {
+            log.warn("Name property will be ignored due to the existence of displayName and at least one of id, symbolicName");
+        }
+
+        PlanInterpreterGuessingType planInterpreter = new PlanInterpreterGuessingType(null, item, sourceYaml, itemType, loader, result).reconstruct();
+        if (!planInterpreter.isResolved()) {
+            throw Exceptions.create("Could not resolve item "
+                + (Strings.isNonBlank(id) ? id : Strings.isNonBlank(symbolicName) ? symbolicName : Strings.isNonBlank(name) ? name : "<no-name>")
+                + ":\n"+sourceYaml, planInterpreter.getErrors());
+        }
+        itemType = planInterpreter.getCatalogItemType();
+        Map<?, ?> itemAsMap = planInterpreter.getItem();
+        // the "plan yaml" includes the services: ... or brooklyn.policies: ... outer key,
+        // as opposed to the rawer { type: xxx } map without that outer key which is valid as item input
+        // TODO this plan yaml is needed for subsequent reconstruction; would be nicer if it weren't! 
+
+        // if symname not set, infer from: id, then name, then item id, then item name
+        if (Strings.isBlank(symbolicName)) {
+            if (Strings.isNonBlank(id)) {
+                if (CatalogUtils.looksLikeVersionedId(id)) {
+                    symbolicName = CatalogUtils.getIdFromVersionedId(id);
+                } else {
+                    symbolicName = id;
+                }
+            } else if (Strings.isNonBlank(name)) {
+                if (CatalogUtils.looksLikeVersionedId(name)) {
+                    symbolicName = CatalogUtils.getIdFromVersionedId(name);
+                } else {
+                    symbolicName = name;
+                }
+            } else {
+                symbolicName = setFromItemIfUnset(symbolicName, itemAsMap, "id");
+                symbolicName = setFromItemIfUnset(symbolicName, itemAsMap, "name");
+                if (Strings.isBlank(symbolicName)) {
+                    log.error("Can't infer catalog item symbolicName from the following plan:\n" + sourceYaml);
+                    throw new IllegalStateException("Can't infer catalog item symbolicName from catalog item metadata");
+                }
+            }
+        }
+
+        // if version not set, infer from: id, then from name, then item version
+        if (CatalogUtils.looksLikeVersionedId(id)) {
+            String versionFromId = CatalogUtils.getVersionFromVersionedId(id);
+            if (versionFromId != null && Strings.isNonBlank(version) && !versionFromId.equals(version)) {
+                throw new IllegalArgumentException("Discrepency between version set in id " + versionFromId + " and version property " + version);
+            }
+            version = versionFromId;
+        }
+        if (Strings.isBlank(version)) {
+            if (CatalogUtils.looksLikeVersionedId(name)) {
+                version = CatalogUtils.getVersionFromVersionedId(name);
+            } else if (Strings.isBlank(version)) {
+                version = setFromItemIfUnset(version, itemAsMap, "version");
+                if (version==null) {
+                    log.warn("No version specified for catalog item " + symbolicName + ". Using default value.");
+                    version = null;
+                }
+            }
+        }
+        
+        // if not set, ID can come from symname:version, failing that, from the plan.id, failing that from the sym name
+        if (Strings.isBlank(id)) {
+            // let ID be inferred, especially from name, to support style where only "name" is specified, with inline version
+            if (Strings.isNonBlank(symbolicName) && Strings.isNonBlank(version)) {
+                id = symbolicName + ":" + version;
+            }
+            id = setFromItemIfUnset(id, itemAsMap, "id");
+            if (Strings.isBlank(id)) {
+                if (Strings.isNonBlank(symbolicName)) {
+                    id = symbolicName;
+                } else {
+                    log.error("Can't infer catalog item id from the following plan:\n" + sourceYaml);
+                    throw new IllegalStateException("Can't infer catalog item id from catalog item metadata");
+                }
+            }
+        }
+
+        if (Strings.isBlank(displayName)) {
+            if (Strings.isNonBlank(name)) displayName = name;
+            displayName = setFromItemIfUnset(displayName, itemAsMap, "name");
+        }
+
+        String description = getFirstAs(catalogMetadata, String.class, "description").orNull();
+        description = setFromItemIfUnset(description, itemAsMap, "description");
+
+        // icon.url is discouraged, but kept for legacy compatibility; should deprecate this
+        final String catalogIconUrl = getFirstAs(catalogMetadata, String.class, "iconUrl", "icon_url", "icon.url").orNull();
+
+        final String deprecated = getFirstAs(catalogMetadata, String.class, "deprecated").orNull();
+        final Boolean catalogDeprecated = Boolean.valueOf(deprecated);
+
+        // run again now that we know the ID
+        planInterpreter = new PlanInterpreterGuessingType(id, item, sourceYaml, itemType, loader, result).reconstruct();
+        if (!planInterpreter.isResolved()) {
+            throw new IllegalStateException("Could not resolve plan once id and itemType are known (recursive reference?): "+sourceYaml);
+        }
+        String sourcePlanYaml = planInterpreter.getPlanYaml();
+        
+        CatalogItemDtoAbstract<?, ?> dto = createItemBuilder(itemType, symbolicName, version)
+            .libraries(libraryBundles)
+            .displayName(displayName)
+            .description(description)
+            .deprecated(catalogDeprecated)
+            .iconUrl(catalogIconUrl)
+            .plan(sourcePlanYaml)
+            .build();
+
+        dto.setManagementContext((ManagementContextInternal) mgmt);
+        result.add(dto);
+    }
+
+    private String setFromItemIfUnset(String oldValue, Map<?,?> item, String fieldAttr) {
+        if (Strings.isNonBlank(oldValue)) return oldValue;
+        if (item!=null) {
+            Object newValue = item.get(fieldAttr);
+            if (newValue instanceof String && Strings.isNonBlank((String)newValue)) 
+                return (String)newValue;
+        }
+        return oldValue;
+    }
+
+    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromLocal(ManagementContext mgmt, Map<Object, Object> catalogMetadata) {
+        CatalogDto dto = CatalogDto.newNamedInstance("Local Scanned Catalog", "All annotated Brooklyn entities detected in the classpath", "scanning-local-classpath");
+        return scanAnnotationsInternal(mgmt, new CatalogDo(dto), catalogMetadata);
+    }
+    
+    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromBundles(ManagementContext mgmt, Collection<CatalogBundle> libraries, Map<Object, Object> catalogMetadata) {
+        CatalogDto dto = CatalogDto.newNamedInstance("Bundles Scanned Catalog", "All annotated Brooklyn entities detected in bundles", "scanning-bundles-classpath-"+libraries.hashCode());
+        List<String> urls = MutableList.of();
+        for (CatalogBundle b: libraries) {
+            // TODO currently does not support pre-installed bundles identified by name:version 
+            // (ie where URL not supplied)
+            if (Strings.isNonBlank(b.getUrl())) {
+                urls.add(b.getUrl());
+            }
+        }
+        
+        if (urls.isEmpty()) {
+            log.warn("No bundles to scan: scanJavaAnnotations currently only applies to OSGi bundles provided by URL"); 
+            return MutableList.of();
+        }
+        
+        CatalogDo subCatalog = new CatalogDo(dto);
+        subCatalog.addToClasspath(urls.toArray(new String[0]));
+        return scanAnnotationsInternal(mgmt, subCatalog, catalogMetadata);
+    }
+    
+    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsInternal(ManagementContext mgmt, CatalogDo subCatalog, Map<Object, Object> catalogMetadata) {
+        // TODO this does java-scanning only;
+        // the call when scanning bundles should use the CatalogItem instead and use OSGi when loading for scanning
+        // (or another scanning mechanism).  see comments on CatalogClasspathDo.load
+        subCatalog.mgmt = mgmt;
+        subCatalog.setClasspathScanForEntities(CatalogScanningModes.ANNOTATIONS);
+        subCatalog.load();
+        // TODO apply metadata?  (extract YAML from the items returned)
+        // also see doc .../catalog/index.md which says we might not apply metadata
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        Collection<CatalogItemDtoAbstract<?, ?>> result = (Collection<CatalogItemDtoAbstract<?, ?>>)(Collection)Collections2.transform(
+                (Collection<CatalogItemDo<Object,Object>>)(Collection)subCatalog.getIdCache().values(), 
+                itemDoToDtoAddingSelectedMetadataDuringScan(catalogMetadata));
+        return result;
+    }
+
+    private class PlanInterpreterGuessingType {
+
+        final String id;
+        final Map<?,?> item;
+        final String itemYaml;
+        final BrooklynClassLoadingContext loader;
+        final List<CatalogItemDtoAbstract<?, ?>> itemsDefinedSoFar;
+        
+        CatalogItemType catalogItemType;
+        String planYaml;
+        @SuppressWarnings("unused")
+        DeploymentPlan plan;
+        AbstractBrooklynObjectSpec<?,?> spec;
+        boolean resolved = false;
+        List<Exception> errors = MutableList.of();
+        
+        public PlanInterpreterGuessingType(@Nullable String id, Object item, String itemYaml, @Nullable CatalogItemType optionalCiType, 
+                BrooklynClassLoadingContext loader, List<CatalogItemDtoAbstract<?,?>> itemsDefinedSoFar) {
+            // ID is useful to prevent recursive references (currently for entities only)
+            this.id = id;
+            
+            if (item instanceof String) {
+                // if just a string supplied, wrap as map
+                this.item = MutableMap.of("type", item);
+                this.itemYaml = "type:\n"+makeAsIndentedObject(itemYaml);                
+            } else {
+                this.item = (Map<?,?>)item;
+                this.itemYaml = itemYaml;
+            }
+            this.catalogItemType = optionalCiType;
+            this.loader = loader;
+            this.itemsDefinedSoFar = itemsDefinedSoFar;
+        }
+
+        public PlanInterpreterGuessingType reconstruct() {
+            if (catalogItemType==CatalogItemType.TEMPLATE) {
+                // template *must* be explicitly defined, and if so, none of the other calls apply
+                attemptType(null, CatalogItemType.TEMPLATE);
+                
+            } else {
+                attemptType(null, CatalogItemType.ENTITY);
+
+                attemptType("services", CatalogItemType.ENTITY);
+                attemptType(POLICIES_KEY, CatalogItemType.POLICY);
+                attemptType(LOCATIONS_KEY, CatalogItemType.LOCATION);
+            }
+            
+            if (!resolved && catalogItemType==CatalogItemType.TEMPLATE) {
+                // anything goes, for an explicit template, because we can't easily recurse into the types
+                planYaml = itemYaml;
+                resolved = true;
+            }
+            
+            return this;
+        }
+        
+        public boolean isResolved() { return resolved; }
+        
+        /** Returns potentially useful errors encountered while guessing types. 
+         * May only be available where the type is known. */
+        public List<Exception> getErrors() {
+            return errors;
+        }
+        
+        public CatalogItemType getCatalogItemType() {
+            return catalogItemType; 
+        }
+        
+        public String getPlanYaml() {
+            return planYaml;
+        }
+        
+        private boolean attemptType(String key, CatalogItemType candidateCiType) {
+            if (resolved) return false;
+            if (catalogItemType!=null && catalogItemType!=candidateCiType) return false;
+            
+            final String candidateYaml;
+            if (key==null) candidateYaml = itemYaml;
+            else {
+                if (item.containsKey(key))
+                    candidateYaml = itemYaml;
+                else
+                    candidateYaml = key + ":\n" + makeAsIndentedList(itemYaml);
+            }
+            // first look in collected items, if a key is given
+            String type = (String) item.get("type");
+            String version = null;
+            if (CatalogUtils.looksLikeVersionedId(type)) {
+                version = CatalogUtils.getVersionFromVersionedId(type);
+                type = CatalogUtils.getIdFromVersionedId(type);
+            }
+            if (type!=null && key!=null) {
+                for (CatalogItemDtoAbstract<?,?> candidate: itemsDefinedSoFar) {
+                    if (candidateCiType == candidate.getCatalogItemType() &&
+                            (type.equals(candidate.getSymbolicName()) || type.equals(candidate.getId()))) {
+                        if (version==null || version.equals(candidate.getVersion())) {
+                            // matched - exit
+                            catalogItemType = candidateCiType;
+                            planYaml = candidateYaml;
+                            resolved = true;
+                            return true;
+                        }
+                    }
+                }
+            }
+            
+            // then try parsing plan - this will use loader
+            try {
+                DeploymentPlan candidatePlan = makePlanFromYaml(candidateYaml);
+                spec = createSpec(id, candidateCiType, candidatePlan, loader);
+                if (spec!=null) {
+                    catalogItemType = candidateCiType;
+                    plan = candidatePlan;
+                    planYaml = candidateYaml;
+                    resolved = true;
+                }
+                return true;
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                // record the error if we have reason to expect this guess to succeed
+                if (item.containsKey("services") && (candidateCiType==CatalogItemType.ENTITY || candidateCiType==CatalogItemType.TEMPLATE)) {
+                    // explicit services supplied, so plan should have been parseable for an entity or a a service
+                    errors.add(e);
+                } else if (catalogItemType!=null && key!=null) {
+                    // explicit itemType supplied, so plan should be parseable in the cases where we're given a key
+                    // (when we're not given a key, the previous block should apply)
+                    errors.add(e);
+                } else {
+                    // all other cases, the error is probably due to us not getting the type right, ignore it
+                    if (log.isTraceEnabled())
+                        log.trace("Guessing type of plan, it looks like it isn't "+candidateCiType+"/"+key+": "+e);
+                }
+            }
+            
+            // finally try parsing a cut-down plan, in case there is a nested reference to a newly defined catalog item
+            if (type!=null && key!=null) {
+                try {
+                    String cutDownYaml = key + ":\n" + makeAsIndentedList("type: "+type);
+                    DeploymentPlan candidatePlan = makePlanFromYaml(cutDownYaml);
+                    Object cutdownSpec = createSpec(id, candidateCiType, candidatePlan, loader);
+                    if (cutdownSpec!=null) {
+                        catalogItemType = candidateCiType;
+                        planYaml = candidateYaml;
+                        resolved = true;
+                    }
+                    return true;
+                } catch (Exception e) {
+                    Exceptions.propagateIfFatal(e);
+                }
+            }
+            
+            return false;
+        }
+        public Map<?,?> getItem() {
+            return item;
+        }
+    }
+    
+    private String makeAsIndentedList(String yaml) {
+        String[] lines = yaml.split("\n");
+        lines[0] = "- "+lines[0];
+        for (int i=1; i<lines.length; i++)
+            lines[i] = "  " + lines[i];
+        return Strings.join(lines, "\n");
+    }
+
+    private String makeAsIndentedObject(String yaml) {
+        String[] lines = yaml.split("\n");
+        for (int i=0; i<lines.length; i++)
+            lines[i] = "  " + lines[i];
+        return Strings.join(lines, "\n");
+    }
+
+    private CatalogItemBuilder<?> createItemBuilder(CatalogItemType itemType, String itemId, String version) {
+        Preconditions.checkNotNull(itemType, "itemType required");
+        switch (itemType) {
+        case ENTITY: return CatalogItemBuilder.newEntity(itemId, version);
+        case TEMPLATE: return CatalogItemBuilder.newTemplate(itemId, version);
+        case POLICY: return CatalogItemBuilder.newPolicy(itemId, version);
+        case LOCATION: return CatalogItemBuilder.newLocation(itemId, version);
+        }
+        throw new IllegalStateException("Unexpected itemType: "+itemType);
+    }
+
+    // these kept as their logic may prove useful; Apr 2015
+//    private boolean isApplicationSpec(EntitySpec<?> spec) {
+//        return !Boolean.TRUE.equals(spec.getConfig().get(EntityManagementUtils.WRAPPER_APP_MARKER));
+//    }
+//
+//    private boolean isEntityPlan(DeploymentPlan plan) {
+//        return plan!=null && !plan.getServices().isEmpty() || !plan.getArtifacts().isEmpty();
+//    }
+//    
+//    private boolean isPolicyPlan(DeploymentPlan plan) {
+//        return !isEntityPlan(plan) && plan.getCustomAttributes().containsKey(POLICIES_KEY);
+//    }
+//
+//    private boolean isLocationPlan(DeploymentPlan plan) {
+//        return !isEntityPlan(plan) && plan.getCustomAttributes().containsKey(LOCATIONS_KEY);
+//    }
+
+    private DeploymentPlan makePlanFromYaml(String yaml) {
+        CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get();
+        return camp.pdp().parseDeploymentPlan(Streams.newReaderWithContents(yaml));
+    }
+
+    //------------------------
+    
+    @Override
+    public CatalogItem<?,?> addItem(String yaml) {
+        return addItem(yaml, false);
+    }
+
+    @Override
+    public List<? extends CatalogItem<?,?>> addItems(String yaml) {
+        return addItems(yaml, false);
+    }
+
+    @Override
+    public CatalogItem<?,?> addItem(String yaml, boolean forceUpdate) {
+        return Iterables.getOnlyElement(addItems(yaml, forceUpdate));
+    }
+    
+    @Override
+    public List<? extends CatalogItem<?,?>> addItems(String yaml, boolean forceUpdate) {
+        log.debug("Adding manual catalog item to "+mgmt+": "+yaml);
+        checkNotNull(yaml, "yaml");
+        List<CatalogItemDtoAbstract<?, ?>> result = collectCatalogItems(yaml);
+        // do this at the end for atomic updates; if there are intra-yaml references, we handle them specially
+        for (CatalogItemDtoAbstract<?, ?> item: result) {
+            addItemDto(item, forceUpdate);
+        }
+        return result;
+    }
+    
+    private CatalogItem<?,?> addItemDto(CatalogItemDtoAbstract<?, ?> itemDto, boolean forceUpdate) {
+        CatalogItem<?, ?> existingDto = checkItemAllowedAndIfSoReturnAnyDuplicate(itemDto, true, forceUpdate);
+        if (existingDto!=null) {
+            // it's a duplicate, and not forced, just return it
+            log.trace("Using existing duplicate for catalog item {}", itemDto.getId());
+            return existingDto;
+        }
+
+        if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
+        manualAdditionsCatalog.addEntry(itemDto);
+
+        // Ensure the cache is populated and it is persisted by the management context
+        getCatalog().addEntry(itemDto);
+
+        // Request that the management context persist the item.
+        if (log.isTraceEnabled()) {
+            log.trace("Scheduling item for persistence addition: {}", itemDto.getId());
+        }
+        if (itemDto.getCatalogItemType() == CatalogItemType.LOCATION) {
+            @SuppressWarnings("unchecked")
+            CatalogItem<Location,LocationSpec<?>> locationItem = (CatalogItem<Location, LocationSpec<?>>) itemDto;
+            ((BasicLocationRegistry)mgmt.getLocationRegistry()).updateDefinedLocation(locationItem);
+        }
+        mgmt.getRebindManager().getChangeListener().onManaged(itemDto);
+
+        return itemDto;
+    }
+
+    /** returns item DTO if item is an allowed duplicate, or null if it should be added (there is no duplicate), 
+     * throwing if item cannot be added */
+    private CatalogItem<?, ?> checkItemAllowedAndIfSoReturnAnyDuplicate(CatalogItem<?,?> itemDto, boolean allowDuplicates, boolean forceUpdate) {
+        if (forceUpdate) return null;
+        CatalogItemDo<?, ?> existingItem = getCatalogItemDo(itemDto.getSymbolicName(), itemDto.getVersion());
+        if (existingItem == null) return null;
+        // check if they are equal
+        CatalogItem<?, ?> existingDto = existingItem.getDto();
+        if (existingDto.equals(itemDto)) {
+            if (allowDuplicates) return existingItem;
+            throw new IllegalStateException("Updating existing catalog entries, even with the same content, is forbidden: " +
+                    itemDto.getSymbolicName() + ":" + itemDto.getVersion() + ". Use forceUpdate argument to override.");
+        } else {
+            throw new IllegalStateException("Updating existing catalog entries is forbidden: " +
+                    itemDto.getSymbolicName() + ":" + itemDto.getVersion() + ". Use forceUpdate argument to override.");
+        }
+    }
+
+    @Override @Deprecated /** @deprecated see super */
+    public void addItem(CatalogItem<?,?> item) {
+        //assume forceUpdate for backwards compatibility
+        log.debug("Adding manual catalog item to "+mgmt+": "+item);
+        checkNotNull(item, "item");
+        CatalogUtils.installLibraries(mgmt, item.getLibraries());
+        if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
+        manualAdditionsCatalog.addEntry(getAbstractCatalogItem(item));
+    }
+
+    @Override @Deprecated /** @deprecated see super */
+    public CatalogItem<?,?> addItem(Class<?> type) {
+        //assume forceUpdate for backwards compatibility
+        log.debug("Adding manual catalog item to "+mgmt+": "+type);
+        checkNotNull(type, "type");
+        if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
+        manualAdditionsClasses.addClass(type);
+        return manualAdditionsCatalog.classpath.addCatalogEntry(type);
+    }
+
+    private synchronized void loadManualAdditionsCatalog() {
+        if (manualAdditionsCatalog!=null) return;
+        CatalogDto manualAdditionsCatalogDto = CatalogDto.newNamedInstance(
+                "Manual Catalog Additions", "User-additions to the catalog while Brooklyn is running, " +
+                "created "+Time.makeDateString(),
+                "manual-additions");
+        CatalogDo manualAdditionsCatalog = catalog.addCatalog(manualAdditionsCatalogDto);
+        if (manualAdditionsCatalog==null) {
+            // not hard to support, but slightly messy -- probably have to use ID's to retrieve the loaded instance
+            // for now block once, then retry
+            log.warn("Blocking until catalog is loaded before changing it");
+            boolean loaded = blockIfNotLoaded(Duration.TEN_SECONDS);
+            if (!loaded)
+                log.warn("Catalog still not loaded after delay; subsequent operations may fail");
+            manualAdditionsCatalog = catalog.addCatalog(manualAdditionsCatalogDto);
+            if (manualAdditionsCatalog==null) {
+                throw new UnsupportedOperationException("Catalogs cannot be added until the base catalog is loaded, and catalog is taking a while to load!");
+            }
+        }
+        
+        log.debug("Creating manual additions catalog for "+mgmt+": "+manualAdditionsCatalog);
+        manualAdditionsClasses = new LoadedClassLoader();
+        ((AggregateClassLoader)manualAdditionsCatalog.classpath.getLocalClassLoader()).addFirst(manualAdditionsClasses);
+        
+        // expose when we're all done
+        this.manualAdditionsCatalog = manualAdditionsCatalog;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItems() {
+        if (!getCatalog().isLoaded()) {
+            // some callers use this to force the catalog to load (maybe when starting as hot_backup without a catalog ?)
+            log.debug("Forcing catalog load on access of catalog items");
+            load();
+        }
+        return ImmutableList.copyOf((Iterable)catalog.getIdCache().values());
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItems(Predicate<? super CatalogItem<T,SpecT>> filter) {
+        Iterable<CatalogItemDo<T,SpecT>> filtered = Iterables.filter((Iterable)catalog.getIdCache().values(), (Predicate<CatalogItem<T,SpecT>>)(Predicate) filter);
+        return Iterables.transform(filtered, BasicBrooklynCatalog.<T,SpecT>itemDoToDto());
+    }
+
+    private static <T,SpecT> Function<CatalogItemDo<T,SpecT>, CatalogItem<T,SpecT>> itemDoToDto() {
+        return new Function<CatalogItemDo<T,SpecT>, CatalogItem<T,SpecT>>() {
+            @Override
+            public CatalogItem<T,SpecT> apply(@Nullable CatalogItemDo<T,SpecT> item) {
+                if (item==null) return null;
+                return item.getDto();
+            }
+        };
+    }
+    
+    private static <T,SpecT> Function<CatalogItemDo<T, SpecT>, CatalogItem<T,SpecT>> itemDoToDtoAddingSelectedMetadataDuringScan(final Map<Object, Object> catalogMetadata) {
+        return new Function<CatalogItemDo<T,SpecT>, CatalogItem<T,SpecT>>() {
+            @Override
+            public CatalogItem<T,SpecT> apply(@Nullable CatalogItemDo<T,SpecT> item) {
+                if (item==null) return null;
+                CatalogItemDtoAbstract<T, SpecT> dto = (CatalogItemDtoAbstract<T, SpecT>) item.getDto();
+
+                // when scanning we only allow version and libraries to be overwritten
+                
+                String version = getFirstAs(catalogMetadata, String.class, "version").orNull();
+                if (Strings.isNonBlank(version)) dto.setVersion(version);
+                
+                Object librariesCombined = catalogMetadata.get("brooklyn.libraries");
+                if (librariesCombined instanceof Collection) {
+                    // will be set by scan -- slightly longwinded way to retrieve, but scanning for osgi needs an overhaul in any case
+                    Collection<CatalogBundle> libraryBundles = CatalogItemDtoAbstract.parseLibraries((Collection<?>) librariesCombined);
+                    dto.setLibraries(libraryBundles);
+                }
+                // replace java type with plan yaml -- needed for libraries / catalog item to be picked up,
+                // but probably useful to transition away from javaType altogether
+                dto.setSymbolicName(dto.getJavaType());
+                switch (dto.getCatalogItemType()) {
+                    case TEMPLATE:
+                    case ENTITY:
+                        dto.setPlanYaml("services: [{ type: "+dto.getJavaType()+" }]");
+                        break;
+                    case POLICY:
+                        dto.setPlanYaml(POLICIES_KEY + ": [{ type: "+dto.getJavaType()+" }]");
+                        break;
+                    case LOCATION:
+                        dto.setPlanYaml(LOCATIONS_KEY + ": [{ type: "+dto.getJavaType()+" }]");
+                        break;
+                }
+                dto.setJavaType(null);
+
+                return dto;
+            }
+        };
+    }
+
+    transient CatalogXmlSerializer serializer;
+    
+    public String toXmlString() {
+        if (serializer==null) loadSerializer();
+        return serializer.toString(catalog.dto);
+    }
+    
+    private synchronized void loadSerializer() {
+        if (serializer==null) 
+            serializer = new CatalogXmlSerializer();
+    }
+
+    @Deprecated
+    public CatalogItem<?,?> getCatalogItemForType(String typeName) {
+        final CatalogItem<?,?> resultI;
+        final BrooklynCatalog catalog = mgmt.getCatalog();
+        if (CatalogUtils.looksLikeVersionedId(typeName)) {
+            //All catalog identifiers of the form xxxx:yyyy are composed of symbolicName+version.
+            //No javaType is allowed as part of the identifier.
+            resultI = CatalogUtils.getCatalogItemOptionalVersion(mgmt, typeName);
+        } else {
+            //Usually for catalog items with javaType (that is items from catalog.xml)
+            //the symbolicName and javaType match because symbolicName (was ID)
+            //is not specified explicitly. But could be the case that there is an item
+            //whose symbolicName is explicitly set to be different from the javaType.
+            //Note that in the XML the attribute is called registeredTypeName.
+            Iterable<CatalogItem<Object,Object>> resultL = catalog.getCatalogItems(CatalogPredicates.javaType(Predicates.equalTo(typeName)));
+            if (!Iterables.isEmpty(resultL)) {
+                //Push newer versions in front of the list (not that there should
+                //be more than one considering the items are coming from catalog.xml).
+                resultI = sortVersionsDesc(resultL).iterator().next();
+                if (log.isDebugEnabled() && Iterables.size(resultL)>1) {
+                    log.debug("Found "+Iterables.size(resultL)+" matches in catalog for type "+typeName+"; returning the result with preferred version, "+resultI);
+                }
+            } else {
+                //As a last resort try searching for items with the same symbolicName supposedly
+                //different from the javaType.
+                resultI = catalog.getCatalogItem(typeName, BrooklynCatalog.DEFAULT_VERSION);
+                if (resultI != null) {
+                    if (resultI.getJavaType() == null) {
+                        //Catalog items scanned from the classpath (using reflection and annotations) now
+                        //get yaml spec rather than a java type. Can't use those when creating apps from
+                        //the legacy app spec format.
+                        log.warn("Unable to find catalog item for type "+typeName +
+                                ". There is an existing catalog item with ID " + resultI.getId() +
+                                " but it doesn't define a class type.");
+                        return null;
+                    }
+                }
+            }
+        }
+        return resultI;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleConverter.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleConverter.java
new file mode 100644
index 0000000..79b39c3
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleConverter.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.catalog.internal;
+
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
+import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.mapper.Mapper;
+
+
+/**
+ *  Convert old-style catalog.xml file formats to the latest version.
+ *  The code is needed only during transition to the new version, can be removed after a while.
+ */
+@Deprecated
+public class CatalogBundleConverter implements Converter {
+
+    private ReflectionConverter delegateConverter;
+
+    public CatalogBundleConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
+        this.delegateConverter = new ReflectionConverter(mapper, reflectionProvider);
+    }
+
+    @Override
+    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
+        return type == CatalogBundleDto.class;
+    }
+
+    @Override
+    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
+        context.convertAnother(source, delegateConverter);
+    }
+
+    @Override
+    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        if (reader.hasMoreChildren()) {
+            return context.convertAnother(context.currentObject(), CatalogBundleDto.class, delegateConverter);
+        } else {
+            return new CatalogBundleDto(null, null, reader.getValue());
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleDto.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleDto.java
new file mode 100644
index 0000000..60472ae
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleDto.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+
+public class CatalogBundleDto implements CatalogBundle {
+    private String symbolicName;
+    private String version;
+    private String url;
+
+    public CatalogBundleDto() {}
+
+    public CatalogBundleDto(String name, String version, String url) {
+        if (name == null && version == null) {
+            Preconditions.checkNotNull(url, "url to an OSGi bundle is required");
+        } else {
+            Preconditions.checkNotNull(name, "both name and version are required");
+            Preconditions.checkNotNull(version, "both name and version are required");
+        }
+
+        this.symbolicName = name;
+        this.version = version;
+        this.url = url;
+    }
+
+    @Override
+    public boolean isNamed() {
+        return symbolicName != null && version != null;
+    }
+
+    @Override
+    public String getSymbolicName() {
+        return symbolicName;
+    }
+
+    @Override
+    public String getVersion() {
+        return version;
+    }
+
+    @Override
+    public String getUrl() {
+        return url;
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("symbolicName", symbolicName)
+                .add("version", version)
+                .add("url", url)
+                .toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(symbolicName, version, url);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        CatalogBundleDto other = (CatalogBundleDto) obj;
+        if (!Objects.equal(symbolicName, other.symbolicName)) return false;
+        if (!Objects.equal(version, other.version)) return false;
+        if (!Objects.equal(url, other.url)) return false;
+        return true;
+    }
+    
+    
+}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/brooklyn/catalog/internal/CatalogVersioningTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogVersioningTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogVersioningTest.java
deleted file mode 100644
index 78c11ea..0000000
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogVersioningTest.java
+++ /dev/null
@@ -1,156 +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.catalog.internal;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-import org.apache.brooklyn.api.catalog.BrooklynCatalog;
-import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.core.management.internal.LocalManagementContext;
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-
-import brooklyn.catalog.CatalogPredicates;
-import brooklyn.entity.basic.Entities;
-
-import com.google.common.base.Predicates;
-import com.google.common.collect.Iterables;
-
-public class CatalogVersioningTest {
-    private LocalManagementContext managementContext;
-    private BrooklynCatalog catalog;
-    
-    @BeforeMethod(alwaysRun = true)
-    public void setUp() throws Exception {
-        managementContext = LocalManagementContextForTests.newInstance();
-        catalog = managementContext.getCatalog();
-    }
-    
-    @AfterMethod(alwaysRun = true)
-    public void tearDown() throws Exception {
-        if (managementContext != null) Entities.destroyAll(managementContext);
-    }
-
-    @Test
-    public void testParsingVersion() {
-        assertVersionParsesAs("foo:1", "foo", "1");
-        assertVersionParsesAs("foo", null, null);
-        assertVersionParsesAs("foo:1.1", "foo", "1.1");
-        assertVersionParsesAs("foo:1_SNAPSHOT", "foo", "1_SNAPSHOT");
-        assertVersionParsesAs("foo:10.9.8_SNAPSHOT", "foo", "10.9.8_SNAPSHOT");
-        assertVersionParsesAs("foo:bar", null, null);
-        assertVersionParsesAs("chef:cookbook", null, null);
-        assertVersionParsesAs("http://foo:8080", null, null);
-    }
-
-    private static void assertVersionParsesAs(String versionedId, String id, String version) {
-        if (version==null) {
-            Assert.assertFalse(CatalogUtils.looksLikeVersionedId(versionedId));
-        } else {
-            Assert.assertTrue(CatalogUtils.looksLikeVersionedId(versionedId));
-            Assert.assertEquals(CatalogUtils.getIdFromVersionedId(versionedId), id);
-            Assert.assertEquals(CatalogUtils.getVersionFromVersionedId(versionedId), version);
-        }
-    }
-
-    @Test
-    public void testAddVersioned() {
-        String symbolicName = "sampleId";
-        String version = "0.1.0";
-        createCatalogItem(symbolicName, version);
-        assertSingleCatalogItem(symbolicName, version);
-    }
-
-    @Test
-    public void testAddSameVersionFails() {
-        String symbolicName = "sampleId";
-        String version = "0.1.0";
-        createCatalogItem(symbolicName, version);
-        createCatalogItem(symbolicName, version);
-        //forced update assumed in the above call
-        assertSingleCatalogItem(symbolicName, version);
-    }
-    
-    @Test
-    public void testGetLatest() {
-        String symbolicName = "sampleId";
-        String v1 = "0.1.0";
-        String v2 = "0.2.0";
-        createCatalogItem(symbolicName, v1);
-        createCatalogItem(symbolicName, v2);
-        CatalogItem<?, ?> item = catalog.getCatalogItem(symbolicName, BasicBrooklynCatalog.DEFAULT_VERSION);
-        assertEquals(item.getSymbolicName(), symbolicName);
-        assertEquals(item.getVersion(), v2);
-    }
-    
-    @Test
-    public void testGetLatestStable() {
-        String symbolicName = "sampleId";
-        String v1 = "0.1.0";
-        String v2 = "0.2.0-SNAPSHOT";
-        createCatalogItem(symbolicName, v1);
-        createCatalogItem(symbolicName, v2);
-        CatalogItem<?, ?> item = catalog.getCatalogItem(symbolicName, BasicBrooklynCatalog.DEFAULT_VERSION);
-        assertEquals(item.getSymbolicName(), symbolicName);
-        assertEquals(item.getVersion(), v1);
-    }
-    
-    @Test
-    public void testDelete() {
-        String symbolicName = "sampleId";
-        String version = "0.1.0";
-        createCatalogItem(symbolicName, version);
-        assertSingleCatalogItem(symbolicName, version);
-        assertTrue(catalog.getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName))).iterator().hasNext());
-        catalog.deleteCatalogItem(symbolicName, version);
-        assertFalse(catalog.getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName))).iterator().hasNext());
-    }
-
-    @Test
-    public void testList() {
-        String symbolicName = "sampleId";
-        String v1 = "0.1.0";
-        String v2 = "0.2.0-SNAPSHOT";
-        createCatalogItem(symbolicName, v1);
-        createCatalogItem(symbolicName, v2);
-        Iterable<CatalogItem<Object, Object>> items = catalog.getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName)));
-        assertEquals(Iterables.size(items), 2);
-    }
-    
-    @SuppressWarnings("deprecation")
-    private void createCatalogItem(String symbolicName, String version) {
-        catalog.addItem(CatalogItemBuilder.newEntity(symbolicName, version).
-                plan("services:\n- type: brooklyn.entity.basic.BasicEntity")
-                .build());
-    }
-
-    private void assertSingleCatalogItem(String symbolicName, String version) {
-        Iterable<CatalogItem<Object, Object>> items = catalog.getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName)));
-        CatalogItem<Object, Object> item = Iterables.getOnlyElement(items);
-        assertEquals(item.getSymbolicName(), symbolicName);
-        assertEquals(item.getVersion(), version);
-    }
-
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/brooklyn/catalog/internal/MyCatalogItems.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/MyCatalogItems.java b/core/src/test/java/brooklyn/catalog/internal/MyCatalogItems.java
deleted file mode 100644
index 44067ed..0000000
--- a/core/src/test/java/brooklyn/catalog/internal/MyCatalogItems.java
+++ /dev/null
@@ -1,37 +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.catalog.internal;
-
-import org.apache.brooklyn.api.catalog.Catalog;
-
-import brooklyn.entity.basic.AbstractApplication;
-import brooklyn.entity.basic.ApplicationBuilder;
-
-public class MyCatalogItems {
-
-    @Catalog(description="Some silly app test")
-    public static class MySillyAppTemplate extends AbstractApplication {
-    }
-    
-    @Catalog(description="Some silly app builder test")
-    public static class MySillyAppBuilderTemplate extends ApplicationBuilder {
-        @Override protected void doBuild() {
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/brooklyn/entity/rebind/ActivePartialRebindVersionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/ActivePartialRebindVersionTest.java b/core/src/test/java/brooklyn/entity/rebind/ActivePartialRebindVersionTest.java
index 41917d4..dc9d8fe 100644
--- a/core/src/test/java/brooklyn/entity/rebind/ActivePartialRebindVersionTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/ActivePartialRebindVersionTest.java
@@ -25,10 +25,10 @@ import org.testng.annotations.Test;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.catalog.internal.CatalogTestUtils;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.core.management.osgi.OsgiVersionMoreEntityTest;
 
-import brooklyn.catalog.internal.CatalogTestUtils;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.entity.rebind.transformer.CompoundTransformer;
 import brooklyn.util.collections.MutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 2b5cebf..3debe30 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
@@ -31,7 +31,6 @@ import org.slf4j.LoggerFactory;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.entity.basic.AbstractApplication;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.ConfigKeys;
@@ -44,6 +43,7 @@ import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 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.test.TestResourceUnavailableException;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 f46df75..92383fc 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
@@ -40,12 +40,12 @@ import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
+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.test.entity.TestEntity;
 
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
-import brooklyn.catalog.internal.CatalogDto;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 
@@ -88,7 +88,7 @@ public class RebindCatalogItemTest extends RebindTestFixtureWithApp {
     protected LocalManagementContext createOrigManagementContext() {
         BrooklynProperties properties = BrooklynProperties.Factory.newDefault();
         properties.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, "classpath://brooklyn/entity/rebind/rebind-catalog-item-test-catalog.xml");
-        properties.put(BrooklynServerConfig.CATALOG_LOAD_MODE, brooklyn.catalog.CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL);
+        properties.put(BrooklynServerConfig.CATALOG_LOAD_MODE, org.apache.brooklyn.core.catalog.CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL);
         return RebindTestUtils.managementContextBuilder(mementoDir, classLoader)
                 .properties(properties)
                 .persistPeriodMillis(getPersistPeriodMillis())

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 d563bea..5c39216 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
@@ -40,10 +40,10 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 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 brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityFunctions;
 import brooklyn.entity.basic.StartableApplication;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
index 3dcef6a..c0bab54 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
@@ -52,12 +52,12 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.mementos.BrooklynMementoPersister.LookupContext;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemDtoAbstract;
+import org.apache.brooklyn.core.catalog.internal.CatalogTestUtils;
 import org.apache.brooklyn.core.management.osgi.OsgiTestResources;
 import org.apache.brooklyn.core.management.osgi.OsgiVersionMoreEntityTest;
 
-import brooklyn.catalog.internal.CatalogItemBuilder;
-import brooklyn.catalog.internal.CatalogItemDtoAbstract;
-import brooklyn.catalog.internal.CatalogTestUtils;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.util.collections.MutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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
new file mode 100644
index 0000000..90e039d
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoTest.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.catalog.internal;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+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 org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
+import org.apache.brooklyn.core.catalog.internal.CatalogBundleDto;
+import org.apache.brooklyn.core.catalog.internal.CatalogDo;
+import org.apache.brooklyn.core.catalog.internal.CatalogDto;
+import org.apache.brooklyn.core.catalog.internal.CatalogEntityItemDto;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemDo;
+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.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;
+
+public class CatalogDtoTest {
+
+    private static final Logger log = LoggerFactory.getLogger(CatalogDtoTest.class);
+
+    private LocalManagementContext managementContext;
+    
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws Exception {
+        managementContext = LocalManagementContextForTests.newInstanceWithOsgi();
+    }
+    
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() throws Exception {
+        if (managementContext != null) Entities.destroyAll(managementContext);
+    }
+
+    @Test(groups="Integration")
+    public void testCatalogLookup() {
+        CatalogDto root = buildExampleCatalog();
+        checkCatalogHealthy(root);
+    }
+    
+    @Test(groups="Integration")
+    public void testCatalogSerializeAndLookup() {
+        CatalogDto root = buildExampleCatalog();
+        CatalogXmlSerializer serializer = new CatalogXmlSerializer();
+        
+        String xml = serializer.toString(root);
+        log.info("Example catalog serialized as:\n"+xml);
+        
+        CatalogDto root2 = (CatalogDto) serializer.fromString(xml);
+        checkCatalogHealthy(root2);
+    }
+
+    protected void checkCatalogHealthy(CatalogDto root) {
+        assertEquals(root.catalogs.size(), 4);
+        CatalogDo loader = new CatalogDo(managementContext, root).load();
+        
+        // test app comes from jar, by default
+        CatalogItemDo<?,?> worker = loader.getIdCache().get(CatalogUtils.getVersionedId(TestApplication.class.getCanonicalName(), BasicBrooklynCatalog.NO_VERSION));
+        assertNotNull(worker);
+        assertEquals(worker.getDisplayName(), "Test App from JAR");
+        
+        // TODO can test scanned elements, links to other catalogs, etc
+    }
+
+    public CatalogDto buildExampleCatalog() {
+        CatalogDo root = new CatalogDo(
+                managementContext,
+                CatalogDto.newNamedInstance("My Local Catalog",
+                        "My favourite local settings, including remote catalogs -- intended partly as a teaching " +
+                        "example for what can be expressed, and how", "contents-built-in-test"));
+        root.setClasspathScanForEntities(CatalogScanningModes.NONE);
+
+        String bundleUrl = MavenRetriever.localUrl(BrooklynMavenArtifacts.artifact("", "brooklyn-core", "jar", "tests"));
+        CatalogDo testEntitiesJavaCatalog = new CatalogDo(
+                managementContext,
+                CatalogDto.newNamedInstance("Test Entities from Java", null, "test-java"));
+        testEntitiesJavaCatalog.setClasspathScanForEntities(CatalogScanningModes.NONE);
+        testEntitiesJavaCatalog.addToClasspath(bundleUrl);
+        testEntitiesJavaCatalog.addEntry(CatalogItemBuilder.newTemplate(TestApplication.class.getCanonicalName(), BasicBrooklynCatalog.NO_VERSION)
+                .displayName("Test App from JAR")
+                .javaType(TestApplication.class.getCanonicalName())
+                .build());
+        testEntitiesJavaCatalog.addEntry(CatalogItemBuilder.newEntity(TestEntity.class.getCanonicalName(), BasicBrooklynCatalog.NO_VERSION)
+                .displayName("Test Entity from JAR")
+                .javaType(TestEntity.class.getCanonicalName())
+                .build());
+        root.addCatalog(testEntitiesJavaCatalog.dto);
+
+        CatalogDo testEntitiesJavaCatalogScanning = new CatalogDo(
+                managementContext,
+                CatalogDto.newNamedInstance("Test Entities from Java Scanning", null, "test-java-scan"));
+        testEntitiesJavaCatalogScanning.addToClasspath(bundleUrl);
+        testEntitiesJavaCatalogScanning.setClasspathScanForEntities(CatalogScanningModes.ANNOTATIONS);
+        root.addCatalog(testEntitiesJavaCatalogScanning.dto);
+
+        CatalogDo osgiCatalog = new CatalogDo(
+                managementContext,
+                CatalogDto.newNamedInstance("Test Entities from OSGi",
+                        "A catalog whose entries define their libraries as a list of OSGi bundles", "test-osgi-defined"));
+        osgiCatalog.setClasspathScanForEntities(CatalogScanningModes.NONE);
+        CatalogEntityItemDto osgiEntity = CatalogItemBuilder.newEntity(TestEntity.class.getCanonicalName(), "Test Entity from OSGi")
+                // NB: this is not actually an OSGi bundle, but it's okay as we don't instantiate the bundles ahead of time (currently)
+                .libraries(ImmutableList.<CatalogBundle>of(new CatalogBundleDto(null, null, bundleUrl)))
+                .build();
+        testEntitiesJavaCatalog.addEntry(osgiEntity);
+        root.addCatalog(osgiCatalog.dto);
+
+        root.addCatalog(CatalogDto.newLinkedInstance("classpath://brooklyn-catalog-empty.xml"));
+        return root.dto;
+    }
+    
+    @Test
+    public void testVersionedIdSplitter() {
+        String id = "simple.id";
+        String version = "0.1.2";
+        String versionedId = CatalogUtils.getVersionedId(id, version);
+        
+        Assert.assertNull(CatalogUtils.getIdFromVersionedId(null));
+        Assert.assertNull(CatalogUtils.getVersionFromVersionedId(null));
+        Assert.assertNull(CatalogUtils.getIdFromVersionedId(id));
+        Assert.assertNull(CatalogUtils.getVersionFromVersionedId(version));
+        Assert.assertEquals(CatalogUtils.getIdFromVersionedId(versionedId), id);
+        Assert.assertEquals(CatalogUtils.getVersionFromVersionedId(versionedId), version);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparatorTest.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparatorTest.java
new file mode 100644
index 0000000..e7d43cd
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparatorTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.catalog.internal;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import org.testng.annotations.Test;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemComparator;
+
+public class CatalogItemComparatorTest {
+    private static final String RC2 = "10.5.8-rc2";
+    private static final String STABLE = "10.5.8";
+
+    @Test
+    public void testComparison() {
+        compare("0.0.1", "0.0.2", 1);
+        compare("0.0.2", "0.0.1", -1);
+        compare("0.0.1-qual", "0.0.2", 1);
+        compare("0.0.1.qual", "0.0.2", 1);
+        compare("0.0.1-qual", "0.0.1_qual", 0);
+        compare("0.0.1.qual", "0.0.1.qual", 0);
+        compare("0.0.1", "0.0.2-SNAPSHOT", -1);
+        compare("0.0.1", "0.0.2.SNAPSHOT", -1);
+        compare("0.0.0_SNAPSHOT", "0.0.1-SNAPSHOT-20141111114709760", 1);
+        compare("0.0.0.SNAPSHOT", "0.0.1.SNAPSHOT-20141111114709760", 1);
+        compare("2.0", "2.0.1-BUILD", 1);
+        compare("2.0", "2.0.1.BUILD", 1);
+        compare("2.0.1", "2.0-BUILD", -1);
+        compare("2.0.1", "2.0.0.BUILD", -1);
+        compare("2.0", "2.0-BUILD", -1);
+        // Note not true for .qualifier: compare("2.0", "2.0.0.BUILD", -1);
+        compare("2.1", "2.0-BUILD", -1);
+        compare("2.1", "2.0.0.BUILD", -1);
+        compare("1", "1.3", 1);
+        compare("1-beta", "1-rc2", 1);
+        // Note not true for .qualifier: compare("1.0.0.beta", "1.0.0.rc2", 1);
+        compare("1-beta1", "1-beta10", 1);
+
+        compare(STABLE, "10.5", -1);
+        compare(STABLE, STABLE, 0);
+
+        compare(STABLE, "10.6", 1);
+        compare(STABLE, "10.5.8.1", 1);
+
+        compare("10.5.8-rc2", "10.5.8-rc3", 1) ;
+        compare("10.5.8-rc2", "10.5.8-rc1", -1);
+
+        compare(STABLE, RC2, -1);
+
+        CatalogItemComparator cmp = CatalogItemComparator.INSTANCE;
+        assertTrue(cmp.compare(v(RC2), v("10.5.8-beta1")) == cmp.compare(v(RC2), v("10.5.8-beta3")));
+    }
+    
+    private void compare(String v1, String v2, int expected) {
+        CatalogItemComparator cmp = CatalogItemComparator.INSTANCE;
+        assertEquals(cmp.compare(v(v1), v(v2)), expected);
+        assertEquals(cmp.compare(v(v2), v(v1)), -expected);
+    }
+    
+    private CatalogItem<?, ?> v(String version) {
+        return CatalogItemBuilder.newEntity("xxx", version).build();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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
new file mode 100644
index 0000000..eea4933
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogLoadTest.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.catalog.internal;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+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 com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+
+public class CatalogLoadTest {
+
+    CatalogXmlSerializer serializer;
+
+    @BeforeMethod
+    public void setUp() {
+        serializer = new CatalogXmlSerializer();
+    }
+
+    private String loadFile(String file) {
+        return ResourceUtils.create(this).getResourceAsString(file);
+    }
+
+    // CAMP YAML parsing not available in core, so YAML catalog tests are in camp, e.g. CatalogYamlEntitiesTest 
+    
+    @Test
+    public void testLoadXmlCatalog() {
+        CatalogDto catalog = (CatalogDto) serializer.fromString(
+                loadFile("classpath://brooklyn/catalog/internal/osgi-catalog.xml"));
+        assertNotNull(catalog);
+        assertEquals(catalog.name, "OSGi catalogue");
+        assertEquals(Iterables.size(catalog.getUniqueEntries()), 1, "Catalog entries = " + Joiner.on(", ").join(catalog.getUniqueEntries()));
+
+        CatalogItemDtoAbstract<?,?> template = Iterables.getOnlyElement(catalog.getUniqueEntries());
+        assertEquals(template.getDisplayName(), "Entity name");
+        assertEquals(template.getVersion(), "9.1.3");
+        assertEquals(template.getJavaType(), "com.example.ExampleApp");
+        assertEquals(template.getLibraries().size(), 2,
+                "Template bundles=" + Joiner.on(", ").join(template.getLibraries()));
+        
+        boolean foundBundle1 = false, foundBundle2 = false;
+        for (CatalogBundle bundle : template.getLibraries()) {
+            if (bundle.getUrl().equals("file://path/to/bundle.jar")) {
+                foundBundle1 = true;
+            }
+            if (bundle.getUrl().equals("http://www.url.com/for/bundle.jar")) {
+                foundBundle2 = true;
+            }
+        }
+        assertTrue(foundBundle1);
+        assertTrue(foundBundle2);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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
new file mode 100644
index 0000000..2d5abad
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogScanTest.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import java.net.URLEncoder;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.api.catalog.BrooklynCatalog;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+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 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;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+public class CatalogScanTest {
+
+    private static final Logger log = LoggerFactory.getLogger(CatalogScanTest.class);
+
+    private BrooklynCatalog defaultCatalog, annotsCatalog, fullCatalog;
+    
+    private List<LocalManagementContext> managementContexts = Lists.newCopyOnWriteArrayList();
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown(){
+        for (LocalManagementContext managementContext : managementContexts) {
+            Entities.destroyAll(managementContext);
+        }
+        managementContexts.clear();
+    }
+    
+    private LocalManagementContext newManagementContext(BrooklynProperties props) {
+        LocalManagementContext result = new LocalManagementContext(props);
+        managementContexts.add(result);
+        return result;
+    }
+    
+    private synchronized void loadFullCatalog() {
+        if (fullCatalog!=null) return;
+        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+        props.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL.getName(), 
+                "data:,"+Urls.encode("<catalog><classpath scan=\"types\"/></catalog>"));
+        fullCatalog = newManagementContext(props).getCatalog();        
+        log.info("ENTITIES loaded for FULL: "+fullCatalog.getCatalogItems(Predicates.alwaysTrue()));
+    }
+    
+    private synchronized void loadTheDefaultCatalog(boolean lookOnDiskForDefaultCatalog) {
+        if (defaultCatalog!=null) return;
+        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+        props.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL.getName(),
+            // if default catalog is picked up from the system, we might get random stuff from ~/.brooklyn/ instead of the default;
+            // useful as an integration check that we default correctly, but irritating for people to use if they have such a catalog installed
+            (lookOnDiskForDefaultCatalog ? "" :
+                "data:,"+Urls.encode(new ResourceUtils(this).getResourceAsString("classpath:/brooklyn/default.catalog.bom"))));
+        LocalManagementContext managementContext = newManagementContext(props);
+        defaultCatalog = managementContext.getCatalog();        
+        log.info("ENTITIES loaded for DEFAULT: "+defaultCatalog.getCatalogItems(Predicates.alwaysTrue()));
+    }
+    
+    @SuppressWarnings("deprecation")
+    private synchronized void loadAnnotationsOnlyCatalog() {
+        if (annotsCatalog!=null) return;
+        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+        props.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL.getName(),
+                "data:,"+URLEncoder.encode("<catalog><classpath scan=\"annotations\"/></catalog>"));
+        LocalManagementContext managementContext = newManagementContext(props);
+        annotsCatalog = managementContext.getCatalog();
+        log.info("ENTITIES loaded with annotation: "+annotsCatalog.getCatalogItems(Predicates.alwaysTrue()));
+    }
+    
+    @Test
+    public void testLoadAnnotations() {
+        loadAnnotationsOnlyCatalog();
+        BrooklynCatalog c = annotsCatalog;
+        
+        Iterable<CatalogItem<Object,Object>> bases = c.getCatalogItems(CatalogPredicates.displayName(Predicates.containsPattern("MyBaseEntity")));
+        Assert.assertEquals(Iterables.size(bases), 0, "should have been empty: "+bases);
+        
+        Iterable<CatalogItem<Object,Object>> asdfjkls = c.getCatalogItems(CatalogPredicates.displayName(Predicates.containsPattern("__asdfjkls__shouldnotbefound")));
+        Assert.assertEquals(Iterables.size(asdfjkls), 0);
+        
+        Iterable<CatalogItem<Object,Object>> silly1 = c.getCatalogItems(CatalogPredicates.displayName(Predicates.equalTo("MySillyAppTemplate")));
+        Iterable<CatalogItem<Object,Object>> silly2 = c.getCatalogItems(CatalogPredicates.javaType(Predicates.equalTo(MySillyAppTemplate.class.getName())));
+        CatalogItem<Object, Object> silly1El = Iterables.getOnlyElement(silly1);
+        Assert.assertEquals(silly1El, Iterables.getOnlyElement(silly2));
+        
+        CatalogItem<Application,EntitySpec<? extends Application>> s1 = c.getCatalogItem(Application.class, silly1El.getSymbolicName(), silly1El.getVersion());
+        Assert.assertEquals(s1, silly1El);
+        
+        Assert.assertEquals(s1.getDescription(), "Some silly app test");
+        
+        Class<? extends Application> app = c.loadClass(s1);
+        Assert.assertEquals(MySillyAppTemplate.class, app);
+        
+        String xml = ((BasicBrooklynCatalog)c).toXmlString();
+        log.info("Catalog is:\n"+xml);
+        Assert.assertTrue(xml.indexOf("Some silly app test") >= 0);
+    }
+
+    @Test
+    public void testAnnotationLoadsSomeApps() {
+        loadAnnotationsOnlyCatalog();
+        Iterable<CatalogItem<Object,Object>> silly1 = annotsCatalog.getCatalogItems(CatalogPredicates.displayName(Predicates.equalTo("MySillyAppTemplate")));
+        Assert.assertEquals(Iterables.getOnlyElement(silly1).getDescription(), "Some silly app test");
+    }
+    
+    @Test
+    public void testAnnotationLoadsSomeAppBuilders() {
+        loadAnnotationsOnlyCatalog();
+        Iterable<CatalogItem<Object,Object>> silly1 = annotsCatalog.getCatalogItems(CatalogPredicates.displayName(Predicates.equalTo("MySillyAppBuilderTemplate")));
+        Assert.assertEquals(Iterables.getOnlyElement(silly1).getDescription(), "Some silly app builder test");
+    }
+    
+    @Test
+    public void testMoreTypesThanAnnotations() {
+        loadAnnotationsOnlyCatalog();
+        loadFullCatalog();
+        
+        int numFromAnnots = Iterables.size(annotsCatalog.getCatalogItems(Predicates.alwaysTrue()));
+        int numFromTypes = Iterables.size(fullCatalog.getCatalogItems(Predicates.alwaysTrue()));
+        
+        Assert.assertTrue(numFromAnnots < numFromTypes, "full="+numFromTypes+" annots="+numFromAnnots);
+    }
+    
+    @Test
+    public void testMoreTypesThanAnnotationsForApps() {
+        loadAnnotationsOnlyCatalog();
+        loadFullCatalog();
+        
+        int numFromAnnots = Iterables.size(annotsCatalog.getCatalogItems(CatalogPredicates.IS_TEMPLATE));
+        int numFromTypes = Iterables.size(fullCatalog.getCatalogItems(CatalogPredicates.IS_TEMPLATE));
+        
+        Assert.assertTrue(numFromAnnots < numFromTypes, "full="+numFromTypes+" annots="+numFromAnnots);
+    }
+    
+    @Test
+    public void testAnnotationIsDefault() {
+        doTestAnnotationIsDefault(false);
+    }
+    
+    // see comment in load method; likely fails if a custom catalog is installed in ~/.brooklyn/
+    @Test(groups="Integration", enabled=false)
+    public void testAnnotationIsDefaultOnDisk() {
+        doTestAnnotationIsDefault(true);
+    }
+
+    private void doTestAnnotationIsDefault(boolean lookOnDiskForDefaultCatalog) {
+        loadTheDefaultCatalog(false);
+        int numInDefault = Iterables.size(defaultCatalog.getCatalogItems(Predicates.alwaysTrue()));
+        
+        loadAnnotationsOnlyCatalog();
+        int numFromAnnots = Iterables.size(annotsCatalog.getCatalogItems(Predicates.alwaysTrue()));
+        
+        Assert.assertEquals(numInDefault, numFromAnnots);
+        Assert.assertTrue(numInDefault>0, "Expected more than 0 entries");
+    }
+
+    // a simple test asserting no errors when listing the real catalog, and listing them for reference
+    // also useful to test variants in a stored catalog to assert they all load
+    // TODO integration tests which build up catalogs assuming other things are installed
+    @Test
+    public void testListCurrentCatalogItems() {
+        LocalManagementContext mgmt = newManagementContext(BrooklynProperties.Factory.newDefault());
+        log.info("ITEMS\n"+Strings.join(mgmt.getCatalog().getCatalogItems(), "\n"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogTestUtils.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogTestUtils.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogTestUtils.java
new file mode 100644
index 0000000..510fdf8
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogTestUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+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.catalog.internal.CatalogUtils;
+
+import com.google.common.annotations.Beta;
+
+public class CatalogTestUtils {
+
+    /** creates entity spec with the java type of a catalog item;
+     * would be nice to have this in {@link CatalogUtils},
+     * but the logic for parsing the yaml is buried in camp code,
+     * so it's a little bit hard to make this a first class method.
+     * <p>
+     * (this impl ignores many things, including config and location.)
+     */
+    @Beta
+    public static EntitySpec<?> createEssentialEntitySpec(ManagementContext mgmt, CatalogItem<?, ?> catalogItem) {
+        BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, catalogItem);
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        EntitySpec<?> spec = EntitySpec.create( (Class)loader.loadClass(catalogItem.getJavaType()) );
+        spec.catalogItemId(catalogItem.getId());
+        return spec;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogVersioningTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogVersioningTest.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogVersioningTest.java
new file mode 100644
index 0000000..f8a75cc
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogVersioningTest.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.api.catalog.BrooklynCatalog;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.core.catalog.CatalogPredicates;
+import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+
+import brooklyn.entity.basic.Entities;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+
+public class CatalogVersioningTest {
+    private LocalManagementContext managementContext;
+    private BrooklynCatalog catalog;
+    
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws Exception {
+        managementContext = LocalManagementContextForTests.newInstance();
+        catalog = managementContext.getCatalog();
+    }
+    
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() throws Exception {
+        if (managementContext != null) Entities.destroyAll(managementContext);
+    }
+
+    @Test
+    public void testParsingVersion() {
+        assertVersionParsesAs("foo:1", "foo", "1");
+        assertVersionParsesAs("foo", null, null);
+        assertVersionParsesAs("foo:1.1", "foo", "1.1");
+        assertVersionParsesAs("foo:1_SNAPSHOT", "foo", "1_SNAPSHOT");
+        assertVersionParsesAs("foo:10.9.8_SNAPSHOT", "foo", "10.9.8_SNAPSHOT");
+        assertVersionParsesAs("foo:bar", null, null);
+        assertVersionParsesAs("chef:cookbook", null, null);
+        assertVersionParsesAs("http://foo:8080", null, null);
+    }
+
+    private static void assertVersionParsesAs(String versionedId, String id, String version) {
+        if (version==null) {
+            Assert.assertFalse(CatalogUtils.looksLikeVersionedId(versionedId));
+        } else {
+            Assert.assertTrue(CatalogUtils.looksLikeVersionedId(versionedId));
+            Assert.assertEquals(CatalogUtils.getIdFromVersionedId(versionedId), id);
+            Assert.assertEquals(CatalogUtils.getVersionFromVersionedId(versionedId), version);
+        }
+    }
+
+    @Test
+    public void testAddVersioned() {
+        String symbolicName = "sampleId";
+        String version = "0.1.0";
+        createCatalogItem(symbolicName, version);
+        assertSingleCatalogItem(symbolicName, version);
+    }
+
+    @Test
+    public void testAddSameVersionFails() {
+        String symbolicName = "sampleId";
+        String version = "0.1.0";
+        createCatalogItem(symbolicName, version);
+        createCatalogItem(symbolicName, version);
+        //forced update assumed in the above call
+        assertSingleCatalogItem(symbolicName, version);
+    }
+    
+    @Test
+    public void testGetLatest() {
+        String symbolicName = "sampleId";
+        String v1 = "0.1.0";
+        String v2 = "0.2.0";
+        createCatalogItem(symbolicName, v1);
+        createCatalogItem(symbolicName, v2);
+        CatalogItem<?, ?> item = catalog.getCatalogItem(symbolicName, BasicBrooklynCatalog.DEFAULT_VERSION);
+        assertEquals(item.getSymbolicName(), symbolicName);
+        assertEquals(item.getVersion(), v2);
+    }
+    
+    @Test
+    public void testGetLatestStable() {
+        String symbolicName = "sampleId";
+        String v1 = "0.1.0";
+        String v2 = "0.2.0-SNAPSHOT";
+        createCatalogItem(symbolicName, v1);
+        createCatalogItem(symbolicName, v2);
+        CatalogItem<?, ?> item = catalog.getCatalogItem(symbolicName, BasicBrooklynCatalog.DEFAULT_VERSION);
+        assertEquals(item.getSymbolicName(), symbolicName);
+        assertEquals(item.getVersion(), v1);
+    }
+    
+    @Test
+    public void testDelete() {
+        String symbolicName = "sampleId";
+        String version = "0.1.0";
+        createCatalogItem(symbolicName, version);
+        assertSingleCatalogItem(symbolicName, version);
+        assertTrue(catalog.getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName))).iterator().hasNext());
+        catalog.deleteCatalogItem(symbolicName, version);
+        assertFalse(catalog.getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName))).iterator().hasNext());
+    }
+
+    @Test
+    public void testList() {
+        String symbolicName = "sampleId";
+        String v1 = "0.1.0";
+        String v2 = "0.2.0-SNAPSHOT";
+        createCatalogItem(symbolicName, v1);
+        createCatalogItem(symbolicName, v2);
+        Iterable<CatalogItem<Object, Object>> items = catalog.getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName)));
+        assertEquals(Iterables.size(items), 2);
+    }
+    
+    @SuppressWarnings("deprecation")
+    private void createCatalogItem(String symbolicName, String version) {
+        catalog.addItem(CatalogItemBuilder.newEntity(symbolicName, version).
+                plan("services:\n- type: brooklyn.entity.basic.BasicEntity")
+                .build());
+    }
+
+    private void assertSingleCatalogItem(String symbolicName, String version) {
+        Iterable<CatalogItem<Object, Object>> items = catalog.getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName)));
+        CatalogItem<Object, Object> item = Iterables.getOnlyElement(items);
+        assertEquals(item.getSymbolicName(), symbolicName);
+        assertEquals(item.getVersion(), version);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/test/java/org/apache/brooklyn/core/catalog/internal/MyCatalogItems.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/MyCatalogItems.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/MyCatalogItems.java
new file mode 100644
index 0000000..1fc0de9
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/MyCatalogItems.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import org.apache.brooklyn.api.catalog.Catalog;
+
+import brooklyn.entity.basic.AbstractApplication;
+import brooklyn.entity.basic.ApplicationBuilder;
+
+public class MyCatalogItems {
+
+    @Catalog(description="Some silly app test")
+    public static class MySillyAppTemplate extends AbstractApplication {
+    }
+    
+    @Catalog(description="Some silly app builder test")
+    public static class MySillyAppBuilderTemplate extends ApplicationBuilder {
+        @Override protected void doBuild() {
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 8a555ed..7220e47 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
@@ -38,14 +38,14 @@ 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.api.policy.PolicySpec;
+import org.apache.brooklyn.core.catalog.internal.CatalogEntityItemDto;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemDtoAbstract;
+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 brooklyn.catalog.internal.CatalogEntityItemDto;
-import brooklyn.catalog.internal.CatalogItemBuilder;
-import brooklyn.catalog.internal.CatalogItemDtoAbstract;
-import brooklyn.catalog.internal.CatalogTestUtils;
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.proxying.InternalEntityFactory;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 7def6cf..9241a73 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
@@ -47,11 +47,11 @@ 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.camp.brooklyn.BrooklynCampConstants;
+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 brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.basic.BasicApplicationImpl;
 import brooklyn.util.ResourceUtils;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 57d53da..33bb6a1 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
@@ -46,11 +46,11 @@ import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.service.BrooklynServiceTypeResolver;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.service.ServiceTypeResolver;
+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 brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.basic.BrooklynTags;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 ff60237..527a44c 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
@@ -32,8 +32,8 @@ import org.apache.brooklyn.api.policy.Policy;
 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 brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.config.ConfigBag;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
index be18cc2..409ca1c 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
@@ -31,11 +31,11 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
+import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.net.Urls;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java
index 7b6328a..1f12663 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java
@@ -29,8 +29,8 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynComponentTemplateResolver;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityDecorationResolver;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.util.text.Strings;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
index 49cbb6e..63ac989 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
@@ -28,8 +28,7 @@ import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
-
-import brooklyn.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 
 public abstract class AbstractTemplateBrooklynLookup<T extends AbstractResource>  extends AbstractBrooklynResourceLookup<T> {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/AssemblyTemplateBrooklynLookup.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/AssemblyTemplateBrooklynLookup.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/AssemblyTemplateBrooklynLookup.java
index 5158550..a41ee05 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/AssemblyTemplateBrooklynLookup.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/AssemblyTemplateBrooklynLookup.java
@@ -31,8 +31,7 @@ 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.camp.brooklyn.spi.creation.BrooklynAssemblyTemplateInstantiator;
-
-import brooklyn.catalog.CatalogPredicates;
+import org.apache.brooklyn.core.catalog.CatalogPredicates;
 
 public class AssemblyTemplateBrooklynLookup extends AbstractTemplateBrooklynLookup<AssemblyTemplate> {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/PlatformComponentTemplateBrooklynLookup.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/PlatformComponentTemplateBrooklynLookup.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/PlatformComponentTemplateBrooklynLookup.java
index 02a0cb6..a7089e8 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/PlatformComponentTemplateBrooklynLookup.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/lookup/PlatformComponentTemplateBrooklynLookup.java
@@ -29,8 +29,7 @@ import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
-
-import brooklyn.catalog.CatalogPredicates;
+import org.apache.brooklyn.core.catalog.CatalogPredicates;
 
 public class PlatformComponentTemplateBrooklynLookup extends AbstractTemplateBrooklynLookup<PlatformComponentTemplate> {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 d2ada07..c0b77f4 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
@@ -30,13 +30,13 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.StartableApplication;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 df4b41b..d96b132 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
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 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.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
@@ -37,7 +38,6 @@ import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
 import brooklyn.util.ResourceUtils;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 1bf481d..66fee85 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
@@ -37,10 +37,8 @@ import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 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 brooklyn.catalog.internal.CatalogUtils;
-
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 
 import brooklyn.util.ResourceUtils;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 c4579ff..3aa9b74 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
@@ -32,10 +32,10 @@ import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Entity;
 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 brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.BasicEntity;
 
 import org.apache.brooklyn.test.TestResourceUnavailableException;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
index 70076f9..7d283a9 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
@@ -33,9 +33,9 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationDefinition;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
+import org.apache.brooklyn.core.catalog.CatalogPredicates;
 import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
 
-import brooklyn.catalog.CatalogPredicates;
 import brooklyn.event.basic.BasicConfigKey;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
index 6a44df3..e6f9d3d 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
@@ -25,9 +25,9 @@ import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
+import org.apache.brooklyn.core.catalog.CatalogPredicates;
 import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
 
-import brooklyn.catalog.CatalogPredicates;
 import brooklyn.event.basic.BasicConfigKey;
 
 import org.apache.brooklyn.test.TestResourceUnavailableException;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
index 3081243..e33f739 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
@@ -24,10 +24,10 @@ import static org.testng.Assert.assertFalse;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlRebindTest;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.testng.annotations.Test;
 
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.BasicEntity;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.StartableApplication;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlVersioningTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlVersioningTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlVersioningTest.java
index a250c37..b98cb94 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlVersioningTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlVersioningTest.java
@@ -30,9 +30,9 @@ import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
+import org.apache.brooklyn.core.catalog.CatalogPredicates;
+import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 
-import brooklyn.catalog.CatalogPredicates;
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
 import brooklyn.entity.basic.BasicApplication;
 import brooklyn.entity.basic.BasicEntity;
 import brooklyn.entity.basic.ConfigKeys;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 09cba24..8d3ed07 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
@@ -66,9 +66,6 @@ import org.apache.brooklyn.cli.CloudExplorer.ComputeListImagesCommand;
 import org.apache.brooklyn.cli.CloudExplorer.ComputeListInstancesCommand;
 import org.apache.brooklyn.cli.CloudExplorer.ComputeTerminateInstancesCommand;
 import org.apache.brooklyn.cli.ItemLister.ListAllCommand;
-
-import brooklyn.catalog.internal.CatalogInitialization;
-
 import org.apache.brooklyn.cli.CloudExplorer.BlobstoreGetBlobCommand;
 import org.apache.brooklyn.cli.CloudExplorer.BlobstoreListContainerCommand;
 import org.apache.brooklyn.cli.CloudExplorer.BlobstoreListContainersCommand;
@@ -79,6 +76,7 @@ import org.apache.brooklyn.cli.CloudExplorer.ComputeListImagesCommand;
 import org.apache.brooklyn.cli.CloudExplorer.ComputeListInstancesCommand;
 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 brooklyn.entity.basic.AbstractApplication;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynLauncher.java b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynLauncher.java
index 8267ca3..504363a 100644
--- a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynLauncher.java
+++ b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynLauncher.java
@@ -49,6 +49,7 @@ import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
 import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynAssemblyTemplateInstantiator;
+import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
 import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl;
 import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
@@ -64,7 +65,6 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
-import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.BrooklynServerPaths;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 0e56483..49596d6 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
@@ -36,9 +36,9 @@ 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.test.entity.LocalManagementContextForTests;
 
-import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.entity.rebind.persister.PersistMode;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.os.Os;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java
index b0796db..02521e5 100644
--- a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java
@@ -22,6 +22,7 @@ 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.ManagementContext;
+import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.launcher.BrooklynLauncher;
@@ -49,7 +50,6 @@ import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.Test;
 
-import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.basic.ApplicationBuilder;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 8a54607..98d5648 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
@@ -46,7 +46,6 @@ import org.codehaus.jackson.node.ObjectNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.AbstractGroup;
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.Lifecycle;
@@ -60,6 +59,7 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.management.entitlement.EntitlementPredicates;
 import org.apache.brooklyn.core.management.entitlement.Entitlements;
 import org.apache.brooklyn.core.management.entitlement.Entitlements.EntityAndItem;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 e841b27..80dc2a3 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
@@ -44,15 +44,13 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.catalog.CatalogPredicates;
+import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
+import org.apache.brooklyn.core.catalog.internal.CatalogDto;
+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 brooklyn.catalog.CatalogPredicates;
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
-import brooklyn.catalog.internal.CatalogDto;
-import brooklyn.catalog.internal.CatalogItemComparator;
-import brooklyn.catalog.internal.CatalogUtils;
-
 import org.apache.brooklyn.rest.api.CatalogApi;
 import org.apache.brooklyn.rest.domain.ApiError;
 import org.apache.brooklyn.rest.domain.CatalogEntitySummary;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/LocationResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/LocationResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/LocationResource.java
index 83fa84d..1dc0bf2 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/LocationResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/LocationResource.java
@@ -32,9 +32,7 @@ import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationDefinition;
-
-import brooklyn.catalog.internal.CatalogUtils;
-
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.location.basic.LocationConfigKeys;
 import org.apache.brooklyn.rest.api.LocationApi;
 import org.apache.brooklyn.rest.domain.LocationSpec;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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 bfe82ba..d09d05b 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
@@ -50,10 +50,10 @@ import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 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 brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.Enrichers;
 import brooklyn.entity.basic.AbstractEntity;



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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/LocalEntityManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/LocalEntityManager.java b/core/src/main/java/brooklyn/management/internal/LocalEntityManager.java
deleted file mode 100644
index a54c1b1..0000000
--- a/core/src/main/java/brooklyn/management/internal/LocalEntityManager.java
+++ /dev/null
@@ -1,818 +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.management.internal;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import groovy.util.ObservableList;
-
-import java.lang.reflect.Proxy;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import javax.annotation.Nullable;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.Group;
-import org.apache.brooklyn.api.entity.proxying.EntitySpec;
-import org.apache.brooklyn.api.entity.proxying.EntityTypeRegistry;
-import org.apache.brooklyn.api.management.AccessController;
-import org.apache.brooklyn.api.management.Task;
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.BrooklynLogging;
-import brooklyn.entity.basic.AbstractEntity;
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.entity.basic.EntityPredicates;
-import brooklyn.entity.proxying.BasicEntityTypeRegistry;
-import brooklyn.entity.proxying.EntityProxy;
-import brooklyn.entity.proxying.EntityProxyImpl;
-import brooklyn.entity.proxying.InternalEntityFactory;
-import brooklyn.entity.proxying.InternalPolicyFactory;
-import brooklyn.entity.trait.Startable;
-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;
-
-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.Predicates;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-public class LocalEntityManager implements EntityManagerInternal {
-
-    private static final Logger log = LoggerFactory.getLogger(LocalEntityManager.class);
-
-    private final LocalManagementContext managementContext;
-    private final BasicEntityTypeRegistry entityTypeRegistry;
-    private final InternalEntityFactory entityFactory;
-    private final InternalPolicyFactory policyFactory;
-    
-    /** Entities that have been created, but have not yet begun to be managed */
-    protected final Map<String,Entity> preRegisteredEntitiesById = Collections.synchronizedMap(new WeakHashMap<String, Entity>());
-
-    /** Entities that are in the process of being managed, but where management is not yet complete */
-    protected final Map<String,Entity> preManagedEntitiesById = Collections.synchronizedMap(new WeakHashMap<String, Entity>());
-    
-    /** Proxies of the managed entities */
-    protected final ConcurrentMap<String,Entity> entityProxiesById = Maps.newConcurrentMap();
-    
-    /** Real managed entities */
-    protected final Map<String,Entity> entitiesById = Maps.newLinkedHashMap();
-    
-    /** Management mode for each entity */
-    protected final Map<String,ManagementTransitionMode> entityModesById = Collections.synchronizedMap(Maps.<String,ManagementTransitionMode>newLinkedHashMap());
-
-    /** Proxies of the managed entities */
-    protected final ObservableList entities = new ObservableList();
-    
-    /** Proxies of the managed entities that are applications */
-    protected final Set<Application> applications = Sets.newConcurrentHashSet();
-
-    private final BrooklynStorage storage;
-    private final Map<String,String> entityTypes;
-    private final Set<String> applicationIds;
-
-    public LocalEntityManager(LocalManagementContext managementContext) {
-        this.managementContext = checkNotNull(managementContext, "managementContext");
-        this.storage = managementContext.getStorage();
-        this.entityTypeRegistry = new BasicEntityTypeRegistry();
-        this.policyFactory = new InternalPolicyFactory(managementContext);
-        this.entityFactory = new InternalEntityFactory(managementContext, entityTypeRegistry, policyFactory);
-        
-        entityTypes = storage.getMap("entities");
-        applicationIds = SetFromLiveMap.create(storage.<String,Boolean>getMap("applications"));
-    }
-
-    public InternalEntityFactory getEntityFactory() {
-        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
-        return entityFactory;
-    }
-
-    public InternalPolicyFactory getPolicyFactory() {
-        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
-        return policyFactory;
-    }
-
-    @Override
-    public EntityTypeRegistry getEntityTypeRegistry() {
-        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
-        return entityTypeRegistry;
-    }
-    
-    @SuppressWarnings("unchecked")
-    @Override
-    public <T extends Entity> T createEntity(EntitySpec<T> spec) {
-        try {
-            T entity = entityFactory.createEntity(spec);
-            Entity proxy = ((AbstractEntity)entity).getProxy();
-            return (T) checkNotNull(proxy, "proxy for entity %s, spec %s", entity, spec);
-        } catch (Throwable e) {
-            log.warn("Failed to create entity using spec "+spec+" (rethrowing)", e);
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    @Override
-    public <T extends Entity> T createEntity(Map<?,?> config, Class<T> type) {
-        return createEntity(EntitySpec.create(config, type));
-    }
-
-    @Override
-    public <T extends Policy> T createPolicy(PolicySpec<T> spec) {
-        try {
-            return policyFactory.createPolicy(spec);
-        } catch (Throwable e) {
-            log.warn("Failed to create policy using spec "+spec+" (rethrowing)", e);
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    @Override
-    public <T extends Enricher> T createEnricher(EnricherSpec<T> spec) {
-        try {
-            return policyFactory.createEnricher(spec);
-        } catch (Throwable e) {
-            log.warn("Failed to create enricher using spec "+spec+" (rethrowing)", e);
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    @Override
-    public Collection<Entity> getEntities() {
-        return ImmutableList.copyOf(entityProxiesById.values());
-    }
-    
-    @Override
-    public Collection<String> getEntityIds() {
-        return ImmutableList.copyOf(entityProxiesById.keySet());
-    }
-    
-    @Override
-    public Collection<Entity> getEntitiesInApplication(Application application) {
-        Predicate<Entity> predicate = EntityPredicates.applicationIdEqualTo(application.getId());
-        return ImmutableList.copyOf(Iterables.filter(entityProxiesById.values(), predicate));
-    }
-
-    @Override
-    public Collection<Entity> findEntities(Predicate<? super Entity> filter) {
-        return ImmutableList.copyOf(Iterables.filter(entityProxiesById.values(), filter));
-    }
-    
-    @Override
-    public Collection<Entity> findEntitiesInApplication(Application application, Predicate<? super Entity> filter) {
-        Predicate<Entity> predicate = Predicates.and(EntityPredicates.applicationIdEqualTo(application.getId()), filter);
-        return ImmutableList.copyOf(Iterables.filter(entityProxiesById.values(), predicate));
-    }
-
-    @Override
-    public Iterable<Entity> getAllEntitiesInApplication(Application application) {
-        Predicate<Entity> predicate = EntityPredicates.applicationIdEqualTo(application.getId());
-        Iterable<Entity> allentities = Iterables.concat(preRegisteredEntitiesById.values(), preManagedEntitiesById.values(), entityProxiesById.values());
-        Iterable<Entity> result = Iterables.filter(allentities, predicate);
-        return ImmutableSet.copyOf(Iterables.transform(result, new Function<Entity, Entity>() {
-            @Override public Entity apply(Entity input) {
-                return Entities.proxy(input);
-            }}));
-    }
-
-    @Override
-    public Entity getEntity(String id) {
-        return entityProxiesById.get(id);
-    }
-    
-    Collection<Application> getApplications() {
-        return ImmutableList.copyOf(applications);
-    }
-    
-    @Override
-    public boolean isManaged(Entity e) {
-        return (isRunning() && getEntity(e.getId()) != null);
-    }
-    
-    boolean isPreRegistered(Entity e) {
-        return preRegisteredEntitiesById.containsKey(e.getId());
-    }
-    
-    void prePreManage(Entity entity) {
-        if (isPreRegistered(entity)) {
-            log.warn(""+this+" redundant call to pre-pre-manage entity "+entity+"; skipping", 
-                    new Exception("source of duplicate pre-pre-manage of "+entity));
-            return;
-        }
-        preRegisteredEntitiesById.put(entity.getId(), entity);
-    }
-    
-    @Override
-    public ManagementTransitionMode getLastManagementTransitionMode(String itemId) {
-        return entityModesById.get(itemId);
-    }
-    
-    @Override
-    public void setManagementTransitionMode(Entity item, ManagementTransitionMode mode) {
-        entityModesById.put(item.getId(), mode);
-    }
-    
-    // TODO synchronization issues here. We guard with isManaged(), but if another thread executing 
-    // concurrently then the managed'ness could be set after our check but before we do 
-    // onManagementStarting etc. However, we can't just synchronize because we're calling alien code 
-    // (the user might override entity.onManagementStarting etc).
-    // 
-    // TODO We need to do some check about isPreManaged - i.e. is there another thread (or is this a
-    // re-entrant call) where the entity is not yet full managed (i.e. isManaged==false) but we're in
-    // the middle of managing it.
-    // 
-    // TODO Also see LocalLocationManager.manage(Entity), if fixing things here
-    @Override
-    public void manage(Entity e) {
-        if (isManaged(e)) {
-            log.warn(""+this+" redundant call to start management of entity (and descendants of) "+e+"; skipping", 
-                    new Exception("source of duplicate management of "+e));
-            return;
-        }
-        manageRecursive(e, ManagementTransitionMode.guessing(BrooklynObjectManagementMode.NONEXISTENT, BrooklynObjectManagementMode.MANAGED_PRIMARY));
-    }
-
-    @Override
-    public void manageRebindedRoot(Entity item) {
-        ManagementTransitionMode mode = getLastManagementTransitionMode(item.getId());
-        Preconditions.checkNotNull(mode, "Mode not set for rebinding %s", item);
-        manageRecursive(item, mode);
-    }
-    
-    protected void checkManagementAllowed(Entity item) {
-        AccessController.Response access = managementContext.getAccessController().canManageEntity(item);
-        if (!access.isAllowed()) {
-            throw new IllegalStateException("Access controller forbids management of "+item+": "+access.getMsg());
-        }
-    }
-    
-    /* TODO we sloppily use "recursive" to ensure ordering of parent-first in many places
-     * (which may not be necessary but seems like a good idea),
-     * and also to collect many entities when doing a big rebind,
-     * ensuring all have #manageNonRecursive called before calling #onManagementStarted.
-     * 
-     * it would be better to have a manageAll(Map<Entity,ManagementTransitionMode> items)
-     * method which did that in two phases, allowing us to selectively rebind, 
-     * esp when we come to want supporting different modes and different brooklyn nodes.
-     * 
-     * the impl of manageAll could sort them with parents before children,
-     * (and manageRecursive could simply populate a map and delegate to manageAll).
-     * 
-     * manageRebindRoot would then go, and the (few) callers would construct the map.
-     * 
-     * similarly we might want an unmanageAll(), 
-     * although possibly all unmanagement should be recursive, if we assume an entity's ancestors are always at least proxied
-     * (and the non-recursive RO path here could maybe be dropped)
-     */
-    
-    /** Applies management lifecycle callbacks (onManagementStarting, for all beforehand, then onManagementStopped, for all after) */
-    protected void manageRecursive(Entity e, final ManagementTransitionMode initialMode) {
-        checkManagementAllowed(e);
-
-        final List<EntityInternal> allEntities =  Lists.newArrayList();
-        Predicate<EntityInternal> manageEntity = new Predicate<EntityInternal>() { public boolean apply(EntityInternal it) {
-            ManagementTransitionMode mode = getLastManagementTransitionMode(it.getId());
-            if (mode==null) {
-                setManagementTransitionMode(it, mode = initialMode);
-            }
-            
-            Boolean isReadOnlyFromEntity = it.getManagementSupport().isReadOnlyRaw();
-            if (isReadOnlyFromEntity==null) {
-                if (mode.isReadOnly()) {
-                    // should have been marked by rebinder
-                    log.warn("Read-only entity "+it+" not marked as such on call to manage; marking and continuing");
-                }
-                it.getManagementSupport().setReadOnly(mode.isReadOnly());
-            } else {
-                if (!isReadOnlyFromEntity.equals(mode.isReadOnly())) {
-                    log.warn("Read-only status at entity "+it+" ("+isReadOnlyFromEntity+") not consistent with management mode "+mode);
-                }
-            }
-            
-            if (it.getManagementSupport().isDeployed()) {
-                if (mode.wasNotLoaded()) {
-                    // silently bail out
-                    return false;
-                } else {
-                    if (mode.wasPrimary() && mode.isPrimary()) {
-                        // active partial rebind; continue
-                    } else if (mode.wasReadOnly() && mode.isReadOnly()) {
-                        // reload in RO mode
-                    } else {
-                        // on initial non-RO rebind, should not have any deployed instances
-                        log.warn("Already deployed "+it+" when managing "+mode+"/"+initialMode+"; ignoring this and all descendants");
-                        return false;
-                    }
-                }
-            }
-            
-            // check RO status is consistent
-            boolean isNowReadOnly = Boolean.TRUE.equals( ((EntityInternal)it).getManagementSupport().isReadOnly() );
-            if (mode.isReadOnly()!=isNowReadOnly) {
-                throw new IllegalStateException("Read-only status mismatch for "+it+": "+mode+" / RO="+isNowReadOnly);
-            }
-
-            allEntities.add(it);
-            preManageNonRecursive(it, mode);
-            it.getManagementSupport().onManagementStarting( new ManagementTransitionInfo(managementContext, mode) ); 
-            return manageNonRecursive(it, mode);
-        } };
-        boolean isRecursive = true;
-        if (initialMode.wasPrimary() && initialMode.isPrimary()) {
-            // already managed, so this shouldn't be recursive 
-            // (in ActivePartialRebind we cheat, calling in to this method then skipping recursion).
-            // it also falls through to here when doing a redundant promotion,
-            // in that case we *should* be recursive; determine by checking whether a child exists and is preregistered.
-            // the TODO above removing manageRebindRoot in favour of explicit mgmt list would clean this up a lot!
-            Entity aChild = Iterables.getFirst(e.getChildren(), null);
-            if (aChild!=null && isPreRegistered(aChild)) {
-                log.debug("Managing "+e+" in mode "+initialMode+", doing this recursively because a child is preregistered");
-            } else {
-                log.debug("Managing "+e+" but skipping recursion, as mode is "+initialMode);
-                isRecursive = false;
-            }
-        }
-        if (!isRecursive) {
-            manageEntity.apply( (EntityInternal)e );
-        } else {
-            recursively(e, manageEntity);
-        }
-        
-        for (EntityInternal it : allEntities) {
-            if (!it.getManagementSupport().isFullyManaged()) {
-                ManagementTransitionMode mode = getLastManagementTransitionMode(it.getId());
-                ManagementTransitionInfo info = new ManagementTransitionInfo(managementContext, mode);
-                
-                it.getManagementSupport().onManagementStarted(info);
-                managementContext.getRebindManager().getChangeListener().onManaged(it);
-            }
-        }
-    }
-    
-    @Override
-    public void unmanage(final Entity e) {
-        unmanage(e, ManagementTransitionMode.guessing(BrooklynObjectManagementMode.MANAGED_PRIMARY, BrooklynObjectManagementMode.NONEXISTENT));
-    }
-    
-    public void unmanage(final Entity e, final ManagementTransitionMode mode) {
-        unmanage(e, mode, false);
-    }
-    
-    private void unmanage(final Entity e, ManagementTransitionMode mode, boolean hasBeenReplaced) {
-        if (shouldSkipUnmanagement(e)) return;
-        final ManagementTransitionInfo info = new ManagementTransitionInfo(managementContext, mode);
-        
-        if (hasBeenReplaced) {
-            // we are unmanaging an old instance after having replaced it
-            // don't unmanage or even clear its fields, because there might be references to it
-            
-            if (mode.wasReadOnly()) {
-                // if coming *from* read only; nothing needed
-            } else {
-                if (!mode.wasPrimary()) {
-                    log.warn("Unexpected mode "+mode+" for unmanage-replace "+e+" (applying anyway)");
-                }
-                // migrating away or in-place active partial rebind:
-                ((EntityInternal)e).getManagementSupport().onManagementStopping(info);
-                stopTasks(e);
-                ((EntityInternal)e).getManagementSupport().onManagementStopped(info);
-            }
-            // do not remove from maps below, bail out now
-            return;
-            
-        } else if (mode.wasReadOnly() && mode.isNoLongerLoaded()) {
-            // we are unmanaging an instance (secondary); either stopping here or primary destroyed elsewhere
-            ((EntityInternal)e).getManagementSupport().onManagementStopping(info);
-            unmanageNonRecursive(e);
-            stopTasks(e);
-            ((EntityInternal)e).getManagementSupport().onManagementStopped(info);
-            managementContext.getRebindManager().getChangeListener().onUnmanaged(e);
-            if (managementContext.getGarbageCollector() != null) managementContext.getGarbageCollector().onUnmanaged(e);
-            
-        } else if (mode.wasPrimary() && mode.isNoLongerLoaded()) {
-            // unmanaging a primary; currently this is done recursively
-            
-            /* TODO tidy up when it is recursive and when it isn't; if something is being unloaded or destroyed,
-             * that probably *is* recursive, but the old mode might be different if in some cases things are read-only.
-             * or maybe nothing needs to be recursive, we just make sure the callers (e.g. HighAvailabilityModeImpl.clearManagedItems)
-             * call in a good order
-             * 
-             * see notes above about recursive/manage/All/unmanageAll
-             */
-            
-            // Need to store all child entities as onManagementStopping removes a child from the parent entity
-            final List<EntityInternal> allEntities =  Lists.newArrayList();        
-            recursively(e, new Predicate<EntityInternal>() { public boolean apply(EntityInternal it) {
-                if (shouldSkipUnmanagement(it)) return false;
-                allEntities.add(it);
-                it.getManagementSupport().onManagementStopping(info);
-                return true;
-            } });
-
-            for (EntityInternal it : allEntities) {
-                if (shouldSkipUnmanagement(it)) continue;
-                unmanageNonRecursive(it);
-                stopTasks(it);
-            }
-            for (EntityInternal it : allEntities) {
-                it.getManagementSupport().onManagementStopped(info);
-                managementContext.getRebindManager().getChangeListener().onUnmanaged(it);
-                if (managementContext.getGarbageCollector() != null) managementContext.getGarbageCollector().onUnmanaged(e);
-            }
-            
-        } else {
-            log.warn("Invalid mode for unmanage: "+mode+" on "+e+" (ignoring)");
-        }
-        
-        preRegisteredEntitiesById.remove(e.getId());
-        preManagedEntitiesById.remove(e.getId());
-        entityProxiesById.remove(e.getId());
-        entitiesById.remove(e.getId());
-        entityModesById.remove(e.getId());
-    }
-    
-    private void stopTasks(Entity entity) {
-        stopTasks(entity, null);
-    }
-    
-    /** stops all tasks (apart from any current one or its descendants) on this entity,
-     * optionally -- if a timeout is given -- waiting for completion and warning on incomplete tasks */
-    @Beta
-    public void stopTasks(Entity entity, @Nullable Duration timeout) {
-        CountdownTimer timeleft = timeout==null ? null : timeout.countdownTimer();
-        // try forcibly interrupting tasks on managed entities
-        Collection<Exception> exceptions = MutableSet.of();
-        try {
-            Set<Task<?>> tasksCancelled = MutableSet.of();
-            for (Task<?> t: managementContext.getExecutionContext(entity).getTasks()) {
-                if (entity.equals(BrooklynTaskTags.getContextEntity(Tasks.current())) && hasTaskAsAncestor(t, Tasks.current())) {
-                    // don't cancel if we are running inside a task on the target entity and
-                    // the task being considered is one we have submitted -- e.g. on "stop" don't cancel ourselves!
-                    // but if our current task is from another entity we probably do want to cancel them (we are probably invoking unmanage)
-                    continue;
-                }
-                
-                if (!t.isDone()) {
-                    try {
-                        log.debug("Cancelling "+t+" on "+entity);
-                        tasksCancelled.add(t);
-                        t.cancel(true);
-                    } catch (Exception e) {
-                        Exceptions.propagateIfFatal(e);
-                        log.debug("Error cancelling "+t+" on "+entity+" (will warn when all tasks are cancelled): "+e, e);
-                        exceptions.add(e);
-                    }
-                }
-            }
-            
-            if (timeleft!=null) {
-                Set<Task<?>> tasksIncomplete = MutableSet.of();
-                // go through all tasks, not just cancelled ones, in case there are previously cancelled ones which are not complete
-                for (Task<?> t: managementContext.getExecutionContext(entity).getTasks()) {
-                    if (hasTaskAsAncestor(t, Tasks.current()))
-                        continue;
-                    if (!Tasks.blockUntilInternalTasksEnded(t, timeleft.getDurationRemaining())) {
-                        tasksIncomplete.add(t);
-                    }
-                }
-                if (!tasksIncomplete.isEmpty()) {
-                    log.warn("Incomplete tasks when stopping "+entity+": "+tasksIncomplete);
-                }
-                if (log.isTraceEnabled())
-                    log.trace("Cancelled "+tasksCancelled+" tasks for "+entity+", with "+
-                            timeleft.getDurationRemaining()+" remaining (of "+timeout+"): "+tasksCancelled);
-            } else {
-                if (log.isTraceEnabled())
-                    log.trace("Cancelled "+tasksCancelled+" tasks for "+entity+": "+tasksCancelled);
-            }
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            log.warn("Error inspecting tasks to cancel on unmanagement: "+e, e);
-        }
-        if (!exceptions.isEmpty())
-            log.warn("Error when cancelling tasks for "+entity+" on unmanagement: "+Exceptions.create(exceptions));
-    }
-
-    private boolean hasTaskAsAncestor(Task<?> t, Task<?> potentialAncestor) {
-        if (t==null || potentialAncestor==null) return false;
-        if (t.equals(potentialAncestor)) return true;
-        return hasTaskAsAncestor(t.getSubmittedByTask(), potentialAncestor);
-    }
-
-    /**
-     * activates management when effector invoked, warning unless context is acceptable
-     * (currently only acceptable context is "start")
-     */
-    void manageIfNecessary(Entity entity, Object context) {
-        if (!isRunning()) {
-            return; // TODO Still a race for terminate being called, and then isManaged below returning false
-        } else if (((EntityInternal)entity).getManagementSupport().wasDeployed()) {
-            return;
-        } else if (isManaged(entity)) {
-            return;
-        } else if (isPreManaged(entity)) {
-            return;
-        } else if (Boolean.TRUE.equals(((EntityInternal)entity).getManagementSupport().isReadOnly())) {
-            return;
-        } else {
-            Entity rootUnmanaged = entity;
-            while (true) {
-                Entity candidateUnmanagedParent = rootUnmanaged.getParent();
-                if (candidateUnmanagedParent == null || isManaged(candidateUnmanagedParent) || isPreManaged(candidateUnmanagedParent))
-                    break;
-                rootUnmanaged = candidateUnmanagedParent;
-            }
-            if (context == Startable.START.getName())
-                log.info("Activating local management for {} on start", rootUnmanaged);
-            else
-                log.warn("Activating local management for {} due to effector invocation on {}: {}", new Object[]{rootUnmanaged, entity, context});
-            manage(rootUnmanaged);
-        }
-    }
-
-    private void recursively(Entity e, Predicate<EntityInternal> action) {
-        Entity otherPreregistered = preRegisteredEntitiesById.get(e.getId());
-        if (otherPreregistered!=null) {
-            // if something has been pre-registered, prefer it
-            // (e.g. if we recursing through children, we might have a proxy from previous iteration;
-            // the most recent will have been pre-registered)
-            e = otherPreregistered;
-        }
-            
-        boolean success = action.apply( (EntityInternal)e );
-        if (!success) {
-            return; // Don't manage children if action false/unnecessary for parent
-        }
-        for (Entity child : e.getChildren()) {
-            recursively(child, action);
-        }
-    }
-
-    /**
-     * Whether the entity is in the process of being managed.
-     */
-    private synchronized boolean isPreManaged(Entity e) {
-        return preManagedEntitiesById.containsKey(e.getId());
-    }
-
-    /**
-     * Should ensure that the entity is now known about, but should not be accessible from other entities yet.
-     * 
-     * Records that the given entity is about to be managed (used for answering {@link isPreManaged(Entity)}.
-     * Note that refs to the given entity are stored in a a weak hashmap so if the subsequent management
-     * attempt fails then this reference to the entity will eventually be discarded (if no-one else holds 
-     * a reference).
-     */
-    private synchronized boolean preManageNonRecursive(Entity e, ManagementTransitionMode mode) {
-        Entity realE = toRealEntity(e);
-        
-        Object old = preManagedEntitiesById.put(e.getId(), realE);
-        preRegisteredEntitiesById.remove(e.getId());
-        
-        if (old!=null && mode.wasNotLoaded()) {
-            if (old.equals(e)) {
-                log.warn("{} redundant call to pre-start management of entity {}, mode {}; ignoring", new Object[] { this, e, mode });
-            } else {
-                throw new IllegalStateException("call to pre-manage entity "+e+" ("+mode+") but different entity "+old+" already known under that id at "+this);
-            }
-            return false;
-        } else {
-            if (log.isTraceEnabled()) log.trace("{} pre-start management of entity {}, mode {}", 
-                new Object[] { this, e, mode });
-            return true;
-        }
-    }
-
-    /**
-     * Should ensure that the entity is now managed somewhere, and known about in all the lists.
-     * Returns true if the entity has now become managed; false if it was already managed (anything else throws exception)
-     * @param isOrWasReadOnly 
-     */
-    private synchronized boolean manageNonRecursive(Entity e, ManagementTransitionMode mode) {
-        Entity old = entitiesById.get(e.getId());
-        
-        if (old!=null && mode.wasNotLoaded()) {
-            if (old.equals(e)) {
-                log.warn("{} redundant call to start management of entity {}; ignoring", this, e);
-            } else {
-                throw new IllegalStateException("call to manage entity "+e+" ("+mode+") but different entity "+old+" already known under that id at "+this);
-            }
-            return false;
-        }
-        
-        BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(e), 
-            "{} starting management of entity {}", this, e);
-        Entity realE = toRealEntity(e);
-        
-        Entity oldProxy = entityProxiesById.get(e.getId());
-        Entity proxyE;
-        if (oldProxy!=null) {
-            if (mode.wasNotLoaded()) {
-                throw new IllegalStateException("call to manage entity "+e+" from unloaded state ("+mode+") but already had proxy "+oldProxy+" already known under that id at "+this);
-            }
-            // make the old proxy point at this new delegate
-            // (some other tricks done in the call below)
-            ((EntityProxyImpl)(Proxy.getInvocationHandler(oldProxy))).resetDelegate(oldProxy, oldProxy, realE);
-            proxyE = oldProxy;
-        } else {
-            proxyE = toProxyEntityIfAvailable(e);
-        }
-        entityProxiesById.put(e.getId(), proxyE);
-        entityTypes.put(e.getId(), realE.getClass().getName());
-        entitiesById.put(e.getId(), realE);
-
-        preManagedEntitiesById.remove(e.getId());
-        if ((e instanceof Application) && (e.getParent()==null)) {
-            applications.add((Application)proxyE);
-            applicationIds.add(e.getId());
-        }
-        if (!entities.contains(proxyE)) 
-            entities.add(proxyE);
-        
-        if (old!=null && old!=e) {
-            // passing the transition info will ensure the right shutdown steps invoked for old instance
-            unmanage(old, mode, true);
-        }
-        
-        return true;
-    }
-
-    /**
-     * Should ensure that the entity is no longer managed anywhere, remove from all lists.
-     * Returns true if the entity has been removed from management; if it was not previously managed (anything else throws exception) 
-     */
-    private boolean unmanageNonRecursive(Entity e) {
-        /*
-         * When method is synchronized, hit deadlock: 
-         * 1. thread called unmanage() on a member of a group, so we got the lock and called group.removeMember;
-         *    this ties to synchronize on AbstractGroupImpl.members 
-         * 2. another thread was doing AbstractGroupImpl.addMember, which is synchronized on AbstractGroupImpl.members;
-         *    it tries to call Entities.manage(child) which calls LocalEntityManager.getEntity(), which is
-         *    synchronized on this.
-         * 
-         * We MUST NOT call alien code from within the management framework while holding locks. 
-         * The AbstractGroup.removeMember is effectively alien because a user could override it, and because
-         * it is entity specific.
-         * 
-         * TODO Does getting then removing from groups risk this entity being added to other groups while 
-         * this is happening? Should abstractEntity.onManagementStopped or some such remove the entity
-         * from its groups?
-         */
-        
-        if (!getLastManagementTransitionMode(e.getId()).isReadOnly()) {
-            e.clearParent();
-            Collection<Group> groups = e.getGroups();
-            for (Group group : groups) {
-                if (!Entities.isNoLongerManaged(group)) group.removeMember(e);
-            }
-            if (e instanceof Group) {
-                Collection<Entity> members = ((Group)e).getMembers();
-                for (Entity member : members) {
-                    if (!Entities.isNoLongerManaged(member)) member.removeGroup((Group)e);
-                }
-            }
-        } else {
-            log.debug("No relations being updated on unmanage of read only {}", e);
-        }
-
-        synchronized (this) {
-            Entity proxyE = toProxyEntityIfAvailable(e);
-            if (e instanceof Application) {
-                applications.remove(proxyE);
-                applicationIds.remove(e.getId());
-            }
-
-            entities.remove(proxyE);
-            entityProxiesById.remove(e.getId());
-            entityModesById.remove(e.getId());
-            Object old = entitiesById.remove(e.getId());
-
-            entityTypes.remove(e.getId());
-            if (old==null) {
-                log.warn("{} call to stop management of unknown entity (already unmanaged?) {}; ignoring", this, e);
-                return false;
-            } else if (!old.equals(e)) {
-                // shouldn't happen...
-                log.error("{} call to stop management of entity {} removed different entity {}", new Object[] { this, e, old });
-                return true;
-            } else {
-                if (log.isDebugEnabled()) log.debug("{} stopped management of entity {}", this, e);
-                return true;
-            }
-        }
-    }
-
-    void addEntitySetListener(CollectionChangeListener<Entity> listener) {
-        //must notify listener in a different thread to avoid deadlock (issue #378)
-        AsyncCollectionChangeAdapter<Entity> wrappedListener = new AsyncCollectionChangeAdapter<Entity>(managementContext.getExecutionManager(), listener);
-        entities.addPropertyChangeListener(new GroovyObservablesPropertyChangeToCollectionChangeAdapter(wrappedListener));
-    }
-
-    void removeEntitySetListener(CollectionChangeListener<Entity> listener) {
-        AsyncCollectionChangeAdapter<Entity> wrappedListener = new AsyncCollectionChangeAdapter<Entity>(managementContext.getExecutionManager(), listener);
-        entities.removePropertyChangeListener(new GroovyObservablesPropertyChangeToCollectionChangeAdapter(wrappedListener));
-    }
-    
-    private boolean shouldSkipUnmanagement(Entity e) {
-        if (e==null) {
-            log.warn(""+this+" call to unmanage null entity; skipping",  
-                new IllegalStateException("source of null unmanagement call to "+this));
-            return true;
-        }
-        if (!isManaged(e)) {
-            log.warn("{} call to stop management of unknown entity (already unmanaged?) {}; skipping, and all descendants", this, e);
-            return true;
-        }
-        return false;
-    }
-    
-    private Entity toProxyEntityIfAvailable(Entity e) {
-        checkNotNull(e, "entity");
-        
-        if (e instanceof EntityProxy) {
-            return e;
-        } else if (e instanceof AbstractEntity) {
-            Entity result = ((AbstractEntity)e).getProxy();
-            return (result == null) ? e : result;
-        } else {
-            // If we don't already know about the proxy, then use the real thing; presumably it's 
-            // the legacy way of creating the entity so didn't get a preManage() call
-
-            return e;
-        }
-    }
-    
-    private Entity toRealEntity(Entity e) {
-        checkNotNull(e, "entity");
-        
-        if (e instanceof AbstractEntity) {
-            return e;
-        } else {
-            Entity result = toRealEntityOrNull(e.getId());
-            if (result == null) {
-                throw new IllegalStateException("No concrete entity known for entity "+e+" ("+e.getId()+", "+e.getEntityType().getName()+")");
-            }
-            return result;
-        }
-    }
-
-    public boolean isKnownEntityId(String id) {
-        return entitiesById.containsKey(id) || preManagedEntitiesById.containsKey(id) || preRegisteredEntitiesById.containsKey(id);
-    }
-    
-    private Entity toRealEntityOrNull(String id) {
-        Entity result;
-        // prefer the preRegistered and preManaged entities, during hot proxying, they should be newer
-        result = preRegisteredEntitiesById.get(id);
-        if (result==null)
-            result = preManagedEntitiesById.get(id);
-        if (result==null)
-            entitiesById.get(id);
-        return result;
-    }
-    
-    private boolean isRunning() {
-        return managementContext.isRunning();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/LocalLocationManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/LocalLocationManager.java b/core/src/main/java/brooklyn/management/internal/LocalLocationManager.java
deleted file mode 100644
index 31dc036..0000000
--- a/core/src/main/java/brooklyn/management/internal/LocalLocationManager.java
+++ /dev/null
@@ -1,463 +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.management.internal;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.io.Closeable;
-import java.util.Collection;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.location.LocationSpec;
-import org.apache.brooklyn.api.location.ProvisioningLocation;
-import org.apache.brooklyn.api.management.AccessController;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.BrooklynLogging;
-import brooklyn.config.BrooklynLogging.LoggingLevel;
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.entity.proxying.InternalLocationFactory;
-import brooklyn.internal.storage.BrooklynStorage;
-
-import org.apache.brooklyn.location.basic.AbstractLocation;
-import org.apache.brooklyn.location.basic.LocationInternal;
-
-import brooklyn.management.entitlement.Entitlements;
-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;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-
-public class LocalLocationManager implements LocationManagerInternal {
-
-    @Beta /* expect to remove when API returns LocationSpec or similar */
-    public static final ConfigKey<Boolean> CREATE_UNMANAGED = ConfigKeys.newBooleanConfigKey("brooklyn.internal.location.createUnmanaged",
-        "If set on a location or spec, causes the manager to create it in an unmanaged state (for peeking)", false);
-    
-    private static final Logger log = LoggerFactory.getLogger(LocalLocationManager.class);
-
-    private final LocalManagementContext managementContext;
-    private final InternalLocationFactory locationFactory;
-    
-    protected final Map<String,Location> locationsById = Maps.newLinkedHashMap();
-    private final Map<String, Location> preRegisteredLocationsById = Maps.newLinkedHashMap();
-
-    /** Management mode for each location */
-    protected final Map<String,ManagementTransitionMode> locationModesById = Maps.newLinkedHashMap();
-
-    private final BrooklynStorage storage;
-    private Map<String, String> locationTypes;
-
-    private static AtomicLong LOCATION_CNT = new AtomicLong(0);
-    
-    public LocalLocationManager(LocalManagementContext managementContext) {
-        this.managementContext = checkNotNull(managementContext, "managementContext");
-        this.locationFactory = new InternalLocationFactory(managementContext);
-        
-        this.storage = managementContext.getStorage();
-        locationTypes = storage.getMap("locations");
-    }
-
-    public InternalLocationFactory getLocationFactory() {
-        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
-        return locationFactory;
-        
-    }
-
-    @Override
-    public <T extends Location> T createLocation(LocationSpec<T> spec) {
-        try {
-            boolean createUnmanaged = ConfigBag.coerceFirstNonNullKeyValue(CREATE_UNMANAGED, 
-                spec.getConfig().get(CREATE_UNMANAGED), spec.getFlags().get(CREATE_UNMANAGED.getName()));
-            if (createUnmanaged) {
-                spec.removeConfig(CREATE_UNMANAGED);
-            }
-
-            T loc = locationFactory.createLocation(spec);
-            if (!createUnmanaged) {
-                manage(loc);
-            } else {
-                // remove references
-                Location parent = loc.getParent();
-                if (parent!=null) {
-                    ((AbstractLocation)parent).removeChild(loc);
-                }
-                preRegisteredLocationsById.remove(loc.getId());
-            }
-            
-            return loc;
-        } catch (Throwable e) {
-            log.warn("Failed to create location using spec "+spec+" (rethrowing)", e);
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    @Override
-    public <T extends Location> T createLocation(Map<?,?> config, Class<T> type) {
-        return createLocation(LocationSpec.create(config, type));
-    }
-
-    @Override
-    public synchronized Collection<Location> getLocations() {
-        return ImmutableList.copyOf(locationsById.values());
-    }
-    
-    @Override
-    public Collection<String> getLocationIds() {
-        return ImmutableList.copyOf(locationsById.keySet());
-    }
-
-    @Override
-    public synchronized Location getLocation(String id) {
-        return locationsById.get(id);
-    }
-    
-    public synchronized Location getLocationEvenIfPreManaged(String id) {
-        Location result = locationsById.get(id);
-        if (result == null) {
-            result = preRegisteredLocationsById.get(id);
-        }
-        return result;
-    }
-    
-    @Override
-    public boolean isManaged(Location loc) {
-        return (isRunning() && loc != null && getLocation(loc.getId()) != null);
-    }
-    
-    synchronized boolean isPreRegistered(Location loc) {
-        return preRegisteredLocationsById.containsKey(loc.getId());
-    }
-    
-    public boolean isKnownLocationId(String id) {
-        return preRegisteredLocationsById.containsKey(id) || locationsById.containsKey(id);
-    }
-    
-    synchronized void prePreManage(Location loc) {
-        if (isPreRegistered(loc)) {
-            log.warn(""+this+" redundant call to pre-pre-manage location "+loc+"; skipping", 
-                    new Exception("source of duplicate pre-pre-manage of "+loc));
-            return;
-        }
-        preRegisteredLocationsById.put(loc.getId(), loc);
-    }
-    
-    @Override
-    public ManagementTransitionMode getLastManagementTransitionMode(String itemId) {
-        return locationModesById.get(itemId);
-    }
-    
-    @Override
-    public void setManagementTransitionMode(Location item, ManagementTransitionMode mode) {
-        locationModesById.put(item.getId(), mode);
-    }
-
-    // TODO synchronization issues here: see comment in LocalEntityManager.manage(Entity)
-    /** management on creation */
-    @Override
-    public Location manage(Location loc) {
-        if (isManaged(loc)) {
-            // TODO put log.warn back in if/when manage(Location) becomes private; or could even have assert.
-            // Can be stricter about contract.
-            return loc;
-        }
-        
-        Location parent = loc.getParent();
-        if (parent != null && !managementContext.getLocationManager().isManaged(parent)) {
-            log.warn("Parent location "+parent+" of "+loc+" is not managed; attempting to manage it (in future this may be disallowed)");
-            return manage(parent);
-        } else {
-            return manageRecursive(loc, ManagementTransitionMode.guessing(BrooklynObjectManagementMode.NONEXISTENT, BrooklynObjectManagementMode.MANAGED_PRIMARY));
-        }
-    }
-    
-    @Override
-    public void manageRebindedRoot(Location item) {
-        ManagementTransitionMode mode = getLastManagementTransitionMode(item.getId());
-        Preconditions.checkNotNull(mode, "Mode not set for rebinding %s", item);
-        manageRecursive(item, mode);
-    }
-
-    protected void checkManagementAllowed(Location item) {
-        AccessController.Response access = managementContext.getAccessController().canManageLocation(item);
-        if (!access.isAllowed()) {
-            throw new IllegalStateException("Access controller forbids management of "+item+": "+access.getMsg());
-        }        
-    }
-
-    protected Location manageRecursive(Location loc, final ManagementTransitionMode initialMode) {
-        // TODO see comments in LocalEntityManager about recursive management / manageRebindRoot v manageAll
-        
-        AccessController.Response access = managementContext.getAccessController().canManageLocation(loc);
-        if (!access.isAllowed()) {
-            throw new IllegalStateException("Access controller forbids management of "+loc+": "+access.getMsg());
-        }
-
-        long count = LOCATION_CNT.incrementAndGet();
-        if (log.isDebugEnabled()) {
-            String msg = "Managing location " + loc + " ("+initialMode+"), from " + Tasks.current()+" / "+Entitlements.getEntitlementContext();
-            LoggingLevel level = (!initialMode.wasNotLoaded() || initialMode.isReadOnly() ? LoggingLevel.TRACE : LoggingLevel.DEBUG);
-            if (count % 100 == 0) {
-                // include trace periodically in case we get leaks or too much location management
-                BrooklynLogging.log(log, level,
-                    msg, new Exception("Informational stack trace of call to manage location "+loc+" ("+count+" calls; "+getLocations().size()+" currently managed)"));
-            } else {
-                BrooklynLogging.log(log, level, msg);
-            }
-        }
-
-        recursively(loc, new Predicate<AbstractLocation>() { public boolean apply(AbstractLocation it) {
-            ManagementTransitionMode mode = getLastManagementTransitionMode(it.getId());
-            if (mode==null) {
-                setManagementTransitionMode(it, mode = initialMode);
-            }
-            
-            if (it.isManaged()) {
-                if (mode.wasNotLoaded()) {
-                    // silently bail out
-                    return false;
-                } else {
-                    // on rebind, we just replace, fall through to below
-                }
-            }
-            
-            boolean result = manageNonRecursive(it, mode);
-            if (result) {
-                it.setManagementContext(managementContext);
-                if (mode.isPrimary()) {
-                    it.onManagementStarted();
-                    if (mode.isCreating()) {
-                        // Never record event on rebind; this isn't the location (e.g. the VM) being "created"
-                        // so don't tell listeners that.
-                        // TODO The location-event history should be persisted; currently it is lost on
-                        // rebind, unless there is a listener that is persisting the state externally itself.
-                        recordLocationEvent(it, Lifecycle.CREATED);
-                    }
-                }
-                managementContext.getRebindManager().getChangeListener().onManaged(it);
-            }
-            return result;
-        } });
-        return loc;
-    }
-    
-    @Override
-    public void unmanage(final Location loc) {
-        unmanage(loc, ManagementTransitionMode.guessing(BrooklynObjectManagementMode.MANAGED_PRIMARY, BrooklynObjectManagementMode.NONEXISTENT));
-    }
-    
-    public void unmanage(final Location loc, final ManagementTransitionMode mode) {
-        unmanage(loc, mode, false);
-    }
-    
-    private void unmanage(final Location loc, final ManagementTransitionMode mode, boolean hasBeenReplaced) {
-        if (shouldSkipUnmanagement(loc)) return;
-
-        if (hasBeenReplaced) {
-            // we are unmanaging an old instance after having replaced it; 
-            // don't unmanage or even clear its fields, because there might be references to it
-            
-            if (mode.wasReadOnly()) {
-                // if coming *from* read only; nothing needed
-            } else {
-                if (!mode.wasPrimary()) {
-                    log.warn("Unexpected mode "+mode+" for unmanage-replace "+loc+" (applying anyway)");
-                }
-                // migrating away or in-place active partial rebind:
-                managementContext.getRebindManager().getChangeListener().onUnmanaged(loc);
-                if (managementContext.gc != null) managementContext.gc.onUnmanaged(loc);
-            }
-            // do not remove from maps below, bail out now
-            return;
-
-        } else if ((mode.wasPrimary() && mode.isReadOnly()) || (mode.wasReadOnly() && mode.isNoLongerLoaded())) {
-            if (mode.isReadOnly() && mode.wasPrimary()) {
-                // TODO shouldn't this fall into "hasBeenReplaced" above?
-                log.debug("Unmanaging on demotion: "+loc+" ("+mode+")");
-            }
-            // we are unmanaging an instance whose primary management is elsewhere (either we were secondary, or we are being demoted)
-            unmanageNonRecursiveRemoveFromRecords(loc, mode);
-            managementContext.getRebindManager().getChangeListener().onUnmanaged(loc);
-            if (managementContext.gc != null) managementContext.gc.onUnmanaged(loc);
-            unmanageNonRecursiveClearItsFields(loc, mode);
-            
-        } else if (mode.isNoLongerLoaded()) {
-            // Need to store all child entities as onManagementStopping removes a child from the parent entity
-            
-            // As above, see TODO in LocalEntityManager about recursive management / unmanagement v manageAll/unmanageAll
-            recursively(loc, new Predicate<AbstractLocation>() { public boolean apply(AbstractLocation it) {
-                if (shouldSkipUnmanagement(it)) return false;
-                boolean result = unmanageNonRecursiveRemoveFromRecords(it, mode);
-                if (result) {
-                    ManagementTransitionMode mode = getLastManagementTransitionMode(it.getId());
-                    if (mode==null) {
-                        // ad hoc creation e.g. tests
-                        log.debug("Missing transition mode for "+it+" when unmanaging; assuming primary/destroying");
-                        mode = ManagementTransitionMode.guessing(BrooklynObjectManagementMode.MANAGED_PRIMARY, BrooklynObjectManagementMode.NONEXISTENT);
-                    }
-                    if (mode.wasPrimary()) it.onManagementStopped();
-                    managementContext.getRebindManager().getChangeListener().onUnmanaged(it);
-                    if (mode.isDestroying()) recordLocationEvent(it, Lifecycle.DESTROYED);
-                    if (managementContext.gc != null) managementContext.gc.onUnmanaged(it);
-                }
-                unmanageNonRecursiveClearItsFields(loc, mode);
-                return result;
-            } });
-            
-        } else {
-            log.warn("Invalid mode for unmanage: "+mode+" on "+loc+" (ignoring)");
-        }
-        
-        if (loc instanceof Closeable) {
-            Streams.closeQuietly( (Closeable)loc );
-        }
-        
-        locationsById.remove(loc.getId());
-        preRegisteredLocationsById.remove(loc.getId());
-        locationModesById.remove(loc.getId());
-        locationTypes.remove(loc.getId());
-    }
-    
-    /**
-     * Adds this location event to the usage record for the given location (creating the usage 
-     * record if one does not already exist).
-     */
-    private void recordLocationEvent(LocationInternal loc, Lifecycle state) {
-        try {
-            managementContext.getUsageManager().recordLocationEvent(loc, state);
-        } catch (RuntimeInterruptedException e) {
-            throw e;
-        } catch (RuntimeException e) {
-            log.warn("Failed to store location lifecycle event for "+loc+" (ignoring)", e);
-        }
-    }
-
-    private void recursively(Location e, Predicate<AbstractLocation> action) {
-        boolean success = action.apply( (AbstractLocation)e );
-        if (!success) {
-            return; // Don't manage children if action false/unnecessary for parent
-        }
-        for (Location child : e.getChildren()) {
-            recursively(child, action);
-        }
-    }
-
-    /**
-     * Should ensure that the location is now managed somewhere, and known about in all the lists.
-     * Returns true if the location has now become managed; false if it was already managed (anything else throws exception)
-     * @param rebindPrimary true if rebinding primary, false if rebinding as copy, null if creating (not rebinding)
-     */
-    private synchronized boolean manageNonRecursive(Location loc, ManagementTransitionMode mode) {
-        Location old = locationsById.put(loc.getId(), loc);
-        preRegisteredLocationsById.remove(loc.getId());
-
-        locationTypes.put(loc.getId(), loc.getClass().getName());
-        
-        if (old!=null && mode.wasNotLoaded()) {
-            if (old.equals(loc)) {
-                log.warn("{} redundant call to start management of location {}", this, loc);
-            } else {
-                throw new IllegalStateException("call to manage location "+loc+" but different location "+old+" already known under that id at "+this);
-            }
-            return false;
-        }
-
-        if (old!=null && old!=loc) {
-            // passing the transition info will ensure the right shutdown steps invoked for old instance
-            unmanage(old, mode, true);
-        }
-        
-        return true;
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private synchronized void unmanageNonRecursiveClearItsFields(Location loc, ManagementTransitionMode mode) {
-        if (mode.isDestroying()) {
-            ((AbstractLocation)loc).setParent(null, true);
-            
-            Location parent = ((AbstractLocation)loc).getParent();
-            if (parent instanceof ProvisioningLocation<?>) {
-                try {
-                    ((ProvisioningLocation)parent).release(loc);
-                } catch (Exception e) {
-                    Exceptions.propagateIfFatal(e);
-                    log.debug("Error releasing "+loc+" in its parent "+parent+": "+e);
-                }
-            }
-        } else {
-            // if not destroying, don't change the parent's children list
-            ((AbstractLocation)loc).setParent(null, false);
-        }
-        // clear config to help with GC; i know you're not supposed to, but this seems to help, else config bag is littered with refs to entities etc
-        // FIXME relies on config().getLocalBag() returning the underlying bag!
-        ((AbstractLocation)loc).config().getLocalBag().clear();
-    }
-    
-    /**
-     * Should ensure that the location is no longer managed anywhere, remove from all lists.
-     * Returns true if the location has been removed from management; if it was not previously managed (anything else throws exception) 
-     */
-    private synchronized boolean unmanageNonRecursiveRemoveFromRecords(Location loc, ManagementTransitionMode mode) {
-        Object old = locationsById.remove(loc.getId());
-        locationTypes.remove(loc.getId());
-        locationModesById.remove(loc.getId());
-        
-        if (old==null) {
-            log.warn("{} call to stop management of unknown location (already unmanaged?) {}; ignoring", this, loc);
-            return false;
-        } else if (!old.equals(loc)) {
-            // shouldn't happen...
-            log.error("{} call to stop management of location {} removed different location {}; ignoring", new Object[] { this, loc, old });
-            return true;
-        } else {
-            if (log.isDebugEnabled()) log.debug("{} stopped management of location {}", this, loc);
-            return true;
-        }
-    }
-
-    private boolean shouldSkipUnmanagement(Location loc) {
-        if (loc==null) {
-            log.warn(""+this+" call to unmanage null location; skipping",  
-                new IllegalStateException("source of null unmanagement call to "+this));
-            return true;
-        }
-        if (!isManaged(loc)) {
-            log.warn("{} call to stop management of unknown location (already unmanaged?) {}; skipping, and all descendants", this, loc);
-            return true;
-        }
-        return false;
-    }
-    
-    private boolean isRunning() {
-        return managementContext.isRunning();
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java b/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
deleted file mode 100644
index db9b33b..0000000
--- a/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
+++ /dev/null
@@ -1,421 +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.management.internal;
-
-import static brooklyn.util.JavaGroovyEquivalents.elvis;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.WeakHashMap;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import org.apache.brooklyn.api.entity.Application;
-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.AccessController;
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.ExecutionManager;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.SubscriptionManager;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.TaskAdaptable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.BrooklynProperties;
-import brooklyn.config.BrooklynProperties.Factory.Builder;
-import brooklyn.entity.drivers.downloads.BasicDownloadsManager;
-import brooklyn.entity.effector.Effectors;
-import brooklyn.entity.proxying.InternalEntityFactory;
-import brooklyn.entity.proxying.InternalLocationFactory;
-import brooklyn.entity.proxying.InternalPolicyFactory;
-import brooklyn.internal.BrooklynFeatureEnablement;
-import brooklyn.internal.storage.DataGridFactory;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.ha.OsgiManager;
-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;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableSet;
-
-/**
- * A local (single node) implementation of the {@link ManagementContext} API.
- */
-public class LocalManagementContext extends AbstractManagementContext {
-    
-    private static final Logger log = LoggerFactory.getLogger(LocalManagementContext.class);
-
-    private static final Set<LocalManagementContext> INSTANCES = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<LocalManagementContext, Boolean>()));
-    
-    private final Builder builder;
-    
-    private final List<ManagementContext.PropertiesReloadListener> reloadListeners = new CopyOnWriteArrayList<ManagementContext.PropertiesReloadListener>();
-
-    @VisibleForTesting
-    static Set<LocalManagementContext> getInstances() {
-        synchronized (INSTANCES) {
-            return ImmutableSet.copyOf(INSTANCES);
-        }
-    }
-
-    // Note also called reflectively by BrooklynLeakListener
-    public static void logAll(Logger logger){
-        for (LocalManagementContext context : getInstances()) {
-            logger.warn("Management Context "+context+" running, creation stacktrace:\n" + Throwables.getStackTraceAsString(context.constructionStackTrace));
-        }
-    }
-
-    /** terminates all (best effort); returns count of sessions closed; if exceptions thrown, returns negative number.
-     * semantics might change, particular in dealing with interminable mgmt contexts. */
-    // Note also called reflectively by BrooklynLeakListener
-    @Beta
-    public static int terminateAll() {
-        int closed=0,dangling=0;
-        for (LocalManagementContext context : getInstances()) {
-            try {
-                context.terminate();
-                closed++;
-            }catch (Throwable t) {
-                Exceptions.propagateIfFatal(t);
-                log.warn("Failed to terminate management context", t);
-                dangling++;
-            }
-        }
-        if (dangling>0) return -dangling;
-        return closed;
-    }
-
-    private String managementPlaneId;
-    private String managementNodeId;
-    private BasicExecutionManager execution;
-    private SubscriptionManager subscriptions;
-    private LocalEntityManager entityManager;
-    private final LocalLocationManager locationManager;
-    private final LocalAccessManager accessManager;
-    private final LocalUsageManager usageManager;
-    private OsgiManager osgiManager;
-    
-    public final Throwable constructionStackTrace = new Throwable("for construction stacktrace").fillInStackTrace();
-    
-    private final Map<String, Object> brooklynAdditionalProperties;
-
-    /**
-     * Creates a LocalManagement with default BrooklynProperties.
-     */
-    public LocalManagementContext() {
-        this(BrooklynProperties.Factory.builderDefault());
-    }
-
-    public LocalManagementContext(BrooklynProperties brooklynProperties) {
-        this(brooklynProperties, (DataGridFactory)null);
-    }
-
-    /**
-     * Creates a new LocalManagementContext.
-     *
-     * @param brooklynProperties the BrooklynProperties.
-     * @param datagridFactory the DataGridFactory to use. If this instance is null, it means that the system
-     *                        is going to use BrooklynProperties to figure out which instance to load or otherwise
-     *                        use a default instance.
-     */
-    @VisibleForTesting
-    public LocalManagementContext(BrooklynProperties brooklynProperties, DataGridFactory datagridFactory) {
-        this(Builder.fromProperties(brooklynProperties), datagridFactory);
-    }
-    
-    public LocalManagementContext(Builder builder) {
-        this(builder, null, null);
-    }
-    
-    public LocalManagementContext(Builder builder, DataGridFactory datagridFactory) {
-        this(builder, null, datagridFactory);
-    }
-
-    public LocalManagementContext(Builder builder, Map<String, Object> brooklynAdditionalProperties) {
-        this(builder, brooklynAdditionalProperties, null);
-    }
-    
-    public LocalManagementContext(BrooklynProperties brooklynProperties, Map<String, Object> brooklynAdditionalProperties) {
-        this(Builder.fromProperties(brooklynProperties), brooklynAdditionalProperties, null);
-    }
-    
-    public LocalManagementContext(Builder builder, Map<String, Object> brooklynAdditionalProperties, DataGridFactory datagridFactory) {
-        super(builder.build(), datagridFactory);
-        
-        checkNotNull(configMap, "brooklynProperties");
-        
-        // TODO in a persisted world the planeId may be injected
-        this.managementPlaneId = Strings.makeRandomId(8);
-        this.managementNodeId = Strings.makeRandomId(8);
-        this.builder = builder;
-        this.brooklynAdditionalProperties = brooklynAdditionalProperties;
-        if (brooklynAdditionalProperties != null)
-            configMap.addFromMap(brooklynAdditionalProperties);
-        
-        BrooklynFeatureEnablement.init(configMap);
-        
-        this.locationManager = new LocalLocationManager(this);
-        this.accessManager = new LocalAccessManager();
-        this.usageManager = new LocalUsageManager(this);
-        
-        if (configMap.getConfig(OsgiManager.USE_OSGI)) {
-            this.osgiManager = new OsgiManager(this);
-            osgiManager.start();
-        }
-        
-        INSTANCES.add(this);
-        log.debug("Created management context "+this);
-    }
-
-    @Override
-    public String getManagementPlaneId() {
-        return managementPlaneId;
-    }
-    
-    @Override
-    public String getManagementNodeId() {
-        return managementNodeId;
-    }
-    
-    @Override
-    public void prePreManage(Entity entity) {
-        getEntityManager().prePreManage(entity);
-    }
-
-    @Override
-    public void prePreManage(Location location) {
-        getLocationManager().prePreManage(location);
-    }
-
-    @Override
-    public synchronized Collection<Application> getApplications() {
-        return getEntityManager().getApplications();
-    }
-
-    @Override
-    public void addEntitySetListener(CollectionChangeListener<Entity> listener) {
-        getEntityManager().addEntitySetListener(listener);
-    }
-
-    @Override
-    public void removeEntitySetListener(CollectionChangeListener<Entity> listener) {
-        getEntityManager().removeEntitySetListener(listener);
-    }
-
-    @Override
-    protected void manageIfNecessary(Entity entity, Object context) {
-        getEntityManager().manageIfNecessary(entity, context);
-    }
-
-    @Override
-    public synchronized LocalEntityManager getEntityManager() {
-        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
-
-        if (entityManager == null) {
-            entityManager = new LocalEntityManager(this);
-        }
-        return entityManager;
-    }
-
-    @Override
-    public InternalEntityFactory getEntityFactory() {
-        return getEntityManager().getEntityFactory();
-    }
-
-    @Override
-    public InternalLocationFactory getLocationFactory() {
-        return getLocationManager().getLocationFactory();
-    }
-
-    @Override
-    public InternalPolicyFactory getPolicyFactory() {
-        return getEntityManager().getPolicyFactory();
-    }
-
-    @Override
-    public synchronized LocalLocationManager getLocationManager() {
-        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
-        return locationManager;
-    }
-
-    @Override
-    public synchronized LocalAccessManager getAccessManager() {
-        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
-        return accessManager;
-    }
-
-    @Override
-    public synchronized LocalUsageManager getUsageManager() {
-        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
-        return usageManager;
-    }
-    
-    @Override
-    public synchronized Maybe<OsgiManager> getOsgiManager() {
-        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
-        if (osgiManager==null) return Maybe.absent("OSGi not available in this instance"); 
-        return Maybe.of(osgiManager);
-    }
-
-    @Override
-    public synchronized AccessController getAccessController() {
-        return getAccessManager().getAccessController();
-    }
-    
-    @Override
-    public synchronized  SubscriptionManager getSubscriptionManager() {
-        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
-
-        if (subscriptions == null) {
-            subscriptions = new LocalSubscriptionManager(getExecutionManager());
-        }
-        return subscriptions;
-    }
-
-    @Override
-    public synchronized ExecutionManager getExecutionManager() {
-        if (!isRunning()) throw new IllegalStateException("Management context no longer running");
-
-        if (execution == null) {
-            execution = new BasicExecutionManager(getManagementNodeId());
-            gc = new BrooklynGarbageCollector(configMap, execution, getStorage());
-        }
-        return execution;
-    }
-    
-    @Override
-    public void terminate() {
-        INSTANCES.remove(this);
-        super.terminate();
-        if (osgiManager!=null) {
-            osgiManager.stop();
-            osgiManager = null;
-        }
-        if (usageManager != null) usageManager.terminate();
-        if (execution != null) execution.shutdownNow();
-        if (gc != null) gc.shutdownNow();
-    }
-
-    @Override
-    protected void finalize() {
-        terminate();
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    @Override
-    public <T> Task<T> runAtEntity(Map flags, Entity entity, Callable<T> c) {
-        manageIfNecessary(entity, elvis(Arrays.asList(flags.get("displayName"), flags.get("description"), flags, c)));
-        return runAtEntity(entity, Tasks.<T>builder().dynamic(true).body(c).flags(flags).build());
-    }
-
-    protected <T> Task<T> runAtEntity(Entity entity, TaskAdaptable<T> task) {
-        getExecutionContext(entity).submit(task);
-        if (DynamicTasks.getTaskQueuingContext()!=null) {
-            // put it in the queueing context so it appears in the GUI
-            // mark it inessential as this is being invoked from code,
-            // the caller will do 'get' to handle errors
-            TaskTags.markInessential(task);
-            DynamicTasks.getTaskQueuingContext().queue(task.asTask());
-        }
-        return task.asTask();
-    }
-    
-    @Override
-    protected <T> Task<T> runAtEntity(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters) {
-        manageIfNecessary(entity, eff);
-        // prefer to submit this from the current execution context so it sets up correct cross-context chaining
-        ExecutionContext ec = BasicExecutionContext.getCurrentExecutionContext();
-        if (ec == null) {
-            log.debug("Top-level effector invocation: {} on {}", eff, entity);
-            ec = getExecutionContext(entity);
-        }
-        return runAtEntity(entity, Effectors.invocation(entity, eff, parameters));
-    }
-
-    @Override
-    public boolean isManagedLocally(Entity e) {
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        return LocalManagementContext.class.getSimpleName()+"["+getManagementPlaneId()+"-"+getManagementNodeId()+"]";
-    }
-
-    @Override
-    public void reloadBrooklynProperties() {
-        log.info("Reloading brooklyn properties from " + builder);
-        if (builder.hasDelegateOriginalProperties())
-            log.warn("When reloading, mgmt context "+this+" properties are fixed, so reload will be of limited utility");
-        
-        BrooklynProperties properties = builder.build();
-        configMap = properties;
-        if (brooklynAdditionalProperties != null) {
-            log.info("Reloading additional brooklyn properties from " + brooklynAdditionalProperties);
-            configMap.addFromMap(brooklynAdditionalProperties);
-        }
-        this.downloadsManager = BasicDownloadsManager.newDefault(configMap);
-        this.entitlementManager = Entitlements.newManager(this, configMap);
-        
-        clearLocationRegistry();
-        
-        BrooklynFeatureEnablement.init(configMap);
-        
-        // Notify listeners that properties have been reloaded
-        for (PropertiesReloadListener listener : reloadListeners) {
-            listener.reloaded();
-        }
-    }
-
-    @VisibleForTesting
-    public void clearLocationRegistry() {
-        // Force reload of location registry
-        this.locationRegistry = null;
-    }
-
-    @Override
-    public void addPropertiesReloadListener(PropertiesReloadListener listener) {
-        reloadListeners.add(checkNotNull(listener, "listener"));
-    }
-
-    @Override
-    public void removePropertiesReloadListener(PropertiesReloadListener listener) {
-        reloadListeners.remove(listener);
-    }
-
-    public void noteStartupComplete() {
-        startupComplete = true;
-    }
-}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerSplitBrainTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerSplitBrainTest.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerSplitBrainTest.java
new file mode 100644
index 0000000..7e34071
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerSplitBrainTest.java
@@ -0,0 +1,474 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
+import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl;
+import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
+import org.apache.brooklyn.core.management.ha.TestEntityFailingRebind.RebindException;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.test.entity.TestApplication;
+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.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.rebind.PersistenceExceptionHandlerImpl;
+import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
+import brooklyn.entity.rebind.persister.InMemoryObjectStore;
+import brooklyn.entity.rebind.persister.ListeningObjectStore;
+import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+import brooklyn.internal.BrooklynFeatureEnablement;
+import brooklyn.test.Asserts;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Ticker;
+import com.google.common.collect.ImmutableList;
+
+@Test
+public class HighAvailabilityManagerSplitBrainTest {
+
+    private static final Logger log = LoggerFactory.getLogger(HighAvailabilityManagerSplitBrainTest.class);
+    
+    private List<HaMgmtNode> nodes = new MutableList<HighAvailabilityManagerSplitBrainTest.HaMgmtNode>();
+    Map<String,String> sharedBackingStore = MutableMap.of();
+    Map<String,Date> sharedBackingStoreDates = MutableMap.of();
+    private AtomicLong sharedTime; // used to set the ticker's return value
+    private ClassLoader classLoader = getClass().getClassLoader();
+    
+    public class HaMgmtNode {
+        // TODO share with HotStandbyTest and WarmStandbyTest and a few others (minor differences but worth it ultimately)
+
+        private ManagementContextInternal mgmt;
+        private String ownNodeId;
+        private String nodeName;
+        private ListeningObjectStore objectStore;
+        private ManagementPlaneSyncRecordPersister persister;
+        private HighAvailabilityManagerImpl ha;
+        private Ticker ticker;
+        private AtomicLong currentTime; // used to set the ticker's return value
+
+        public void setUp() throws Exception {
+            if (sharedTime==null)
+                currentTime = new AtomicLong(System.currentTimeMillis());
+            
+            ticker = new Ticker() {
+                // strictly not a ticker because returns millis UTC, but it works fine even so
+                @Override public long read() {
+                    if (sharedTime!=null) return sharedTime.get();
+                    return currentTime.get();
+                }
+            };
+            
+            nodeName = "node "+nodes.size();
+            mgmt = newLocalManagementContext();
+            ownNodeId = mgmt.getManagementNodeId();
+            objectStore = new ListeningObjectStore(newPersistenceObjectStore());
+            objectStore.injectManagementContext(mgmt);
+            objectStore.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);
+            persister = new ManagementPlaneSyncRecordPersisterToObjectStore(mgmt, objectStore, classLoader);
+            ((ManagementPlaneSyncRecordPersisterToObjectStore)persister).preferRemoteTimestampInMemento();
+            BrooklynMementoPersisterToObjectStore persisterObj = new BrooklynMementoPersisterToObjectStore(objectStore, mgmt.getBrooklynProperties(), classLoader);
+            mgmt.getRebindManager().setPersister(persisterObj, PersistenceExceptionHandlerImpl.builder().build());
+            ha = ((HighAvailabilityManagerImpl)mgmt.getHighAvailabilityManager())
+                .setPollPeriod(Duration.PRACTICALLY_FOREVER)
+                .setHeartbeatTimeout(Duration.THIRTY_SECONDS)
+                .setLocalTicker(ticker)
+                .setRemoteTicker(ticker)
+                .setPersister(persister);
+            log.info("Created "+nodeName+" "+ownNodeId);
+        }
+        
+        public void tearDown() throws Exception {
+            if (ha != null) ha.stop();
+            if (mgmt != null) Entities.destroyAll(mgmt);
+            if (objectStore != null) objectStore.deleteCompletely();
+        }
+        
+        private long tickerCurrentMillis() {
+            return ticker.read();
+        }
+        
+        private long tickerAdvance(Duration duration) {
+            if (sharedTime!=null)
+                throw new IllegalStateException("Using shared ticker; cannot advance private node clock");
+            currentTime.addAndGet(duration.toMilliseconds());
+            return tickerCurrentMillis();
+        }
+        
+        @Override
+        public String toString() {
+            return nodeName+" "+ownNodeId;
+        }
+    }
+    
+    private Boolean prevThrowOnRebind;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        prevThrowOnRebind = TestEntityFailingRebind.getThrowOnRebind();
+        TestEntityFailingRebind.setThrowOnRebind(true);
+        nodes.clear();
+        sharedBackingStore.clear();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        try {
+            for (HaMgmtNode n: nodes)
+                n.tearDown();
+        } finally {
+            if (prevThrowOnRebind != null) TestEntityFailingRebind.setThrowOnRebind(prevThrowOnRebind);
+        }
+    }
+
+    public HaMgmtNode newNode() throws Exception {
+        HaMgmtNode node = new HaMgmtNode();
+        node.setUp();
+        nodes.add(node);
+        return node;
+    }
+
+    private void sharedTickerAdvance(Duration duration) {
+        if (sharedTime==null) {
+            for (HaMgmtNode n: nodes)
+                n.tickerAdvance(duration);
+        } else {
+            sharedTime.addAndGet(duration.toMilliseconds());
+        }
+    }
+    
+    private long sharedTickerCurrentMillis() {
+        return sharedTime.get();
+    }
+    
+    protected void useSharedTime() {
+        if (!nodes.isEmpty())
+            throw new IllegalStateException("shared time must be set up before any nodes created");
+        sharedTime = new AtomicLong(System.currentTimeMillis());
+    }
+
+    protected ManagementContextInternal newLocalManagementContext() {
+        return new LocalManagementContextForTests();
+    }
+
+    protected PersistenceObjectStore newPersistenceObjectStore() {
+        return new InMemoryObjectStore(sharedBackingStore, sharedBackingStoreDates);
+    }
+    
+    @Test
+    public void testDoubleRebindFails() throws Exception {
+        useSharedTime();
+        HaMgmtNode n1 = newNode();
+        HaMgmtNode n2 = newNode();
+
+        // first auto should become master
+        n1.ha.start(HighAvailabilityMode.AUTO);
+        n2.ha.start(HighAvailabilityMode.AUTO);
+        assertEquals(n1.ha.getNodeState(), ManagementNodeState.MASTER);
+
+        TestApplication app = ApplicationBuilder.newManagedApp(
+                EntitySpec.create(TestApplication.class).impl(TestEntityFailingRebind.class), n1.mgmt);
+        app.start(ImmutableList.<Location>of());
+
+        n1.mgmt.getRebindManager().forcePersistNow(false, null);
+
+        //don't publish state for a while (i.e. long store delays, failures)
+        sharedTickerAdvance(Duration.ONE_MINUTE);
+
+        try {
+            n2.ha.publishAndCheck(false);
+            fail("n2 rebind failure expected");
+        } catch (Exception e) {
+            assertNestedRebindException(e);
+        }
+
+        // re-check should re-assert successfully, no rebind expected as he was previously master
+        n1.ha.publishAndCheck(false);
+        ManagementPlaneSyncRecord memento;
+        memento = n1.ha.loadManagementPlaneSyncRecord(true);
+        assertEquals(memento.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.MASTER);
+        assertEquals(memento.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.FAILED);
+
+        // hot backup permitted by the TestEntityFailingRebind
+        n1.ha.changeMode(HighAvailabilityMode.HOT_BACKUP);
+        memento = n1.ha.loadManagementPlaneSyncRecord(true);
+        assertEquals(memento.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.HOT_BACKUP);
+        try {
+            n1.ha.changeMode(HighAvailabilityMode.MASTER);
+            fail("n1 rebind failure expected");
+        } catch (Exception e) {
+            assertNestedRebindException(e);
+        }
+
+        memento = n1.ha.loadManagementPlaneSyncRecord(true);
+        assertEquals(memento.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.FAILED);
+        assertEquals(memento.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.FAILED);
+    }
+
+    @Test
+    public void testStandbyRebind() throws Exception {
+        useSharedTime();
+        HaMgmtNode n1 = newNode();
+        HaMgmtNode n2 = newNode();
+
+        // first auto should become master
+        n1.ha.start(HighAvailabilityMode.AUTO);
+        n2.ha.start(HighAvailabilityMode.AUTO);
+
+        TestApplication app = ApplicationBuilder.newManagedApp(
+                EntitySpec.create(TestApplication.class).impl(TestEntityFailingRebind.class), n1.mgmt);
+        app.start(ImmutableList.<Location>of());
+
+        n1.mgmt.getRebindManager().forcePersistNow(false, null);
+
+        //don't publish state for a while (i.e. long store delays, failures)
+        sharedTickerAdvance(Duration.ONE_MINUTE);
+
+        try {
+            n2.ha.publishAndCheck(false);
+            fail("n2 rebind failure expected");
+        } catch (Exception e) {
+            assertNestedRebindException(e);
+        }
+
+        TestEntityFailingRebind.setThrowOnRebind(false);
+        n1.ha.publishAndCheck(false);
+
+        ManagementPlaneSyncRecord memento = n1.ha.loadManagementPlaneSyncRecord(true);
+        assertEquals(memento.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.MASTER);
+        assertEquals(memento.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.FAILED);
+    }
+    
+    private void assertNestedRebindException(Throwable t) {
+        Throwable ptr = t;
+        while (ptr != null) {
+            if (ptr instanceof RebindException) {
+                return;
+            }
+            ptr = ptr.getCause();
+        }
+        Exceptions.propagate(t);
+    }
+    
+    @Test
+    public void testIfNodeStopsBeingAbleToWrite() throws Exception {
+        useSharedTime();
+        log.info("time at start "+sharedTickerCurrentMillis());
+        
+        HaMgmtNode n1 = newNode();
+        HaMgmtNode n2 = newNode();
+        
+        // first auto should become master
+        n1.ha.start(HighAvailabilityMode.AUTO);
+        ManagementPlaneSyncRecord memento1 = n1.ha.loadManagementPlaneSyncRecord(true);
+        
+        log.info(n1+" HA: "+memento1);
+        assertEquals(memento1.getMasterNodeId(), n1.ownNodeId);
+        Long time0 = sharedTickerCurrentMillis();
+        assertEquals(memento1.getManagementNodes().get(n1.ownNodeId).getRemoteTimestamp(), time0);
+        assertEquals(memento1.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.MASTER);
+
+        // second - make explicit hot; that's a strictly more complex case than cold standby, so provides pretty good coverage
+        n2.ha.start(HighAvailabilityMode.HOT_STANDBY);
+        ManagementPlaneSyncRecord memento2 = n2.ha.loadManagementPlaneSyncRecord(true);
+        
+        log.info(n2+" HA: "+memento2);
+        assertEquals(memento2.getMasterNodeId(), n1.ownNodeId);
+        assertEquals(memento2.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.MASTER);
+        assertEquals(memento2.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.HOT_STANDBY);
+        assertEquals(memento2.getManagementNodes().get(n1.ownNodeId).getRemoteTimestamp(), time0);
+        assertEquals(memento2.getManagementNodes().get(n2.ownNodeId).getRemoteTimestamp(), time0);
+        
+        // and no entities at either
+        assertEquals(n1.mgmt.getApplications().size(), 0);
+        assertEquals(n2.mgmt.getApplications().size(), 0);
+
+        // create
+        TestApplication app = ApplicationBuilder.newManagedApp(EntitySpec.create(TestApplication.class), n1.mgmt);
+        app.start(ImmutableList.<Location>of());
+        app.setAttribute(TestApplication.MY_ATTRIBUTE, "hello");
+        
+        assertEquals(n1.mgmt.getApplications().size(), 1);
+        assertEquals(n2.mgmt.getApplications().size(), 0);
+        log.info("persisting "+n1.ownNodeId);
+        n1.mgmt.getRebindManager().forcePersistNow(false, null);
+        
+        n1.objectStore.setWritesFailSilently(true);
+        log.info(n1+" writes off");
+        sharedTickerAdvance(Duration.ONE_MINUTE);
+        log.info("time now "+sharedTickerCurrentMillis());
+        Long time1 = sharedTickerCurrentMillis();
+        
+        log.info("publish "+n2.ownNodeId);
+        n2.ha.publishAndCheck(false);
+        ManagementPlaneSyncRecord memento2b = n2.ha.loadManagementPlaneSyncRecord(true);
+        log.info(n2+" HA now: "+memento2b);
+        
+        // n2 infers n1 as failed 
+        assertEquals(memento2b.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.FAILED);
+        assertEquals(memento2b.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.MASTER);
+        assertEquals(memento2b.getMasterNodeId(), n2.ownNodeId);
+        assertEquals(memento2b.getManagementNodes().get(n1.ownNodeId).getRemoteTimestamp(), time0);
+        assertEquals(memento2b.getManagementNodes().get(n2.ownNodeId).getRemoteTimestamp(), time1);
+        
+        assertEquals(n1.mgmt.getApplications().size(), 1);
+        assertEquals(n2.mgmt.getApplications().size(), 1);
+        assertEquals(n1.mgmt.getApplications().iterator().next().getAttribute(TestApplication.MY_ATTRIBUTE), "hello");
+        
+        n1.objectStore.setWritesFailSilently(false);
+        log.info(n1+" writes on");
+        
+        sharedTickerAdvance(Duration.ONE_SECOND);
+        log.info("time now "+sharedTickerCurrentMillis());
+        Long time2 = sharedTickerCurrentMillis();
+        
+        log.info("publish "+n1.ownNodeId);
+        n1.ha.publishAndCheck(false);
+        ManagementPlaneSyncRecord memento1b = n1.ha.loadManagementPlaneSyncRecord(true);
+        log.info(n1+" HA now: "+memento1b);
+        
+        ManagementNodeState expectedStateAfterDemotion = BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY) ?
+            ManagementNodeState.HOT_STANDBY : ManagementNodeState.STANDBY;
+        
+        // n1 comes back and demotes himself 
+        assertEquals(memento1b.getManagementNodes().get(n1.ownNodeId).getStatus(), expectedStateAfterDemotion);
+        assertEquals(memento1b.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.MASTER);
+        assertEquals(memento1b.getMasterNodeId(), n2.ownNodeId);
+        assertEquals(memento1b.getManagementNodes().get(n1.ownNodeId).getRemoteTimestamp(), time2);
+        assertEquals(memento1b.getManagementNodes().get(n2.ownNodeId).getRemoteTimestamp(), time1);
+        
+        // n2 now sees itself as master, with n1 in standby again
+        ManagementPlaneSyncRecord memento2c = n2.ha.loadManagementPlaneSyncRecord(true);
+        log.info(n2+" HA now: "+memento2c);
+        assertEquals(memento2c.getManagementNodes().get(n1.ownNodeId).getStatus(), expectedStateAfterDemotion);
+        assertEquals(memento2c.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.MASTER);
+        assertEquals(memento2c.getMasterNodeId(), n2.ownNodeId);
+        assertEquals(memento2c.getManagementNodes().get(n1.ownNodeId).getRemoteTimestamp(), time2);
+        assertEquals(memento2c.getManagementNodes().get(n2.ownNodeId).getRemoteTimestamp(), time2);
+
+        // right number of entities at n2; n1 may or may not depending whether hot standby is default
+        assertEquals(n2.mgmt.getApplications().size(), 1);
+        assertEquals(n1.mgmt.getApplications().size(), BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY) ? 1 : 0);
+    }
+    
+    @Test(invocationCount=50, groups="Integration")
+    public void testIfNodeStopsBeingAbleToWriteManyTimes() throws Exception {
+        testIfNodeStopsBeingAbleToWrite();
+    }
+    
+    @Test
+    public void testSimultaneousStartup() throws Exception {
+        doTestConcurrentStartup(5, null);
+    }
+
+    @Test
+    public void testNearSimultaneousStartup() throws Exception {
+        doTestConcurrentStartup(20, Duration.millis(20));
+    }
+
+    @Test(invocationCount=50, groups="Integration")
+    public void testNearSimultaneousStartupManyTimes() throws Exception {
+        doTestConcurrentStartup(20, Duration.millis(20));
+    }
+
+    protected void doTestConcurrentStartup(int size, final Duration staggerStart) throws Exception {
+        useSharedTime();
+        
+        List<Thread> spawned = MutableList.of();
+        for (int i=0; i<size; i++) {
+            final HaMgmtNode n = newNode();
+            Thread t = new Thread() { public void run() {
+                if (staggerStart!=null) Time.sleep(staggerStart.multiply(Math.random()));
+                n.ha.start(HighAvailabilityMode.AUTO);
+                n.ha.setPollPeriod(Duration.millis(20));
+            } };
+            spawned.add(t);
+            t.start();
+        }
+
+        try {
+            final Stopwatch timer = Stopwatch.createStarted();
+            Asserts.succeedsEventually(new Runnable() {
+                @Override public void run() {
+                    ManagementPlaneSyncRecord memento = nodes.get(0).ha.loadManagementPlaneSyncRecord(true);
+                    List<ManagementNodeState> counts = MutableList.of(), savedCounts = MutableList.of();
+                    for (HaMgmtNode n: nodes) {
+                        counts.add(n.ha.getNodeState());
+                        ManagementNodeSyncRecord m = memento.getManagementNodes().get(n.ownNodeId);
+                        if (m!=null) {
+                            savedCounts.add(m.getStatus());
+                        }
+                    }
+                    log.info("while starting "+nodes.size()+" nodes: "
+                        +Collections.frequency(counts, ManagementNodeState.MASTER)+" M + "
+                        +Collections.frequency(counts, ManagementNodeState.HOT_STANDBY)+" hot + "
+                        +Collections.frequency(counts, ManagementNodeState.STANDBY)+" warm + "
+                        +Collections.frequency(counts, ManagementNodeState.INITIALIZING)+" init; "
+                        + memento.getManagementNodes().size()+" saved, "
+                        +Collections.frequency(savedCounts, ManagementNodeState.MASTER)+" M + "
+                        +Collections.frequency(savedCounts, ManagementNodeState.HOT_STANDBY)+" hot + "
+                        +Collections.frequency(savedCounts, ManagementNodeState.STANDBY)+" warm + "
+                        +Collections.frequency(savedCounts, ManagementNodeState.INITIALIZING)+" init");
+
+                    if (timer.isRunning() && Duration.of(timer).compareTo(Duration.TEN_SECONDS)>0) {
+                        log.warn("we seem to have a problem stabilizing");  //handy place to set a suspend-VM breakpoint!
+                        timer.stop();
+                    }
+                    assertEquals(Collections.frequency(counts, ManagementNodeState.MASTER), 1);
+                    assertEquals(Collections.frequency(counts, ManagementNodeState.HOT_STANDBY)+Collections.frequency(counts, ManagementNodeState.STANDBY), nodes.size()-1);
+                    assertEquals(Collections.frequency(savedCounts, ManagementNodeState.MASTER), 1);
+                    assertEquals(Collections.frequency(savedCounts, ManagementNodeState.HOT_STANDBY)+Collections.frequency(savedCounts, ManagementNodeState.STANDBY), nodes.size()-1);
+                }});
+        } catch (Throwable t) {
+            log.warn("Failed to stabilize (rethrowing): "+t, t);
+            throw Exceptions.propagate(t);
+        }
+        
+        for (Thread t: spawned)
+            t.join(Duration.THIRTY_SECONDS.toMilliseconds());
+    }
+    
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerTestFixture.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerTestFixture.java
new file mode 100644
index 0000000..5a7f79a
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerTestFixture.java
@@ -0,0 +1,287 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
+import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl;
+import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordDeltaImpl;
+import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
+import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl.PromotionListener;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+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.BrooklynVersion;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.rebind.PersistenceExceptionHandlerImpl;
+import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
+import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord;
+import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord.Builder;
+import brooklyn.test.Asserts;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Ticker;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+@Test
+public abstract class HighAvailabilityManagerTestFixture {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(HighAvailabilityManagerTestFixture.class);
+    
+    private ManagementPlaneSyncRecordPersister persister;
+    protected ManagementContextInternal managementContext;
+    private String ownNodeId;
+    private HighAvailabilityManagerImpl manager;
+    private Ticker ticker;
+    private AtomicLong currentTime; // used to set the ticker's return value
+    private RecordingPromotionListener promotionListener;
+    private ClassLoader classLoader = getClass().getClassLoader();
+    private PersistenceObjectStore objectStore;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        currentTime = new AtomicLong(1000000000L);
+        ticker = new Ticker() {
+            // strictly not a ticker because returns millis UTC, but it works fine even so
+            @Override public long read() {
+                return currentTime.get();
+            }
+        };
+        promotionListener = new RecordingPromotionListener();
+        managementContext = newLocalManagementContext();
+        ownNodeId = managementContext.getManagementNodeId();
+        objectStore = newPersistenceObjectStore();
+        objectStore.injectManagementContext(managementContext);
+        objectStore.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);
+        persister = new ManagementPlaneSyncRecordPersisterToObjectStore(managementContext, objectStore, classLoader);
+        ((ManagementPlaneSyncRecordPersisterToObjectStore)persister).preferRemoteTimestampInMemento();
+        BrooklynMementoPersisterToObjectStore persisterObj = new BrooklynMementoPersisterToObjectStore(
+                objectStore, 
+                managementContext.getBrooklynProperties(), 
+                classLoader);
+        managementContext.getRebindManager().setPersister(persisterObj, PersistenceExceptionHandlerImpl.builder().build());
+        manager = ((HighAvailabilityManagerImpl)managementContext.getHighAvailabilityManager())
+                .setPollPeriod(getPollPeriod())
+                .setHeartbeatTimeout(Duration.THIRTY_SECONDS)
+                .setPromotionListener(promotionListener)
+                .setLocalTicker(ticker)
+                .setRemoteTicker(getRemoteTicker())
+                .setPersister(persister);
+        persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
+            .node(newManagerMemento(ownNodeId, ManagementNodeState.HOT_STANDBY))
+            .build());
+
+    }
+
+    protected ManagementContextInternal newLocalManagementContext() {
+        return LocalManagementContextForTests.newInstance();
+    }
+
+    protected abstract PersistenceObjectStore newPersistenceObjectStore();
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (manager != null) manager.stop();
+        if (managementContext != null) Entities.destroyAll(managementContext);
+        if (objectStore != null) objectStore.deleteCompletely();
+    }
+    
+    // The web-console could still be polling (e.g. if have just restarted brooklyn), before the persister is set.
+    // Must not throw NPE, but instead return something sensible (e.g. an empty state record).
+    @Test
+    public void testGetManagementPlaneSyncStateDoesNotThrowNpeBeforePersisterSet() throws Exception {
+        HighAvailabilityManagerImpl manager2 = new HighAvailabilityManagerImpl(managementContext)
+            .setPollPeriod(Duration.millis(10))
+            .setHeartbeatTimeout(Duration.THIRTY_SECONDS)
+            .setPromotionListener(promotionListener)
+            .setLocalTicker(ticker)
+            .setRemoteTicker(ticker);
+        try {
+            ManagementPlaneSyncRecord state = manager2.loadManagementPlaneSyncRecord(true);
+            assertNotNull(state);
+        } finally {
+            manager2.stop();
+        }
+
+    }
+    // Can get a log.error about our management node's heartbeat being out of date. Caused by
+    // poller first writing a heartbeat record, and then the clock being incremented. But the
+    // next poll fixes it.
+    public void testPromotes() throws Exception {
+        persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
+                .node(newManagerMemento(ownNodeId, ManagementNodeState.HOT_STANDBY))
+                .node(newManagerMemento("node1", ManagementNodeState.MASTER))
+                .setMaster("node1")
+                .build());
+        
+        manager.start(HighAvailabilityMode.AUTO);
+        
+        // Simulate passage of time; ticker used by this HA-manager so it will "correctly" publish
+        // its own heartbeat with the new time; but node1's record is now out-of-date.
+        tickerAdvance(Duration.seconds(31));
+        
+        // Expect to be notified of our promotion, as the only other node
+        promotionListener.assertCalledEventually();
+    }
+
+    @Test(groups="Integration") // because one second wait in succeedsContinually
+    public void testDoesNotPromoteIfMasterTimeoutNotExpired() throws Exception {
+        persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
+                .node(newManagerMemento(ownNodeId, ManagementNodeState.HOT_STANDBY))
+                .node(newManagerMemento("node1", ManagementNodeState.MASTER))
+                .setMaster("node1")
+                .build());
+        
+        manager.start(HighAvailabilityMode.AUTO);
+        
+        tickerAdvance(Duration.seconds(25));
+        
+        // Expect not to be notified, as 25s < 30s timeout
+        // (it's normally a fake clock so won't hit 30, even waiting 1s below - but in "IntegrationTest" subclasses it is real!)
+        Asserts.succeedsContinually(new Runnable() {
+            @Override public void run() {
+                assertTrue(promotionListener.callTimestamps.isEmpty(), "calls="+promotionListener.callTimestamps);
+            }});
+    }
+
+    public void testGetManagementPlaneStatus() throws Exception {
+        // with the name zzzzz the mgr created here should never be promoted by the alphabetical strategy!
+        
+        tickerAdvance(Duration.FIVE_SECONDS);
+        persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
+                .node(newManagerMemento(ownNodeId, ManagementNodeState.STANDBY))
+                .node(newManagerMemento("zzzzzzz_node1", ManagementNodeState.STANDBY))
+                .build());
+        persister.loadSyncRecord();
+        long zzzTime = tickerCurrentMillis();
+        tickerAdvance(Duration.FIVE_SECONDS);
+        
+        manager.start(HighAvailabilityMode.AUTO);
+        ManagementPlaneSyncRecord memento = manager.loadManagementPlaneSyncRecord(true);
+        
+        // Note can assert timestamp because not "real" time; it's using our own Ticker
+        assertEquals(memento.getMasterNodeId(), ownNodeId);
+        assertEquals(memento.getManagementNodes().keySet(), ImmutableSet.of(ownNodeId, "zzzzzzz_node1"));
+        assertEquals(memento.getManagementNodes().get(ownNodeId).getNodeId(), ownNodeId);
+        assertEquals(memento.getManagementNodes().get(ownNodeId).getStatus(), ManagementNodeState.MASTER);
+        assertEquals(memento.getManagementNodes().get(ownNodeId).getLocalTimestamp(), tickerCurrentMillis());
+        assertEquals(memento.getManagementNodes().get("zzzzzzz_node1").getNodeId(), "zzzzzzz_node1");
+        assertEquals(memento.getManagementNodes().get("zzzzzzz_node1").getStatus(), ManagementNodeState.STANDBY);
+        assertEquals(memento.getManagementNodes().get("zzzzzzz_node1").getLocalTimestamp(), zzzTime);
+    }
+
+    @Test(groups="Integration", invocationCount=50) //because we have had non-deterministic failures 
+    public void testGetManagementPlaneStatusManyTimes() throws Exception {
+        testGetManagementPlaneStatus();
+    }
+    
+    @Test
+    public void testGetManagementPlaneSyncStateInfersTimedOutNodeAsFailed() throws Exception {
+        persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
+                .node(newManagerMemento(ownNodeId, ManagementNodeState.HOT_STANDBY))
+                .node(newManagerMemento("node1", ManagementNodeState.MASTER))
+                .setMaster("node1")
+                .build());
+        
+        manager.start(HighAvailabilityMode.HOT_STANDBY);
+        
+        ManagementPlaneSyncRecord state = manager.loadManagementPlaneSyncRecord(true);
+        assertEquals(state.getManagementNodes().get("node1").getStatus(), ManagementNodeState.MASTER);
+        assertEquals(state.getManagementNodes().get(ownNodeId).getStatus(), ManagementNodeState.HOT_STANDBY);
+        
+        // Simulate passage of time; ticker used by this HA-manager so it will "correctly" publish
+        // its own heartbeat with the new time; but node1's record is now out-of-date.
+        tickerAdvance(Duration.seconds(31));
+        
+        ManagementPlaneSyncRecord state2 = manager.loadManagementPlaneSyncRecord(true);
+        assertEquals(state2.getManagementNodes().get("node1").getStatus(), ManagementNodeState.FAILED);
+        assertNotEquals(state.getManagementNodes().get(ownNodeId).getStatus(), ManagementNodeState.FAILED);
+    }
+
+    protected Duration getPollPeriod() {
+        return Duration.millis(10);
+    }
+    
+    protected long tickerCurrentMillis() {
+        return ticker.read();
+    }
+    
+    protected long tickerAdvance(Duration duration) {
+        currentTime.addAndGet(duration.toMilliseconds());
+        return tickerCurrentMillis();
+    }
+
+    protected Ticker getRemoteTicker() {
+        return ticker;
+    }
+    
+    protected ManagementNodeSyncRecord newManagerMemento(String nodeId, ManagementNodeState status) {
+        Builder rb = BasicManagementNodeSyncRecord.builder();
+        rb.brooklynVersion(BrooklynVersion.get()).nodeId(nodeId).status(status);
+        rb.localTimestamp(tickerCurrentMillis());
+        if (getRemoteTicker()!=null)
+            rb.remoteTimestamp(getRemoteTicker().read());
+        return rb.build();
+    }
+    
+    public static class RecordingPromotionListener implements PromotionListener {
+        public final List<Long> callTimestamps = Lists.newCopyOnWriteArrayList();
+        
+        @Override
+        public void promotingToMaster() {
+            callTimestamps.add(System.currentTimeMillis());
+        }
+        
+        public void assertNotCalled() {
+            assertTrue(callTimestamps.isEmpty(), "calls="+callTimestamps);
+        }
+
+        public void assertCalled() {
+            assertFalse(callTimestamps.isEmpty(), "calls="+callTimestamps);
+        }
+
+        public void assertCalledEventually() {
+            Asserts.succeedsEventually(new Runnable() {
+                @Override public void run() {
+                    assertCalled();
+                }});
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/HotStandbyTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/HotStandbyTest.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/HotStandbyTest.java
new file mode 100644
index 0000000..da4f998
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/HotStandbyTest.java
@@ -0,0 +1,667 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.util.ArrayDeque;
+import java.util.Date;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.Feed;
+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.ha.HighAvailabilityMode;
+import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
+import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl;
+import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
+import org.apache.brooklyn.core.management.internal.AbstractManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.test.entity.TestApplication;
+import org.apache.brooklyn.test.entity.TestEntity;
+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 brooklyn.entity.rebind.PersistenceExceptionHandlerImpl;
+import brooklyn.entity.rebind.RebindFeedTest.MyEntityWithFunctionFeedImpl;
+import brooklyn.entity.rebind.RebindFeedTest.MyEntityWithNewFeedsEachTimeImpl;
+import brooklyn.entity.rebind.RebindManagerImpl;
+import brooklyn.entity.rebind.RebindTestFixture;
+import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
+import brooklyn.entity.rebind.persister.InMemoryObjectStore;
+import brooklyn.entity.rebind.persister.ListeningObjectStore;
+import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation.LocalhostMachine;
+
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.javalang.JavaClassNames;
+import brooklyn.util.repeat.Repeater;
+import brooklyn.util.text.ByteSizeStrings;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.collect.Iterables;
+
+public class HotStandbyTest {
+
+    private static final Logger log = LoggerFactory.getLogger(HotStandbyTest.class);
+    
+    private List<HaMgmtNode> nodes = new MutableList<HotStandbyTest.HaMgmtNode>();
+    Map<String,String> sharedBackingStore = MutableMap.of();
+    Map<String,Date> sharedBackingStoreDates = MutableMap.of();
+    private ClassLoader classLoader = getClass().getClassLoader();
+    
+    public class HaMgmtNode {
+        // TODO share with WarmStandbyTest and SplitBrainTest and a few others (minor differences but worth it ultimately)
+
+        private ManagementContextInternal mgmt;
+        private String ownNodeId;
+        private String nodeName;
+        private ListeningObjectStore objectStore;
+        private ManagementPlaneSyncRecordPersister persister;
+        private HighAvailabilityManagerImpl ha;
+        private Duration persistOrRebindPeriod = Duration.ONE_SECOND;
+
+        public void setUp() throws Exception {
+            nodeName = "node "+nodes.size();
+            mgmt = newLocalManagementContext();
+            ownNodeId = mgmt.getManagementNodeId();
+            objectStore = new ListeningObjectStore(newPersistenceObjectStore());
+            objectStore.injectManagementContext(mgmt);
+            objectStore.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);
+            persister = new ManagementPlaneSyncRecordPersisterToObjectStore(mgmt, objectStore, classLoader);
+            ((ManagementPlaneSyncRecordPersisterToObjectStore)persister).preferRemoteTimestampInMemento();
+            BrooklynMementoPersisterToObjectStore persisterObj = new BrooklynMementoPersisterToObjectStore(objectStore, mgmt.getBrooklynProperties(), classLoader);
+            ((RebindManagerImpl)mgmt.getRebindManager()).setPeriodicPersistPeriod(persistOrRebindPeriod);
+            mgmt.getRebindManager().setPersister(persisterObj, PersistenceExceptionHandlerImpl.builder().build());
+            ha = ((HighAvailabilityManagerImpl)mgmt.getHighAvailabilityManager())
+                .setPollPeriod(Duration.PRACTICALLY_FOREVER)
+                .setHeartbeatTimeout(Duration.THIRTY_SECONDS)
+                .setPersister(persister);
+            log.info("Created "+nodeName+" "+ownNodeId);
+        }
+        
+        public void tearDownThisOnly() throws Exception {
+            if (ha != null) ha.stop();
+            if (mgmt!=null) mgmt.getRebindManager().stop();
+            if (mgmt != null) Entities.destroyAll(mgmt);
+        }
+        
+        public void tearDownAll() throws Exception {
+            tearDownThisOnly();
+            // can't delete the object store until all being torn down
+            if (objectStore != null) objectStore.deleteCompletely();
+        }
+        
+        @Override
+        public String toString() {
+            return nodeName+" "+ownNodeId;
+        }
+
+        public RebindManagerImpl rebinder() {
+            return (RebindManagerImpl)mgmt.getRebindManager();
+        }
+    }
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        nodes.clear();
+        sharedBackingStore.clear();
+    }
+    
+    public HaMgmtNode newNode(Duration persistOrRebindPeriod) throws Exception {
+        HaMgmtNode node = new HaMgmtNode();
+        node.persistOrRebindPeriod = persistOrRebindPeriod;
+        node.setUp();
+        nodes.add(node);
+        return node;
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        for (HaMgmtNode n: nodes)
+            n.tearDownAll();
+    }
+
+    protected ManagementContextInternal newLocalManagementContext() {
+        return new LocalManagementContextForTests();
+    }
+
+    protected PersistenceObjectStore newPersistenceObjectStore() {
+        return new InMemoryObjectStore(sharedBackingStore, sharedBackingStoreDates);
+    }
+
+    private HaMgmtNode createMaster(Duration persistOrRebindPeriod) throws Exception {
+        HaMgmtNode n1 = newNode(persistOrRebindPeriod);
+        n1.ha.start(HighAvailabilityMode.AUTO);
+        assertEquals(n1.ha.getNodeState(), ManagementNodeState.MASTER);
+        return n1;
+    }
+    
+    private HaMgmtNode createHotStandby(Duration rebindPeriod) throws Exception {
+        HaMgmtNode n2 = newNode(rebindPeriod);
+        n2.ha.start(HighAvailabilityMode.HOT_STANDBY);
+        assertEquals(n2.ha.getNodeState(), ManagementNodeState.HOT_STANDBY);
+        return n2;
+    }
+
+    private TestApplication createFirstAppAndPersist(HaMgmtNode n1) throws Exception {
+        TestApplication app = TestApplication.Factory.newManagedInstanceForTests(n1.mgmt);
+        // for testing without enrichers, if desired:
+//        TestApplication app = ApplicationBuilder.newManagedApp(EntitySpec.create(TestApplication.class).impl(TestApplicationNoEnrichersImpl.class), n1.mgmt);
+        app.setDisplayName("First App");
+        app.start(MutableList.<Location>of());
+        app.config().set(TestEntity.CONF_NAME, "first-app");
+        app.setAttribute(TestEntity.SEQUENCE, 3);
+        
+        forcePersistNow(n1);
+        return app;
+    }
+
+    protected void forcePersistNow(HaMgmtNode n1) {
+        n1.mgmt.getRebindManager().forcePersistNow(false, null);
+    }
+    
+    private Application expectRebindSequenceNumber(HaMgmtNode master, HaMgmtNode hotStandby, Application app, int expectedSensorSequenceValue, boolean immediate) {
+        Application appRO = hotStandby.mgmt.lookup(app.getId(), Application.class);
+
+        if (immediate) {
+            forcePersistNow(master);
+            forceRebindNow(hotStandby);
+            EntityTestUtils.assertAttributeEquals(appRO, TestEntity.SEQUENCE, expectedSensorSequenceValue);
+        } else {
+            EntityTestUtils.assertAttributeEqualsEventually(appRO, TestEntity.SEQUENCE, expectedSensorSequenceValue);
+        }
+        
+        log.info("got sequence number "+expectedSensorSequenceValue+" from "+appRO);
+        
+        // make sure the instance (proxy) is unchanged
+        Application appRO2 = hotStandby.mgmt.lookup(app.getId(), Application.class);
+        Assert.assertTrue(appRO2==appRO);
+        
+        return appRO;
+    }
+
+    private void forceRebindNow(HaMgmtNode hotStandby) {
+        hotStandby.mgmt.getRebindManager().rebind(null, null, ManagementNodeState.HOT_STANDBY);
+    }
+    
+    @Test
+    public void testHotStandbySeesInitialCustomNameConfigAndSensorValueButDoesntAllowChange() throws Exception {
+        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
+        TestApplication app = createFirstAppAndPersist(n1);
+        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
+
+        assertEquals(n2.mgmt.getApplications().size(), 1);
+        Application appRO = n2.mgmt.lookup(app.getId(), Application.class);
+        Assert.assertNotNull(appRO);
+        Assert.assertTrue(appRO instanceof TestApplication);
+        assertEquals(appRO.getDisplayName(), "First App");
+        assertEquals(appRO.getConfig(TestEntity.CONF_NAME), "first-app");
+        assertEquals(appRO.getAttribute(TestEntity.SEQUENCE), (Integer)3);
+
+        try {
+            ((TestApplication)appRO).setAttribute(TestEntity.SEQUENCE, 4);
+            Assert.fail("Should not have allowed sensor to be set");
+        } catch (Exception e) {
+            Assert.assertTrue(e.toString().toLowerCase().contains("read-only"), "Error message did not contain expected text: "+e);
+        }
+        assertEquals(appRO.getAttribute(TestEntity.SEQUENCE), (Integer)3);
+    }
+
+    @Test
+    public void testHotStandbySeesChangesToNameConfigAndSensorValue() throws Exception {
+        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
+        TestApplication app = createFirstAppAndPersist(n1);
+        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
+
+        assertEquals(n2.mgmt.getApplications().size(), 1);
+        Application appRO = n2.mgmt.lookup(app.getId(), Application.class);
+        Assert.assertNotNull(appRO);
+        assertEquals(appRO.getChildren().size(), 0);
+        
+        // test changes
+
+        app.setDisplayName("First App Renamed");
+        app.config().set(TestEntity.CONF_NAME, "first-app-renamed");
+        app.setAttribute(TestEntity.SEQUENCE, 4);
+
+        appRO = expectRebindSequenceNumber(n1, n2, app, 4, true);
+        assertEquals(n2.mgmt.getEntityManager().getEntities().size(), 1);
+        assertEquals(appRO.getDisplayName(), "First App Renamed");
+        assertEquals(appRO.getConfig(TestEntity.CONF_NAME), "first-app-renamed");
+        
+        // and change again for good measure!
+
+        app.setDisplayName("First App");
+        app.config().set(TestEntity.CONF_NAME, "first-app-restored");
+        app.setAttribute(TestEntity.SEQUENCE, 5);
+        
+        appRO = expectRebindSequenceNumber(n1, n2, app, 5, true);
+        assertEquals(n2.mgmt.getEntityManager().getEntities().size(), 1);
+        assertEquals(appRO.getDisplayName(), "First App");
+        assertEquals(appRO.getConfig(TestEntity.CONF_NAME), "first-app-restored");
+    }
+
+
+    public void testHotStandbySeesStructuralChangesIncludingRemoval() throws Exception {
+        doTestHotStandbySeesStructuralChangesIncludingRemoval(true);
+    }
+    
+    @Test(groups="Integration") // due to time (it waits for background persistence)
+    public void testHotStandbyUnforcedSeesStructuralChangesIncludingRemoval() throws Exception {
+        doTestHotStandbySeesStructuralChangesIncludingRemoval(false);
+    }
+    
+    public void doTestHotStandbySeesStructuralChangesIncludingRemoval(boolean immediate) throws Exception {
+        HaMgmtNode n1 = createMaster(immediate ? Duration.PRACTICALLY_FOREVER : Duration.millis(200));
+        TestApplication app = createFirstAppAndPersist(n1);
+        HaMgmtNode n2 = createHotStandby(immediate ? Duration.PRACTICALLY_FOREVER : Duration.millis(200));
+
+        assertEquals(n2.mgmt.getApplications().size(), 1);
+        Application appRO = n2.mgmt.lookup(app.getId(), Application.class);
+        Assert.assertNotNull(appRO);
+        assertEquals(appRO.getChildren().size(), 0);
+        
+        // test additions - new child, new app
+        
+        TestEntity child = app.addChild(EntitySpec.create(TestEntity.class).configure(TestEntity.CONF_NAME, "first-child"));
+        Entities.manage(child);
+        TestApplication app2 = TestApplication.Factory.newManagedInstanceForTests(n1.mgmt);
+        app2.config().set(TestEntity.CONF_NAME, "second-app");
+        
+        app.setAttribute(TestEntity.SEQUENCE, 4);
+        appRO = expectRebindSequenceNumber(n1, n2, app, 4, immediate);
+        
+        assertEquals(appRO.getChildren().size(), 1);
+        Entity childRO = Iterables.getOnlyElement(appRO.getChildren());
+        assertEquals(childRO.getId(), child.getId());
+        assertEquals(childRO.getConfig(TestEntity.CONF_NAME), "first-child");
+        
+        assertEquals(n2.mgmt.getApplications().size(), 2);
+        Application app2RO = n2.mgmt.lookup(app2.getId(), Application.class);
+        Assert.assertNotNull(app2RO);
+        assertEquals(app2RO.getConfig(TestEntity.CONF_NAME), "second-app");
+        
+        assertEquals(n2.mgmt.getEntityManager().getEntities().size(), 3);
+        
+        // now test removals
+        
+        Entities.unmanage(child);
+        Entities.unmanage(app2);
+        
+        app.setAttribute(TestEntity.SEQUENCE, 5);
+        appRO = expectRebindSequenceNumber(n1, n2, app, 5, immediate);
+        
+        EntityTestUtils.assertAttributeEqualsEventually(appRO, TestEntity.SEQUENCE, 5);
+        assertEquals(n2.mgmt.getEntityManager().getEntities().size(), 1);
+        assertEquals(appRO.getChildren().size(), 0);
+        assertEquals(n2.mgmt.getApplications().size(), 1);
+        Assert.assertNull(n2.mgmt.lookup(app2.getId(), Application.class));
+        Assert.assertNull(n2.mgmt.lookup(child.getId(), Application.class));
+    }
+
+    @Test(groups="Integration", invocationCount=50)
+    public void testHotStandbySeesStructuralChangesIncludingRemovalManyTimes() throws Exception {
+        doTestHotStandbySeesStructuralChangesIncludingRemoval(true);
+    }
+
+    Deque<Long> usedMemory = new ArrayDeque<Long>();
+    protected long noteUsedMemory(String message) {
+        Time.sleep(Duration.millis(200));
+        for (HaMgmtNode n: nodes) {
+            ((AbstractManagementContext)n.mgmt).getGarbageCollector().gcIteration();
+        }
+        System.gc(); System.gc();
+        Time.sleep(Duration.millis(50));
+        System.gc(); System.gc();
+        long mem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+        usedMemory.addLast(mem);
+        log.info("Memory used - "+message+": "+ByteSizeStrings.java().apply(mem));
+        return mem;
+    }
+    public void assertUsedMemoryLessThan(String event, long max) {
+        noteUsedMemory(event);
+        long nowUsed = usedMemory.peekLast();
+        if (nowUsed > max) {
+            // aggressively try to force GC
+            Time.sleep(Duration.ONE_SECOND);
+            usedMemory.removeLast();
+            noteUsedMemory(event+" (extra GC)");
+            nowUsed = usedMemory.peekLast();
+            if (nowUsed > max) {
+                Assert.fail("Too much memory used - "+ByteSizeStrings.java().apply(nowUsed)+" > max "+ByteSizeStrings.java().apply(max));
+            }
+        }
+    }
+    public void assertUsedMemoryMaxDelta(String event, long deltaMegabytes) {
+        assertUsedMemoryLessThan(event, usedMemory.peekLast() + deltaMegabytes*1024*1024);
+    }
+
+    @Test(groups="Integration")
+    public void testHotStandbyDoesNotLeakLotsOfRebinds() throws Exception {
+        log.info("Starting test "+JavaClassNames.niceClassAndMethod());
+        final int DELTA = 2;
+        
+        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
+        TestApplication app = createFirstAppAndPersist(n1);
+        long initialUsed = noteUsedMemory("Created app");
+        
+        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
+        assertUsedMemoryMaxDelta("Standby created", DELTA);
+        
+        forcePersistNow(n1);
+        forceRebindNow(n2);
+        assertUsedMemoryMaxDelta("Persisted and rebinded once", DELTA);
+        
+        for (int i=0; i<10; i++) {
+            forcePersistNow(n1);
+            forceRebindNow(n2);
+        }
+        assertUsedMemoryMaxDelta("Persisted and rebinded 10x", DELTA);
+        
+        for (int i=0; i<1000; i++) {
+            if ((i+1)%100==0) {
+                noteUsedMemory("iteration "+(i+1));
+                usedMemory.removeLast();
+            }
+            forcePersistNow(n1);
+            forceRebindNow(n2);
+        }
+        assertUsedMemoryMaxDelta("Persisted and rebinded 1000x", DELTA);
+        
+        Entities.unmanage(app);
+        forcePersistNow(n1);
+        forceRebindNow(n2);
+        
+        assertUsedMemoryLessThan("And now all unmanaged", initialUsed + DELTA*1000*1000);
+    }
+
+    static class BigObject {
+        public BigObject(int sizeBytes) { array = new byte[sizeBytes]; }
+        byte[] array;
+    }
+    
+    @Test(groups="Integration")
+    public void testHotStandbyDoesNotLeakBigObjects() throws Exception {
+        log.info("Starting test "+JavaClassNames.niceClassAndMethod());
+        final int SIZE = 5;
+        final int SIZE_UP_BOUND = SIZE+2;
+        final int SIZE_DOWN_BOUND = SIZE-1;
+        final int GRACE = 2;
+        // the XML persistence uses a lot of space, we approx at between 2x and 3c
+        final int SIZE_IN_XML = 3*SIZE;
+        final int SIZE_IN_XML_DOWN = 2*SIZE;
+        
+        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
+        TestApplication app = createFirstAppAndPersist(n1);        
+        noteUsedMemory("Finished seeding");
+        Long initialUsed = usedMemory.peekLast();
+        app.config().set(TestEntity.CONF_OBJECT, new BigObject(SIZE*1000*1000));
+        assertUsedMemoryMaxDelta("Set a big config object", SIZE_UP_BOUND);
+        forcePersistNow(n1);
+        assertUsedMemoryMaxDelta("Persisted a big config object", SIZE_IN_XML);
+        
+        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
+        forceRebindNow(n2);
+        assertUsedMemoryMaxDelta("Rebinded", SIZE_UP_BOUND);
+        
+        for (int i=0; i<10; i++)
+            forceRebindNow(n2);
+        assertUsedMemoryMaxDelta("Several more rebinds", GRACE);
+        for (int i=0; i<10; i++) {
+            forcePersistNow(n1);
+            forceRebindNow(n2);
+        }
+        assertUsedMemoryMaxDelta("And more rebinds and more persists", GRACE);
+        
+        app.config().set(TestEntity.CONF_OBJECT, "big is now small");
+        assertUsedMemoryMaxDelta("Big made small at primary", -SIZE_DOWN_BOUND);
+        forcePersistNow(n1);
+        assertUsedMemoryMaxDelta("And persisted", -SIZE_IN_XML_DOWN);
+        
+        forceRebindNow(n2);
+        assertUsedMemoryMaxDelta("And at secondary", -SIZE_DOWN_BOUND);
+        
+        Entities.unmanage(app);
+        forcePersistNow(n1);
+        forceRebindNow(n2);
+        
+        assertUsedMemoryLessThan("And now all unmanaged", initialUsed+GRACE*1000*1000);
+    }
+
+    @Test(groups="Integration") // because it's slow
+    // Sept 2014 - there is a small leak, of 200 bytes per child created and destroyed;
+    // but this goes away when the app is destroyed; it may be a benign record
+    public void testHotStandbyDoesNotLeakLotsOfRebindsCreatingAndDestroyingAChildEntity() throws Exception {
+        log.info("Starting test "+JavaClassNames.niceClassAndMethod());
+        final int DELTA = 2;
+        
+        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
+        TestApplication app = createFirstAppAndPersist(n1);
+        long initialUsed = noteUsedMemory("Created app");
+        
+        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
+        assertUsedMemoryMaxDelta("Standby created", DELTA);
+        
+        TestEntity lastChild = app.addChild(EntitySpec.create(TestEntity.class).configure(TestEntity.CONF_NAME, "first-child"));
+        Entities.manage(lastChild);
+        forcePersistNow(n1);
+        forceRebindNow(n2);
+        assertUsedMemoryMaxDelta("Child created and rebinded once", DELTA);
+        
+        for (int i=0; i<1000; i++) {
+            if (i==9 || (i+1)%100==0) {
+                noteUsedMemory("iteration "+(i+1));
+                usedMemory.removeLast();
+            }
+            TestEntity newChild = app.addChild(EntitySpec.create(TestEntity.class).configure(TestEntity.CONF_NAME, "first-child"));
+            Entities.manage(newChild);
+            Entities.unmanage(lastChild);
+            lastChild = newChild;
+            
+            forcePersistNow(n1);
+            forceRebindNow(n2);
+        }
+        assertUsedMemoryMaxDelta("Persisted and rebinded 1000x", DELTA);
+        
+        Entities.unmanage(app);
+        forcePersistNow(n1);
+        forceRebindNow(n2);
+        
+        assertUsedMemoryLessThan("And now all unmanaged", initialUsed + DELTA*1000*1000);
+    }
+    
+    protected void assertHotStandby(HaMgmtNode n1) {
+        assertEquals(n1.ha.getNodeState(), ManagementNodeState.HOT_STANDBY);
+        Assert.assertTrue(n1.rebinder().isReadOnlyRunning());
+        Assert.assertFalse(n1.rebinder().isPersistenceRunning());
+    }
+
+    protected void assertMaster(HaMgmtNode n1) {
+        assertEquals(n1.ha.getNodeState(), ManagementNodeState.MASTER);
+        Assert.assertFalse(n1.rebinder().isReadOnlyRunning());
+        Assert.assertTrue(n1.rebinder().isPersistenceRunning());
+    }
+
+    @Test
+    public void testChangeMode() throws Exception {
+        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
+        TestApplication app = createFirstAppAndPersist(n1);
+        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
+
+        TestEntity child = app.addChild(EntitySpec.create(TestEntity.class).configure(TestEntity.CONF_NAME, "first-child"));
+        Entities.manage(child);
+        TestApplication app2 = TestApplication.Factory.newManagedInstanceForTests(n1.mgmt);
+        app2.config().set(TestEntity.CONF_NAME, "second-app");
+
+        forcePersistNow(n1);
+        n2.ha.setPriority(1);
+        n1.ha.changeMode(HighAvailabilityMode.HOT_STANDBY);
+        
+        // both now hot standby
+        assertHotStandby(n1);
+        assertHotStandby(n2);
+        
+        assertEquals(n1.mgmt.getApplications().size(), 2);
+        Application app2RO = n1.mgmt.lookup(app2.getId(), Application.class);
+        Assert.assertNotNull(app2RO);
+        assertEquals(app2RO.getConfig(TestEntity.CONF_NAME), "second-app");
+        try {
+            ((TestApplication)app2RO).setAttribute(TestEntity.SEQUENCE, 4);
+            Assert.fail("Should not have allowed sensor to be set");
+        } catch (Exception e) {
+            Assert.assertTrue(e.toString().toLowerCase().contains("read-only"), "Error message did not contain expected text: "+e);
+        }
+
+        n1.ha.changeMode(HighAvailabilityMode.AUTO);
+        n2.ha.changeMode(HighAvailabilityMode.HOT_STANDBY, true, false);
+        // both still hot standby (n1 will defer to n2 as it has higher priority)
+        assertHotStandby(n1);
+        assertHotStandby(n2);
+        
+        // with priority 1, n2 will now be elected
+        n2.ha.changeMode(HighAvailabilityMode.AUTO);
+        assertHotStandby(n1);
+        assertMaster(n2);
+        
+        assertEquals(n2.mgmt.getApplications().size(), 2);
+        Application app2B = n2.mgmt.lookup(app2.getId(), Application.class);
+        Assert.assertNotNull(app2B);
+        assertEquals(app2B.getConfig(TestEntity.CONF_NAME), "second-app");
+        ((TestApplication)app2B).setAttribute(TestEntity.SEQUENCE, 4);
+        
+        forcePersistNow(n2);
+        forceRebindNow(n1);
+        Application app2BRO = n1.mgmt.lookup(app2.getId(), Application.class);
+        Assert.assertNotNull(app2BRO);
+        EntityTestUtils.assertAttributeEquals(app2BRO, TestEntity.SEQUENCE, 4);
+    }
+
+    @Test(groups="Integration", invocationCount=20)
+    public void testChangeModeManyTimes() throws Exception {
+        testChangeMode();
+    }
+
+    @Test
+    public void testChangeModeToDisabledAndBack() throws Exception {
+        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
+        n1.mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachine.class));
+        @SuppressWarnings("unused")
+        TestApplication app = createFirstAppAndPersist(n1);
+        
+        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
+        
+        // disabled n1 allows n2 to become master when next we tell it to check
+        n1.ha.changeMode(HighAvailabilityMode.DISABLED);
+        n2.ha.changeMode(HighAvailabilityMode.AUTO);
+        assertMaster(n2);
+        assertEquals(n1.ha.getNodeState(), ManagementNodeState.FAILED);
+        Assert.assertTrue(n1.mgmt.getApplications().isEmpty(), "n1 should have had no apps; instead had: "+n1.mgmt.getApplications());
+        Assert.assertTrue(n1.mgmt.getEntityManager().getEntities().isEmpty(), "n1 should have had no entities; instead had: "+n1.mgmt.getEntityManager().getEntities());
+        Assert.assertTrue(n1.mgmt.getLocationManager().getLocations().isEmpty(), "n1 should have had no locations; instead had: "+n1.mgmt.getLocationManager().getLocations());
+        
+        // we can now change n1 back to hot_standby
+        n1.ha.changeMode(HighAvailabilityMode.HOT_STANDBY);
+        assertHotStandby(n1);
+        // and it sees apps
+        Assert.assertFalse(n1.mgmt.getApplications().isEmpty(), "n1 should have had apps now");
+        Assert.assertFalse(n1.mgmt.getLocationManager().getLocations().isEmpty(), "n1 should have had locations now");
+        // and if n2 is disabled, n1 promotes
+        n2.ha.changeMode(HighAvailabilityMode.DISABLED);
+        n1.ha.changeMode(HighAvailabilityMode.AUTO);
+        assertMaster(n1);
+        assertEquals(n2.ha.getNodeState(), ManagementNodeState.FAILED);
+    }
+    
+    @Test
+    public void testHotStandbyDoesNotStartFeeds() throws Exception {
+        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
+        TestApplication app = createFirstAppAndPersist(n1);
+        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).impl(MyEntityWithFunctionFeedImpl.class));
+        forcePersistNow(n1);
+        Assert.assertTrue(entity.feeds().getFeeds().size() > 0, "Feeds: "+entity.feeds().getFeeds());
+        for (Feed feed : entity.feeds().getFeeds()) {
+            assertTrue(feed.isRunning(), "Feed expected running, but it is non-running");
+        }
+
+        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
+        TestEntity entityRO = (TestEntity) n2.mgmt.lookup(entity.getId(), Entity.class);
+        Assert.assertTrue(entityRO.feeds().getFeeds().size() > 0, "Feeds: "+entity.feeds().getFeeds());
+        for (Feed feedRO : entityRO.feeds().getFeeds()) {
+            assertFalse(feedRO.isRunning(), "Feed expected non-active, but it is running");
+        }
+    }
+    
+    @Test(groups="Integration")
+    public void testHotStandbyDoesNotStartFeedsRebindingManyTimes() throws Exception {
+        testHotStandbyDoesNotStartFeeds();
+        final HaMgmtNode hsb = createHotStandby(Duration.millis(10));
+        Repeater.create("until 10 rebinds").every(Duration.millis(100)).until(
+            new Callable<Boolean>() {
+                @Override
+                public Boolean call() throws Exception {
+                    return ((RebindManagerImpl)hsb.mgmt.getRebindManager()).getReadOnlyRebindCount() >= 10;
+                }
+            }).runRequiringTrue();
+        // make sure not too many tasks (allowing 5 for rebind etc; currently just 2)
+        RebindTestFixture.waitForTaskCountToBecome(hsb.mgmt, 5);
+    }
+
+    @Test(groups="Integration")
+    public void testHotStandbyDoesNotStartFeedsRebindingManyTimesWithAnotherFeedGenerator() throws Exception {
+        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
+        TestApplication app = createFirstAppAndPersist(n1);
+        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).impl(MyEntityWithNewFeedsEachTimeImpl.class));
+        forcePersistNow(n1);
+        Assert.assertTrue(entity.feeds().getFeeds().size() == 4, "Feeds: "+entity.feeds().getFeeds());
+        
+        final HaMgmtNode hsb = createHotStandby(Duration.millis(10));
+        Repeater.create("until 10 rebinds").every(Duration.millis(100)).until(
+            new Callable<Boolean>() {
+                @Override
+                public Boolean call() throws Exception {
+                    return ((RebindManagerImpl)hsb.mgmt.getRebindManager()).getReadOnlyRebindCount() >= 10;
+                }
+            }).runRequiringTrue();
+        // make sure not too many tasks (allowing 5 for rebind etc; currently just 2)
+        RebindTestFixture.waitForTaskCountToBecome(hsb.mgmt, 5);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/ImmutableManagementPlaneSyncRecord.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/ImmutableManagementPlaneSyncRecord.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/ImmutableManagementPlaneSyncRecord.java
new file mode 100644
index 0000000..2f08057
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/ImmutableManagementPlaneSyncRecord.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+
+public class ImmutableManagementPlaneSyncRecord implements ManagementPlaneSyncRecord {
+    private final String masterNodeId;
+    private final Map<String, ManagementNodeSyncRecord> managementNodes;
+
+    ImmutableManagementPlaneSyncRecord(String masterNodeId, Map<String, ManagementNodeSyncRecord> nodes) {
+        this.masterNodeId = masterNodeId;
+        this.managementNodes = ImmutableMap.copyOf(nodes);
+    }
+    
+    @Override
+    public String getMasterNodeId() {
+        return masterNodeId;
+    }
+
+    @Override
+    public Map<String, ManagementNodeSyncRecord> getManagementNodes() {
+        return managementNodes;
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this).add("master", masterNodeId).add("nodes", managementNodes.keySet()).toString();
+    }
+    
+    @Override
+    public String toVerboseString() {
+        return Objects.toStringHelper(this).add("master", masterNodeId).add("nodes", managementNodes).toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordPersisterInMemory.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordPersisterInMemory.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordPersisterInMemory.java
new file mode 100644
index 0000000..5791c88
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/ManagementPlaneSyncRecordPersisterInMemory.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.management.ha;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
+import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.rebind.persister.InMemoryObjectStore;
+import brooklyn.util.time.Duration;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/** @deprecated since 0.7.0 use {@link ManagementPlaneSyncRecordPersisterToObjectStore}
+ * with {@link InMemoryObjectStore}
+ * <code>
+ * new ManagementPlaneSyncRecordPersisterToObjectStore(new InMemoryObjectStore(), classLoader)
+ * </code> */
+@Deprecated
+public class ManagementPlaneSyncRecordPersisterInMemory implements ManagementPlaneSyncRecordPersister {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ManagementPlaneSyncRecordPersisterInMemory.class);
+
+    private final MutableManagementPlaneSyncRecord memento = new MutableManagementPlaneSyncRecord();
+    
+    private volatile boolean running = true;
+    
+    @Override
+    public synchronized void stop() {
+        running = false;
+    }
+
+    @Override
+    public ManagementPlaneSyncRecord loadSyncRecord() throws IOException {
+        if (!running) {
+            throw new IllegalStateException("Persister not running; cannot load memento");
+        }
+        
+        return memento.snapshot();
+    }
+    
+    @VisibleForTesting
+    @Override
+    public synchronized void waitForWritesCompleted(Duration timeout) throws InterruptedException, TimeoutException {
+        // The synchronized is sufficient - guarantee that no concurrent calls
+        return;
+    }
+
+    @Override
+    public synchronized void delta(Delta delta) {
+        if (!running) {
+            if (LOG.isDebugEnabled()) LOG.debug("Persister not running; ignoring checkpointed delta of manager-memento");
+            return;
+        }
+        
+        for (ManagementNodeSyncRecord m : delta.getNodes()) {
+            memento.addNode(m);
+        }
+        for (String id : delta.getRemovedNodeIds()) {
+            memento.deleteNode(id);
+        }
+        switch (delta.getMasterChange()) {
+        case NO_CHANGE:
+            break; // no-op
+        case SET_MASTER:
+            memento.setMasterNodeId(checkNotNull(delta.getNewMasterOrNull()));
+            break;
+        case CLEAR_MASTER:
+            memento.setMasterNodeId(null);
+            break; // no-op
+        default:
+            throw new IllegalStateException("Unknown state for master-change: "+delta.getMasterChange());
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/MasterChooserTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/MasterChooserTest.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/MasterChooserTest.java
new file mode 100644
index 0000000..8958c4b
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/MasterChooserTest.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
+import org.apache.brooklyn.core.management.ha.BasicMasterChooser.AlphabeticMasterChooser;
+import org.apache.brooklyn.core.management.ha.BasicMasterChooser.ScoredRecord;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.BrooklynVersion;
+import brooklyn.entity.basic.EntityFunctions;
+import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord;
+import brooklyn.util.time.Duration;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class MasterChooserTest {
+
+    private MutableManagementPlaneSyncRecord memento;
+    private AlphabeticMasterChooser chooser;
+    private long now;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        memento = new MutableManagementPlaneSyncRecord();
+        chooser = new AlphabeticMasterChooser();
+        now = System.currentTimeMillis();
+    }
+    
+    @Test
+    public void testChoosesFirstAlphanumeric() throws Exception {
+        memento.addNode(newManagerMemento("node1",  ManagementNodeState.STANDBY, now));
+        memento.addNode(newManagerMemento("node2",  ManagementNodeState.STANDBY, now));
+        memento.addNode(newManagerMemento("node3",  ManagementNodeState.STANDBY, now));
+        Duration heartbeatTimeout = Duration.THIRTY_SECONDS;
+        String ownNodeId = "node2";
+        assertEquals(chooser.choose(memento, heartbeatTimeout, ownNodeId).getNodeId(), "node1");
+    }
+    
+    @Test
+    public void testReturnsNullIfNoValid() throws Exception {
+        memento.addNode(newManagerMemento("node1", ManagementNodeState.STANDBY, now - 31*1000));
+        Duration heartbeatTimeout = Duration.THIRTY_SECONDS;
+        assertNull(chooser.choose(memento, heartbeatTimeout, "node2"));
+    }
+    
+    @Test
+    public void testFiltersOutByHeartbeat() throws Exception {
+        memento.addNode(newManagerMemento("node1", ManagementNodeState.STANDBY, now - 31*1000));
+        memento.addNode(newManagerMemento("node2", ManagementNodeState.STANDBY, now - 20*1000));
+        memento.addNode(newManagerMemento("node3", ManagementNodeState.STANDBY, now));
+        Duration heartbeatTimeout = Duration.THIRTY_SECONDS;
+        assertEquals(getIds(chooser.sort(chooser.filterHealthy(memento, heartbeatTimeout, now))), ImmutableList.of("node2", "node3"));
+    }
+    
+    protected static List<String> getIds(List<ScoredRecord<?>> filterHealthy) {
+        return ImmutableList.copyOf(Iterables.transform(filterHealthy, EntityFunctions.id()));
+    }
+
+    @Test
+    public void testFiltersOutByStatusNotPreferringMaster() throws Exception {
+        assertEquals(doTestFiltersOutByStatus(false, false), ImmutableList.of("node4", "node5"));
+    }
+    @Test
+    public void testFiltersOutByStatusPreferringMaster() throws Exception {
+        assertEquals(doTestFiltersOutByStatus(true, false), ImmutableList.of("node5", "node4"));
+    }
+    
+    @Test
+    public void testFiltersOutByStatusNotPreferringHot() throws Exception {
+        assertEquals(doTestFiltersOutByStatus(false, true), ImmutableList.of("node4", "node5", "node6"));
+    }
+    @Test
+    public void testFiltersOutByStatusPreferringHot() throws Exception {
+        assertEquals(doTestFiltersOutByStatus(true, true), ImmutableList.of("node5", "node6", "node4"));
+    }
+    
+    protected List<String> doTestFiltersOutByStatus(boolean preferHot, boolean includeHot) throws Exception {
+        chooser = new AlphabeticMasterChooser(preferHot);
+        memento.addNode(newManagerMemento("node1", ManagementNodeState.FAILED, now));
+        memento.addNode(newManagerMemento("node2", ManagementNodeState.TERMINATED, now));
+        memento.addNode(newManagerMemento("node3", null, now));
+        memento.addNode(newManagerMemento("node4",  ManagementNodeState.STANDBY, now));
+        memento.addNode(newManagerMemento("node5", ManagementNodeState.MASTER, now));
+        if (includeHot)
+            memento.addNode(newManagerMemento("node6",  ManagementNodeState.HOT_STANDBY, now));
+        return getIds(chooser.sort(chooser.filterHealthy(memento, Duration.THIRTY_SECONDS, now)));
+    }
+
+    @Test
+    public void testExplicityPriority() throws Exception {
+        chooser = new AlphabeticMasterChooser();
+        memento.addNode(newManagerMemento("node1", ManagementNodeState.STANDBY, now, BrooklynVersion.get(), 2L));
+        memento.addNode(newManagerMemento("node2", ManagementNodeState.STANDBY, now, BrooklynVersion.get(), -1L));
+        memento.addNode(newManagerMemento("node3", ManagementNodeState.STANDBY, now, BrooklynVersion.get(), null));
+        List<String> order = getIds(chooser.sort(chooser.filterHealthy(memento, Duration.THIRTY_SECONDS, now)));
+        assertEquals(order, ImmutableList.of("node1", "node3", "node2"));
+    }
+
+    @Test
+    public void testVersionsMaybeNull() throws Exception {
+        chooser = new AlphabeticMasterChooser();
+        memento.addNode(newManagerMemento("node1", ManagementNodeState.STANDBY, now, "v10", null));
+        memento.addNode(newManagerMemento("node2", ManagementNodeState.STANDBY, now, "v3-snapshot", null));
+        memento.addNode(newManagerMemento("node3", ManagementNodeState.STANDBY, now, "v3-snapshot", -1L));
+        memento.addNode(newManagerMemento("node4", ManagementNodeState.STANDBY, now, "v3-snapshot", null));
+        memento.addNode(newManagerMemento("node5", ManagementNodeState.STANDBY, now, "v3-stable", null));
+        memento.addNode(newManagerMemento("node6", ManagementNodeState.STANDBY, now, "v1", null));
+        memento.addNode(newManagerMemento("node7", ManagementNodeState.STANDBY, now, null, null));
+        List<String> order = getIds(chooser.sort(chooser.filterHealthy(memento, Duration.THIRTY_SECONDS, now)));
+        assertEquals(order, ImmutableList.of("node1", "node5", "node6", "node2", "node4", "node7", "node3"));
+    }
+
+    private ManagementNodeSyncRecord newManagerMemento(String nodeId, ManagementNodeState status, long timestamp) {
+        return newManagerMemento(nodeId, status, timestamp, BrooklynVersion.get(), null);
+    }
+    private ManagementNodeSyncRecord newManagerMemento(String nodeId, ManagementNodeState status, long timestamp,
+            String version, Long priority) {
+        return BasicManagementNodeSyncRecord.builder().brooklynVersion(version).nodeId(nodeId).status(status).localTimestamp(timestamp).remoteTimestamp(timestamp).
+            priority(priority).build();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/MutableManagementPlaneSyncRecord.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/MutableManagementPlaneSyncRecord.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/MutableManagementPlaneSyncRecord.java
new file mode 100644
index 0000000..d9c8943
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/MutableManagementPlaneSyncRecord.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
+
+import com.google.common.collect.Maps;
+
+public class MutableManagementPlaneSyncRecord implements ManagementPlaneSyncRecord {
+    private String masterNodeId;
+    private Map<String, ManagementNodeSyncRecord> managementNodes = Maps.newConcurrentMap();
+
+    @Override
+    public String getMasterNodeId() {
+        return masterNodeId;
+    }
+
+    @Override
+    public Map<String, ManagementNodeSyncRecord> getManagementNodes() {
+        return managementNodes;
+    }
+
+    @Override
+    public String toVerboseString() {
+        return toString();
+    }
+
+    public ImmutableManagementPlaneSyncRecord snapshot() {
+        return new ImmutableManagementPlaneSyncRecord(masterNodeId, managementNodes);
+    }
+    
+    public void setMasterNodeId(String masterNodeId) {
+        this.masterNodeId = masterNodeId;
+    }
+    
+    public void addNode(ManagementNodeSyncRecord memento) {
+        managementNodes.put(memento.getNodeId(), memento);
+    }
+    
+    public void deleteNode(String nodeId) {
+        managementNodes.remove(nodeId);
+    }
+}
\ No newline at end of file


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

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


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

Branch: refs/heads/master
Commit: 6caee5899a7e6f26095caeda6518cbaaa4abd271
Parents: 9cf456d
Author: Hadrian Zbarcea <ha...@apache.org>
Authored: Sat Aug 15 00:17:33 2015 -0400
Committer: Hadrian Zbarcea <ha...@apache.org>
Committed: Sat Aug 15 00:17:33 2015 -0400

----------------------------------------------------------------------
 .../java/brooklyn/catalog/CatalogLoadMode.java  |   73 -
 .../brooklyn/catalog/CatalogPredicates.java     |  143 --
 .../catalog/internal/BasicBrooklynCatalog.java  | 1315 ------------------
 .../internal/CatalogBundleConverter.java        |   63 -
 .../catalog/internal/CatalogBundleDto.java      |   93 --
 .../catalog/internal/CatalogClasspathDo.java    |  334 -----
 .../catalog/internal/CatalogClasspathDto.java   |   43 -
 .../brooklyn/catalog/internal/CatalogDo.java    |  365 -----
 .../brooklyn/catalog/internal/CatalogDto.java   |  230 ---
 .../catalog/internal/CatalogDtoUtils.java       |   67 -
 .../catalog/internal/CatalogEntityItemDto.java  |   43 -
 .../catalog/internal/CatalogInitialization.java |  449 ------
 .../catalog/internal/CatalogItemBuilder.java    |  128 --
 .../catalog/internal/CatalogItemComparator.java |  143 --
 .../catalog/internal/CatalogItemDo.java         |  193 ---
 .../internal/CatalogItemDtoAbstract.java        |  404 ------
 .../catalog/internal/CatalogLibrariesDo.java    |   42 -
 .../catalog/internal/CatalogLibrariesDto.java   |   53 -
 .../internal/CatalogLocationItemDto.java        |   43 -
 .../catalog/internal/CatalogPolicyItemDto.java  |   43 -
 .../internal/CatalogTemplateItemDto.java        |   42 -
 .../brooklyn/catalog/internal/CatalogUtils.java |  254 ----
 .../catalog/internal/CatalogXmlSerializer.java  |   73 -
 .../brooklyn/config/BrooklynServerConfig.java   |    4 +-
 .../brooklyn/entity/basic/AbstractEntity.java   |    2 +-
 .../rebind/BasicCatalogItemRebindSupport.java   |    2 +-
 .../rebind/RebindContextLookupContext.java      |    3 +-
 .../brooklyn/entity/rebind/RebindIteration.java |    4 +-
 .../rebind/dto/BasicCatalogItemMemento.java     |    5 +-
 .../entity/rebind/dto/MementosGenerators.java   |    2 +-
 .../BrooklynMementoPersisterToObjectStore.java  |    2 +-
 .../CatalogItemLibrariesConverter.java          |    3 +-
 .../rebind/persister/XmlMementoSerializer.java  |    4 +-
 .../main/java/brooklyn/util/ResourceUtils.java  |    6 +-
 .../brooklyn/core/catalog/CatalogLoadMode.java  |   73 +
 .../core/catalog/CatalogPredicates.java         |  142 ++
 .../catalog/internal/BasicBrooklynCatalog.java  | 1315 ++++++++++++++++++
 .../internal/CatalogBundleConverter.java        |   63 +
 .../core/catalog/internal/CatalogBundleDto.java |   93 ++
 .../catalog/internal/CatalogClasspathDo.java    |  334 +++++
 .../catalog/internal/CatalogClasspathDto.java   |   43 +
 .../core/catalog/internal/CatalogDo.java        |  365 +++++
 .../core/catalog/internal/CatalogDto.java       |  230 +++
 .../core/catalog/internal/CatalogDtoUtils.java  |   67 +
 .../catalog/internal/CatalogEntityItemDto.java  |   43 +
 .../catalog/internal/CatalogInitialization.java |  449 ++++++
 .../catalog/internal/CatalogItemBuilder.java    |  128 ++
 .../catalog/internal/CatalogItemComparator.java |  143 ++
 .../core/catalog/internal/CatalogItemDo.java    |  193 +++
 .../internal/CatalogItemDtoAbstract.java        |  404 ++++++
 .../catalog/internal/CatalogLibrariesDo.java    |   42 +
 .../catalog/internal/CatalogLibrariesDto.java   |   53 +
 .../internal/CatalogLocationItemDto.java        |   43 +
 .../catalog/internal/CatalogPolicyItemDto.java  |   43 +
 .../internal/CatalogTemplateItemDto.java        |   42 +
 .../core/catalog/internal/CatalogUtils.java     |  254 ++++
 .../catalog/internal/CatalogXmlSerializer.java  |   74 +
 .../OsgiBrooklynClassLoadingContext.java        |    2 +-
 .../ha/HighAvailabilityManagerImpl.java         |    4 +-
 .../internal/AbstractManagementContext.java     |    6 +-
 .../internal/ManagementContextInternal.java     |    2 +-
 .../NonDeploymentManagementContext.java         |    2 +-
 .../location/basic/BasicLocationRegistry.java   |    2 +-
 .../location/basic/CatalogLocationResolver.java |    3 +-
 .../test/java/brooklyn/BrooklynVersionTest.java |    7 +-
 .../brooklyn/camp/lite/CampYamlLiteTest.java    |    8 +-
 .../catalog/internal/CatalogDtoTest.java        |  149 --
 .../internal/CatalogItemComparatorTest.java     |   80 --
 .../catalog/internal/CatalogLoadTest.java       |   77 -
 .../catalog/internal/CatalogScanTest.java       |  199 ---
 .../catalog/internal/CatalogTestUtils.java      |   46 -
 .../catalog/internal/CatalogVersioningTest.java |  156 ---
 .../catalog/internal/MyCatalogItems.java        |   37 -
 .../rebind/ActivePartialRebindVersionTest.java  |    2 +-
 .../entity/rebind/RebindCatalogEntityTest.java  |    2 +-
 .../entity/rebind/RebindCatalogItemTest.java    |    6 +-
 .../entity/rebind/RebindTestFixture.java        |    2 +-
 .../persister/XmlMementoSerializerTest.java     |    6 +-
 .../core/catalog/internal/CatalogDtoTest.java   |  158 +++
 .../internal/CatalogItemComparatorTest.java     |   82 ++
 .../core/catalog/internal/CatalogLoadTest.java  |   80 ++
 .../core/catalog/internal/CatalogScanTest.java  |  200 +++
 .../core/catalog/internal/CatalogTestUtils.java |   47 +
 .../catalog/internal/CatalogVersioningTest.java |  159 +++
 .../core/catalog/internal/MyCatalogItems.java   |   37 +
 .../osgi/OsgiVersionMoreEntityTest.java         |   10 +-
 .../BrooklynAssemblyTemplateInstantiator.java   |    4 +-
 .../BrooklynComponentTemplateResolver.java      |    2 +-
 .../BrooklynEntityDecorationResolver.java       |    2 +-
 .../spi/creation/BrooklynEntityMatcher.java     |    2 +-
 .../service/BrooklynServiceTypeResolver.java    |    2 +-
 .../lookup/AbstractTemplateBrooklynLookup.java  |    3 +-
 .../lookup/AssemblyTemplateBrooklynLookup.java  |    3 +-
 ...PlatformComponentTemplateBrooklynLookup.java |    3 +-
 .../camp/brooklyn/AbstractYamlRebindTest.java   |    2 +-
 .../camp/brooklyn/AbstractYamlTest.java         |    2 +-
 .../CatalogOsgiVersionMoreEntityTest.java       |    4 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java |    2 +-
 .../catalog/CatalogYamlLocationTest.java        |    2 +-
 .../brooklyn/catalog/CatalogYamlPolicyTest.java |    2 +-
 .../brooklyn/catalog/CatalogYamlRebindTest.java |    2 +-
 .../catalog/CatalogYamlVersioningTest.java      |    4 +-
 .../main/java/org/apache/brooklyn/cli/Main.java |    4 +-
 .../brooklyn/launcher/BrooklynLauncher.java     |    2 +-
 .../BrooklynLauncherRebindCatalogTest.java      |    2 +-
 .../brooklyn/launcher/BrooklynLauncherTest.java |    2 +-
 .../rest/resources/ApplicationResource.java     |    2 +-
 .../rest/resources/CatalogResource.java         |   12 +-
 .../rest/resources/LocationResource.java        |    4 +-
 .../rest/util/BrooklynRestResourceUtils.java    |    2 +-
 .../brooklyn/rest/util/WebResourceUtils.java    |    4 +-
 .../rest/resources/CatalogResourceTest.java     |    2 +-
 .../util/BrooklynRestResourceUtilsTest.java     |    6 +-
 113 files changed, 5482 insertions(+), 5481 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java b/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java
deleted file mode 100644
index 6341a10..0000000
--- a/core/src/main/java/brooklyn/catalog/CatalogLoadMode.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.catalog;
-
-import org.slf4j.LoggerFactory;
-
-import brooklyn.catalog.internal.CatalogInitialization;
-import brooklyn.entity.rebind.persister.PersistMode;
-
-/** @deprecated since 0.7.0 replaced by {@link CatalogInitialization} */
-@Deprecated
-public enum CatalogLoadMode {
-    /**
-     * The server will load its initial catalog from the URL configured in
-     * {@link brooklyn.config.BrooklynServerConfig#BROOKLYN_CATALOG_URL} and will
-     * disregard existing persisted state.
-     */
-    LOAD_BROOKLYN_CATALOG_URL,
-
-    /**
-     * The server will load its initial catalog from previously persisted state,
-     * and will behave as {@link #LOAD_BROOKLYN_CATALOG_URL} if no state exists.
-     */
-    LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE,
-
-    /**
-     * The server will load its initial catalog from previously persisted state.
-     * The catalog will be empty if no previous state exists.
-     */
-    LOAD_PERSISTED_STATE;
-
-    /**
-     * @return A catalog load mode suitable for the given persistence mode:
-     * <ul>
-     * <li>disabled: {@link #LOAD_BROOKLYN_CATALOG_URL}</li>
-     * <li>rebind: {@link #LOAD_PERSISTED_STATE}</li>
-     * <li>auto or clean: {@link #LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE}</li>
-     * </ul>
-     */
-    public static CatalogLoadMode forPersistMode(PersistMode m) {
-        // Clean case relies on the persistence directory being cleaned and rebind manager
-        // believing the store to be empty.
-        switch (m) {
-        case DISABLED:
-            return LOAD_BROOKLYN_CATALOG_URL;
-        case AUTO:
-        case CLEAN:
-            return LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE;
-        case REBIND:
-            return LOAD_PERSISTED_STATE;
-        default:
-            LoggerFactory.getLogger(CatalogLoadMode.class)
-                    .warn("Unhandled persistence mode {}. Catalog defaulting to {}", m.name(), LOAD_BROOKLYN_CATALOG_URL.name());
-            return LOAD_BROOKLYN_CATALOG_URL;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/CatalogPredicates.java b/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
deleted file mode 100644
index 07dcbe0..0000000
--- a/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
+++ /dev/null
@@ -1,143 +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.catalog;
-
-import javax.annotation.Nullable;
-
-import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
-import org.apache.brooklyn.api.entity.Application;
-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.api.location.LocationSpec;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.policy.Policy;
-import org.apache.brooklyn.api.policy.PolicySpec;
-import org.apache.brooklyn.core.management.entitlement.Entitlements;
-
-import brooklyn.catalog.internal.CatalogUtils;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-
-public class CatalogPredicates {
-
-    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> isCatalogItemType(final CatalogItemType ciType) {
-        return new Predicate<CatalogItem<T,SpecT>>() {
-            @Override
-            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
-                return (item != null) && item.getCatalogItemType()==ciType;
-            }
-        };
-    }
-
-    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> deprecated(final boolean deprecated) {
-        return new Predicate<CatalogItem<T,SpecT>>() {
-            @Override
-            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
-                return (item != null) && item.isDeprecated() == deprecated;
-            }
-        };
-    }
-
-    public static final Predicate<CatalogItem<Application,EntitySpec<? extends Application>>> IS_TEMPLATE = 
-            CatalogPredicates.<Application,EntitySpec<? extends Application>>isCatalogItemType(CatalogItemType.TEMPLATE);
-    public static final Predicate<CatalogItem<Entity,EntitySpec<?>>> IS_ENTITY = 
-            CatalogPredicates.<Entity,EntitySpec<?>>isCatalogItemType(CatalogItemType.ENTITY);
-    public static final Predicate<CatalogItem<Policy,PolicySpec<?>>> IS_POLICY = 
-            CatalogPredicates.<Policy,PolicySpec<?>>isCatalogItemType(CatalogItemType.POLICY);
-    public static final Predicate<CatalogItem<Location,LocationSpec<?>>> IS_LOCATION = 
-            CatalogPredicates.<Location,LocationSpec<?>>isCatalogItemType(CatalogItemType.LOCATION);
-    
-    public static final Function<CatalogItem<?,?>,String> ID_OF_ITEM_TRANSFORMER = new Function<CatalogItem<?,?>, String>() {
-        @Override @Nullable
-        public String apply(@Nullable CatalogItem<?,?> input) {
-            if (input==null) return null;
-            return input.getId();
-        }
-    };
-
-    /** @deprecated since 0.7.0 use {@link #displayName(Predicate)} */
-    @Deprecated
-    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> name(final Predicate<? super String> filter) {
-        return displayName(filter);
-    }
-
-    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> displayName(final Predicate<? super String> filter) {
-        return new Predicate<CatalogItem<T,SpecT>>() {
-            @Override
-            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
-                return (item != null) && filter.apply(item.getDisplayName());
-            }
-        };
-    }
-
-    @Deprecated
-    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> registeredTypeName(final Predicate<? super String> filter) {
-        return symbolicName(filter);
-    }
-
-    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> symbolicName(final Predicate<? super String> filter) {
-        return new Predicate<CatalogItem<T,SpecT>>() {
-            @Override
-            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
-                return (item != null) && filter.apply(item.getSymbolicName());
-            }
-        };
-    }
-
-    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> javaType(final Predicate<? super String> filter) {
-        return new Predicate<CatalogItem<T,SpecT>>() {
-            @Override
-            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
-                return (item != null) && filter.apply(item.getJavaType());
-            }
-        };
-    }
-
-    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> xml(final Predicate<? super String> filter) {
-        return new Predicate<CatalogItem<T,SpecT>>() {
-            @Override
-            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
-                return (item != null) && filter.apply(item.toXmlString());
-            }
-        };
-    }
-
-    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> entitledToSee(final ManagementContext mgmt) {
-        return new Predicate<CatalogItem<T,SpecT>>() {
-            @Override
-            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
-                return (item != null) && 
-                    Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, item.getCatalogItemId());
-            }
-        };
-    }
- 
-    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> isBestVersion(final ManagementContext mgmt) {
-        return new Predicate<CatalogItem<T,SpecT>>() {
-            @Override
-            public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
-                return CatalogUtils.isBestVersion(mgmt, item);
-            }
-        };
-    }
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
deleted file mode 100644
index 88474e7..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ /dev/null
@@ -1,1315 +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.catalog.internal;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import io.brooklyn.camp.CampPlatform;
-import io.brooklyn.camp.spi.AssemblyTemplate;
-import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
-import io.brooklyn.camp.spi.pdp.DeploymentPlan;
-
-import java.lang.reflect.Method;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-
-import javax.annotation.Nullable;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.yaml.snakeyaml.Yaml;
-
-import brooklyn.basic.BrooklynObjectInternal.ConfigurationSupportInternal;
-import brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator;
-
-import org.apache.brooklyn.api.basic.AbstractBrooklynObjectSpec;
-import org.apache.brooklyn.api.catalog.BrooklynCatalog;
-import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.location.LocationSpec;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
-import org.apache.brooklyn.api.policy.Policy;
-import org.apache.brooklyn.api.policy.PolicySpec;
-import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
-
-import brooklyn.catalog.CatalogPredicates;
-import brooklyn.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
-import brooklyn.config.BrooklynServerConfig;
-
-import org.apache.brooklyn.location.basic.BasicLocationRegistry;
-
-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;
-import brooklyn.util.javalang.Reflections;
-import brooklyn.util.stream.Streams;
-import brooklyn.util.text.Strings;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-import brooklyn.util.yaml.Yamls;
-import brooklyn.util.yaml.Yamls.YamlExtract;
-
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.Iterables;
-
-/* TODO the complex tree-structured catalogs are only useful when we are relying on those separate catalog classloaders
- * to isolate classpaths. with osgi everything is just put into the "manual additions" catalog. */
-public class BasicBrooklynCatalog implements BrooklynCatalog {
-    private static final String POLICIES_KEY = "brooklyn.policies";
-    private static final String LOCATIONS_KEY = "brooklyn.locations";
-    public static final String NO_VERSION = "0.0.0.SNAPSHOT";
-
-    private static final Logger log = LoggerFactory.getLogger(BasicBrooklynCatalog.class);
-
-    public static class BrooklynLoaderTracker {
-        public static final ThreadLocal<BrooklynClassLoadingContext> loader = new ThreadLocal<BrooklynClassLoadingContext>();
-        
-        public static void setLoader(BrooklynClassLoadingContext val) {
-            loader.set(val);
-        }
-        
-        // TODO Stack, for recursive calls?
-        public static void unsetLoader(BrooklynClassLoadingContext val) {
-            loader.set(null);
-        }
-        
-        public static BrooklynClassLoadingContext getLoader() {
-            return loader.get();
-        }
-    }
-
-    private final ManagementContext mgmt;
-    private CatalogDo catalog;
-    private volatile CatalogDo manualAdditionsCatalog;
-    private volatile LoadedClassLoader manualAdditionsClasses;
-    private final AggregateClassLoader rootClassLoader = AggregateClassLoader.newInstanceWithNoLoaders();
-
-    public BasicBrooklynCatalog(ManagementContext mgmt) {
-        this(mgmt, CatalogDto.newNamedInstance("empty catalog", "empty catalog", "empty catalog, expected to be reset later"));
-    }
-
-    public BasicBrooklynCatalog(ManagementContext mgmt, CatalogDto dto) {
-        this.mgmt = checkNotNull(mgmt, "managementContext");
-        this.catalog = new CatalogDo(mgmt, dto);
-    }
-
-    public boolean blockIfNotLoaded(Duration timeout) {
-        try {
-            return getCatalog().blockIfNotLoaded(timeout);
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-    
-    public void reset(CatalogDto dto) {
-        reset(dto, true);
-    }
-
-    public void reset(CatalogDto dto, boolean failOnLoadError) {
-        // Unregister all existing persisted items.
-        for (CatalogItem<?, ?> toRemove : getCatalogItems()) {
-            if (log.isTraceEnabled()) {
-                log.trace("Scheduling item for persistence removal: {}", toRemove.getId());
-            }
-            mgmt.getRebindManager().getChangeListener().onUnmanaged(toRemove);
-        }
-        CatalogDo catalog = new CatalogDo(mgmt, dto);
-        CatalogUtils.logDebugOrTraceIfRebinding(log, "Resetting "+this+" catalog to "+dto);
-        catalog.load(mgmt, null, failOnLoadError);
-        CatalogUtils.logDebugOrTraceIfRebinding(log, "Reloaded catalog for "+this+", now switching");
-        this.catalog = catalog;
-        resetRootClassLoader();
-        this.manualAdditionsCatalog = null;
-
-        // Inject management context into and persist all the new entries.
-        for (CatalogItem<?, ?> entry : getCatalogItems()) {
-            boolean setManagementContext = false;
-            if (entry instanceof CatalogItemDo) {
-                CatalogItemDo<?, ?> cid = CatalogItemDo.class.cast(entry);
-                if (cid.getDto() instanceof CatalogItemDtoAbstract) {
-                    CatalogItemDtoAbstract<?, ?> cdto = CatalogItemDtoAbstract.class.cast(cid.getDto());
-                    if (cdto.getManagementContext() == null) {
-                        cdto.setManagementContext((ManagementContextInternal) mgmt);
-                    }
-                    setManagementContext = true;
-                }
-            }
-            if (!setManagementContext) {
-                log.warn("Can't set management context on entry with unexpected type in catalog. type={}, " +
-                        "expected={}", entry, CatalogItemDo.class);
-            }
-            if (log.isTraceEnabled()) {
-                log.trace("Scheduling item for persistence addition: {}", entry.getId());
-            }
-            mgmt.getRebindManager().getChangeListener().onManaged(entry);
-        }
-
-    }
-
-    /**
-     * Resets the catalog to the given entries
-     */
-    @Override
-    public void reset(Collection<CatalogItem<?, ?>> entries) {
-        CatalogDto newDto = CatalogDto.newDtoFromCatalogItems(entries, "explicit-catalog-reset");
-        reset(newDto);
-    }
-    
-    public CatalogDo getCatalog() {
-        return catalog;
-    }
-
-    protected CatalogItemDo<?,?> getCatalogItemDo(String symbolicName, String version) {
-        String fixedVersionId = getFixedVersionId(symbolicName, version);
-        if (fixedVersionId == null) {
-            //no items with symbolicName exist
-            return null;
-        }
-
-        return catalog.getIdCache().get( CatalogUtils.getVersionedId(symbolicName, fixedVersionId) );
-    }
-    
-    private String getFixedVersionId(String symbolicName, String version) {
-        if (!DEFAULT_VERSION.equals(version)) {
-            return version;
-        } else {
-            return getDefaultVersion(symbolicName);
-        }
-    }
-
-    private String getDefaultVersion(String symbolicName) {
-        Iterable<CatalogItem<Object, Object>> versions = getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName)));
-        Collection<CatalogItem<Object, Object>> orderedVersions = sortVersionsDesc(versions);
-        if (!orderedVersions.isEmpty()) {
-            return orderedVersions.iterator().next().getVersion();
-        } else {
-            return null;
-        }
-    }
-
-    private <T,SpecT> Collection<CatalogItem<T,SpecT>> sortVersionsDesc(Iterable<CatalogItem<T,SpecT>> versions) {
-        return ImmutableSortedSet.orderedBy(CatalogItemComparator.<T,SpecT>getInstance()).addAll(versions).build();
-    }
-
-    @Override
-    @Deprecated
-    public CatalogItem<?,?> getCatalogItem(String symbolicName) {
-        return getCatalogItem(symbolicName, DEFAULT_VERSION);
-    }
-    
-    @Override
-    public CatalogItem<?,?> getCatalogItem(String symbolicName, String version) {
-        if (symbolicName == null) return null;
-        checkNotNull(version, "version");
-        CatalogItemDo<?, ?> itemDo = getCatalogItemDo(symbolicName, version);
-        if (itemDo == null) return null;
-        return itemDo.getDto();
-    }
-    
-    @Override
-    @Deprecated
-    public void deleteCatalogItem(String id) {
-        //Delete only if installed through the
-        //deprecated methods. Don't support DEFAULT_VERSION for delete.
-        deleteCatalogItem(id, NO_VERSION);
-    }
-
-    @Override
-    public void deleteCatalogItem(String symbolicName, String version) {
-        log.debug("Deleting manual catalog item from "+mgmt+": "+symbolicName + ":" + version);
-        checkNotNull(symbolicName, "id");
-        checkNotNull(version, "version");
-        if (DEFAULT_VERSION.equals(version)) {
-            throw new IllegalStateException("Deleting items with unspecified version (argument DEFAULT_VERSION) not supported.");
-        }
-        CatalogItem<?, ?> item = getCatalogItem(symbolicName, version);
-        CatalogItemDtoAbstract<?,?> itemDto = getAbstractCatalogItem(item);
-        if (itemDto == null) {
-            throw new NoSuchElementException("No catalog item found with id "+symbolicName);
-        }
-        if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
-        manualAdditionsCatalog.deleteEntry(itemDto);
-        
-        // Ensure the cache is de-populated
-        getCatalog().deleteEntry(itemDto);
-
-        // And indicate to the management context that it should be removed.
-        if (log.isTraceEnabled()) {
-            log.trace("Scheduling item for persistence removal: {}", itemDto.getId());
-        }
-        if (itemDto.getCatalogItemType() == CatalogItemType.LOCATION) {
-            @SuppressWarnings("unchecked")
-            CatalogItem<Location,LocationSpec<?>> locationItem = (CatalogItem<Location, LocationSpec<?>>) itemDto;
-            ((BasicLocationRegistry)mgmt.getLocationRegistry()).removeDefinedLocation(locationItem);
-        }
-        mgmt.getRebindManager().getChangeListener().onUnmanaged(itemDto);
-
-    }
-
-    @Override
-    @Deprecated
-    public <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String id) {
-        return getCatalogItem(type, id, DEFAULT_VERSION);
-    }
-    
-    @SuppressWarnings("unchecked")
-    @Override
-    public <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String id, String version) {
-        if (id==null || version==null) return null;
-        CatalogItem<?,?> result = getCatalogItem(id, version);
-        if (result==null) return null;
-        if (type==null || type.isAssignableFrom(result.getCatalogItemJavaType())) 
-            return (CatalogItem<T,SpecT>)result;
-        return null;
-    }
-
-    @Override
-    public void persist(CatalogItem<?, ?> catalogItem) {
-        checkArgument(getCatalogItem(catalogItem.getSymbolicName(), catalogItem.getVersion()) != null, "Unknown catalog item %s", catalogItem);
-        mgmt.getRebindManager().getChangeListener().onChanged(catalogItem);
-    }
-    
-    @Override
-    public ClassLoader getRootClassLoader() {
-        if (rootClassLoader.isEmpty() && catalog!=null) {
-            resetRootClassLoader();
-        }
-        return rootClassLoader;
-    }
-
-    private void resetRootClassLoader() {
-        rootClassLoader.reset(ImmutableList.of(catalog.getRootClassLoader()));
-    }
-
-    /**
-     * Loads this catalog. No effect if already loaded.
-     */
-    public void load() {
-        log.debug("Loading catalog for " + mgmt);
-        getCatalog().load(mgmt, null);
-        if (log.isDebugEnabled()) {
-            log.debug("Loaded catalog for " + mgmt + ": " + catalog + "; search classpath is " + catalog.getRootClassLoader());
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public <T, SpecT> SpecT createSpec(CatalogItem<T, SpecT> item) {
-        if (item == null) return null;
-        CatalogItemDo<T,SpecT> loadedItem = (CatalogItemDo<T, SpecT>) getCatalogItemDo(item.getSymbolicName(), item.getVersion());
-        if (loadedItem == null) throw new RuntimeException(item+" not in catalog; cannot create spec");
-        Class<SpecT> specType = loadedItem.getSpecType();
-        if (specType==null) return null;
-
-        String yaml = loadedItem.getPlanYaml();
-
-        if (yaml!=null) {
-            // preferred way is to parse the yaml, to resolve references late;
-            // the parsing on load is to populate some fields, but it is optional.
-            // TODO messy for location and policy that we need brooklyn.{locations,policies} root of the yaml, but it works;
-            // see related comment when the yaml is set, in addAbstractCatalogItems
-            // (not sure if anywhere else relies on that syntax; if not, it should be easy to fix!)
-            DeploymentPlan plan = makePlanFromYaml(yaml);
-            BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, item);
-            SpecT spec;
-            switch (item.getCatalogItemType()) {
-                case TEMPLATE:
-                case ENTITY:
-                    spec = createEntitySpec(loadedItem.getSymbolicName(), plan, loader);
-                    break;
-                case POLICY:
-                    spec = createPolicySpec(loadedItem.getSymbolicName(), plan, loader);
-                    break;
-                case LOCATION:
-                    spec = createLocationSpec(plan, loader);
-                    break;
-                default: throw new RuntimeException("Only entity, policy & location catalog items are supported. Unsupported catalog item type " + item.getCatalogItemType());
-            }
-            ((AbstractBrooklynObjectSpec<?, ?>)spec).catalogItemId(item.getId());
-            
-            if (Strings.isBlank( ((AbstractBrooklynObjectSpec<?, ?>)spec).getDisplayName() ))
-                ((AbstractBrooklynObjectSpec<?, ?>)spec).displayName(item.getDisplayName());
-            
-            return spec;
-        }
-
-        // revert to legacy mechanism
-        SpecT spec = null;
-        Method method;
-        try {
-            method = Reflections.findMethod(specType, "create", Class.class);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            throw new IllegalStateException("Unsupported creation of spec type "+specType+"; it must have a public static create(Class) method", e);            
-        }
-        try {
-            if (loadedItem.getJavaType()!=null) {
-                SpecT specT = (SpecT) method.invoke(null, loadedItem.loadJavaClass(mgmt));
-                spec = specT;
-            }
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            throw new IllegalStateException("Error creating "+specType+" "+loadedItem.getJavaType()+": "+e, e);
-        }
-
-        if (spec==null) 
-            throw new IllegalStateException("Unknown how to create instance of "+this);
-
-        return spec;
-    }
-
-    private <T, SpecT> SpecT createSpec(String optionalId, CatalogItemType ciType, DeploymentPlan plan, BrooklynClassLoadingContext loader) {
-        Preconditions.checkNotNull(ciType, "catalog item type for "+plan); 
-        switch (ciType) {
-        case TEMPLATE:
-        case ENTITY: 
-            return createEntitySpec(optionalId, plan, loader);
-        case LOCATION: return createLocationSpec(plan, loader);
-        case POLICY: return createPolicySpec(optionalId, plan, loader);
-        }
-        throw new IllegalStateException("Unknown CI Type "+ciType+" for "+plan);
-    }
-    
-    @SuppressWarnings("unchecked")
-    private <T, SpecT> SpecT createEntitySpec(String symbolicName, DeploymentPlan plan, BrooklynClassLoadingContext loader) {
-        CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get();
-
-        // TODO should not register new AT each time we instantiate from the same plan; use some kind of cache
-        AssemblyTemplate at;
-        BrooklynLoaderTracker.setLoader(loader);
-        try {
-            at = camp.pdp().registerDeploymentPlan(plan);
-        } finally {
-            BrooklynLoaderTracker.unsetLoader(loader);
-        }
-
-        try {
-            AssemblyTemplateInstantiator instantiator = at.getInstantiator().newInstance();
-            if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
-                return (SpecT) ((AssemblyTemplateSpecInstantiator)instantiator).createNestedSpec(at, camp, loader, 
-                    getInitialEncounteredSymbol(symbolicName));
-            }
-            throw new IllegalStateException("Unable to instantiate YAML; incompatible instantiator "+instantiator+" for "+at);
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    private MutableSet<String> getInitialEncounteredSymbol(String symbolicName) {
-        return symbolicName==null ? MutableSet.<String>of() : MutableSet.of(symbolicName);
-    }
-
-    private <T, SpecT> SpecT createPolicySpec(String symbolicName, DeploymentPlan plan, BrooklynClassLoadingContext loader) {
-        return createPolicySpec(plan, loader, getInitialEncounteredSymbol(symbolicName));
-    }
-
-    private <T, SpecT> SpecT createPolicySpec(DeploymentPlan plan, BrooklynClassLoadingContext loader, Set<String> encounteredCatalogTypes) {
-        //Would ideally re-use io.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityDecorationResolver.PolicySpecResolver
-        //but it is CAMP specific and there is no easy way to get hold of it.
-        Object policies = checkNotNull(plan.getCustomAttributes().get(POLICIES_KEY), "policy config");
-        if (!(policies instanceof Iterable<?>)) {
-            throw new IllegalStateException("The value of " + POLICIES_KEY + " must be an Iterable.");
-        }
-
-        Object policy = Iterables.getOnlyElement((Iterable<?>)policies);
-
-        return createPolicySpec(loader, policy, encounteredCatalogTypes);
-    }
-
-    @SuppressWarnings("unchecked")
-    private <T, SpecT> SpecT createPolicySpec(BrooklynClassLoadingContext loader, Object policy, Set<String> encounteredCatalogTypes) {
-        Map<String, Object> itemMap;
-        if (policy instanceof String) {
-            itemMap = ImmutableMap.<String, Object>of("type", policy);
-        } else if (policy instanceof Map) {
-            itemMap = (Map<String, Object>) policy;
-        } else {
-            throw new IllegalStateException("Policy expected to be string or map. Unsupported object type " + policy.getClass().getName() + " (" + policy.toString() + ")");
-        }
-
-        String versionedId = (String) checkNotNull(Yamls.getMultinameAttribute(itemMap, "policy_type", "policyType", "type"), "policy type");
-        PolicySpec<? extends Policy> spec;
-        CatalogItem<?, ?> policyItem = CatalogUtils.getCatalogItemOptionalVersion(mgmt, versionedId);
-        if (policyItem != null && !encounteredCatalogTypes.contains(policyItem.getSymbolicName())) {
-            if (policyItem.getCatalogItemType() != CatalogItemType.POLICY) {
-                throw new IllegalStateException("Non-policy catalog item in policy context: " + policyItem);
-            }
-            //TODO re-use createSpec
-            BrooklynClassLoadingContext itemLoader = CatalogUtils.newClassLoadingContext(mgmt, policyItem);
-            if (policyItem.getPlanYaml() != null) {
-                DeploymentPlan plan = makePlanFromYaml(policyItem.getPlanYaml());
-                encounteredCatalogTypes.add(policyItem.getSymbolicName());
-                return createPolicySpec(plan, itemLoader, encounteredCatalogTypes);
-            } else if (policyItem.getJavaType() != null) {
-                spec = PolicySpec.create((Class<Policy>)itemLoader.loadClass(policyItem.getJavaType()));
-            } else {
-                throw new IllegalStateException("Invalid policy item - neither yaml nor javaType: " + policyItem);
-            }
-        } else {
-            spec = PolicySpec.create(loader.loadClass(versionedId, Policy.class));
-        }
-        Map<String, Object> brooklynConfig = (Map<String, Object>) itemMap.get("brooklyn.config");
-        if (brooklynConfig != null) {
-            spec.configure(brooklynConfig);
-        }
-        return (SpecT) spec;
-    }
-    
-    private <T, SpecT> SpecT createLocationSpec(DeploymentPlan plan, BrooklynClassLoadingContext loader) {
-        // See #createPolicySpec; this impl is modeled on that.
-        // spec.catalogItemId is set by caller
-        Object locations = checkNotNull(plan.getCustomAttributes().get(LOCATIONS_KEY), "location config");
-        if (!(locations instanceof Iterable<?>)) {
-            throw new IllegalStateException("The value of " + LOCATIONS_KEY + " must be an Iterable.");
-        }
-
-        Object location = Iterables.getOnlyElement((Iterable<?>)locations);
-
-        return createLocationSpec(loader, location); 
-    }
-
-    @SuppressWarnings("unchecked")
-    private <T, SpecT> SpecT createLocationSpec(BrooklynClassLoadingContext loader, Object location) {
-        Map<String, Object> itemMap;
-        if (location instanceof String) {
-            itemMap = ImmutableMap.<String, Object>of("type", location);
-        } else if (location instanceof Map) {
-            itemMap = (Map<String, Object>) location;
-        } else {
-            throw new IllegalStateException("Location expected to be string or map. Unsupported object type " + location.getClass().getName() + " (" + location.toString() + ")");
-        }
-
-        String type = (String) checkNotNull(Yamls.getMultinameAttribute(itemMap, "location_type", "locationType", "type"), "location type");
-        Map<String, Object> brooklynConfig = (Map<String, Object>) itemMap.get("brooklyn.config");
-        Maybe<Class<? extends Location>> javaClass = loader.tryLoadClass(type, Location.class);
-        if (javaClass.isPresent()) {
-            LocationSpec<?> spec = LocationSpec.create(javaClass.get());
-            if (brooklynConfig != null) {
-                spec.configure(brooklynConfig);
-            }
-            return (SpecT) spec;
-        } else {
-            Maybe<Location> loc = mgmt.getLocationRegistry().resolve(type, false, brooklynConfig);
-            if (loc.isPresent()) {
-                // TODO extensions?
-                Map<String, Object> locConfig = ((ConfigurationSupportInternal)loc.get().config()).getBag().getAllConfig();
-                Class<? extends Location> locType = loc.get().getClass();
-                Set<Object> locTags = loc.get().tags().getTags();
-                String locDisplayName = loc.get().getDisplayName();
-                return (SpecT) LocationSpec.create(locType)
-                        .configure(locConfig)
-                        .displayName(locDisplayName)
-                        .tags(locTags);
-            } else {
-                throw new IllegalStateException("No class or resolver found for location type "+type);
-            }
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    /** @deprecated since 0.7.0 use {@link #createSpec(CatalogItem)} */
-    @Deprecated
-    public <T,SpecT> Class<? extends T> loadClass(CatalogItem<T,SpecT> item) {
-        if (log.isDebugEnabled())
-            log.debug("Loading class for catalog item " + item);
-        checkNotNull(item);
-        CatalogItemDo<?,?> loadedItem = getCatalogItemDo(item.getSymbolicName(), item.getVersion());
-        if (loadedItem==null) throw new NoSuchElementException("Unable to load '"+item.getId()+"' to instantiate it");
-        return (Class<? extends T>) loadedItem.getJavaClass();
-    }
-    
-    @SuppressWarnings("unchecked")
-    @Override
-    /** @deprecated since 0.7.0 use {@link #createSpec(CatalogItem)} */
-    @Deprecated
-    public <T> Class<? extends T> loadClassByType(String typeName, Class<T> typeClass) {
-        final CatalogItem<?,?> resultI = getCatalogItemForType(typeName);
-
-        if (resultI == null) {
-            throw new NoSuchElementException("Unable to find catalog item for type "+typeName);
-        }
-
-        return (Class<? extends T>) loadClass(resultI);
-    }
-
-    @Deprecated /** @deprecated since 0.7.0 only used by other deprecated items */ 
-    private <T,SpecT> CatalogItemDtoAbstract<T,SpecT> getAbstractCatalogItem(CatalogItem<T,SpecT> item) {
-        while (item instanceof CatalogItemDo) item = ((CatalogItemDo<T,SpecT>)item).itemDto;
-        if (item==null) return null;
-        if (item instanceof CatalogItemDtoAbstract) return (CatalogItemDtoAbstract<T,SpecT>) item;
-        throw new IllegalStateException("Cannot unwrap catalog item '"+item+"' (type "+item.getClass()+") to restore DTO");
-    }
-    
-    @SuppressWarnings("unchecked")
-    private static <T> Maybe<T> getFirstAs(Map<?,?> map, Class<T> type, String firstKey, String ...otherKeys) {
-        if (map==null) return Maybe.absent("No map available");
-        String foundKey = null;
-        Object value = null;
-        if (map.containsKey(firstKey)) foundKey = firstKey;
-        else for (String key: otherKeys) {
-            if (map.containsKey(key)) {
-                foundKey = key;
-                break;
-            }
-        }
-        if (foundKey==null) return Maybe.absent("Missing entry '"+firstKey+"'");
-        value = map.get(foundKey);
-        if (type.equals(String.class) && Number.class.isInstance(value)) value = value.toString();
-        if (!type.isInstance(value)) 
-            throw new IllegalArgumentException("Entry for '"+firstKey+"' should be of type "+type+", not "+value.getClass());
-        return Maybe.of((T)value);
-    }
-    
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    private Maybe<Map<?,?>> getFirstAsMap(Map<?,?> map, String firstKey, String ...otherKeys) {
-        return (Maybe<Map<?,?>>)(Maybe) getFirstAs(map, Map.class, firstKey, otherKeys);
-    }
-
-    private List<CatalogItemDtoAbstract<?,?>> collectCatalogItems(String yaml) {
-        Map<?,?> itemDef = Yamls.getAs(Yamls.parseAll(yaml), Map.class);
-        Map<?,?> catalogMetadata = getFirstAsMap(itemDef, "brooklyn.catalog").orNull();
-        if (catalogMetadata==null)
-            log.warn("No `brooklyn.catalog` supplied in catalog request; using legacy mode for "+itemDef);
-        catalogMetadata = MutableMap.copyOf(catalogMetadata);
-
-        List<CatalogItemDtoAbstract<?, ?>> result = MutableList.of();
-        
-        collectCatalogItems(Yamls.getTextOfYamlAtPath(yaml, "brooklyn.catalog").getMatchedYamlTextOrWarn(), 
-            catalogMetadata, result, null);
-        
-        itemDef.remove("brooklyn.catalog");
-        catalogMetadata.remove("item");
-        catalogMetadata.remove("items");
-        if (!itemDef.isEmpty()) {
-            log.debug("Reading brooklyn.catalog peer keys as item ('top-level syntax')");
-            Map<String,?> rootItem = MutableMap.of("item", itemDef);
-            String rootItemYaml = yaml;
-            YamlExtract yamlExtract = Yamls.getTextOfYamlAtPath(rootItemYaml, "brooklyn.catalog");
-            String match = yamlExtract.withOriginalIndentation(true).withKeyIncluded(true).getMatchedYamlTextOrWarn();
-            if (match!=null) {
-                if (rootItemYaml.startsWith(match)) rootItemYaml = Strings.removeFromStart(rootItemYaml, match);
-                else rootItemYaml = Strings.replaceAllNonRegex(rootItemYaml, "\n"+match, "");
-            }
-            collectCatalogItems("item:\n"+makeAsIndentedObject(rootItemYaml), rootItem, result, catalogMetadata);
-        }
-        
-        return result;
-    }
-
-    @SuppressWarnings("unchecked")
-    private void collectCatalogItems(String sourceYaml, Map<?,?> itemMetadata, List<CatalogItemDtoAbstract<?, ?>> result, Map<?,?> parentMetadata) {
-
-        if (sourceYaml==null) sourceYaml = new Yaml().dump(itemMetadata);
-
-        Map<Object,Object> catalogMetadata = MutableMap.builder().putAll(parentMetadata).putAll(itemMetadata).build();
-        
-        // brooklyn.libraries we treat specially, to append the list, with the child's list preferred in classloading order
-        // `libraries` is supported in some places as a legacy syntax; it should always be `brooklyn.libraries` for new apps
-        // TODO in 0.8.0 require brooklyn.libraries, don't allow "libraries" on its own
-        List<?> librariesNew = MutableList.copyOf(getFirstAs(itemMetadata, List.class, "brooklyn.libraries", "libraries").orNull());
-        Collection<CatalogBundle> libraryBundlesNew = CatalogItemDtoAbstract.parseLibraries(librariesNew);
-        
-        List<?> librariesCombined = MutableList.copyOf(librariesNew)
-            .appendAll(getFirstAs(parentMetadata, List.class, "brooklyn.libraries", "libraries").orNull());
-        if (!librariesCombined.isEmpty())
-            catalogMetadata.put("brooklyn.libraries", librariesCombined);
-        Collection<CatalogBundle> libraryBundles = CatalogItemDtoAbstract.parseLibraries(librariesCombined);
-
-        // TODO as this may take a while if downloading, the REST call should be async
-        // (this load is required for the scan below and I think also for yaml resolution)
-        CatalogUtils.installLibraries(mgmt, libraryBundlesNew);
-
-        Boolean scanJavaAnnotations = getFirstAs(itemMetadata, Boolean.class, "scanJavaAnnotations", "scan_java_annotations").orNull();
-        if (scanJavaAnnotations==null || !scanJavaAnnotations) {
-            // don't scan
-        } else {
-            // scan for annotations: if libraries here, scan them; if inherited libraries error; else scan classpath
-            if (!libraryBundlesNew.isEmpty()) {
-                result.addAll(scanAnnotationsFromBundles(mgmt, libraryBundlesNew, catalogMetadata));
-            } else if (libraryBundles.isEmpty()) {
-                result.addAll(scanAnnotationsFromLocal(mgmt, catalogMetadata));
-            } else {
-                throw new IllegalStateException("Cannot scan catalog node no local bundles, and with inherited bundles we will not scan the classpath");
-            }
-        }
-        
-        Object items = catalogMetadata.remove("items");
-        Object item = catalogMetadata.remove("item");
-
-        if (items!=null) {
-            int count = 0;
-            for (Map<?,?> i: ((List<Map<?,?>>)items)) {
-                collectCatalogItems(Yamls.getTextOfYamlAtPath(sourceYaml, "items", count).getMatchedYamlTextOrWarn(), 
-                    i, result, catalogMetadata);
-                count++;
-            }
-        }
-        
-        if (item==null) return;
-
-        // now look at the actual item, first correcting the sourceYaml and interpreting the catalog metadata
-        String itemYaml = Yamls.getTextOfYamlAtPath(sourceYaml, "item").getMatchedYamlTextOrWarn();
-        if (itemYaml!=null) sourceYaml = itemYaml;
-        else sourceYaml = new Yaml().dump(item);
-        
-        CatalogItemType itemType = TypeCoercions.coerce(getFirstAs(catalogMetadata, Object.class, "itemType", "item_type").orNull(), CatalogItemType.class);
-        BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, "<load>:0", libraryBundles);
-
-        String id = getFirstAs(catalogMetadata, String.class, "id").orNull();
-        String version = getFirstAs(catalogMetadata, String.class, "version").orNull();
-        String symbolicName = getFirstAs(catalogMetadata, String.class, "symbolicName").orNull();
-        String displayName = getFirstAs(catalogMetadata, String.class, "displayName").orNull();
-        String name = getFirstAs(catalogMetadata, String.class, "name").orNull();
-
-        if ((Strings.isNonBlank(id) || Strings.isNonBlank(symbolicName)) && 
-                Strings.isNonBlank(displayName) &&
-                Strings.isNonBlank(name) && !name.equals(displayName)) {
-            log.warn("Name property will be ignored due to the existence of displayName and at least one of id, symbolicName");
-        }
-
-        PlanInterpreterGuessingType planInterpreter = new PlanInterpreterGuessingType(null, item, sourceYaml, itemType, loader, result).reconstruct();
-        if (!planInterpreter.isResolved()) {
-            throw Exceptions.create("Could not resolve item "
-                + (Strings.isNonBlank(id) ? id : Strings.isNonBlank(symbolicName) ? symbolicName : Strings.isNonBlank(name) ? name : "<no-name>")
-                + ":\n"+sourceYaml, planInterpreter.getErrors());
-        }
-        itemType = planInterpreter.getCatalogItemType();
-        Map<?, ?> itemAsMap = planInterpreter.getItem();
-        // the "plan yaml" includes the services: ... or brooklyn.policies: ... outer key,
-        // as opposed to the rawer { type: xxx } map without that outer key which is valid as item input
-        // TODO this plan yaml is needed for subsequent reconstruction; would be nicer if it weren't! 
-
-        // if symname not set, infer from: id, then name, then item id, then item name
-        if (Strings.isBlank(symbolicName)) {
-            if (Strings.isNonBlank(id)) {
-                if (CatalogUtils.looksLikeVersionedId(id)) {
-                    symbolicName = CatalogUtils.getIdFromVersionedId(id);
-                } else {
-                    symbolicName = id;
-                }
-            } else if (Strings.isNonBlank(name)) {
-                if (CatalogUtils.looksLikeVersionedId(name)) {
-                    symbolicName = CatalogUtils.getIdFromVersionedId(name);
-                } else {
-                    symbolicName = name;
-                }
-            } else {
-                symbolicName = setFromItemIfUnset(symbolicName, itemAsMap, "id");
-                symbolicName = setFromItemIfUnset(symbolicName, itemAsMap, "name");
-                if (Strings.isBlank(symbolicName)) {
-                    log.error("Can't infer catalog item symbolicName from the following plan:\n" + sourceYaml);
-                    throw new IllegalStateException("Can't infer catalog item symbolicName from catalog item metadata");
-                }
-            }
-        }
-
-        // if version not set, infer from: id, then from name, then item version
-        if (CatalogUtils.looksLikeVersionedId(id)) {
-            String versionFromId = CatalogUtils.getVersionFromVersionedId(id);
-            if (versionFromId != null && Strings.isNonBlank(version) && !versionFromId.equals(version)) {
-                throw new IllegalArgumentException("Discrepency between version set in id " + versionFromId + " and version property " + version);
-            }
-            version = versionFromId;
-        }
-        if (Strings.isBlank(version)) {
-            if (CatalogUtils.looksLikeVersionedId(name)) {
-                version = CatalogUtils.getVersionFromVersionedId(name);
-            } else if (Strings.isBlank(version)) {
-                version = setFromItemIfUnset(version, itemAsMap, "version");
-                if (version==null) {
-                    log.warn("No version specified for catalog item " + symbolicName + ". Using default value.");
-                    version = null;
-                }
-            }
-        }
-        
-        // if not set, ID can come from symname:version, failing that, from the plan.id, failing that from the sym name
-        if (Strings.isBlank(id)) {
-            // let ID be inferred, especially from name, to support style where only "name" is specified, with inline version
-            if (Strings.isNonBlank(symbolicName) && Strings.isNonBlank(version)) {
-                id = symbolicName + ":" + version;
-            }
-            id = setFromItemIfUnset(id, itemAsMap, "id");
-            if (Strings.isBlank(id)) {
-                if (Strings.isNonBlank(symbolicName)) {
-                    id = symbolicName;
-                } else {
-                    log.error("Can't infer catalog item id from the following plan:\n" + sourceYaml);
-                    throw new IllegalStateException("Can't infer catalog item id from catalog item metadata");
-                }
-            }
-        }
-
-        if (Strings.isBlank(displayName)) {
-            if (Strings.isNonBlank(name)) displayName = name;
-            displayName = setFromItemIfUnset(displayName, itemAsMap, "name");
-        }
-
-        String description = getFirstAs(catalogMetadata, String.class, "description").orNull();
-        description = setFromItemIfUnset(description, itemAsMap, "description");
-
-        // icon.url is discouraged, but kept for legacy compatibility; should deprecate this
-        final String catalogIconUrl = getFirstAs(catalogMetadata, String.class, "iconUrl", "icon_url", "icon.url").orNull();
-
-        final String deprecated = getFirstAs(catalogMetadata, String.class, "deprecated").orNull();
-        final Boolean catalogDeprecated = Boolean.valueOf(deprecated);
-
-        // run again now that we know the ID
-        planInterpreter = new PlanInterpreterGuessingType(id, item, sourceYaml, itemType, loader, result).reconstruct();
-        if (!planInterpreter.isResolved()) {
-            throw new IllegalStateException("Could not resolve plan once id and itemType are known (recursive reference?): "+sourceYaml);
-        }
-        String sourcePlanYaml = planInterpreter.getPlanYaml();
-        
-        CatalogItemDtoAbstract<?, ?> dto = createItemBuilder(itemType, symbolicName, version)
-            .libraries(libraryBundles)
-            .displayName(displayName)
-            .description(description)
-            .deprecated(catalogDeprecated)
-            .iconUrl(catalogIconUrl)
-            .plan(sourcePlanYaml)
-            .build();
-
-        dto.setManagementContext((ManagementContextInternal) mgmt);
-        result.add(dto);
-    }
-
-    private String setFromItemIfUnset(String oldValue, Map<?,?> item, String fieldAttr) {
-        if (Strings.isNonBlank(oldValue)) return oldValue;
-        if (item!=null) {
-            Object newValue = item.get(fieldAttr);
-            if (newValue instanceof String && Strings.isNonBlank((String)newValue)) 
-                return (String)newValue;
-        }
-        return oldValue;
-    }
-
-    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromLocal(ManagementContext mgmt, Map<Object, Object> catalogMetadata) {
-        CatalogDto dto = CatalogDto.newNamedInstance("Local Scanned Catalog", "All annotated Brooklyn entities detected in the classpath", "scanning-local-classpath");
-        return scanAnnotationsInternal(mgmt, new CatalogDo(dto), catalogMetadata);
-    }
-    
-    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromBundles(ManagementContext mgmt, Collection<CatalogBundle> libraries, Map<Object, Object> catalogMetadata) {
-        CatalogDto dto = CatalogDto.newNamedInstance("Bundles Scanned Catalog", "All annotated Brooklyn entities detected in bundles", "scanning-bundles-classpath-"+libraries.hashCode());
-        List<String> urls = MutableList.of();
-        for (CatalogBundle b: libraries) {
-            // TODO currently does not support pre-installed bundles identified by name:version 
-            // (ie where URL not supplied)
-            if (Strings.isNonBlank(b.getUrl())) {
-                urls.add(b.getUrl());
-            }
-        }
-        
-        if (urls.isEmpty()) {
-            log.warn("No bundles to scan: scanJavaAnnotations currently only applies to OSGi bundles provided by URL"); 
-            return MutableList.of();
-        }
-        
-        CatalogDo subCatalog = new CatalogDo(dto);
-        subCatalog.addToClasspath(urls.toArray(new String[0]));
-        return scanAnnotationsInternal(mgmt, subCatalog, catalogMetadata);
-    }
-    
-    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsInternal(ManagementContext mgmt, CatalogDo subCatalog, Map<Object, Object> catalogMetadata) {
-        // TODO this does java-scanning only;
-        // the call when scanning bundles should use the CatalogItem instead and use OSGi when loading for scanning
-        // (or another scanning mechanism).  see comments on CatalogClasspathDo.load
-        subCatalog.mgmt = mgmt;
-        subCatalog.setClasspathScanForEntities(CatalogScanningModes.ANNOTATIONS);
-        subCatalog.load();
-        // TODO apply metadata?  (extract YAML from the items returned)
-        // also see doc .../catalog/index.md which says we might not apply metadata
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        Collection<CatalogItemDtoAbstract<?, ?>> result = (Collection<CatalogItemDtoAbstract<?, ?>>)(Collection)Collections2.transform(
-                (Collection<CatalogItemDo<Object,Object>>)(Collection)subCatalog.getIdCache().values(), 
-                itemDoToDtoAddingSelectedMetadataDuringScan(catalogMetadata));
-        return result;
-    }
-
-    private class PlanInterpreterGuessingType {
-
-        final String id;
-        final Map<?,?> item;
-        final String itemYaml;
-        final BrooklynClassLoadingContext loader;
-        final List<CatalogItemDtoAbstract<?, ?>> itemsDefinedSoFar;
-        
-        CatalogItemType catalogItemType;
-        String planYaml;
-        @SuppressWarnings("unused")
-        DeploymentPlan plan;
-        AbstractBrooklynObjectSpec<?,?> spec;
-        boolean resolved = false;
-        List<Exception> errors = MutableList.of();
-        
-        public PlanInterpreterGuessingType(@Nullable String id, Object item, String itemYaml, @Nullable CatalogItemType optionalCiType, 
-                BrooklynClassLoadingContext loader, List<CatalogItemDtoAbstract<?,?>> itemsDefinedSoFar) {
-            // ID is useful to prevent recursive references (currently for entities only)
-            this.id = id;
-            
-            if (item instanceof String) {
-                // if just a string supplied, wrap as map
-                this.item = MutableMap.of("type", item);
-                this.itemYaml = "type:\n"+makeAsIndentedObject(itemYaml);                
-            } else {
-                this.item = (Map<?,?>)item;
-                this.itemYaml = itemYaml;
-            }
-            this.catalogItemType = optionalCiType;
-            this.loader = loader;
-            this.itemsDefinedSoFar = itemsDefinedSoFar;
-        }
-
-        public PlanInterpreterGuessingType reconstruct() {
-            if (catalogItemType==CatalogItemType.TEMPLATE) {
-                // template *must* be explicitly defined, and if so, none of the other calls apply
-                attemptType(null, CatalogItemType.TEMPLATE);
-                
-            } else {
-                attemptType(null, CatalogItemType.ENTITY);
-
-                attemptType("services", CatalogItemType.ENTITY);
-                attemptType(POLICIES_KEY, CatalogItemType.POLICY);
-                attemptType(LOCATIONS_KEY, CatalogItemType.LOCATION);
-            }
-            
-            if (!resolved && catalogItemType==CatalogItemType.TEMPLATE) {
-                // anything goes, for an explicit template, because we can't easily recurse into the types
-                planYaml = itemYaml;
-                resolved = true;
-            }
-            
-            return this;
-        }
-        
-        public boolean isResolved() { return resolved; }
-        
-        /** Returns potentially useful errors encountered while guessing types. 
-         * May only be available where the type is known. */
-        public List<Exception> getErrors() {
-            return errors;
-        }
-        
-        public CatalogItemType getCatalogItemType() {
-            return catalogItemType; 
-        }
-        
-        public String getPlanYaml() {
-            return planYaml;
-        }
-        
-        private boolean attemptType(String key, CatalogItemType candidateCiType) {
-            if (resolved) return false;
-            if (catalogItemType!=null && catalogItemType!=candidateCiType) return false;
-            
-            final String candidateYaml;
-            if (key==null) candidateYaml = itemYaml;
-            else {
-                if (item.containsKey(key))
-                    candidateYaml = itemYaml;
-                else
-                    candidateYaml = key + ":\n" + makeAsIndentedList(itemYaml);
-            }
-            // first look in collected items, if a key is given
-            String type = (String) item.get("type");
-            String version = null;
-            if (CatalogUtils.looksLikeVersionedId(type)) {
-                version = CatalogUtils.getVersionFromVersionedId(type);
-                type = CatalogUtils.getIdFromVersionedId(type);
-            }
-            if (type!=null && key!=null) {
-                for (CatalogItemDtoAbstract<?,?> candidate: itemsDefinedSoFar) {
-                    if (candidateCiType == candidate.getCatalogItemType() &&
-                            (type.equals(candidate.getSymbolicName()) || type.equals(candidate.getId()))) {
-                        if (version==null || version.equals(candidate.getVersion())) {
-                            // matched - exit
-                            catalogItemType = candidateCiType;
-                            planYaml = candidateYaml;
-                            resolved = true;
-                            return true;
-                        }
-                    }
-                }
-            }
-            
-            // then try parsing plan - this will use loader
-            try {
-                DeploymentPlan candidatePlan = makePlanFromYaml(candidateYaml);
-                spec = createSpec(id, candidateCiType, candidatePlan, loader);
-                if (spec!=null) {
-                    catalogItemType = candidateCiType;
-                    plan = candidatePlan;
-                    planYaml = candidateYaml;
-                    resolved = true;
-                }
-                return true;
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                // record the error if we have reason to expect this guess to succeed
-                if (item.containsKey("services") && (candidateCiType==CatalogItemType.ENTITY || candidateCiType==CatalogItemType.TEMPLATE)) {
-                    // explicit services supplied, so plan should have been parseable for an entity or a a service
-                    errors.add(e);
-                } else if (catalogItemType!=null && key!=null) {
-                    // explicit itemType supplied, so plan should be parseable in the cases where we're given a key
-                    // (when we're not given a key, the previous block should apply)
-                    errors.add(e);
-                } else {
-                    // all other cases, the error is probably due to us not getting the type right, ignore it
-                    if (log.isTraceEnabled())
-                        log.trace("Guessing type of plan, it looks like it isn't "+candidateCiType+"/"+key+": "+e);
-                }
-            }
-            
-            // finally try parsing a cut-down plan, in case there is a nested reference to a newly defined catalog item
-            if (type!=null && key!=null) {
-                try {
-                    String cutDownYaml = key + ":\n" + makeAsIndentedList("type: "+type);
-                    DeploymentPlan candidatePlan = makePlanFromYaml(cutDownYaml);
-                    Object cutdownSpec = createSpec(id, candidateCiType, candidatePlan, loader);
-                    if (cutdownSpec!=null) {
-                        catalogItemType = candidateCiType;
-                        planYaml = candidateYaml;
-                        resolved = true;
-                    }
-                    return true;
-                } catch (Exception e) {
-                    Exceptions.propagateIfFatal(e);
-                }
-            }
-            
-            return false;
-        }
-        public Map<?,?> getItem() {
-            return item;
-        }
-    }
-    
-    private String makeAsIndentedList(String yaml) {
-        String[] lines = yaml.split("\n");
-        lines[0] = "- "+lines[0];
-        for (int i=1; i<lines.length; i++)
-            lines[i] = "  " + lines[i];
-        return Strings.join(lines, "\n");
-    }
-
-    private String makeAsIndentedObject(String yaml) {
-        String[] lines = yaml.split("\n");
-        for (int i=0; i<lines.length; i++)
-            lines[i] = "  " + lines[i];
-        return Strings.join(lines, "\n");
-    }
-
-    private CatalogItemBuilder<?> createItemBuilder(CatalogItemType itemType, String itemId, String version) {
-        Preconditions.checkNotNull(itemType, "itemType required");
-        switch (itemType) {
-        case ENTITY: return CatalogItemBuilder.newEntity(itemId, version);
-        case TEMPLATE: return CatalogItemBuilder.newTemplate(itemId, version);
-        case POLICY: return CatalogItemBuilder.newPolicy(itemId, version);
-        case LOCATION: return CatalogItemBuilder.newLocation(itemId, version);
-        }
-        throw new IllegalStateException("Unexpected itemType: "+itemType);
-    }
-
-    // these kept as their logic may prove useful; Apr 2015
-//    private boolean isApplicationSpec(EntitySpec<?> spec) {
-//        return !Boolean.TRUE.equals(spec.getConfig().get(EntityManagementUtils.WRAPPER_APP_MARKER));
-//    }
-//
-//    private boolean isEntityPlan(DeploymentPlan plan) {
-//        return plan!=null && !plan.getServices().isEmpty() || !plan.getArtifacts().isEmpty();
-//    }
-//    
-//    private boolean isPolicyPlan(DeploymentPlan plan) {
-//        return !isEntityPlan(plan) && plan.getCustomAttributes().containsKey(POLICIES_KEY);
-//    }
-//
-//    private boolean isLocationPlan(DeploymentPlan plan) {
-//        return !isEntityPlan(plan) && plan.getCustomAttributes().containsKey(LOCATIONS_KEY);
-//    }
-
-    private DeploymentPlan makePlanFromYaml(String yaml) {
-        CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get();
-        return camp.pdp().parseDeploymentPlan(Streams.newReaderWithContents(yaml));
-    }
-
-    //------------------------
-    
-    @Override
-    public CatalogItem<?,?> addItem(String yaml) {
-        return addItem(yaml, false);
-    }
-
-    @Override
-    public List<? extends CatalogItem<?,?>> addItems(String yaml) {
-        return addItems(yaml, false);
-    }
-
-    @Override
-    public CatalogItem<?,?> addItem(String yaml, boolean forceUpdate) {
-        return Iterables.getOnlyElement(addItems(yaml, forceUpdate));
-    }
-    
-    @Override
-    public List<? extends CatalogItem<?,?>> addItems(String yaml, boolean forceUpdate) {
-        log.debug("Adding manual catalog item to "+mgmt+": "+yaml);
-        checkNotNull(yaml, "yaml");
-        List<CatalogItemDtoAbstract<?, ?>> result = collectCatalogItems(yaml);
-        // do this at the end for atomic updates; if there are intra-yaml references, we handle them specially
-        for (CatalogItemDtoAbstract<?, ?> item: result) {
-            addItemDto(item, forceUpdate);
-        }
-        return result;
-    }
-    
-    private CatalogItem<?,?> addItemDto(CatalogItemDtoAbstract<?, ?> itemDto, boolean forceUpdate) {
-        CatalogItem<?, ?> existingDto = checkItemAllowedAndIfSoReturnAnyDuplicate(itemDto, true, forceUpdate);
-        if (existingDto!=null) {
-            // it's a duplicate, and not forced, just return it
-            log.trace("Using existing duplicate for catalog item {}", itemDto.getId());
-            return existingDto;
-        }
-
-        if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
-        manualAdditionsCatalog.addEntry(itemDto);
-
-        // Ensure the cache is populated and it is persisted by the management context
-        getCatalog().addEntry(itemDto);
-
-        // Request that the management context persist the item.
-        if (log.isTraceEnabled()) {
-            log.trace("Scheduling item for persistence addition: {}", itemDto.getId());
-        }
-        if (itemDto.getCatalogItemType() == CatalogItemType.LOCATION) {
-            @SuppressWarnings("unchecked")
-            CatalogItem<Location,LocationSpec<?>> locationItem = (CatalogItem<Location, LocationSpec<?>>) itemDto;
-            ((BasicLocationRegistry)mgmt.getLocationRegistry()).updateDefinedLocation(locationItem);
-        }
-        mgmt.getRebindManager().getChangeListener().onManaged(itemDto);
-
-        return itemDto;
-    }
-
-    /** returns item DTO if item is an allowed duplicate, or null if it should be added (there is no duplicate), 
-     * throwing if item cannot be added */
-    private CatalogItem<?, ?> checkItemAllowedAndIfSoReturnAnyDuplicate(CatalogItem<?,?> itemDto, boolean allowDuplicates, boolean forceUpdate) {
-        if (forceUpdate) return null;
-        CatalogItemDo<?, ?> existingItem = getCatalogItemDo(itemDto.getSymbolicName(), itemDto.getVersion());
-        if (existingItem == null) return null;
-        // check if they are equal
-        CatalogItem<?, ?> existingDto = existingItem.getDto();
-        if (existingDto.equals(itemDto)) {
-            if (allowDuplicates) return existingItem;
-            throw new IllegalStateException("Updating existing catalog entries, even with the same content, is forbidden: " +
-                    itemDto.getSymbolicName() + ":" + itemDto.getVersion() + ". Use forceUpdate argument to override.");
-        } else {
-            throw new IllegalStateException("Updating existing catalog entries is forbidden: " +
-                    itemDto.getSymbolicName() + ":" + itemDto.getVersion() + ". Use forceUpdate argument to override.");
-        }
-    }
-
-    @Override @Deprecated /** @deprecated see super */
-    public void addItem(CatalogItem<?,?> item) {
-        //assume forceUpdate for backwards compatibility
-        log.debug("Adding manual catalog item to "+mgmt+": "+item);
-        checkNotNull(item, "item");
-        CatalogUtils.installLibraries(mgmt, item.getLibraries());
-        if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
-        manualAdditionsCatalog.addEntry(getAbstractCatalogItem(item));
-    }
-
-    @Override @Deprecated /** @deprecated see super */
-    public CatalogItem<?,?> addItem(Class<?> type) {
-        //assume forceUpdate for backwards compatibility
-        log.debug("Adding manual catalog item to "+mgmt+": "+type);
-        checkNotNull(type, "type");
-        if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
-        manualAdditionsClasses.addClass(type);
-        return manualAdditionsCatalog.classpath.addCatalogEntry(type);
-    }
-
-    private synchronized void loadManualAdditionsCatalog() {
-        if (manualAdditionsCatalog!=null) return;
-        CatalogDto manualAdditionsCatalogDto = CatalogDto.newNamedInstance(
-                "Manual Catalog Additions", "User-additions to the catalog while Brooklyn is running, " +
-                "created "+Time.makeDateString(),
-                "manual-additions");
-        CatalogDo manualAdditionsCatalog = catalog.addCatalog(manualAdditionsCatalogDto);
-        if (manualAdditionsCatalog==null) {
-            // not hard to support, but slightly messy -- probably have to use ID's to retrieve the loaded instance
-            // for now block once, then retry
-            log.warn("Blocking until catalog is loaded before changing it");
-            boolean loaded = blockIfNotLoaded(Duration.TEN_SECONDS);
-            if (!loaded)
-                log.warn("Catalog still not loaded after delay; subsequent operations may fail");
-            manualAdditionsCatalog = catalog.addCatalog(manualAdditionsCatalogDto);
-            if (manualAdditionsCatalog==null) {
-                throw new UnsupportedOperationException("Catalogs cannot be added until the base catalog is loaded, and catalog is taking a while to load!");
-            }
-        }
-        
-        log.debug("Creating manual additions catalog for "+mgmt+": "+manualAdditionsCatalog);
-        manualAdditionsClasses = new LoadedClassLoader();
-        ((AggregateClassLoader)manualAdditionsCatalog.classpath.getLocalClassLoader()).addFirst(manualAdditionsClasses);
-        
-        // expose when we're all done
-        this.manualAdditionsCatalog = manualAdditionsCatalog;
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    @Override
-    public <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItems() {
-        if (!getCatalog().isLoaded()) {
-            // some callers use this to force the catalog to load (maybe when starting as hot_backup without a catalog ?)
-            log.debug("Forcing catalog load on access of catalog items");
-            load();
-        }
-        return ImmutableList.copyOf((Iterable)catalog.getIdCache().values());
-    }
-    
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    @Override
-    public <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItems(Predicate<? super CatalogItem<T,SpecT>> filter) {
-        Iterable<CatalogItemDo<T,SpecT>> filtered = Iterables.filter((Iterable)catalog.getIdCache().values(), (Predicate<CatalogItem<T,SpecT>>)(Predicate) filter);
-        return Iterables.transform(filtered, BasicBrooklynCatalog.<T,SpecT>itemDoToDto());
-    }
-
-    private static <T,SpecT> Function<CatalogItemDo<T,SpecT>, CatalogItem<T,SpecT>> itemDoToDto() {
-        return new Function<CatalogItemDo<T,SpecT>, CatalogItem<T,SpecT>>() {
-            @Override
-            public CatalogItem<T,SpecT> apply(@Nullable CatalogItemDo<T,SpecT> item) {
-                if (item==null) return null;
-                return item.getDto();
-            }
-        };
-    }
-    
-    private static <T,SpecT> Function<CatalogItemDo<T, SpecT>, CatalogItem<T,SpecT>> itemDoToDtoAddingSelectedMetadataDuringScan(final Map<Object, Object> catalogMetadata) {
-        return new Function<CatalogItemDo<T,SpecT>, CatalogItem<T,SpecT>>() {
-            @Override
-            public CatalogItem<T,SpecT> apply(@Nullable CatalogItemDo<T,SpecT> item) {
-                if (item==null) return null;
-                CatalogItemDtoAbstract<T, SpecT> dto = (CatalogItemDtoAbstract<T, SpecT>) item.getDto();
-
-                // when scanning we only allow version and libraries to be overwritten
-                
-                String version = getFirstAs(catalogMetadata, String.class, "version").orNull();
-                if (Strings.isNonBlank(version)) dto.setVersion(version);
-                
-                Object librariesCombined = catalogMetadata.get("brooklyn.libraries");
-                if (librariesCombined instanceof Collection) {
-                    // will be set by scan -- slightly longwinded way to retrieve, but scanning for osgi needs an overhaul in any case
-                    Collection<CatalogBundle> libraryBundles = CatalogItemDtoAbstract.parseLibraries((Collection<?>) librariesCombined);
-                    dto.setLibraries(libraryBundles);
-                }
-                // replace java type with plan yaml -- needed for libraries / catalog item to be picked up,
-                // but probably useful to transition away from javaType altogether
-                dto.setSymbolicName(dto.getJavaType());
-                switch (dto.getCatalogItemType()) {
-                    case TEMPLATE:
-                    case ENTITY:
-                        dto.setPlanYaml("services: [{ type: "+dto.getJavaType()+" }]");
-                        break;
-                    case POLICY:
-                        dto.setPlanYaml(POLICIES_KEY + ": [{ type: "+dto.getJavaType()+" }]");
-                        break;
-                    case LOCATION:
-                        dto.setPlanYaml(LOCATIONS_KEY + ": [{ type: "+dto.getJavaType()+" }]");
-                        break;
-                }
-                dto.setJavaType(null);
-
-                return dto;
-            }
-        };
-    }
-
-    transient CatalogXmlSerializer serializer;
-    
-    public String toXmlString() {
-        if (serializer==null) loadSerializer();
-        return serializer.toString(catalog.dto);
-    }
-    
-    private synchronized void loadSerializer() {
-        if (serializer==null) 
-            serializer = new CatalogXmlSerializer();
-    }
-
-    @Deprecated
-    public CatalogItem<?,?> getCatalogItemForType(String typeName) {
-        final CatalogItem<?,?> resultI;
-        final BrooklynCatalog catalog = mgmt.getCatalog();
-        if (CatalogUtils.looksLikeVersionedId(typeName)) {
-            //All catalog identifiers of the form xxxx:yyyy are composed of symbolicName+version.
-            //No javaType is allowed as part of the identifier.
-            resultI = CatalogUtils.getCatalogItemOptionalVersion(mgmt, typeName);
-        } else {
-            //Usually for catalog items with javaType (that is items from catalog.xml)
-            //the symbolicName and javaType match because symbolicName (was ID)
-            //is not specified explicitly. But could be the case that there is an item
-            //whose symbolicName is explicitly set to be different from the javaType.
-            //Note that in the XML the attribute is called registeredTypeName.
-            Iterable<CatalogItem<Object,Object>> resultL = catalog.getCatalogItems(CatalogPredicates.javaType(Predicates.equalTo(typeName)));
-            if (!Iterables.isEmpty(resultL)) {
-                //Push newer versions in front of the list (not that there should
-                //be more than one considering the items are coming from catalog.xml).
-                resultI = sortVersionsDesc(resultL).iterator().next();
-                if (log.isDebugEnabled() && Iterables.size(resultL)>1) {
-                    log.debug("Found "+Iterables.size(resultL)+" matches in catalog for type "+typeName+"; returning the result with preferred version, "+resultI);
-                }
-            } else {
-                //As a last resort try searching for items with the same symbolicName supposedly
-                //different from the javaType.
-                resultI = catalog.getCatalogItem(typeName, BrooklynCatalog.DEFAULT_VERSION);
-                if (resultI != null) {
-                    if (resultI.getJavaType() == null) {
-                        //Catalog items scanned from the classpath (using reflection and annotations) now
-                        //get yaml spec rather than a java type. Can't use those when creating apps from
-                        //the legacy app spec format.
-                        log.warn("Unable to find catalog item for type "+typeName +
-                                ". There is an existing catalog item with ID " + resultI.getId() +
-                                " but it doesn't define a class type.");
-                        return null;
-                    }
-                }
-            }
-        }
-        return resultI;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java
deleted file mode 100644
index 5ef4a96..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java
+++ /dev/null
@@ -1,63 +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.catalog.internal;
-
-import com.thoughtworks.xstream.converters.Converter;
-import com.thoughtworks.xstream.converters.MarshallingContext;
-import com.thoughtworks.xstream.converters.UnmarshallingContext;
-import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
-import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
-import com.thoughtworks.xstream.io.HierarchicalStreamReader;
-import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
-import com.thoughtworks.xstream.mapper.Mapper;
-
-
-/**
- *  Convert old-style catalog.xml file formats to the latest version.
- *  The code is needed only during transition to the new version, can be removed after a while.
- */
-@Deprecated
-public class CatalogBundleConverter implements Converter {
-
-    private ReflectionConverter delegateConverter;
-
-    public CatalogBundleConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
-        this.delegateConverter = new ReflectionConverter(mapper, reflectionProvider);
-    }
-
-    @Override
-    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
-        return type == CatalogBundleDto.class;
-    }
-
-    @Override
-    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
-        context.convertAnother(source, delegateConverter);
-    }
-
-    @Override
-    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
-        if (reader.hasMoreChildren()) {
-            return context.convertAnother(context.currentObject(), CatalogBundleDto.class, delegateConverter);
-        } else {
-            return new CatalogBundleDto(null, null, reader.getValue());
-        }
-    }
-
-}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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
new file mode 100644
index 0000000..2bf9cca
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java
@@ -0,0 +1,334 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.reflections.util.ClasspathHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.api.catalog.Catalog;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+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 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;
+import brooklyn.util.time.Time;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.Iterables;
+
+public class CatalogClasspathDo {
+
+    public static enum CatalogScanningModes {
+        /** the classpath is not scanned; 
+         * for any catalog which is presented over the internet this is recommended (to prevent loading) and is the default; 
+         * (you should explicitly list the items to include; it may be useful to autogenerate it by using a local catalog
+         * scanning with ANNOTATIONS, viwing that by running mgmt.getCatalog().toXmlString(),
+         * then editing the resulting XML (e.g. setting the classpath and removing the scan attribute) */
+        NONE, 
+        
+        /** types in the classpath are scanned for annotations indicating inclusion in the catalog ({@link Catalog});
+         * this is the default if no catalog is supplied, scanning the local classpath */
+        ANNOTATIONS,
+        
+        @Beta
+        /** all catalog-friendly types are included, 
+         * even if not annotated for inclusion in the catalog; useful for quick hacking,
+         * or a classpath (and possibly in future a regex, if added) which is known to have only good things in it;
+         * however the precise semantics of what is included is subject to change,
+         * and it is strongly recommended to use the {@link Catalog} annotation and scan for annotations 
+         * <p>
+         * a catalog-friendly type is currently defined as:
+         * any concrete non-anonymous (and not a non-static inner) class implementing Entity or Policy;
+         * and additionally for entities and applications, an interface with the {@link ImplementedBy} annotation;
+         * note that this means classes done "properly" with both an interface and an implementation
+         * will be included twice, once as interface and once as implementation;
+         * this guarantees inclusion of anything previously included (implementations; 
+         * and this will be removed from catalog in future likely),
+         * plus things now done properly (which will become the only way in the future)
+         **/
+        TYPES
+    }
+    
+    private static final Logger log = LoggerFactory.getLogger(CatalogClasspathDo.class);
+    
+    private final CatalogDo catalog;
+    private final CatalogClasspathDto classpath;
+    private final CatalogScanningModes scanMode;
+    
+    boolean isLoaded = false;
+    private URL[] urls;
+    
+    private final AggregateClassLoader classloader = AggregateClassLoader.newInstanceWithNoLoaders();
+    private volatile boolean classloaderLoaded = false;
+
+    public CatalogClasspathDo(CatalogDo catalog) {
+        this.catalog = Preconditions.checkNotNull(catalog, "catalog");
+        this.classpath = catalog.dto.classpath;
+        this.scanMode = (classpath != null) ? classpath.scan : null;
+    }
+    
+    /** causes all scanning-based classpaths to scan the classpaths
+    * (but does _not_ load all JARs) */
+    // TODO this does a Java scan; we also need an OSGi scan which uses the OSGi classloaders when loading for scanning and resolving dependencies 
+    synchronized void load() {
+        if (classpath == null || isLoaded) return;
+
+        if (classpath.getEntries() == null) {
+            urls = new URL[0];
+        } else {
+            urls = new URL[classpath.getEntries().size()];
+            for (int i=0; i<urls.length; i++) {
+                try {
+                    String u = classpath.getEntries().get(i);
+                    if (u.startsWith("classpath:")) {
+                        // special support for classpath: url's
+                        // TODO put convenience in ResourceUtils for extracting to a normal url
+                        // (or see below)
+                        InputStream uin = ResourceUtils.create(this).getResourceFromUrl(u);
+                        File f = Os.newTempFile("brooklyn-catalog-"+u, null);
+                        FileOutputStream fout = new FileOutputStream(f);
+                        try {
+                            Streams.copy(uin, fout);
+                        } finally {
+                            Streams.closeQuietly(fout);
+                            Streams.closeQuietly(uin);
+                        }
+                        u = f.toURI().toString();
+                    }
+                    urls[i] = new URL(u);
+                    
+                    // TODO potential disk leak above as we have no way to know when the temp file can be removed earlier than server shutdown;
+                    // a better way to handle this is to supply a stream handler (but URLConnection is a little bit hard to work with):
+//                    urls[i] = new URL(null, classpath.getEntries().get(i)   // (handy construtor for reparsing urls, without splitting into uri first)
+//                        , new URLStreamHandler() {
+//                            @Override
+//                            protected URLConnection openConnection(URL u) throws IOException {
+//                                new ResourceUtils(null). ???
+//                            }
+//                        });
+                } catch (Exception e) {
+                    Exceptions.propagateIfFatal(e);
+                    log.error("Error loading URL "+classpath.getEntries().get(i)+" in definition of catalog "+catalog+"; skipping definition");
+                    throw Exceptions.propagate(e);
+                }
+            }
+        }
+        
+        // prefix is supported (but not really used yet) --
+        // seems to have _better_ URL-discovery with prefixes 
+        // (might also offer regex ? but that is post-load filter as opposed to native optimisation)
+        String prefix = null;
+
+        if (scanMode==null || scanMode==CatalogScanningModes.NONE)
+            return;
+        
+        Stopwatch timer = Stopwatch.createStarted();
+        ReflectionScanner scanner = null;
+        if (!catalog.isLocal()) {
+            log.warn("Scanning not supported for remote catalogs; ignoring scan request in "+catalog);
+        } else if (classpath.getEntries() == null || classpath.getEntries().isEmpty()) {
+            // scan default classpath:
+            ClassLoader baseCL = null;
+            Iterable<URL> baseCP = null;
+            if (catalog.mgmt instanceof ManagementContextInternal) {
+                baseCL = ((ManagementContextInternal)catalog.mgmt).getBaseClassLoader();
+                baseCP = ((ManagementContextInternal)catalog.mgmt).getBaseClassPathForScanning();
+            }
+            scanner = new ReflectionScanner(baseCP, prefix, baseCL, catalog.getRootClassLoader());
+            if (scanner.getSubTypesOf(Entity.class).isEmpty()) {
+                try {
+                    ((ManagementContextInternal)catalog.mgmt).setBaseClassPathForScanning(ClasspathHelper.forJavaClassPath());
+                    log.debug("Catalog scan of default classloader returned nothing; reverting to java.class.path");
+                    baseCP = ((ManagementContextInternal)catalog.mgmt).getBaseClassPathForScanning();
+                    scanner = new ReflectionScanner(baseCP, prefix, baseCL, catalog.getRootClassLoader());
+                } catch (Exception e) {
+                    log.info("Catalog scan is empty, and unable to use java.class.path (base classpath is "+baseCP+"): "+e);
+                    Exceptions.propagateIfFatal(e);
+                }
+            }
+        } else {
+            // scan specified jars:
+            scanner = new ReflectionScanner(urls==null || urls.length==0 ? null : Arrays.asList(urls), prefix, getLocalClassLoader());
+        }
+        
+        if (scanner!=null) {
+            int count = 0, countApps = 0;
+            if (scanMode==CatalogScanningModes.ANNOTATIONS) {
+                Set<Class<?>> catalogClasses = scanner.getTypesAnnotatedWith(Catalog.class);
+                for (Class<?> c: catalogClasses) {
+                    try {
+                        CatalogItem<?,?> item = addCatalogEntry(c);
+                        count++;
+                        if (CatalogTemplateItemDto.class.isInstance(item)) countApps++;
+                    } catch (Exception e) {
+                        log.warn("Failed to add catalog entry for "+c+"; continuing scan...", e);
+                    }
+                }
+            } else if (scanMode==CatalogScanningModes.TYPES) {
+                Iterable<Class<?>> entities = this.excludeInvalidClasses(
+                        Iterables.concat(scanner.getSubTypesOf(Entity.class),
+                                // not sure why we have to look for sub-types of Application, 
+                                // they should be picked up as sub-types of Entity, but in maven builds (only!)
+                                // they are not -- i presume a bug in scanner
+                                scanner.getSubTypesOf(Application.class), 
+                                scanner.getSubTypesOf(ApplicationBuilder.class)));
+                for (Class<?> c: entities) {
+                    if (Application.class.isAssignableFrom(c) || ApplicationBuilder.class.isAssignableFrom(c)) {
+                        addCatalogEntry(new CatalogTemplateItemDto(), c);
+                        countApps++;
+                    } else {
+                        addCatalogEntry(new CatalogEntityItemDto(), c);
+                    }
+                    count++;
+                }
+                Iterable<Class<? extends Policy>> policies = this.excludeInvalidClasses(scanner.getSubTypesOf(Policy.class));
+                for (Class<?> c: policies) {
+                    addCatalogEntry(new CatalogPolicyItemDto(), c);
+                    count++;
+                }
+                
+                Iterable<Class<? extends Location>> locations = this.excludeInvalidClasses(scanner.getSubTypesOf(Location.class));
+                for (Class<?> c: locations) {
+                    addCatalogEntry(new CatalogLocationItemDto(), c);
+                    count++;
+                }
+            } else {
+                throw new IllegalStateException("Unsupported catalog scan mode "+scanMode+" for "+this);
+            }
+            log.debug("Catalog '"+catalog.dto.name+"' classpath scan completed: loaded "+
+                    count+" item"+Strings.s(count)+" ("+countApps+" app"+Strings.s(countApps)+") in "+Time.makeTimeStringRounded(timer));
+        }
+        
+        isLoaded = true;
+    }
+
+    /** removes inner classes (non-static nesteds) and others; 
+     * bear in mind named ones will be hard to instantiate without the outer class instance) */
+    private <T> Iterable<Class<? extends T>> excludeInvalidClasses(Iterable<Class<? extends T>> input) {
+        Predicate<Class<? extends T>> f = new Predicate<Class<? extends T>>() {
+            @Override
+            public boolean apply(@Nullable Class<? extends T> input) {
+                if (input==null) return false;
+                if (input.isLocalClass() || input.isAnonymousClass()) return false;
+                if (Modifier.isAbstract(input.getModifiers())) {
+                    if (input.getAnnotation(ImplementedBy.class)==null)
+                        return false;
+                }
+                // non-abstract top-level classes are okay
+                if (!input.isMemberClass()) return true;
+                if (!Modifier.isStatic(input.getModifiers())) return false;
+                // nested classes only okay if static
+                return true;
+            }
+        };
+        return Iterables.filter(input, f);
+    }
+
+    /** augments the given item with annotations and class data for the given class, then adds to catalog
+     * @deprecated since 0.7.0 the classpath DO is replaced by libraries */
+    @Deprecated
+    public CatalogItem<?,?> addCatalogEntry(Class<?> c) {
+        if (Application.class.isAssignableFrom(c)) return addCatalogEntry(new CatalogTemplateItemDto(), c);
+        if (ApplicationBuilder.class.isAssignableFrom(c)) return addCatalogEntry(new CatalogTemplateItemDto(), c);
+        if (Entity.class.isAssignableFrom(c)) return addCatalogEntry(new CatalogEntityItemDto(), c);
+        if (Policy.class.isAssignableFrom(c)) return addCatalogEntry(new CatalogPolicyItemDto(), c);
+        if (Location.class.isAssignableFrom(c)) return addCatalogEntry(new CatalogLocationItemDto(), c);
+        throw new IllegalStateException("Cannot add "+c+" to catalog: unsupported type "+c.getName());
+    }
+    
+    /** augments the given item with annotations and class data for the given class, then adds to catalog 
+     * @deprecated since 0.7.0 the classpath DO is replaced by libraries */
+    @Deprecated
+    public CatalogItem<?,?> addCatalogEntry(CatalogItemDtoAbstract<?,?> item, Class<?> c) {
+        Catalog annotations = c.getAnnotation(Catalog.class);
+        item.setSymbolicName(c.getName());
+        item.setJavaType(c.getName());
+        item.setDisplayName(firstNonEmpty(c.getSimpleName(), c.getName()));
+        if (annotations!=null) {
+            item.setDisplayName(firstNonEmpty(annotations.name(), item.getDisplayName()));
+            item.setDescription(firstNonEmpty(annotations.description()));
+            item.setIconUrl(firstNonEmpty(annotations.iconUrl()));
+        }
+        if (log.isTraceEnabled())
+            log.trace("adding to catalog: "+c+" (from catalog "+catalog+")");
+        catalog.addEntry(item);
+        return item;
+    }
+
+    private static String firstNonEmpty(String ...candidates) {
+        for (String c: candidates)
+            if (c!=null && !c.isEmpty()) return c;
+        return null;
+    }
+
+    /** returns classloader for the entries specified here */
+    public ClassLoader getLocalClassLoader() {
+        if (!classloaderLoaded) loadLocalClassLoader();
+        return classloader;
+    }
+
+    protected synchronized void loadLocalClassLoader() {
+        if (classloaderLoaded) return;
+        if (urls==null) return;
+        classloader.addFirst(new UrlClassLoader(urls));
+        classloaderLoaded = true;
+        return;
+    }
+
+    /** adds the given URL as something this classloader will load
+     * (however no scanning is done) */
+    public void addToClasspath(URL u, boolean updateDto) {
+        if (updateDto) classpath.getEntries().add(u.toExternalForm());
+        addToClasspath(new UrlClassLoader(u));
+    }
+
+    /** adds the given URL as something this classloader will load
+     * (however no scanning is done).
+     * <p>
+     * the DTO will _not_ be updated. */
+    public void addToClasspath(ClassLoader loader) {
+        classloader.addFirst(loader);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDto.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDto.java
new file mode 100644
index 0000000..5779a4e
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDto.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
+
+public class CatalogClasspathDto {
+    
+    /** whether/what to scan; defaults to 'none' */
+    CatalogScanningModes scan;
+    private List<String> entries;
+    
+    public synchronized void addEntry(String url) {
+        if (entries==null)
+            entries = new CopyOnWriteArrayList<String>();
+        
+        entries.add(url);
+    }
+
+    public synchronized List<String> getEntries() {
+        return entries;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDo.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDo.java
new file mode 100644
index 0000000..8e53e28
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDo.java
@@ -0,0 +1,365 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.javalang.AggregateClassLoader;
+import brooklyn.util.net.Urls;
+import brooklyn.util.time.CountdownTimer;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+
+public class CatalogDo {
+
+    private static final Logger log = LoggerFactory.getLogger(CatalogDo.class);
+    
+    volatile boolean isLoaded = false;
+    final CatalogDto dto;
+    ManagementContext mgmt = null;
+    CatalogDo parent = null;
+    
+    List<CatalogDo> childrenCatalogs = new ArrayList<CatalogDo>();
+    CatalogClasspathDo classpath;
+    private Map<String, CatalogItemDo<?,?>> cacheById;
+
+    AggregateClassLoader childrenClassLoader = AggregateClassLoader.newInstanceWithNoLoaders();
+    ClassLoader recursiveClassLoader;
+
+    protected CatalogDo(CatalogDto dto) {
+        this.dto = Preconditions.checkNotNull(dto);
+    }
+    
+    public CatalogDo(ManagementContext mgmt, CatalogDto dto) {
+        this(dto);
+        this.mgmt = mgmt;
+    }
+
+    boolean isLoaded() {
+        return isLoaded;
+    }
+
+    /** Calls {@link #load(CatalogDo)} with a null parent. */
+    public CatalogDo load() {
+        return load(null);
+    }
+
+    /** Calls {@link #load(ManagementContext, CatalogDo)} with the catalog's existing management context. */
+    public CatalogDo load(CatalogDo parent) {
+        return load(mgmt, parent);
+    }
+
+    /** Calls {@link #load(ManagementContext, CatalogDo, boolean)} failing on load errors. */
+    public synchronized CatalogDo load(ManagementContext mgmt, CatalogDo parent) {
+        return load(mgmt, parent, true);
+    }
+
+    /** causes all URL-based catalogs to have their manifests loaded,
+     * and all scanning-based classpaths to scan the classpaths
+     * (but does not load all JARs)
+     */
+    public synchronized CatalogDo load(ManagementContext mgmt, CatalogDo parent, boolean failOnLoadError) {
+        if (isLoaded()) {
+            if (mgmt!=null && !Objects.equal(mgmt, this.mgmt)) {
+                throw new IllegalStateException("Cannot set mgmt "+mgmt+" on "+this+" after catalog is loaded");
+            }
+            log.debug("Catalog "+this+" is already loaded");
+            return this;
+        }
+        loadThisCatalog(mgmt, parent, failOnLoadError);
+        loadChildrenCatalogs(failOnLoadError);
+        buildCaches();
+        return this;
+    }
+
+    protected synchronized void loadThisCatalog(ManagementContext mgmt, CatalogDo parent, boolean failOnLoadError) {
+        if (isLoaded()) return;
+        CatalogUtils.logDebugOrTraceIfRebinding(log, "Loading catalog {} into {}", this, parent);
+        if (this.parent!=null && !this.parent.equals(parent))
+            log.warn("Catalog "+this+" being initialised with different parent "+parent+" when already parented by "+this.parent, new Throwable("source of reparented "+this));
+        if (this.mgmt!=null && !this.mgmt.equals(mgmt))
+            log.warn("Catalog "+this+" being initialised with different mgmt "+mgmt+" when already managed by "+this.mgmt, new Throwable("source of reparented "+this));
+        this.parent = parent;
+        this.mgmt = mgmt;
+        dto.populate();
+        loadCatalogClasspath();
+        loadCatalogItems(failOnLoadError);
+        isLoaded = true;
+        synchronized (this) {
+            notifyAll();
+        }
+    }
+
+    private void loadCatalogClasspath() {
+        try {
+            classpath = new CatalogClasspathDo(this);
+            classpath.load();
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            log.error("Unable to load catalog "+this+" (ignoring): "+e);
+            log.info("Trace for failure to load "+this+": "+e, e);
+        }
+    }
+
+    private void loadCatalogItems(boolean failOnLoadError) {
+        Iterable<CatalogItemDtoAbstract<?, ?>> entries = dto.getUniqueEntries();
+        if (entries!=null) {
+            for (CatalogItemDtoAbstract<?,?> entry : entries) {
+                try {
+                    CatalogUtils.installLibraries(mgmt, entry.getLibraries());
+                } catch (Exception e) {
+                    Exceptions.propagateIfFatal(e);
+                    if (failOnLoadError) {
+                        Exceptions.propagate(e);
+                    } else {
+                        log.error("Loading bundles for catalog item " + entry + " failed: " + e.getMessage(), e);
+                    }
+                }
+            }
+        }
+    }
+
+    public boolean blockIfNotLoaded(Duration timeout) throws InterruptedException {
+        if (isLoaded()) return true;
+        synchronized (this) {
+            if (isLoaded()) return true;
+            CountdownTimer timer = CountdownTimer.newInstanceStarted(timeout);
+            while (!isLoaded())
+                if (!timer.waitOnForExpiry(this))
+                    return false;
+            return true;
+        }
+    }
+    
+    protected void loadChildrenCatalogs(boolean failOnLoadError) {
+        if (dto.catalogs!=null) {
+            for (CatalogDto child: dto.catalogs) {
+                loadCatalog(child, failOnLoadError);
+            }
+        }
+    }
+    
+    CatalogDo loadCatalog(CatalogDto child, boolean failOnLoadError) {
+        CatalogDo childL = new CatalogDo(child);
+        childrenCatalogs.add(childL);
+        childL.load(mgmt, this, failOnLoadError);
+        childrenClassLoader.addFirst(childL.getRecursiveClassLoader());
+        clearCache(false);
+        return childL;
+    }
+
+    protected Map<String, CatalogItemDo<?,?>> getIdCache() {
+        Map<String, CatalogItemDo<?,?>> cache = this.cacheById;
+        if (cache==null) cache = buildCaches();
+        return cache;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    protected synchronized Map<String, CatalogItemDo<?,?>> buildCaches() {
+        if (cacheById != null) return cacheById;
+        CatalogUtils.logDebugOrTraceIfRebinding(log, "Building cache for {}", this);
+        if (!isLoaded()) 
+            log.debug("Catalog not fully loaded when loading cache of "+this);
+        
+        Map<String, CatalogItemDo<?,?>> cache = new LinkedHashMap<String, CatalogItemDo<?,?>>();
+        
+        // build the cache; first from children catalogs, then from local entities
+        // so that root and near-root takes precedence over deeper items;
+        // and go through in reverse order so that things at the top of the file take precedence
+        // (both in the cache and in the aggregate class loader);
+        // however anything added _subsequently_ will take precedence (again in both)
+        if (dto.catalogs!=null) { 
+            List<CatalogDo> catalogsReversed = new ArrayList<CatalogDo>(childrenCatalogs);
+            Collections.reverse(catalogsReversed);
+            for (CatalogDo child: catalogsReversed) {
+                cache.putAll(child.getIdCache());
+            }
+        }
+        if (dto.getUniqueEntries()!=null) {
+            List<CatalogItemDtoAbstract<?,?>> entriesReversed = MutableList.copyOf(dto.getUniqueEntries());
+            Collections.reverse(entriesReversed);
+            for (CatalogItemDtoAbstract<?,?> entry: entriesReversed)
+                cache.put(entry.getId(), new CatalogItemDo(this, entry));
+        }
+        this.cacheById = cache;
+        return cache;
+    }
+    
+    protected synchronized void clearCache(boolean deep) {
+        this.cacheById = null;
+        if (deep) {
+            for (CatalogDo child : childrenCatalogs) {
+                child.clearCache(true);
+            }
+        }
+        clearParentCache();
+    }
+    protected void clearParentCache() {
+        if (this.parent!=null)
+            this.parent.clearCache(false);
+    }
+    
+    /**
+     * Adds the given entry to the catalog, with no enrichment.
+     * Callers may prefer {@link CatalogClasspathDo#addCatalogEntry(CatalogItemDtoAbstract, Class)}
+     */
+    public synchronized void addEntry(CatalogItemDtoAbstract<?,?> entry) {
+        dto.addEntry(entry);
+        
+        // could do clearCache(false); but this is slightly more efficient...
+        if (cacheById != null) {
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            CatalogItemDo<?, ?> cdo = new CatalogItemDo(this, entry);
+            cacheById.put(entry.getId(), cdo);
+        }        
+        clearParentCache();
+        
+        if (mgmt != null) {
+            mgmt.getRebindManager().getChangeListener().onManaged(entry);
+        }
+   }
+    
+    /**
+     * Removes the given entry from the catalog.
+     */
+    public synchronized void deleteEntry(CatalogItemDtoAbstract<?, ?> entry) {
+        dto.removeEntry(entry);
+        
+        // could do clearCache(false); but this is slightly more efficient...
+        if (cacheById != null) {
+            cacheById.remove(entry.getId());
+        }
+        clearParentCache();
+        
+        if (mgmt != null) {
+            // TODO: Can the entry be in more than one catalogue? The management context has no notion of
+            // catalogue hierarchy so this will effectively remove it from all catalogues.
+            // (YES- we're assuming ID's are unique across all catalogues; if not, things get out of sync;
+            // however see note at top of BasicBrooklynCatalog --
+            // manualCatalog and OSGi is used for everything now except legacy XML trees)
+            mgmt.getRebindManager().getChangeListener().onUnmanaged(entry);
+        }
+    }
+
+    /** returns loaded catalog, if this has been loaded */
+    CatalogDo addCatalog(CatalogDto child) {
+        if (dto.catalogs == null)
+            dto.catalogs = new ArrayList<CatalogDto>();
+        dto.catalogs.add(child);
+        if (!isLoaded())
+            return null;
+        return loadCatalog(child, true);
+    }
+    
+    /** adds the given urls; filters out any nulls supplied */
+    public synchronized void addToClasspath(String ...urls) {
+        if (dto.classpath == null)
+            dto.classpath = new CatalogClasspathDto();
+        for (String url: urls) {
+            if (url!=null)
+                dto.classpath.addEntry(url);
+        }
+        if (isLoaded())
+            throw new IllegalStateException("dynamic classpath entry value update not supported");
+        // easy enough to add, just support unload+reload (and can also allow dynamic setScan below)
+        // but more predictable if we don't; the one exception is in the manualAdditionsCatalog
+        // where BasicBrooklynCatalog reaches in and updates the DTO and/or CompositeClassLoader directly, if necessary
+//            for (String url: urls)
+//                loadedClasspath.addEntry(url);
+    }
+
+    public synchronized void setClasspathScanForEntities(CatalogScanningModes value) {
+        if (dto.classpath == null)
+            dto.classpath = new CatalogClasspathDto();
+        dto.classpath.scan = value;
+        if (isLoaded()) 
+            throw new IllegalStateException("dynamic classpath scan value update not supported");
+        // easy enough to add, see above
+    }
+
+    @Override
+    public String toString() {
+        String size = cacheById == null ? "not yet loaded" : "size " + cacheById.size();
+        return "Loaded:" + dto + "(" + size + ")";
+    }
+
+    /** is "local" if it and all ancestors are not based on any remote urls */ 
+    public boolean isLocal() {
+        if (dto.url != null) {
+            String proto = Urls.getProtocol(dto.url);
+            if (proto != null) {
+                // 'file' is the only protocol accepted as "local"
+                if (!"file".equals(proto)) return false;
+            }
+        }
+        return parent == null || parent.isLocal();
+    }
+
+    /** classloader for only the entries in this catalog's classpath */ 
+    public ClassLoader getLocalClassLoader() {
+        if (classpath != null) return classpath.getLocalClassLoader();
+        return null;
+    }
+
+    /** recursive classloader is the local classloader plus all children catalog's classloader */
+    public ClassLoader getRecursiveClassLoader() {
+        if (recursiveClassLoader == null) loadRecursiveClassLoader();
+        return recursiveClassLoader;
+    }
+    
+    protected synchronized void loadRecursiveClassLoader() {
+        if (recursiveClassLoader!=null) return;
+        AggregateClassLoader cl = AggregateClassLoader.newInstanceWithNoLoaders();
+        cl.addFirst(childrenClassLoader);
+        ClassLoader local = getLocalClassLoader();
+        if (local != null) cl.addFirst(local);
+        if (parent == null) {
+            // we are root.  include the mgmt base classloader and/or standard class loaders 
+            ClassLoader base = mgmt != null ? ((ManagementContextInternal)mgmt).getBaseClassLoader() : null;
+            if (base != null) cl.addFirst(base);
+            else {
+                cl.addFirst(getClass().getClassLoader());
+                cl.addFirst(Object.class.getClassLoader());
+            }
+        }
+        recursiveClassLoader = cl;
+    }
+    
+    /** the root classloader is the recursive CL from the outermost catalog
+     * (which includes the base classloader from the mgmt context, if set) */
+    public ClassLoader getRootClassLoader() {
+        if (parent != null) return parent.getRootClassLoader();
+        return getRecursiveClassLoader();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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
new file mode 100644
index 0000000..847f114
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDto.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import java.io.InputStream;
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.PropagatedRuntimeException;
+import brooklyn.util.stream.Streams;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Lists;
+
+@Beta
+public class CatalogDto {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CatalogDto.class);
+
+    String id;
+    String url;
+    String contents;
+    String contentsDescription;
+    String name;
+    String description;
+    CatalogClasspathDto classpath;
+    private List<CatalogItemDtoAbstract<?,?>> entries = null;
+    
+    // for thread-safety, any dynamic additions to this should be handled by a method 
+    // in this class which does copy-on-write
+    List<CatalogDto> catalogs = null;
+
+    public static CatalogDto newDefaultLocalScanningDto(CatalogClasspathDo.CatalogScanningModes scanMode) {
+        CatalogDo result = new CatalogDo(
+                newNamedInstance("Local Scanned Catalog", "All annotated Brooklyn entities detected in the default classpath", "scanning-local-classpath") );
+        result.setClasspathScanForEntities(scanMode);
+        return result.dto;
+    }
+
+    /** @deprecated since 0.7.0 use {@link #newDtoFromXmlUrl(String)} if you must, but note the xml format itself is deprecated */
+    @Deprecated
+    public static CatalogDto newDtoFromUrl(String url) {
+        return newDtoFromXmlUrl(url);
+    }
+    
+    /** @deprecated since 0.7.0 the xml format is deprecated; use YAML parse routines on BasicBrooklynCatalog */
+    @Deprecated
+    public static CatalogDto newDtoFromXmlUrl(String url) {
+        if (LOG.isDebugEnabled()) LOG.debug("Retrieving catalog from: {}", url);
+        try {
+            InputStream source = ResourceUtils.create().getResourceFromUrl(url);
+            String contents = Streams.readFullyString(source);
+            return newDtoFromXmlContents(contents, url);
+        } catch (Throwable t) {
+            Exceptions.propagateIfFatal(t);
+            throw new PropagatedRuntimeException("Unable to retrieve catalog from " + url + ": " + t, t);
+        }
+    }
+
+    /** @deprecated since 0.7.0 the xml format is deprecated; use YAML parse routines on BasicBrooklynCatalog */
+    @Deprecated
+    public static CatalogDto newDtoFromXmlContents(String xmlContents, String originDescription) {
+        CatalogDto result = (CatalogDto) new CatalogXmlSerializer().deserialize(new StringReader(xmlContents));
+        result.contentsDescription = originDescription;
+
+        if (LOG.isDebugEnabled()) LOG.debug("Retrieved catalog from: {}", originDescription);
+        return result;
+    }
+
+    /**
+     * Creates a DTO.
+     * <p>
+     * The way contents is treated may change; thus this (and much of catalog) should be treated as beta.
+     * 
+     * @param name
+     * @param description
+     * @param optionalContentsDescription optional description of contents; if null, we normally expect source 'contents' to be set later;
+     *   if the DTO has no 'contents' (ie XML source) then a description should be supplied so we know who is populating it
+     *   (e.g. manual additions); without this, warnings may be generated
+     *   
+     * @return a new Catalog DTO
+     */
+    public static CatalogDto newNamedInstance(String name, String description, String optionalContentsDescription) {
+        CatalogDto result = new CatalogDto();
+        result.name = name;
+        result.description = description;
+        if (optionalContentsDescription!=null) result.contentsDescription = optionalContentsDescription;
+        return result;
+    }
+
+    /** Used when caller wishes to create an explicitly empty catalog */
+    public static CatalogDto newEmptyInstance(String optionalContentsDescription) {
+        CatalogDto result = new CatalogDto();
+        if (optionalContentsDescription!=null) result.contentsDescription = optionalContentsDescription;
+        return result;
+    }
+
+    public static CatalogDto newLinkedInstance(String url) {
+        CatalogDto result = new CatalogDto();
+        result.contentsDescription = url;
+        result.contents = ResourceUtils.create().getResourceAsString(url);
+        return result;
+    }
+
+    /** @deprecated since 0.7.0 use {@link #newDtoFromCatalogItems(Collection, String)}, supplying a description for tracking */
+    @Deprecated
+    public static CatalogDto newDtoFromCatalogItems(Collection<CatalogItem<?, ?>> entries) {
+        return newDtoFromCatalogItems(entries, null);
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static CatalogDto newDtoFromCatalogItems(Collection<CatalogItem<?, ?>> entries, String description) {
+        CatalogDto result = new CatalogDto();
+        result.contentsDescription = description;
+        // Weird casts because compiler does not seem to like
+        // .copyInto(Lists.<CatalogItemDtoAbstract<?, ?>>newArrayListWithExpectedSize(entries.size()));
+        result.entries = (List<CatalogItemDtoAbstract<?, ?>>) (List) FluentIterable.from(entries)
+                .filter(CatalogItemDtoAbstract.class)
+                .copyInto(Lists.newArrayListWithExpectedSize(entries.size()));
+        return result;
+    }
+    
+    void populate() {
+        if (contents==null) {
+            if (url != null) {
+                contents = ResourceUtils.create().getResourceAsString(url);
+                contentsDescription = url;
+            } else if (contentsDescription==null) {
+                LOG.debug("Catalog DTO has no contents and no description; ignoring call to populate it. Description should be set to suppress this message.");
+                return;
+            } else {
+                LOG.trace("Nothing needs doing (no contents or URL) for catalog with contents described as "+contentsDescription+".");
+                return;
+            }
+        }
+        
+        CatalogDto remoteDto = newDtoFromXmlContents(contents, contentsDescription);
+        try {
+            copyFrom(remoteDto, true);
+        } catch (Exception e) {
+            Exceptions.propagate(e);
+        }
+    }        
+
+    /**
+     * @throws NullPointerException If source is null (and !skipNulls)
+     */
+    void copyFrom(CatalogDto source, boolean skipNulls) throws IllegalAccessException {
+        if (source==null) {
+            if (skipNulls) return;
+            throw new NullPointerException("source DTO is null, when copying to "+this);
+        }
+        
+        if (!skipNulls || source.id != null) id = source.id;
+        if (!skipNulls || source.contentsDescription != null) contentsDescription = source.contentsDescription;
+        if (!skipNulls || source.contents != null) contents = source.contents;
+        if (!skipNulls || source.name != null) name = source.name;
+        if (!skipNulls || source.description != null) description = source.description;
+        if (!skipNulls || source.classpath != null) classpath = source.classpath;
+        if (!skipNulls || source.entries != null) entries = source.entries;
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .omitNullValues()
+                .add("name", name)
+                .add("id", id)
+                .add("contentsDescription", contentsDescription)
+                .toString();
+    }
+
+    // temporary fix for issue where entries might not be unique
+    Iterable<CatalogItemDtoAbstract<?, ?>> getUniqueEntries() {
+        if (entries==null) return null;
+        Map<String, CatalogItemDtoAbstract<?, ?>> result = getEntriesMap();
+        return result.values();
+    }
+
+    private Map<String, CatalogItemDtoAbstract<?, ?>> getEntriesMap() {
+        if (entries==null) return null;
+        Map<String,CatalogItemDtoAbstract<?, ?>> result = MutableMap.of();
+        for (CatalogItemDtoAbstract<?,?> entry: entries) {
+            result.put(entry.getId(), entry);
+        }
+        return result;
+    }
+
+    void removeEntry(CatalogItemDtoAbstract<?, ?> entry) {
+        if (entries!=null)
+            entries.remove(entry);
+    }
+
+    void addEntry(CatalogItemDtoAbstract<?, ?> entry) {
+        if (entries == null) entries = MutableList.of();
+        CatalogItemDtoAbstract<?, ?> oldEntry = getEntriesMap().get(entry.getId());
+        entries.add(entry);
+        if (oldEntry!=null)
+            removeEntry(oldEntry);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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
new file mode 100644
index 0000000..e2df123
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogDtoUtils.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.exceptions.Exceptions;
+
+public class CatalogDtoUtils {
+
+    private static final Logger log = LoggerFactory.getLogger(CatalogDtoUtils.class);
+    
+    public static CatalogDto newDefaultLocalScanningDto(CatalogScanningModes scanMode) {
+        return CatalogDto.newDefaultLocalScanningDto(scanMode);
+    }
+
+    /** throws if there are any problems in retrieving or copying */
+    public static void populateFromUrl(CatalogDto dto, String url) {
+        CatalogDto remoteDto = newDtoFromUrl(url);
+        try {
+            copyDto(remoteDto, dto, true);
+        } catch (Exception e) {
+            Exceptions.propagate(e);
+        }
+    }
+
+    /** does a shallow copy.
+     * "skipNulls" means not to copy any fields from the source which are null */ 
+    static void copyDto(CatalogDto source, CatalogDto target, boolean skipNulls) throws IllegalArgumentException, IllegalAccessException {
+        target.copyFrom(source, skipNulls);
+    }
+
+    public static CatalogDto newDtoFromUrl(String url) {
+        if (log.isDebugEnabled()) log.debug("Retrieving catalog from: {}", url);
+        try {
+            InputStream source = ResourceUtils.create().getResourceFromUrl(url);
+            CatalogDto result = (CatalogDto) new CatalogXmlSerializer().deserialize(new InputStreamReader(source));
+            if (log.isDebugEnabled()) log.debug("Retrieved catalog from: {}", url);
+            return result;
+        } catch (Throwable t) {
+            log.debug("Unable to retrieve catalog from: "+url+" ("+t+")");
+            throw Exceptions.propagate(t);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogEntityItemDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogEntityItemDto.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogEntityItemDto.java
new file mode 100644
index 0000000..ec7cd90
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogEntityItemDto.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+
+
+public class CatalogEntityItemDto extends CatalogItemDtoAbstract<Entity,EntitySpec<?>> {
+    
+    @Override
+    public CatalogItemType getCatalogItemType() {
+        return CatalogItemType.ENTITY;
+    }
+
+    @Override
+    public Class<Entity> getCatalogItemJavaType() {
+        return Entity.class;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Override
+    public Class<EntitySpec<?>> getSpecType() {
+        return (Class)EntitySpec.class;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/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
new file mode 100644
index 0000000..047a168
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInitialization.java
@@ -0,0 +1,449 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+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 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;
+import brooklyn.util.text.Strings;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+@Beta
+public class CatalogInitialization implements ManagementContextInjectable {
+
+    /*
+
+    A1) if not persisting, go to B1
+    A2) if --catalog-reset, delete persisted catalog items
+    A3) if there is a persisted catalog (and it wasn't not deleted by A2), read it and go to C1
+    A4) go to B1
+
+    B1) look for --catalog-initial, if so read it, then go to C1
+    B2) look for BrooklynServerConfig.BROOKLYN_CATALOG_URL, if so, read it, supporting YAML or XML (warning if XML), then go to C1
+    B3) look for ~/.brooklyn/catalog.bom, if exists, read it then go to C1
+    B4) look for ~/.brooklyn/brooklyn.xml, if exists, warn, read it then go to C1
+    B5) read all classpath://brooklyn/default.catalog.bom items, if they exist (and for now they will)
+    B6) go to C1
+
+    C1) if --catalog-add, read and add those items
+
+    D1) if persisting, read the rest of the persisted items (entities etc)
+
+     */
+
+    private static final Logger log = LoggerFactory.getLogger(CatalogInitialization.class);
+    
+    private String initialUri;
+    private boolean reset;
+    private String additionsUri;
+    private boolean force;
+
+    private boolean disallowLocal = false;
+    private List<Function<CatalogInitialization, Void>> callbacks = MutableList.of();
+    private boolean 
+        /** has run an unofficial initialization (i.e. an early load, triggered by an early read of the catalog) */
+        hasRunUnofficialInitialization = false, 
+        /** has run an official initialization, but it is not a permanent one (e.g. during a hot standby mode, or a run failed) */
+        hasRunTransientOfficialInitialization = false, 
+        /** has run an official initialization which is permanent (node is master, and the new catalog is now set) */
+        hasRunFinalInitialization = false;
+    /** is running a populate method; used to prevent recursive loops */
+    private boolean isPopulating = false;
+    
+    private ManagementContext managementContext;
+    private boolean isStartingUp = false;
+    private boolean failOnStartupErrors = false;
+    
+    private Object populatingCatalogMutex = new Object();
+    
+    public CatalogInitialization(String initialUri, boolean reset, String additionUri, boolean force) {
+        this.initialUri = initialUri;
+        this.reset = reset;
+        this.additionsUri = additionUri;
+        this.force = force;
+    }
+    
+    public CatalogInitialization() {
+        this(null, false, null, false);
+    }
+
+    @Override
+    public void injectManagementContext(ManagementContext managementContext) {
+        Preconditions.checkNotNull(managementContext, "management context");
+        if (this.managementContext!=null && managementContext!=this.managementContext)
+            throw new IllegalStateException("Cannot switch management context, from "+this.managementContext+" to "+managementContext);
+        this.managementContext = managementContext;
+    }
+    
+    /** Called by the framework to set true while starting up, and false afterwards,
+     * in order to assist in appropriate logging and error handling. */
+    public void setStartingUp(boolean isStartingUp) {
+        this.isStartingUp = isStartingUp;
+    }
+
+    public void setFailOnStartupErrors(boolean startupFailOnCatalogErrors) {
+        this.failOnStartupErrors = startupFailOnCatalogErrors;
+    }
+
+    public CatalogInitialization addPopulationCallback(Function<CatalogInitialization, Void> callback) {
+        callbacks.add(callback);
+        return this;
+    }
+    
+    public ManagementContext getManagementContext() {
+        return Preconditions.checkNotNull(managementContext, "management context has not been injected into "+this);
+    }
+
+    public boolean isInitialResetRequested() {
+        return reset;
+    }
+
+    /** Returns true if the canonical initialization has completed, 
+     * that is, an initialization which is done when a node is rebinded as master
+     * (or an initialization done by the startup routines when not running persistence);
+     * see also {@link #hasRunAnyInitialization()}. */
+    public boolean hasRunFinalInitialization() { return hasRunFinalInitialization; }
+    /** Returns true if an official initialization has run,
+     * even if it was a transient run, e.g. so that the launch sequence can tell whether rebind has triggered initialization */
+    public boolean hasRunOfficialInitialization() { return hasRunFinalInitialization || hasRunTransientOfficialInitialization; }
+    /** Returns true if the initializer has run at all,
+     * including transient initializations which might be needed before a canonical becoming-master rebind,
+     * for instance because the catalog is being accessed before loading rebind information
+     * (done by {@link #populateUnofficial(BasicBrooklynCatalog)}) */
+    public boolean hasRunAnyInitialization() { return hasRunFinalInitialization || hasRunTransientOfficialInitialization || hasRunUnofficialInitialization; }
+
+    /** makes or updates the mgmt catalog, based on the settings in this class 
+     * @param nodeState the management node for which this is being read; if master, then we expect this run to be the last one,
+     *   and so subsequent applications should ignore any initialization data (e.g. on a subsequent promotion to master, 
+     *   after a master -> standby -> master cycle)
+     * @param needsInitialItemsLoaded whether the catalog needs the initial items loaded
+     * @param needsAdditionalItemsLoaded whether the catalog needs the additions loaded
+     * @param optionalExcplicitItemsForResettingCatalog
+     *   if supplied, the catalog is reset to contain only these items, before calling any other initialization
+     *   for use primarily when rebinding
+     */
+    public void populateCatalog(ManagementNodeState nodeState, boolean needsInitialItemsLoaded, boolean needsAdditionsLoaded, Collection<CatalogItem<?, ?>> optionalExcplicitItemsForResettingCatalog) {
+        if (log.isDebugEnabled()) {
+            String message = "Populating catalog for "+nodeState+", needsInitial="+needsInitialItemsLoaded+", needsAdditional="+needsAdditionsLoaded+", explicitItems="+(optionalExcplicitItemsForResettingCatalog==null ? "null" : optionalExcplicitItemsForResettingCatalog.size())+"; from "+JavaClassNames.callerNiceClassAndMethod(1);
+            if (!ManagementNodeState.isHotProxy(nodeState)) {
+                log.debug(message);
+            } else {
+                // in hot modes, make this message trace so we don't get too much output then
+                log.trace(message);
+            }
+        }
+        synchronized (populatingCatalogMutex) {
+            try {
+                if (hasRunFinalInitialization() && (needsInitialItemsLoaded || needsAdditionsLoaded)) {
+                    // if we have already run "final" then we should only ever be used to reset the catalog, 
+                    // not to initialize or add; e.g. we are being given a fixed list on a subsequent master rebind after the initial master rebind 
+                    log.warn("Catalog initialization called to populate initial, even though it has already run the final official initialization");
+                }
+                isPopulating = true;
+                BasicBrooklynCatalog catalog = (BasicBrooklynCatalog) managementContext.getCatalog();
+                if (!catalog.getCatalog().isLoaded()) {
+                    catalog.load();
+                } else {
+                    if (needsInitialItemsLoaded && hasRunAnyInitialization()) {
+                        // an indication that something caused it to load early; not severe, but unusual
+                        if (hasRunTransientOfficialInitialization) {
+                            log.debug("Catalog initialization now populating, but has noted a previous official run which was not final (probalby loaded while in a standby mode, or a previous run failed); overwriting any items installed earlier");
+                        } else {
+                            log.warn("Catalog initialization now populating, but has noted a previous unofficial run (it may have been an early web request); overwriting any items installed earlier");
+                        }
+                        catalog.reset(ImmutableList.<CatalogItem<?,?>>of());
+                    }
+                }
+
+                populateCatalogImpl(catalog, needsInitialItemsLoaded, needsAdditionsLoaded, optionalExcplicitItemsForResettingCatalog);
+                if (nodeState == ManagementNodeState.MASTER) {
+                    // TODO ideally this would remain false until it has *persisted* the changed catalog;
+                    // if there is a subsequent startup failure the forced additions will not be persisted,
+                    // but nor will they be loaded on a subsequent run.
+                    // callers will have to restart a brooklyn, or reach into this class to change this field,
+                    // or (recommended) manually adjust the catalog.
+                    // TODO also, if a node comes up in standby, the addition might not take effector for a while
+                    //
+                    // however since these options are mainly for use on the very first brooklyn run, it's not such a big deal; 
+                    // once up and running the typical way to add items is via the REST API
+                    hasRunFinalInitialization = true;
+                }
+            } finally {
+                if (!hasRunFinalInitialization) {
+                    hasRunTransientOfficialInitialization = true;
+                }
+                isPopulating = false;
+            }
+        }
+    }
+
+    private void populateCatalogImpl(BasicBrooklynCatalog catalog, boolean needsInitialItemsLoaded, boolean needsAdditionsLoaded, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) {
+        applyCatalogLoadMode();
+        
+        if (optionalItemsForResettingCatalog!=null) {
+            catalog.reset(optionalItemsForResettingCatalog);
+        }
+        
+        if (needsInitialItemsLoaded) {
+            populateInitial(catalog);
+        }
+
+        if (needsAdditionsLoaded) {
+            populateAdditions(catalog);
+            populateViaCallbacks(catalog);
+        }
+    }
+
+    private enum PopulateMode { YAML, XML, AUTODETECT }
+    
+    protected void populateInitial(BasicBrooklynCatalog catalog) {
+        if (disallowLocal) {
+            if (!hasRunFinalInitialization()) {
+                log.debug("CLI initial catalog not being read when local catalog load mode is disallowed.");
+            }
+            return;
+        }
+
+//        B1) look for --catalog-initial, if so read it, then go to C1
+//        B2) look for BrooklynServerConfig.BROOKLYN_CATALOG_URL, if so, read it, supporting YAML or XML (warning if XML), then go to C1
+//        B3) look for ~/.brooklyn/catalog.bom, if exists, read it then go to C1
+//        B4) look for ~/.brooklyn/brooklyn.xml, if exists, warn, read it then go to C1
+//        B5) read all classpath://brooklyn/default.catalog.bom items, if they exist (and for now they will)
+//        B6) go to C1
+
+        if (initialUri!=null) {
+            populateInitialFromUri(catalog, initialUri, PopulateMode.AUTODETECT);
+            return;
+        }
+        
+        String catalogUrl = managementContext.getConfig().getConfig(BrooklynServerConfig.BROOKLYN_CATALOG_URL);
+        if (Strings.isNonBlank(catalogUrl)) {
+            populateInitialFromUri(catalog, catalogUrl, PopulateMode.AUTODETECT);
+            return;
+        }
+        
+        catalogUrl = Os.mergePaths(BrooklynServerConfig.getMgmtBaseDir( managementContext.getConfig() ), "catalog.bom");
+        if (new File(catalogUrl).exists()) {
+            populateInitialFromUri(catalog, new File(catalogUrl).toURI().toString(), PopulateMode.YAML);
+            return;
+        }
+        
+        catalogUrl = Os.mergePaths(BrooklynServerConfig.getMgmtBaseDir( managementContext.getConfig() ), "catalog.xml");
+        if (new File(catalogUrl).exists()) {
+            populateInitialFromUri(catalog, new File(catalogUrl).toURI().toString(), PopulateMode.XML);
+            return;
+        }
+
+        // otherwise look for for classpath:/brooklyn/default.catalog.bom --
+        // there is one on the classpath which says to scan, and provides a few templates;
+        // if one is supplied by user in the conf/ dir that will override the item from the classpath
+        // (TBD - we might want to scan for all such bom's?)
+        
+        catalogUrl = "classpath:/brooklyn/default.catalog.bom";
+        if (new ResourceUtils(this).doesUrlExist(catalogUrl)) {
+            populateInitialFromUri(catalog, catalogUrl, PopulateMode.YAML);
+            return;
+        }
+        
+        log.info("No catalog found on classpath or specified; catalog will not be initialized.");
+        return;
+    }
+    
+    private void populateInitialFromUri(BasicBrooklynCatalog catalog, String catalogUrl, PopulateMode mode) {
+        log.debug("Loading initial catalog from {}", catalogUrl);
+
+        Exception problem = null;
+        Object result = null;
+        
+        String contents = null;
+        try {
+            contents = new ResourceUtils(this).getResourceAsString(catalogUrl);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            if (problem==null) problem = e;
+        }
+
+        if (contents!=null && (mode==PopulateMode.YAML || mode==PopulateMode.AUTODETECT)) {
+            // try YAML first
+            try {
+                catalog.reset(MutableList.<CatalogItem<?,?>>of());
+                result = catalog.addItems(contents);
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                if (problem==null) problem = e;
+            }
+        }
+        
+        if (result==null && contents!=null && (mode==PopulateMode.XML || mode==PopulateMode.AUTODETECT)) {
+            // then try XML
+            try {
+                populateInitialFromUriXml(catalog, catalogUrl, contents);
+                // clear YAML problem
+                problem = null;
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                if (problem==null) problem = e;
+            }
+        }
+        
+        if (result!=null) {
+            log.debug("Loaded initial catalog from {}: {}", catalogUrl, result);
+        }
+        if (problem!=null) {
+            log.warn("Error importing catalog from " + catalogUrl + ": " + problem, problem);
+            // TODO inform mgmt of error
+        }
+
+    }
+
+    // deprecated XML format
+    @SuppressWarnings("deprecation")
+    private void populateInitialFromUriXml(BasicBrooklynCatalog catalog, String catalogUrl, String contents) {
+        CatalogDto dto = CatalogDto.newDtoFromXmlContents(contents, catalogUrl);
+        if (dto!=null) {
+            catalog.reset(dto);
+        }
+    }
+
+    boolean hasRunAdditions = false;
+    protected void populateAdditions(BasicBrooklynCatalog catalog) {
+        if (Strings.isNonBlank(additionsUri)) {
+            if (disallowLocal) {
+                if (!hasRunAdditions) {
+                    log.warn("CLI additions supplied but not supported when catalog load mode disallows local loads; ignoring.");
+                }
+                return;
+            }   
+            if (!hasRunAdditions) {
+                log.debug("Adding to catalog from CLI: "+additionsUri+" (force: "+force+")");
+            }
+            Iterable<? extends CatalogItem<?, ?>> items = catalog.addItems(
+                new ResourceUtils(this).getResourceAsString(additionsUri), force);
+            
+            if (!hasRunAdditions)
+                log.debug("Added to catalog from CLI: "+items);
+            else
+                log.debug("Added to catalog from CLI: count "+Iterables.size(items));
+            
+            hasRunAdditions = true;
+        }
+    }
+
+    protected void populateViaCallbacks(BasicBrooklynCatalog catalog) {
+        for (Function<CatalogInitialization, Void> callback: callbacks)
+            callback.apply(this);
+    }
+
+    private Object setFromCLMMutex = new Object();
+    private boolean setFromCatalogLoadMode = false;
+
+    /** @deprecated since introduced in 0.7.0, only for legacy compatibility with 
+     * {@link CatalogLoadMode} {@link BrooklynServerConfig#CATALOG_LOAD_MODE},
+     * allowing control of catalog loading from a brooklyn property */
+    @Deprecated
+    public void applyCatalogLoadMode() {
+        synchronized (setFromCLMMutex) {
+            if (setFromCatalogLoadMode) return;
+            setFromCatalogLoadMode = true;
+            Maybe<Object> clmm = ((ManagementContextInternal)managementContext).getConfig().getConfigRaw(BrooklynServerConfig.CATALOG_LOAD_MODE, false);
+            if (clmm.isAbsent()) return;
+            org.apache.brooklyn.core.catalog.CatalogLoadMode clm = TypeCoercions.coerce(clmm.get(), org.apache.brooklyn.core.catalog.CatalogLoadMode.class);
+            log.warn("Legacy CatalogLoadMode "+clm+" set: applying, but this should be changed to use new CLI --catalogXxx commands");
+            switch (clm) {
+            case LOAD_BROOKLYN_CATALOG_URL:
+                reset = true;
+                break;
+            case LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE:
+                // now the default
+                break;
+            case LOAD_PERSISTED_STATE:
+                disallowLocal = true;
+                break;
+            }
+        }
+    }
+
+    /** Creates the catalog based on parameters set here, if not yet loaded,
+     * but ignoring persisted state and warning if persistence is on and we are starting up
+     * (because the official persistence is preferred and the catalog will be subsequently replaced);
+     * for use when the catalog is accessed before persistence is completed. 
+     * <p>
+     * This method is primarily used during testing, which in many cases does not enforce the full startup order
+     * and which wants a local catalog in any case. It may also be invoked if a client requests the catalog
+     * while the server is starting up. */
+    public void populateUnofficial(BasicBrooklynCatalog catalog) {
+        synchronized (populatingCatalogMutex) {
+            // check isPopulating in case this method gets called from inside another populate call
+            if (hasRunAnyInitialization() || isPopulating) return;
+            log.debug("Populating catalog unofficially ("+catalog+")");
+            isPopulating = true;
+            try {
+                if (isStartingUp) {
+                    log.warn("Catalog access requested when not yet initialized; populating best effort rather than through recommended pathway. Catalog data may be replaced subsequently.");
+                }
+                populateCatalogImpl(catalog, true, true, null);
+            } finally {
+                hasRunUnofficialInitialization = true;
+                isPopulating = false;
+            }
+        }
+    }
+
+    public void handleException(Throwable throwable, Object details) {
+        if (throwable instanceof InterruptedException)
+            throw new RuntimeInterruptedException((InterruptedException) throwable);
+        if (throwable instanceof RuntimeInterruptedException)
+            throw (RuntimeInterruptedException) throwable;
+
+        log.error("Error loading catalog item '"+details+"': "+throwable);
+        log.debug("Trace for error loading catalog item '"+details+"': "+throwable, throwable);
+
+        // TODO give more detail when adding
+        ((ManagementContextInternal)getManagementContext()).errors().add(throwable);
+        
+        if (isStartingUp && failOnStartupErrors) {
+            throw new FatalRuntimeException("Unable to load catalog item '"+details+"': "+throwable, throwable);
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java
new file mode 100644
index 0000000..e157a51
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.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.catalog.internal;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+
+import com.google.common.base.Preconditions;
+
+public class CatalogItemBuilder<CatalogItemType extends CatalogItemDtoAbstract<?, ?>> {
+    private CatalogItemType dto;
+
+    public static CatalogItemBuilder<CatalogEntityItemDto> newEntity(String symbolicName, String version) {
+        return new CatalogItemBuilder<CatalogEntityItemDto>(new CatalogEntityItemDto())
+                .symbolicName(symbolicName)
+                .version(version);
+    }
+
+    public static CatalogItemBuilder<CatalogTemplateItemDto> newTemplate(String symbolicName, String version) {
+        return new CatalogItemBuilder<CatalogTemplateItemDto>(new CatalogTemplateItemDto())
+                .symbolicName(symbolicName)
+                .version(version);
+    }
+
+    public static CatalogItemBuilder<CatalogPolicyItemDto> newPolicy(String symbolicName, String version) {
+        return new CatalogItemBuilder<CatalogPolicyItemDto>(new CatalogPolicyItemDto())
+                .symbolicName(symbolicName)
+                .version(version);
+    }
+
+    public static CatalogItemBuilder<CatalogLocationItemDto> newLocation(String symbolicName, String version) {
+        return new CatalogItemBuilder<CatalogLocationItemDto>(new CatalogLocationItemDto())
+                .symbolicName(symbolicName)
+                .version(version);
+    }
+
+    public CatalogItemBuilder(CatalogItemType dto) {
+        this.dto = dto;
+        this.dto.setLibraries(Collections.<CatalogBundle>emptyList());
+    }
+
+    public CatalogItemBuilder<CatalogItemType> symbolicName(String symbolicName) {
+        dto.setSymbolicName(symbolicName);
+        return this;
+    }
+
+    @Deprecated
+    public CatalogItemBuilder<CatalogItemType> javaType(String javaType) {
+        dto.setJavaType(javaType);
+        return this;
+    }
+
+    /** @deprecated since 0.7.0 use {@link #displayName}*/
+    @Deprecated
+    public CatalogItemBuilder<CatalogItemType> name(String name) {
+        return displayName(name);
+    }
+
+    public CatalogItemBuilder<CatalogItemType> displayName(String displayName) {
+        dto.setDisplayName(displayName);
+        return this;
+    }
+
+    public CatalogItemBuilder<CatalogItemType> description(String description) {
+        dto.setDescription(description);
+        return this;
+    }
+
+    public CatalogItemBuilder<CatalogItemType> iconUrl(String iconUrl) {
+        dto.setIconUrl(iconUrl);
+        return this;
+    }
+
+    public CatalogItemBuilder<CatalogItemType> version(String version) {
+        dto.setVersion(version);
+        return this;
+    }
+
+    public CatalogItemBuilder<CatalogItemType> deprecated(boolean deprecated) {
+        dto.setDeprecated(deprecated);
+        return this;
+    }
+
+    public CatalogItemBuilder<CatalogItemType> libraries(Collection<CatalogBundle> libraries) {
+        dto.setLibraries(libraries);
+        return this;
+    }
+
+    public CatalogItemBuilder<CatalogItemType> plan(String yaml) {
+        dto.setPlanYaml(yaml);
+        return this;
+    }
+
+    public CatalogItemType build() {
+        Preconditions.checkNotNull(dto.getSymbolicName());
+        Preconditions.checkNotNull(dto.getVersion());
+
+        if (dto.getLibraries() == null) {
+            dto.setLibraries(Collections.<CatalogBundle>emptyList());
+        }
+
+        CatalogItemType ret = dto;
+
+        //prevent mutations through the builder
+        dto = null;
+
+        return ret;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6caee589/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparator.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparator.java
new file mode 100644
index 0000000..6832503
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparator.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.catalog.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+
+import brooklyn.util.text.NaturalOrderComparator;
+
+/**
+ * Largest version first order.
+ * 
+ * When using the comparator to sort - first using symbolicName
+ * and if equal puts larger versions first, snapshots at the back.
+ */
+public class CatalogItemComparator<T,SpecT> implements Comparator<CatalogItem<T, SpecT>> {
+    private static final String SNAPSHOT = "SNAPSHOT";
+
+    public static final CatalogItemComparator<?, ?> INSTANCE = new CatalogItemComparator<Object, Object>();
+
+    @SuppressWarnings("unchecked")
+    public static <T,SpecT> CatalogItemComparator<T,SpecT> getInstance() {
+        return (CatalogItemComparator<T, SpecT>) INSTANCE;
+    }
+
+    @Override
+    public int compare(CatalogItem<T, SpecT> o1, CatalogItem<T, SpecT> o2) {
+        int symbolicNameComparison = o1.getSymbolicName().compareTo(o2.getSymbolicName());
+        if (symbolicNameComparison != 0) {
+            return symbolicNameComparison;
+        } else {
+            String v1 = o1.getVersion();
+            String v2 = o2.getVersion();
+
+            boolean isV1Snapshot = v1.toUpperCase().contains(SNAPSHOT);
+            boolean isV2Snapshot = v2.toUpperCase().contains(SNAPSHOT);
+            if (isV1Snapshot == isV2Snapshot) {
+                String[] v1Parts = split(v1);
+                String[] v2Parts = split(v2);
+                return -compare(v1Parts, v2Parts);
+            } else if (isV1Snapshot) {
+                return 1;
+            } else {
+                return -1;
+            }
+        }
+    }
+
+    private String[] split(String v) {
+        Collection<String> parts = new ArrayList<String>();
+        int startPos = 0;
+        int delimPos;
+        while ((delimPos = v.indexOf('.', startPos)) != -1) {
+            String part = v.substring(startPos, delimPos);
+            if (parse(part) != -1) {
+                parts.add(part);
+            } else {
+                break;
+            }
+            startPos = delimPos+1;
+        }
+        String remaining = v.substring(startPos);
+        parts.addAll(Arrays.asList(remaining.split("[^\\d]", 2)));
+        return parts.toArray(new String[parts.size()]);
+    }
+
+    private int compare(String[] v1Parts, String[] v2Parts) {
+        int len = Math.max(v1Parts.length, v2Parts.length);
+        for (int i = 0; i < len; i++) {
+            if (i == v1Parts.length) {
+                return isNumber(v2Parts[i]) ? -1 : 1;
+            }
+            if (i == v2Parts.length) {
+                return isNumber(v1Parts[i]) ? 1 : -1;
+            }
+
+            String p1 = v1Parts[i];
+            String p2 = v2Parts[i];
+            int n1 = parse(p1);
+            int n2 = parse(p2);
+            if (n1 != -1 && n2 != -1) {
+                if (n1 != n2) {
+                    return compare(n1, n2);
+                }
+            } else if (n1 == -1 && n2 != -1) {
+                return -1;
+            } else if (n1 != -1 && n2 == -1) {
+                return 1;
+            } else {
+                int cmp = NaturalOrderComparator.INSTANCE.compare(p1, p2);
+                if (cmp < 0) {
+                    return -1;
+                } else if (cmp > 0) {
+                    return 1;
+                }
+            }
+        }
+        return 0;
+    }
+
+    private boolean isNumber(String v) {
+        return parse(v) != -1;
+    }
+
+    //Replace with Integer.compare in J7
+    private int compare(int n1, int n2) {
+        if (n1 == n2) {
+            return 0;
+        } else if (n1 < n2) {
+            return -1;
+        } else {
+            return 1;
+        }
+    }
+
+    private int parse(String p) {
+        try {
+            return Integer.parseInt(p);
+        } catch (NumberFormatException e) {
+            return -1;
+        }
+    }
+}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentManagementContext.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentManagementContext.java
new file mode 100644
index 0000000..9d98792
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentManagementContext.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.management.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.URI;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.api.basic.BrooklynObject;
+import org.apache.brooklyn.api.catalog.BrooklynCatalog;
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.drivers.EntityDriverManager;
+import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolverManager;
+import org.apache.brooklyn.api.entity.rebind.ChangeListener;
+import org.apache.brooklyn.api.entity.rebind.PersistenceExceptionHandler;
+import org.apache.brooklyn.api.entity.rebind.RebindExceptionHandler;
+import org.apache.brooklyn.api.entity.rebind.RebindManager;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationRegistry;
+import org.apache.brooklyn.api.management.AccessController;
+import org.apache.brooklyn.api.management.EntityManager;
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.ExecutionManager;
+import org.apache.brooklyn.api.management.LocationManager;
+import org.apache.brooklyn.api.management.SubscriptionContext;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
+import org.apache.brooklyn.api.management.ha.HighAvailabilityManager;
+import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
+import org.apache.brooklyn.api.mementos.BrooklynMementoPersister;
+import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.management.ha.OsgiManager;
+
+import brooklyn.catalog.internal.CatalogInitialization;
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.StringConfigMap;
+import brooklyn.entity.basic.AbstractEntity;
+import brooklyn.entity.proxying.InternalEntityFactory;
+import brooklyn.entity.proxying.InternalLocationFactory;
+import brooklyn.entity.proxying.InternalPolicyFactory;
+import brooklyn.internal.storage.BrooklynStorage;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Objects;
+
+public class NonDeploymentManagementContext implements ManagementContextInternal {
+
+    private static final Logger log = LoggerFactory.getLogger(NonDeploymentManagementContext.class);
+    
+    public enum NonDeploymentManagementContextMode {
+        PRE_MANAGEMENT,
+        MANAGEMENT_REBINDING,
+        MANAGEMENT_STARTING,
+        MANAGEMENT_STARTED,
+        MANAGEMENT_STOPPING,
+        MANAGEMENT_STOPPED;
+        
+        public boolean isPreManaged() {
+            return this == PRE_MANAGEMENT || this == MANAGEMENT_REBINDING;
+        }
+    }
+    
+    private final AbstractEntity entity;
+    private NonDeploymentManagementContextMode mode;
+    private ManagementContextInternal initialManagementContext;
+    
+    private final QueueingSubscriptionManager qsm;
+    private final BasicSubscriptionContext subscriptionContext;
+    private NonDeploymentEntityManager entityManager;
+    private NonDeploymentLocationManager locationManager;
+    private NonDeploymentAccessManager accessManager;
+    private NonDeploymentUsageManager usageManager;
+    private EntitlementManager entitlementManager;;
+
+    public NonDeploymentManagementContext(AbstractEntity entity, NonDeploymentManagementContextMode mode) {
+        this.entity = checkNotNull(entity, "entity");
+        this.mode = checkNotNull(mode, "mode");
+        qsm = new QueueingSubscriptionManager();
+        subscriptionContext = new BasicSubscriptionContext(qsm, entity);
+        entityManager = new NonDeploymentEntityManager(null);
+        locationManager = new NonDeploymentLocationManager(null);
+        accessManager = new NonDeploymentAccessManager(null);
+        usageManager = new NonDeploymentUsageManager(null);
+        
+        // TODO might need to be some kind of "system" which can see that the system is running at this point
+        // though quite possibly we are entirely behind the auth-wall at this point
+        entitlementManager = Entitlements.minimal();
+    }
+
+    @Override
+    public String getManagementPlaneId() {
+        return (initialManagementContext == null) ? null : initialManagementContext.getManagementPlaneId();
+    }
+    
+    @Override
+    public String getManagementNodeId() {
+        return (initialManagementContext == null) ? null : initialManagementContext.getManagementNodeId();
+    }
+
+    @Override
+    public Maybe<URI> getManagementNodeUri() {
+        return (initialManagementContext == null) ? Maybe.<URI>absent() : initialManagementContext.getManagementNodeUri();
+    }
+
+    public void setManagementContext(ManagementContextInternal val) {
+        this.initialManagementContext = checkNotNull(val, "initialManagementContext");
+        this.entityManager = new NonDeploymentEntityManager(val);
+        this.locationManager = new NonDeploymentLocationManager(val);
+        this.accessManager = new NonDeploymentAccessManager(val);
+        this.usageManager = new NonDeploymentUsageManager(val);
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this).add("entity", entity.getId()).add("mode", mode).toString();
+    }
+    
+    public void setMode(NonDeploymentManagementContextMode mode) {
+        this.mode = checkNotNull(mode, "mode");
+    }
+    public NonDeploymentManagementContextMode getMode() {
+        return mode;
+    }
+    
+    @Override
+    public Collection<Application> getApplications() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean isRunning() {
+        // Assume that the real management context has not been terminated, so always true
+        return true;
+    }
+    
+    @Override
+    public boolean isStartupComplete() {
+        // This mgmt context is only used by items who are not yet fully started.
+        // It's slightly misleading as this does not refer to the main mgmt context.
+        // OTOH it probably won't be used.  TBC.  -Alex, Apr 2015
+        return false;
+    }
+
+    @Override
+    public InternalEntityFactory getEntityFactory() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getEntityFactory();
+    }
+
+    @Override
+    public InternalLocationFactory getLocationFactory() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getLocationFactory();
+    }
+
+    @Override
+    public InternalPolicyFactory getPolicyFactory() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getPolicyFactory();
+    }
+
+    @Override
+    public EntityManager getEntityManager() {
+        return entityManager;
+    }
+    
+    @Override
+    public LocationManager getLocationManager() {
+        return locationManager;
+    }
+
+    @Override
+    public AccessManager getAccessManager() {
+        return accessManager;
+    }
+
+    @Override
+    public UsageManager getUsageManager() {
+        return usageManager;
+    }
+    
+    @Override
+    public Maybe<OsgiManager> getOsgiManager() {
+        return Maybe.absent();
+    }
+
+    @Override
+    public AccessController getAccessController() {
+        return getAccessManager().getAccessController();
+    }
+
+    @Override
+    public ExecutionManager getExecutionManager() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getExecutionManager();
+    }
+
+    @Override
+    public QueueingSubscriptionManager getSubscriptionManager() {
+        return qsm;
+    }
+
+    @Override
+    public synchronized SubscriptionContext getSubscriptionContext(Entity entity) {
+        if (!this.entity.equals(entity)) throw new IllegalStateException("Non-deployment context "+this+" can only use a single Entity: has "+this.entity+", but passed "+entity);
+        if (mode==NonDeploymentManagementContextMode.MANAGEMENT_STOPPED)
+            throw new IllegalStateException("Entity "+entity+" is no longer managed; subscription context not available");
+        return subscriptionContext;
+    }
+
+    @Override
+    public ExecutionContext getExecutionContext(Entity entity) {
+        if (!this.entity.equals(entity)) throw new IllegalStateException("Non-deployment context "+this+" can only use a single Entity: has "+this.entity+", but passed "+entity);
+        if (mode==NonDeploymentManagementContextMode.MANAGEMENT_STOPPED)
+            throw new IllegalStateException("Entity "+entity+" is no longer managed; execution context not available");
+        checkInitialManagementContextReal();
+        return initialManagementContext.getExecutionContext(entity);
+    }
+
+    @Override
+    public ExecutionContext getServerExecutionContext() {
+        return initialManagementContext.getServerExecutionContext();
+    }
+
+    // TODO the methods below should delegate to the application?
+    @Override
+    public EntityDriverManager getEntityDriverManager() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getEntityDriverManager();
+    }
+
+    @Override
+    public DownloadResolverManager getEntityDownloadsManager() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getEntityDownloadsManager();
+    }
+
+    @Override
+    public StringConfigMap getConfig() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getConfig();
+    }
+
+    @Override
+    public BrooklynProperties getBrooklynProperties() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getBrooklynProperties();
+    }
+
+    @Override
+    public BrooklynStorage getStorage() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getStorage();
+    }
+    
+    @Override
+    public RebindManager getRebindManager() {
+        // There was a race where EffectorUtils on invoking an effector calls:
+        //     mgmtSupport.getEntityChangeListener().onEffectorCompleted(eff);
+        // but where the entity/app may be being unmanaged concurrently (e.g. calling app.stop()).
+        // So now we allow the change-listener to be called.
+        
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getRebindManager();
+        } else {
+            return new NonDeploymentRebindManager();
+        }
+    }
+
+    @Override
+    public HighAvailabilityManager getHighAvailabilityManager() {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getHighAvailabilityManager();
+        } else {
+            return new NonDeploymentHighAvailabilityManager();
+        }
+    }
+
+    @Override
+    public LocationRegistry getLocationRegistry() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getLocationRegistry();
+    }
+
+    @Override
+    public BrooklynCatalog getCatalog() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getCatalog();
+    }
+    
+    @Override
+    public ClassLoader getCatalogClassLoader() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getCatalogClassLoader();
+    }
+    
+    @Override
+    public EntitlementManager getEntitlementManager() {
+        return entitlementManager;
+    }
+    
+    @Override
+    public <T> T invokeEffectorMethodSync(final Entity entity, final Effector<T> eff, final Object args) throws ExecutionException {
+        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot invoke effector "+eff+" on entity "+entity);
+    }
+    
+    @Override
+    public <T> Task<T> invokeEffector(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters) {
+        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot invoke effector "+eff+" on entity "+entity);
+    }
+
+    @Override
+    public ClassLoader getBaseClassLoader() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getBaseClassLoader();
+    }
+
+    @Override
+    public Iterable<URL> getBaseClassPathForScanning() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getBaseClassPathForScanning();
+    }
+
+    @Override
+    public void addEntitySetListener(CollectionChangeListener<Entity> listener) {
+        checkInitialManagementContextReal();
+        initialManagementContext.addEntitySetListener(listener);
+    }
+
+    @Override
+    public void removeEntitySetListener(CollectionChangeListener<Entity> listener) {
+        checkInitialManagementContextReal();
+        initialManagementContext.removeEntitySetListener(listener);
+    }
+
+    @Override
+    public void terminate() {
+        if (isInitialManagementContextReal()) {
+            initialManagementContext.terminate();
+        } else {
+            // no-op; the non-deployment management context has nothing needing terminated
+        }
+    }
+
+    @Override
+    public long getTotalEffectorInvocations() {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getTotalEffectorInvocations();
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public void setBaseClassPathForScanning(Iterable<URL> urls) {
+        checkInitialManagementContextReal();
+        initialManagementContext.setBaseClassPathForScanning(urls);
+    }
+
+    @Override
+    public void setManagementNodeUri(URI uri) {
+        checkInitialManagementContextReal();
+        initialManagementContext.setManagementNodeUri(uri);
+    }
+
+    @Override
+    public void prePreManage(Entity entity) {
+        // should throw?  but in 0.7.0-SNAPSHOT it was no-op
+        log.warn("Ignoring call to prePreManage("+entity+") on "+this);
+    }
+
+    @Override
+    public void prePreManage(Location location) {
+        // should throw?  but in 0.7.0-SNAPSHOT it was no-op
+        log.warn("Ignoring call to prePreManage("+location+") on "+this);
+    }
+
+    private boolean isInitialManagementContextReal() {
+        return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext));
+    }
+    
+    private void checkInitialManagementContextReal() {
+        if (!isInitialManagementContextReal()) {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation.");
+        }
+    }
+    
+    @Override
+    public void reloadBrooklynProperties() {
+        checkInitialManagementContextReal();
+        initialManagementContext.reloadBrooklynProperties();
+    }
+    
+    @Override
+    public void addPropertiesReloadListener(PropertiesReloadListener listener) {
+        checkInitialManagementContextReal();
+        initialManagementContext.addPropertiesReloadListener(listener);
+    }
+    
+    @Override
+    public void removePropertiesReloadListener(PropertiesReloadListener listener) {
+        checkInitialManagementContextReal();
+        initialManagementContext.removePropertiesReloadListener(listener);
+    }
+
+    @Override
+    public BrooklynObject lookup(String id) {
+        checkInitialManagementContextReal();
+        return initialManagementContext.lookup(id);
+    }
+
+    @Override
+    public <T extends BrooklynObject> T lookup(String id, Class<T> type) {
+        checkInitialManagementContextReal();
+        return initialManagementContext.lookup(id, type);
+    }
+
+    @Override
+    public List<Throwable> errors() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.errors();
+    }
+
+    @Override
+    public CatalogInitialization getCatalogInitialization() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getCatalogInitialization();
+    }
+    
+    @Override
+    public void setCatalogInitialization(CatalogInitialization catalogInitialization) {
+        checkInitialManagementContextReal();
+        initialManagementContext.setCatalogInitialization(catalogInitialization);
+    }
+
+    /**
+     * For when the initial management context is not "real"; the changeListener is a no-op, but everything else forbidden.
+     * 
+     * @author aled
+     */
+    private class NonDeploymentRebindManager implements RebindManager {
+
+        @Override
+        public ChangeListener getChangeListener() {
+            return ChangeListener.NOOP;
+        }
+
+        @Override
+        public void setPersister(BrooklynMementoPersister persister) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+
+        @Override
+        public void setPersister(BrooklynMementoPersister persister, PersistenceExceptionHandler exceptionHandler) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+
+        @Override
+        public BrooklynMementoPersister getPersister() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+
+        @Override
+        public List<Application> rebind() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+
+        @Override
+        public List<Application> rebind(ClassLoader classLoader) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+
+        @Override
+        public List<Application> rebind(ClassLoader classLoader, RebindExceptionHandler exceptionHandler) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        
+        @Override
+        public List<Application> rebind(ClassLoader classLoader, RebindExceptionHandler exceptionHandler, ManagementNodeState mode) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+
+        @Override
+        public void startPersistence() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        
+        @Override
+        public void stopPersistence() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+
+        @Override
+        public void startReadOnly(ManagementNodeState state) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        
+        @Override
+        public void stopReadOnly() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+
+        @Override
+        public void start() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        
+        @Override
+        public void stop() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+
+        @Override
+        public void waitForPendingComplete(Duration timeout, boolean canTrigger) throws InterruptedException, TimeoutException {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public void forcePersistNow() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public void forcePersistNow(boolean full, PersistenceExceptionHandler exceptionHandler) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public BrooklynMementoRawData retrieveMementoRawData() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public boolean isAwaitingInitialRebind() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        
+        @Override
+        public Map<String, Object> getMetrics() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+    }
+
+    /**
+     * For when the initial management context is not "real".
+     * 
+     * @author aled
+     */
+    private class NonDeploymentHighAvailabilityManager implements HighAvailabilityManager {
+        @Override
+        public ManagementNodeState getNodeState() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public boolean isRunning() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public HighAvailabilityManager setPersister(ManagementPlaneSyncRecordPersister persister) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public void disabled() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public void start(HighAvailabilityMode startMode) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public void stop() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public ManagementPlaneSyncRecordPersister getPersister() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public ManagementPlaneSyncRecord getLastManagementPlaneSyncRecord() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public ManagementPlaneSyncRecord loadManagementPlaneSyncRecord(boolean x) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public void changeMode(HighAvailabilityMode startMode) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public void setPriority(long priority) {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public long getPriority() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public Map<String, Object> getMetrics() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public void publishClearNonMaster() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+        @Override
+        public long getLastStateChange() {
+            throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentUsageManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentUsageManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentUsageManager.java
new file mode 100644
index 0000000..55a4a78
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentUsageManager.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.management.usage.ApplicationUsage;
+import org.apache.brooklyn.core.management.usage.LocationUsage;
+
+import brooklyn.entity.basic.Lifecycle;
+
+import com.google.common.base.Predicate;
+
+
+public class NonDeploymentUsageManager implements UsageManager {
+
+    // TODO All the `isInitialManagementContextReal()` code-checks is a code-smell.
+    // Expect we can delete a lot of this once we guarantee that all entities are 
+    // instantiated via EntitySpec / EntityManager. Until then, we'll live with this.
+    
+    private final ManagementContextInternal initialManagementContext;
+    
+    public NonDeploymentUsageManager(ManagementContextInternal initialManagementContext) {
+        this.initialManagementContext = initialManagementContext;
+    }
+    
+    private boolean isInitialManagementContextReal() {
+        return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext));
+    }
+
+    @Override
+    public void recordApplicationEvent(Application app, Lifecycle state) {
+        if (isInitialManagementContextReal()) {
+            initialManagementContext.getUsageManager().recordApplicationEvent(app, state);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public void recordLocationEvent(Location loc, Lifecycle state) {
+        if (isInitialManagementContextReal()) {
+            initialManagementContext.getUsageManager().recordLocationEvent(loc, state);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public LocationUsage getLocationUsage(String locationId) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getUsageManager().getLocationUsage(locationId);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public Set<LocationUsage> getLocationUsage(Predicate<? super LocationUsage> filter) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getUsageManager().getLocationUsage(filter);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public ApplicationUsage getApplicationUsage(String appId) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getUsageManager().getApplicationUsage(appId);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public Set<ApplicationUsage> getApplicationUsage(Predicate<? super ApplicationUsage> filter) {
+        if (isInitialManagementContextReal()) {
+            return initialManagementContext.getUsageManager().getApplicationUsage(filter);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    @Deprecated
+    public void addUsageListener(org.apache.brooklyn.core.management.internal.UsageManager.UsageListener listener) {
+        addUsageListener(new org.apache.brooklyn.core.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener));
+    }
+
+    @Override
+    @Deprecated
+    public void removeUsageListener(org.apache.brooklyn.core.management.internal.UsageManager.UsageListener listener) {
+        removeUsageListener(new org.apache.brooklyn.core.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener));
+    }
+    
+    @Override
+    public void addUsageListener(org.apache.brooklyn.core.management.internal.UsageListener listener) {
+        if (isInitialManagementContextReal()) {
+            initialManagementContext.getUsageManager().addUsageListener(listener);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+
+    @Override
+    public void removeUsageListener(org.apache.brooklyn.core.management.internal.UsageListener listener) {
+        if (isInitialManagementContextReal()) {
+            initialManagementContext.getUsageManager().removeUsageListener(listener);
+        } else {
+            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/QueueingSubscriptionManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/QueueingSubscriptionManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/QueueingSubscriptionManager.java
new file mode 100644
index 0000000..8f54d95
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/QueueingSubscriptionManager.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.event.Sensor;
+import org.apache.brooklyn.api.event.SensorEvent;
+import org.apache.brooklyn.api.management.SubscriptionHandle;
+
+import com.google.common.base.Objects;
+
+@SuppressWarnings("rawtypes")
+public class QueueingSubscriptionManager extends AbstractSubscriptionManager {
+
+    static class QueuedSubscription<T> {
+        Map<String, Object> flags;
+        Subscription<T> s;
+    }
+
+    public AbstractSubscriptionManager delegate = null;
+    public boolean useDelegateForSubscribing = false;
+    public boolean useDelegateForPublishing = false;
+    
+    List<QueuedSubscription> queuedSubscriptions = new ArrayList<QueuedSubscription>();
+    List<SensorEvent> queuedSensorEvents = new ArrayList<SensorEvent>();
+    
+    @Override
+    protected synchronized <T> SubscriptionHandle subscribe(Map<String, Object> flags, Subscription<T> s) {
+        if (useDelegateForSubscribing)
+            return delegate.subscribe(flags, s);
+        
+        QueuedSubscription<T> qs = new QueuedSubscription<T>();
+        qs.flags = flags;
+        s.subscriber = getSubscriber(flags, s);
+        qs.s = s;
+        queuedSubscriptions.add(qs);
+        return s;
+    }
+
+    @Override
+    public synchronized <T> void publish(SensorEvent<T> event) {
+        if (useDelegateForPublishing) {
+            delegate.publish(event);
+            return;
+        }
+        
+        queuedSensorEvents.add(event);
+    }
+
+    public void setDelegate(AbstractSubscriptionManager delegate) {
+        this.delegate = delegate;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public synchronized void startDelegatingForSubscribing() {
+        assert delegate!=null;
+        for (QueuedSubscription s: queuedSubscriptions) {
+            delegate.subscribe(s.flags, s.s);
+        }
+        queuedSubscriptions.clear();
+        useDelegateForSubscribing = true;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public synchronized void startDelegatingForPublishing() {
+        assert delegate!=null;
+        for (SensorEvent evt: queuedSensorEvents) {
+            delegate.publish(evt);
+        }
+        queuedSensorEvents.clear();
+        useDelegateForPublishing = true;
+    }
+    
+    public synchronized void stopDelegatingForSubscribing() {
+        useDelegateForSubscribing = false;
+    }
+    
+    public synchronized void stopDelegatingForPublishing() {
+        useDelegateForPublishing = false;
+    }
+
+    @Override
+    public synchronized boolean unsubscribe(SubscriptionHandle subscriptionId) {
+        if (useDelegateForSubscribing)
+            return delegate.unsubscribe(subscriptionId);
+        
+        Iterator<QueuedSubscription> qi = queuedSubscriptions.iterator();
+        while (qi.hasNext()) {
+            QueuedSubscription q = qi.next();
+            if (Objects.equal(subscriptionId, q.s)) {
+                qi.remove();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public synchronized Set<SubscriptionHandle> getSubscriptionsForSubscriber(Object subscriber) {
+        if (useDelegateForSubscribing)
+            return delegate.getSubscriptionsForSubscriber(subscriber);
+        
+        Set<SubscriptionHandle> result = new LinkedHashSet<SubscriptionHandle>();
+        for (QueuedSubscription q: queuedSubscriptions) {
+            if (Objects.equal(subscriber, getSubscriber(q.flags, q.s))) result.add(q.s);
+        }
+        return result;
+    }
+
+    @Override
+    public synchronized Set<SubscriptionHandle> getSubscriptionsForEntitySensor(Entity source, Sensor<?> sensor) {
+        if (useDelegateForSubscribing)
+            return delegate.getSubscriptionsForEntitySensor(source, sensor);
+        
+        Set<SubscriptionHandle> result = new LinkedHashSet<SubscriptionHandle>();
+        for (QueuedSubscription q: queuedSubscriptions) {
+            if ((q.s.sensor==null || Objects.equal(q.s.sensor, sensor)) &&
+                    (q.s.producer==null || Objects.equal(q.s.producer, sensor)))
+                result.add(q.s);
+        }
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/Subscription.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/Subscription.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/Subscription.java
new file mode 100644
index 0000000..acd30c3
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/Subscription.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.management.internal;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+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.management.SubscriptionHandle;
+
+import brooklyn.util.text.Identifiers;
+
+import com.google.common.base.Predicate;
+
+class Subscription<T> implements SubscriptionHandle {
+    public final String id = Identifiers.makeRandomId(8);
+    
+    public Object subscriber;
+    public Object subscriberExecutionManagerTag;
+    /** whether the tag was supplied by user, in which case we should not clear execution semantics */
+    public boolean subscriberExecutionManagerTagSupplied;
+    public final Entity producer;
+    public final Sensor<T> sensor;
+    public final SensorEventListener<? super T> listener;
+    public Map<String,Object> flags;
+    public Predicate<SensorEvent<T>> eventFilter;
+
+    public Subscription(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        this.producer = producer;
+        this.sensor = sensor;
+        this.listener = listener;
+    }
+    
+    @Override
+    public boolean equals(Object other) {
+        return (other instanceof Subscription && ((Subscription<?>)other).id==id);
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+    
+    @Override
+    public String toString() {
+        return "Subscription["+id+";"+subscriber+"@"+LocalSubscriptionManager.makeEntitySensorToken(producer,sensor)+"]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/SubscriptionTracker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/SubscriptionTracker.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/SubscriptionTracker.java
new file mode 100644
index 0000000..acc0cb6
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/SubscriptionTracker.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.management.internal;
+
+import java.util.Collection;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.Group;
+import org.apache.brooklyn.api.event.Sensor;
+import org.apache.brooklyn.api.event.SensorEventListener;
+import org.apache.brooklyn.api.management.SubscriptionContext;
+import org.apache.brooklyn.api.management.SubscriptionHandle;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.SetMultimap;
+
+/**
+ * Tracks subscriptions associated that are registered with particular entities. Gives utilities for unsubscribing from all
+ * subscriptions on a given entity, etc.
+ */
+public class SubscriptionTracker {
+
+    // This class is thread-safe. All modifications to subscriptions are synchronized on the 
+    // "subscriptions" field. However, calls to alien code (i.e. context.subscribe etc) is
+    // done without holding the lock.
+    //
+    // If two threads do subscribe() and unsubscribeAll() concurrently, then it's non-derministic
+    // whether the subscription will be in place at the end (but that's unavoidable). However, it
+    // is guaranteed that the internal state of the SubscriptionTracker will be consistent: if
+    // the "subscriptions" includes the new subscription then that subscription will really exist,
+    // and vice versa.
+    
+    protected SubscriptionContext context;
+    
+    private final SetMultimap<Entity, SubscriptionHandle> subscriptions = HashMultimap.create();
+
+    public SubscriptionTracker(SubscriptionContext subscriptionContext) {
+        this.context = subscriptionContext;
+    }
+    
+    /** @see SubscriptionContext#subscribe(Entity, Sensor, SensorEventListener) */
+    public <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        SubscriptionHandle handle = context.subscribe(producer, sensor, listener);
+        synchronized (subscriptions) {
+            subscriptions.put(producer, handle);
+        }
+        return handle;
+    }
+    
+    /** @see SubscriptionContext#subscribeToChildren(Entity, Sensor, SensorEventListener) */
+    public <T> SubscriptionHandle subscribeToChildren(Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        SubscriptionHandle handle = context.subscribeToChildren(parent, sensor, listener);
+        synchronized (subscriptions) {
+            subscriptions.put(parent, handle);
+        }
+        return handle;
+    }
+
+    /**
+     * @see SubscriptionContext#subscribeToMembers(Group, Sensor, SensorEventListener)
+     */
+    public <T> SubscriptionHandle subscribeToMembers(Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        SubscriptionHandle handle = context.subscribeToMembers(parent, sensor, listener);
+        synchronized (subscriptions) {
+            subscriptions.put(parent, handle);
+        }
+        return handle;
+    }    
+
+    /**
+     * Unsubscribes the given producer.
+     *
+     * @see SubscriptionContext#unsubscribe(SubscriptionHandle)
+     */
+    public boolean unsubscribe(Entity producer) {
+        Collection<SubscriptionHandle> handles;
+        synchronized (subscriptions) {
+            handles = subscriptions.removeAll(producer);
+        }
+        if (handles != null) {
+            for (SubscriptionHandle handle : handles) {
+                context.unsubscribe(handle);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+    * Unsubscribes the given producer.
+    *
+    * @see SubscriptionContext#unsubscribe(SubscriptionHandle)
+    */
+   public boolean unsubscribe(Entity producer, SubscriptionHandle handle) {
+       synchronized (subscriptions) {
+           subscriptions.remove(producer, handle);
+       }
+       return context.unsubscribe(handle);
+   }
+
+    /**
+    * @return an ordered list of all subscription handles
+    */
+   public Collection<SubscriptionHandle> getAllSubscriptions() {
+       synchronized (subscriptions) {
+           return ImmutableList.copyOf(subscriptions.values());
+       }
+   }
+   
+   public void unsubscribeAll() {
+       Collection<SubscriptionHandle> subscriptionsSnapshot;
+       synchronized (subscriptions) {
+           subscriptionsSnapshot = ImmutableList.copyOf(subscriptions.values());
+           subscriptions.clear();
+       }
+       for (SubscriptionHandle s: subscriptionsSnapshot) {
+           context.unsubscribe(s);
+       }
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/UsageListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/UsageListener.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/UsageListener.java
new file mode 100644
index 0000000..566c0f0
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/UsageListener.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.management.usage.ApplicationUsage.ApplicationEvent;
+import org.apache.brooklyn.core.management.usage.LocationUsage.LocationEvent;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface UsageListener {
+    
+    /**
+     * A no-op implementation of {@link UsageListener}, for users to extend.
+     * 
+     * Users are encouraged to extend this class, which will shield the user 
+     * from the addition of other usage event methods being added. If additional
+     * methods are added in a future release, a no-op implementation will be
+     * added to this class.
+     */
+    @Beta
+    public static class BasicUsageListener implements UsageListener {
+        @Override
+        public void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event) {
+        }
+        
+        @Override public void onLocationEvent(LocationMetadata loc, LocationEvent event) {
+        }
+    }
+    
+    /**
+     * Users should never implement this interface directly; methods may be added in future releases
+     * without notice.
+     */
+    @Beta
+    public interface ApplicationMetadata {
+        /**
+         * Access the application directly with caution: by the time the listener fires, 
+         * the application may no longer be managed.
+         */
+        @Beta
+        Application getApplication();
+        
+        String getApplicationId();
+        
+        String getApplicationName();
+        
+        String getEntityType();
+        
+        String getCatalogItemId();
+        
+        Map<String, String> getMetadata();
+    }
+    
+    /**
+     * Users should never implement this interface directly; methods may be added in future releases
+     * without notice.
+     */
+    @Beta
+    public interface LocationMetadata {
+        /**
+         * Access the location directly with caution: by the time the listener fires, 
+         * the location may no longer be managed.
+         */
+        @Beta
+        Location getLocation();
+        
+        String getLocationId();
+        
+        Map<String, String> getMetadata();
+    }
+    
+    public static final UsageListener NOOP = new UsageListener() {
+        @Override public void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event) {}
+        @Override public void onLocationEvent(LocationMetadata loc, LocationEvent event) {}
+    };
+    
+    @Beta
+    void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event);
+    
+    @Beta
+    void onLocationEvent(LocationMetadata loc, LocationEvent event);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/UsageManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/UsageManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/UsageManager.java
new file mode 100644
index 0000000..75c242a
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/UsageManager.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.management.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.management.usage.ApplicationUsage;
+import org.apache.brooklyn.core.management.usage.LocationUsage;
+import org.apache.brooklyn.core.management.usage.ApplicationUsage.ApplicationEvent;
+import org.apache.brooklyn.core.management.usage.LocationUsage.LocationEvent;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.util.time.Duration;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.reflect.TypeToken;
+
+@Beta
+public interface UsageManager {
+
+    @SuppressWarnings("serial")
+    public static final ConfigKey<List<org.apache.brooklyn.core.management.internal.UsageListener>> USAGE_LISTENERS = ConfigKeys.newConfigKey(
+            new TypeToken<List<org.apache.brooklyn.core.management.internal.UsageListener>>() {},
+            "brooklyn.usageManager.listeners", "Optional usage listeners (i.e. for metering)",
+            ImmutableList.<org.apache.brooklyn.core.management.internal.UsageListener>of());
+    
+    public static final ConfigKey<Duration> USAGE_LISTENER_TERMINATION_TIMEOUT = ConfigKeys.newConfigKey(
+            Duration.class,
+            "brooklyn.usageManager.listeners.timeout",
+            "Timeout on termination, to wait for queue of usage listener events to be processed",
+            Duration.TEN_SECONDS);
+
+    /**
+     * @since 0.7.0
+     * @deprecated since 0.7.0; use {@link org.apache.brooklyn.core.management.internal.UsageListener}; see {@link UsageListenerAdapter} 
+     */
+    public interface UsageListener {
+        public static final UsageListener NOOP = new UsageListener() {
+            @Override public void onApplicationEvent(String applicationId, String applicationName, String entityType, 
+                    String catalogItemId, Map<String, String> metadata, ApplicationEvent event) {}
+            @Override public void onLocationEvent(String locationId, Map<String, String> metadata, LocationEvent event) {}
+        };
+        
+        public static class UsageListenerAdapter implements org.apache.brooklyn.core.management.internal.UsageListener {
+            private final UsageListener listener;
+
+            public UsageListenerAdapter(UsageListener listener) {
+                this.listener = checkNotNull(listener, "listener");
+            }
+            
+            @Override
+            public void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event) {
+                listener.onApplicationEvent(app.getApplicationId(), app.getApplicationName(), app.getEntityType(), app.getCatalogItemId(), app.getMetadata(), event);
+            }
+
+            @Override
+            public void onLocationEvent(LocationMetadata loc, LocationEvent event) {
+                listener.onLocationEvent(loc.getLocationId(), loc.getMetadata(), event);
+            }
+            
+            @Override
+            public boolean equals(Object obj) {
+                return (obj instanceof UsageListenerAdapter) && listener.equals(((UsageListenerAdapter)obj).listener);
+            }
+            
+            @Override
+            public int hashCode() {
+                return Objects.hashCode(listener);
+            }
+        }
+        
+        void onApplicationEvent(String applicationId, String applicationName, String entityType, String catalogItemId,
+                Map<String, String> metadata, ApplicationEvent event);
+        
+        void onLocationEvent(String locationId, Map<String, String> metadata, LocationEvent event);
+    }
+
+    /**
+     * Adds this application event to the usage record for the given app (creating the usage 
+     * record if one does not already exist).
+     */
+    void recordApplicationEvent(Application app, Lifecycle state);
+    
+    /**
+     * Adds this location event to the usage record for the given location (creating the usage 
+     * record if one does not already exist).
+     */
+    void recordLocationEvent(Location loc, Lifecycle state);
+
+    /**
+     * Returns the usage info for the location with the given id, or null if unknown.
+     */
+    LocationUsage getLocationUsage(String locationId);
+    
+    /**
+     * Returns the usage info that matches the given predicate.
+     * For example, could be used to find locations used within a given time period.
+     */
+    Set<LocationUsage> getLocationUsage(Predicate<? super LocationUsage> filter);
+    
+    /**
+     * Returns the usage info for the application with the given id, or null if unknown.
+     */
+    ApplicationUsage getApplicationUsage(String appId);
+    
+    /**
+     * Returns the usage info that matches the given predicate.
+     * For example, could be used to find applications used within a given time period.
+     */
+    Set<ApplicationUsage> getApplicationUsage(Predicate<? super ApplicationUsage> filter);
+
+    /**
+     * @since 0.7.0
+     * @deprecated since 0.7.0; use {@link #removeUsageListener(org.apache.brooklyn.core.management.internal.UsageListener)};
+     *             see {@link org.apache.brooklyn.core.management.internal.UsageManager.UsageListener.UsageListenerAdapter} 
+     */
+    void addUsageListener(org.apache.brooklyn.core.management.internal.UsageManager.UsageListener listener);
+
+    /**
+     * @since 0.7.0
+     * @deprecated since 0.7.0; use {@link #removeUsageListener(org.apache.brooklyn.core.management.internal.UsageListener)}
+     */
+    @Deprecated
+    void removeUsageListener(org.apache.brooklyn.core.management.internal.UsageManager.UsageListener listener);
+    
+    /**
+     * Adds the given listener, to be notified on recording of application/location events.
+     * The listener notifications may be asynchronous.
+     * 
+     * As of 0.7.0, the listener is not persisted so will be lost on restart/rebind. This
+     * behaviour may change in a subsequent release. 
+     */
+    void addUsageListener(org.apache.brooklyn.core.management.internal.UsageListener listener);
+
+    /**
+     * Removes the given listener.
+     */
+    void removeUsageListener(org.apache.brooklyn.core.management.internal.UsageListener listener);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/usage/ApplicationUsage.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/usage/ApplicationUsage.java b/core/src/main/java/org/apache/brooklyn/core/management/usage/ApplicationUsage.java
new file mode 100644
index 0000000..8260923
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/usage/ApplicationUsage.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.usage;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import brooklyn.entity.basic.Lifecycle;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+/**
+ */
+public class ApplicationUsage {
+    
+    public static class ApplicationEvent {
+        private final Date date;
+        private final Lifecycle state;
+        private final String user;
+
+        public ApplicationEvent(Lifecycle state, String user) {
+            this(new Date(), state, user);
+        }
+
+        public ApplicationEvent(Date date, Lifecycle state) {
+            this(date,state, null);
+        }
+
+        public ApplicationEvent(Date date, Lifecycle state, String user) {
+            this.date = checkNotNull(date, "date");
+            this.state = checkNotNull(state, "state");
+            this.user = user;
+        }
+
+        public Date getDate() {
+            return date;
+        }
+
+        public Lifecycle getState() {
+            return state;
+        }
+
+        public String getUser() {
+            return user;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof ApplicationEvent)) return false;
+            ApplicationEvent o = (ApplicationEvent) other;
+            return Objects.equal(date, o.date) && Objects.equal(state, o.state) && Objects.equal(user, o.user);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(date, state, user);
+        }
+        
+        @Override
+        public String toString() {
+            return Objects.toStringHelper(this).add("date", date).add("state", state).add("entitlementContext", user).toString();
+        }
+    }
+    
+    private final String applicationId;
+    private final String applicationName;
+    private final String entityType;
+    private final Map<String, String> metadata;
+    private final List<ApplicationEvent> events = Collections.synchronizedList(Lists.<ApplicationEvent>newArrayList());
+
+    public ApplicationUsage(String applicationId, String applicationName, String entityType, Map<String, String> metadata) {
+        this.applicationId = checkNotNull(applicationId, "applicationId");
+        // allow name to be null, happens in certain failed rebind cases
+        this.applicationName = applicationName;
+        this.entityType = checkNotNull(entityType, "entityType");
+        this.metadata = checkNotNull(metadata, "metadata");
+    }
+
+    public String getApplicationId() {
+        return applicationId;
+    }
+
+    public String getApplicationName() {
+        return applicationName;
+    }
+
+    public String getEntityType() {
+        return entityType;
+    }
+
+    public Map<String, String> getMetadata() {
+        return metadata;
+    }
+    
+    public List<ApplicationEvent> getEvents() {
+        synchronized (events) {
+            return ImmutableList.copyOf(events);
+        }
+    }
+
+    public void addEvent(ApplicationEvent event) {
+        events.add(checkNotNull(event, "event"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/usage/LocationUsage.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/usage/LocationUsage.java b/core/src/main/java/org/apache/brooklyn/core/management/usage/LocationUsage.java
new file mode 100644
index 0000000..32bd03b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/usage/LocationUsage.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.management.usage;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import brooklyn.entity.basic.Lifecycle;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+/**
+ */
+public class LocationUsage {
+    
+    public static class LocationEvent {
+        private final Date date;
+        private final Lifecycle state;
+        private final String entityId;
+        private final String entityType;
+        private final String applicationId;
+        private final String user;
+
+        public LocationEvent(Lifecycle state, String entityId, String entityType, String applicationId, String user) {
+            this(new Date(), state, entityId, entityType, applicationId, user);
+        }
+        
+        public LocationEvent(Date date, Lifecycle state, String entityId, String entityType, String applicationId, String user) {
+            this.date = checkNotNull(date, "date");
+            this.state = checkNotNull(state, "state");
+            this.entityId = checkNotNull(entityId, "entityId");
+            this.entityType = checkNotNull(entityType, "entityType");
+            this.applicationId = checkNotNull(applicationId, "applicationId (entity "+entityId+")");
+            this.user = user;
+        }
+
+        public Date getDate() {
+            return date;
+        }
+
+        public Lifecycle getState() {
+            return state;
+        }
+
+        public String getEntityId() {
+            return entityId;
+        }
+
+        public String getEntityType() {
+            return entityType;
+        }
+
+        public String getApplicationId() {
+            return applicationId;
+        }
+
+        public String getUser() {
+            return user;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof LocationEvent)) return false;
+            LocationEvent o = (LocationEvent) other;
+            return Objects.equal(date, o.date) && Objects.equal(state, o.state) 
+                    && Objects.equal(entityId, o.entityId) && Objects.equal(entityType, o.entityType)
+                    && Objects.equal(applicationId, o.applicationId) && Objects.equal(user, o.user);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(date, state, entityId, entityType, applicationId, user);
+        }
+        
+        @Override
+        public String toString() {
+            return Objects.toStringHelper(this)
+                    .add("date", date)
+                    .add("state", state)
+                    .add("entityId", entityId)
+                    .add("appId", applicationId)
+                    .add("user", user)
+                    .toString();
+        }
+    }
+    
+    private final String locationId;
+    private final Map<String, String> metadata;
+    private final List<LocationEvent> events = Collections.synchronizedList(Lists.<LocationEvent>newArrayList());
+
+    public LocationUsage(String locationId, Map<String, String> metadata) {
+        this.locationId = checkNotNull(locationId, "locationId");
+        this.metadata = checkNotNull(metadata, "metadata");
+    }
+
+    public String getLocationId() {
+        return locationId;
+    }
+
+    public Map<String, String> getMetadata() {
+        return metadata;
+    }
+    
+    public List<LocationEvent> getEvents() {
+        synchronized (events) {
+            return ImmutableList.copyOf(events);
+        }
+    }
+
+    public void addEvent(LocationEvent event) {
+        events.add(checkNotNull(event, "event"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 47dca3e..c5a74a0 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
@@ -35,6 +35,8 @@ 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.api.mementos.LocationMemento;
+import org.apache.brooklyn.core.management.internal.LocalLocationManager;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,8 +55,6 @@ import brooklyn.internal.storage.impl.BasicReference;
 import org.apache.brooklyn.location.geo.HasHostGeoInfo;
 import org.apache.brooklyn.location.geo.HostGeoInfo;
 
-import brooklyn.management.internal.LocalLocationManager;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.SetFromLiveMap;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.flags.FlagUtils;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 490335a..9a6466e 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
@@ -40,12 +40,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.management.internal.LocalLocationManager;
 
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.config.ConfigMap;
 import brooklyn.config.ConfigPredicates;
 import brooklyn.config.ConfigUtils;
-import brooklyn.management.internal.LocalLocationManager;
 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/6602f694/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 127a665..70735e5 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
@@ -28,6 +28,7 @@ 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.api.location.MachineLocation;
+import org.apache.brooklyn.core.management.internal.LocalLocationManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,7 +41,6 @@ import com.google.common.net.HostAndPort;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Sanitizer;
-import brooklyn.management.internal.LocalLocationManager;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.flags.TypeCoercions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/BrooklynVersionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/BrooklynVersionTest.java b/core/src/test/java/brooklyn/BrooklynVersionTest.java
index 7fbe976..179e698 100644
--- a/core/src/test/java/brooklyn/BrooklynVersionTest.java
+++ b/core/src/test/java/brooklyn/BrooklynVersionTest.java
@@ -37,8 +37,9 @@ import com.google.common.collect.Lists;
 import brooklyn.catalog.internal.CatalogEntityItemDto;
 import brooklyn.catalog.internal.CatalogItemBuilder;
 import brooklyn.catalog.internal.CatalogItemDtoAbstract;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.osgi.OsgiTestResources;
+
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.osgi.OsgiTestResources;
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 0917b0e..73cf5ce 100644
--- a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
+++ b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
@@ -49,6 +49,8 @@ import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
 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.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
 
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.catalog.internal.BasicBrooklynCatalog;
@@ -59,8 +61,6 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.effector.AddChildrenEffector;
 import brooklyn.entity.effector.Effectors;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.osgi.OsgiStandaloneTest;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
index 731a2d8..25a050e 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
@@ -28,13 +28,13 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
 
 import brooklyn.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.BrooklynMavenArtifacts;
 import brooklyn.util.maven.MavenRetriever;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
index 8630430..c417421 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
@@ -30,13 +30,13 @@ import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.catalog.internal.MyCatalogItems.MySillyAppTemplate;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 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/6602f694/core/src/test/java/brooklyn/catalog/internal/CatalogVersioningTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogVersioningTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogVersioningTest.java
index 53a0b9c..78c11ea 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogVersioningTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogVersioningTest.java
@@ -28,11 +28,11 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 
 import com.google.common.base.Predicates;
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/BrooklynAppLiveTestSupport.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/BrooklynAppLiveTestSupport.java b/core/src/test/java/brooklyn/entity/BrooklynAppLiveTestSupport.java
index 668a788..626a035 100644
--- a/core/src/test/java/brooklyn/entity/BrooklynAppLiveTestSupport.java
+++ b/core/src/test/java/brooklyn/entity/BrooklynAppLiveTestSupport.java
@@ -18,6 +18,7 @@
  */
 package brooklyn.entity;
 
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
@@ -28,7 +29,6 @@ import org.testng.annotations.BeforeMethod;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.ManagementContextInternal;
 
 /**
  * To be extended by live tests.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/BrooklynAppUnitTestSupport.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/BrooklynAppUnitTestSupport.java b/core/src/test/java/brooklyn/entity/BrooklynAppUnitTestSupport.java
index 4e23de0..bb7beaf 100644
--- a/core/src/test/java/brooklyn/entity/BrooklynAppUnitTestSupport.java
+++ b/core/src/test/java/brooklyn/entity/BrooklynAppUnitTestSupport.java
@@ -19,6 +19,7 @@
 package brooklyn.entity;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
@@ -29,7 +30,6 @@ import org.testng.annotations.BeforeMethod;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.BrooklynConfigKeys;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.ManagementContextInternal;
 
 /**
  * To be extended by unit/integration tests.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/EffectorMetadataTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/EffectorMetadataTest.java b/core/src/test/java/brooklyn/entity/EffectorMetadataTest.java
index 66daf0b..337cbfd 100644
--- a/core/src/test/java/brooklyn/entity/EffectorMetadataTest.java
+++ b/core/src/test/java/brooklyn/entity/EffectorMetadataTest.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.entity.ParameterType;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.entity.proxying.ImplementedBy;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.management.internal.EffectorUtils;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -39,7 +40,6 @@ import brooklyn.entity.basic.BasicParameterType;
 import brooklyn.entity.basic.MethodEffector;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.trait.Startable;
-import brooklyn.management.internal.EffectorUtils;
 
 import com.google.common.collect.ImmutableList;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/EffectorSayHiGroovyTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/EffectorSayHiGroovyTest.groovy b/core/src/test/java/brooklyn/entity/EffectorSayHiGroovyTest.groovy
index 5ce49a2..57362f2 100644
--- a/core/src/test/java/brooklyn/entity/EffectorSayHiGroovyTest.groovy
+++ b/core/src/test/java/brooklyn/entity/EffectorSayHiGroovyTest.groovy
@@ -42,7 +42,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.management.ManagementContext
 import org.apache.brooklyn.api.management.Task
 
-import brooklyn.management.internal.EffectorUtils
+import org.apache.brooklyn.core.management.internal.EffectorUtils
 
 import org.apache.brooklyn.test.entity.TestApplication
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 0505456..62579e3 100644
--- a/core/src/test/java/brooklyn/entity/EffectorSayHiTest.java
+++ b/core/src/test/java/brooklyn/entity/EffectorSayHiTest.java
@@ -33,6 +33,7 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.BeforeMethod;
@@ -43,7 +44,6 @@ import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.MethodEffector;
 import brooklyn.entity.trait.Startable;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.task.BasicTask;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/drivers/downloads/BasicDownloadsRegistryTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/drivers/downloads/BasicDownloadsRegistryTest.java b/core/src/test/java/brooklyn/entity/drivers/downloads/BasicDownloadsRegistryTest.java
index 64bfcd4..472b3d9 100644
--- a/core/src/test/java/brooklyn/entity/drivers/downloads/BasicDownloadsRegistryTest.java
+++ b/core/src/test/java/brooklyn/entity/drivers/downloads/BasicDownloadsRegistryTest.java
@@ -24,6 +24,7 @@ import static org.testng.Assert.fail;
 import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolver;
 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.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.testng.annotations.AfterMethod;
@@ -38,8 +39,6 @@ import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.location.basic.SimulatedLocation;
 
-import brooklyn.management.internal.LocalManagementContext;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/drivers/downloads/DownloadProducerFromLocalRepoTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/drivers/downloads/DownloadProducerFromLocalRepoTest.java b/core/src/test/java/brooklyn/entity/drivers/downloads/DownloadProducerFromLocalRepoTest.java
index 2ed52e4..afda43a 100644
--- a/core/src/test/java/brooklyn/entity/drivers/downloads/DownloadProducerFromLocalRepoTest.java
+++ b/core/src/test/java/brooklyn/entity/drivers/downloads/DownloadProducerFromLocalRepoTest.java
@@ -26,6 +26,7 @@ import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolverManager.
 import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolverManager.DownloadTargets;
 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.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.testng.annotations.AfterMethod;
@@ -39,8 +40,6 @@ import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.location.basic.SimulatedLocation;
 
-import brooklyn.management.internal.LocalManagementContext;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 



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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynObjectManagerInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynObjectManagerInternal.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynObjectManagerInternal.java
new file mode 100644
index 0000000..d22ffae
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynObjectManagerInternal.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.management.internal;
+
+import org.apache.brooklyn.api.basic.BrooklynObject;
+
+public interface BrooklynObjectManagerInternal<T extends BrooklynObject> {
+
+    ManagementTransitionMode getLastManagementTransitionMode(String itemId);
+    void setManagementTransitionMode(T item, ManagementTransitionMode mode);
+
+    /** 
+     * Begins management for the given rebinded root, recursively; 
+     * if rebinding as a read-only copy, {@link #setReadOnly(T, boolean)} should be called prior to this.
+     */
+    void manageRebindedRoot(T item);
+    
+    void unmanage(final T e, final ManagementTransitionMode info);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/CollectionChangeListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/CollectionChangeListener.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/CollectionChangeListener.java
new file mode 100644
index 0000000..45fa765
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/CollectionChangeListener.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+public interface CollectionChangeListener<Item> {
+    void onItemAdded(Item item);
+    void onItemRemoved(Item item);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..028f2d2
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/EffectorUtils.java
@@ -0,0 +1,356 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static brooklyn.util.GroovyJavaMethods.truth;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.BasicParameterType;
+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;
+import com.google.common.collect.Maps;
+
+/**
+ * Utility methods for invoking effectors.
+ */
+public class EffectorUtils {
+
+    private static final Logger log = LoggerFactory.getLogger(EffectorUtils.class);
+
+    /** prepares arguments for an effector either accepting:
+     *  an array, which should contain the arguments in order, optionally omitting those which have defaults defined;
+     *  or a map, which should contain the arguments by name, again optionally omitting those which have defaults defined,
+     *  and in this case also performing type coercion.
+     */
+    public static Object[] prepareArgsForEffector(Effector<?> eff, Object args) {
+        if (args != null && args.getClass().isArray()) {
+            return prepareArgsForEffectorFromArray(eff, (Object[]) args);
+        }
+        if (args instanceof Map) {
+            return prepareArgsForEffectorFromMap(eff, (Map) args);
+        }
+        log.warn("Deprecated effector invocation style for call to "+eff+", expecting a map or an array, got: "+args);
+        if (log.isDebugEnabled()) {
+            log.debug("Deprecated effector invocation style for call to "+eff+", expecting a map or an array, got: "+args,
+                new Throwable("Trace for deprecated effector invocation style"));
+        }
+        return oldPrepareArgsForEffector(eff, args);
+    }
+
+    /** method used for calls such as   entity.effector(arg1, arg2)
+     * get routed here from AbstractEntity.invokeMethod */
+    private static Object[] prepareArgsForEffectorFromArray(Effector<?> eff, Object args[]) {
+        int newArgsNeeded = eff.getParameters().size();
+        if (args.length==1 && args[0] instanceof Map) {
+            if (newArgsNeeded!=1 || !eff.getParameters().get(0).getParameterClass().isAssignableFrom(args[0].getClass())) {
+                // treat a map in an array as a map passed directly (unless the method takes a single-arg map)
+                // this is to support   effector(param1: val1)
+                return prepareArgsForEffectorFromMap(eff, (Map) args[0]);
+            }
+        }
+        return prepareArgsForEffectorAsMapFromArray(eff, args).values().toArray(new Object[0]);
+    }
+
+    public static Map prepareArgsForEffectorAsMapFromArray(Effector<?> eff, Object args[]) {
+        int newArgsNeeded = eff.getParameters().size();
+        List l = Lists.newArrayList();
+        l.addAll(Arrays.asList(args));
+        Map newArgs = new LinkedHashMap();
+
+        for (int index = 0; index < eff.getParameters().size(); index++) {
+            ParameterType<?> it = eff.getParameters().get(index);
+
+            if (l.size() >= newArgsNeeded) {
+                //all supplied (unnamed) arguments must be used; ignore map
+                newArgs.put(it.getName(), l.remove(0));
+                // TODO do we ignore arguments in the same order that groovy does?
+            } else if (!l.isEmpty() && it.getParameterClass().isInstance(l.get(0))) {
+                //if there are parameters supplied, and type is correct, they get applied before default values
+                //(this is akin to groovy)
+                newArgs.put(it.getName(), l.remove(0));
+            } else if (it instanceof BasicParameterType && ((BasicParameterType)it).hasDefaultValue()) {
+                //finally, default values are used to make up for missing parameters
+                newArgs.put(it.getName(), ((BasicParameterType)it).getDefaultValue());
+            } else {
+                throw new IllegalArgumentException("Invalid arguments (count mismatch) for effector "+eff+": "+args);
+            }
+
+            newArgsNeeded--;
+        }
+        if (newArgsNeeded > 0) {
+            throw new IllegalArgumentException("Invalid arguments (missing "+newArgsNeeded+") for effector "+eff+": "+args);
+        }
+        if (!l.isEmpty()) {
+            throw new IllegalArgumentException("Invalid arguments ("+l.size()+" extra) for effector "+eff+": "+args);
+        }
+        return newArgs;
+    }
+
+    private static Object[] prepareArgsForEffectorFromMap(Effector<?> eff, Map m) {
+        m = Maps.newLinkedHashMap(m); //make editable copy
+        List newArgs = Lists.newArrayList();
+        int newArgsNeeded = eff.getParameters().size();
+
+        for (int index = 0; index < eff.getParameters().size(); index++) {
+            ParameterType<?> it = eff.getParameters().get(index);
+            Object v;
+            if (truth(it.getName()) && m.containsKey(it.getName())) {
+                // argument is in the map
+                v = m.remove(it.getName());
+            } else if (it instanceof BasicParameterType && ((BasicParameterType)it).hasDefaultValue()) {
+                //finally, default values are used to make up for missing parameters
+                v = ((BasicParameterType)it).getDefaultValue();
+            } else {
+                throw new IllegalArgumentException("Invalid arguments (missing argument "+it+") for effector "+eff+": "+m);
+            }
+
+            newArgs.add(TypeCoercions.coerce(v, it.getParameterClass()));
+            newArgsNeeded--;
+        }
+        if (newArgsNeeded>0)
+            throw new IllegalArgumentException("Invalid arguments (missing "+newArgsNeeded+") for effector "+eff+": "+m);
+        return newArgs.toArray(new Object[newArgs.size()]);
+    }
+
+    /**
+     * Takes arguments, and returns an array of arguments suitable for use by the Effector
+     * according to the ParameterTypes it exposes.
+     * <p>
+     * The args can be:
+     * <ol>
+     * <li>an array of ordered arguments
+     * <li>a collection (which will be automatically converted to an array)
+     * <li>a single argument (which will then be wrapped in an array)
+     * <li>a map containing the (named) arguments
+     * <li>an array or collection single entry of a map (treated same as 5 above)
+     * <li>a semi-populated array or collection that also containing a map as first arg -
+     *     uses ordered args in array, but uses named values from map in preference.
+     * <li>semi-populated array or collection, where default values will otherwise be used.
+     * </ol>
+     */
+    public static Object[] oldPrepareArgsForEffector(Effector<?> eff, Object args) {
+        //attempt to coerce unexpected types
+        Object[] argsArray;
+        if (args==null) {
+            argsArray = new Object[0];
+        } else if (args.getClass().isArray()) {
+            argsArray = (Object[]) args;
+        } else {
+            if (args instanceof Collection) {
+                argsArray = ((Collection) args).toArray(new Object[((Collection) args).size()]);
+            } else {
+                argsArray = new Object[] { args };
+            }
+        }
+
+        //if args starts with a map, assume it contains the named arguments
+        //(but only use it when we have insufficient supplied arguments)
+        List l = Lists.newArrayList();
+        l.addAll(Arrays.asList(argsArray));
+        Map m = (argsArray.length > 0 && argsArray[0] instanceof Map ? Maps.newLinkedHashMap((Map) l.remove(0)) : null);
+        List newArgs = Lists.newArrayList();
+        int newArgsNeeded = eff.getParameters().size();
+        boolean mapUsed = false;
+
+        for (int index = 0; index < eff.getParameters().size(); index++) {
+            ParameterType<?> it = eff.getParameters().get(index);
+
+            if (l.size() >= newArgsNeeded) {
+                //all supplied (unnamed) arguments must be used; ignore map
+                newArgs.add(l.remove(0));
+            } else if (truth(m) && truth(it.getName()) && m.containsKey(it.getName())) {
+                //some arguments were not supplied, and this one is in the map
+                newArgs.add(m.remove(it.getName()));
+            } else if (index == 0 && Map.class.isAssignableFrom(it.getParameterClass())) {
+                //if first arg is a map it takes the supplied map
+                newArgs.add(m);
+                mapUsed = true;
+            } else if (!l.isEmpty() && it.getParameterClass().isInstance(l.get(0))) {
+                //if there are parameters supplied, and type is correct, they get applied before default values
+                //(this is akin to groovy)
+                newArgs.add(l.remove(0));
+            } else if (it instanceof BasicParameterType && ((BasicParameterType)it).hasDefaultValue()) {
+                //finally, default values are used to make up for missing parameters
+                newArgs.add(((BasicParameterType)it).getDefaultValue());
+            } else {
+                throw new IllegalArgumentException("Invalid arguments (count mismatch) for effector "+eff+": "+args);
+            }
+
+            newArgsNeeded--;
+        }
+        if (newArgsNeeded > 0) {
+            throw new IllegalArgumentException("Invalid arguments (missing "+newArgsNeeded+") for effector "+eff+": "+args);
+        }
+        if (!l.isEmpty()) {
+            throw new IllegalArgumentException("Invalid arguments ("+l.size()+" extra) for effector "+eff+": "+args);
+        }
+        if (truth(m) && !mapUsed) {
+            throw new IllegalArgumentException("Invalid arguments ("+m.size()+" extra named) for effector "+eff+": "+args);
+        }
+        return newArgs.toArray(new Object[newArgs.size()]);
+    }
+
+    /**
+     * Invokes a method effector so that its progress is tracked. For internal use only, when we know the effector is backed by a method which is local.
+     */
+    public static <T> T invokeMethodEffector(Entity entity, Effector<T> eff, Object[] args) {
+        String name = eff.getName();
+
+        try {
+            if (log.isDebugEnabled()) log.debug("Invoking effector {} on {}", new Object[] {name, entity});
+            if (log.isTraceEnabled()) log.trace("Invoking effector {} on {} with args {}", new Object[] {name, entity, args});
+            EntityManagementSupport mgmtSupport = ((EntityInternal)entity).getManagementSupport();
+            if (!mgmtSupport.isDeployed()) {
+                mgmtSupport.attemptLegacyAutodeployment(name);
+            }
+            ManagementContextInternal mgmtContext = (ManagementContextInternal) ((EntityInternal) entity).getManagementContext();
+
+            mgmtSupport.getEntityChangeListener().onEffectorStarting(eff, args);
+            try {
+                return mgmtContext.invokeEffectorMethodSync(entity, eff, args);
+            } finally {
+                mgmtSupport.getEntityChangeListener().onEffectorCompleted(eff);
+            }
+        } catch (Exception e) {
+            handleEffectorException(entity, eff, e);
+            // (won't return below)
+            return null;
+        }
+    }
+
+    public static void handleEffectorException(Entity entity, Effector<?> effector, Throwable throwable) {
+        String message = "Error invoking " + effector.getName() + " at " + entity;
+        // Avoid throwing a PropagatedRuntimeException that just repeats the last PropagatedRuntimeException.
+        if (throwable instanceof PropagatedRuntimeException &&
+                throwable.getMessage() != null &&
+                throwable.getMessage().startsWith(message)) {
+            throw PropagatedRuntimeException.class.cast(throwable);
+        } else {
+            log.warn(message + ": " + Exceptions.collapseText(throwable));
+            throw new PropagatedRuntimeException(message, throwable);
+        }
+    }
+
+    public static <T> Task<T> invokeEffectorAsync(Entity entity, Effector<T> eff, Map<String,?> parameters) {
+        String name = eff.getName();
+
+        if (log.isDebugEnabled()) log.debug("Invoking-async effector {} on {}", new Object[] { name, entity });
+        if (log.isTraceEnabled()) log.trace("Invoking-async effector {} on {} with args {}", new Object[] { name, entity, parameters });
+        EntityManagementSupport mgmtSupport = ((EntityInternal)entity).getManagementSupport();
+        if (!mgmtSupport.isDeployed()) {
+            mgmtSupport.attemptLegacyAutodeployment(name);
+        }
+        ManagementContextInternal mgmtContext = (ManagementContextInternal) ((EntityInternal)entity).getManagementContext();
+
+        // FIXME seems brittle to have the listeners in the Utils method; better to move into the context.invokeEff
+        // (or whatever the last mile before invoking the effector is - though currently there is not such a canonical place!)
+        mgmtSupport.getEntityChangeListener().onEffectorStarting(eff, parameters);
+        try {
+            return mgmtContext.invokeEffector(entity, eff, parameters);
+        } finally {
+            // FIXME this is really Effector submitted
+            mgmtSupport.getEntityChangeListener().onEffectorCompleted(eff);
+        }
+    }
+
+    /** @deprecated since 0.7.0, not used */
+    @Deprecated
+    public static Effector<?> findEffectorMatching(Entity entity, Method method) {
+        outer: for (Effector<?> effector : entity.getEntityType().getEffectors()) {
+            if (!effector.getName().equals(entity)) continue;
+            if (effector.getParameters().size() != method.getParameterTypes().length) continue;
+            for (int i = 0; i < effector.getParameters().size(); i++) {
+                if (effector.getParameters().get(i).getParameterClass() != method.getParameterTypes()[i]) continue outer;
+            }
+            return effector;
+        }
+        return null;
+    }
+
+    /** @deprecated since 0.7.0, expects parameters but does not use them! */
+    @Deprecated
+    public static Effector<?> findEffectorMatching(Set<Effector<?>> effectors, String effectorName, Map<String, ?> parameters) {
+        // TODO Support overloading: check parameters as well
+        for (Effector<?> effector : effectors) {
+            if (effector.getName().equals(effectorName)) {
+                return effector;
+            }
+        }
+        return null;
+    }
+
+    /** matches effectors by name only (not parameters) */
+    public static Maybe<Effector<?>> findEffector(Collection<? extends Effector<?>> effectors, String effectorName) {
+        for (Effector<?> effector : effectors) {
+            if (effector.getName().equals(effectorName)) {
+                return Maybe.<Effector<?>>of(effector);
+            }
+        }
+        return Maybe.absent(new NoSuchElementException("No effector with name "+effectorName+" (contenders "+effectors+")"));
+    }
+
+    /** matches effectors by name only (not parameters), based on what is declared on the entity static type */
+    public static Maybe<Effector<?>> findEffectorDeclared(Entity entity, String effectorName) {
+        return findEffector(entity.getEntityType().getEffectors(), effectorName);
+    }
+
+    /** @deprecated since 0.7.0 use {@link #getTaskFlagsForEffectorInvocation(Entity, Effector, ConfigBag)} */
+    public static Map<Object,Object> getTaskFlagsForEffectorInvocation(Entity entity, Effector<?> effector) {
+        return getTaskFlagsForEffectorInvocation(entity, effector, null);
+    }
+    
+    /** returns a (mutable) map of the standard flags which should be placed on an effector */
+    public static Map<Object,Object> getTaskFlagsForEffectorInvocation(Entity entity, Effector<?> effector, ConfigBag parameters) {
+        return MutableMap.builder()
+                .put("description", "Invoking effector "+effector.getName()
+                    +" on "+entity.getDisplayName()
+                    +(parameters!=null ? " with parameters "+parameters.getAllConfig() : ""))
+                .put("displayName", effector.getName())
+                .put("tags", MutableList.of(
+                        BrooklynTaskTags.EFFECTOR_TAG, 
+                        BrooklynTaskTags.tagForEffectorCall(entity, effector.getName(), parameters), 
+                        BrooklynTaskTags.tagForTargetEntity(entity)))
+                .build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityChangeListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityChangeListener.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityChangeListener.java
new file mode 100644
index 0000000..d6552ff
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityChangeListener.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import org.apache.brooklyn.api.entity.Effector;
+import org.apache.brooklyn.api.entity.Feed;
+import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.api.policy.Enricher;
+import org.apache.brooklyn.api.policy.Policy;
+
+import brooklyn.config.ConfigKey;
+
+public interface EntityChangeListener {
+
+    // TODO for testing only!
+    public static final EntityChangeListener NOOP = new EntityChangeListener() {
+        @Override public void onChanged() {}
+        @Override public void onAttributeChanged(AttributeSensor<?> attribute) {}
+        @Override public void onConfigChanged(ConfigKey<?> key) {}
+        @Override public void onLocationsChanged() {}
+        @Override public void onMembersChanged() {}
+        @Override public void onTagsChanged() {}
+        @Override public void onChildrenChanged() {}
+        @Override public void onPolicyAdded(Policy policy) {}
+        @Override public void onPolicyRemoved(Policy policy) {}
+        @Override public void onEnricherAdded(Enricher enricher) {}
+        @Override public void onEnricherRemoved(Enricher enricher) {}
+        @Override public void onFeedAdded(Feed feed) {}
+        @Override public void onFeedRemoved(Feed feed) {}
+        @Override public void onEffectorStarting(Effector<?> effector, Object parameters) {}
+        @Override public void onEffectorCompleted(Effector<?> effector) {}
+    };
+    
+    void onChanged();
+
+    void onAttributeChanged(AttributeSensor<?> attribute);
+
+    void onConfigChanged(ConfigKey<?> key);
+
+    void onLocationsChanged();
+    
+    void onTagsChanged();
+
+    void onMembersChanged();
+
+    void onChildrenChanged();
+
+    void onPolicyAdded(Policy policy);
+
+    void onPolicyRemoved(Policy policy);
+
+    void onEnricherAdded(Enricher enricher);
+
+    void onEnricherRemoved(Enricher enricher);
+
+    void onFeedAdded(Feed feed);
+
+    void onFeedRemoved(Feed feed);
+
+    void onEffectorStarting(Effector<?> effector, Object parameters);
+    
+    void onEffectorCompleted(Effector<?> effector);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagementSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagementSupport.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagementSupport.java
new file mode 100644
index 0000000..dbba29c
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagementSupport.java
@@ -0,0 +1,478 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.Feed;
+import org.apache.brooklyn.api.event.AttributeSensor;
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.SubscriptionContext;
+import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
+import org.apache.brooklyn.api.policy.Enricher;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+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.NonDeploymentManagementContext.NonDeploymentManagementContextMode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.AbstractEntity;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.util.exceptions.Exceptions;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
+
+/**
+ * Encapsulates management activities at an entity.
+ * <p>
+ * On entity deployment, ManagementContext.manage(entity) causes
+ * <p>
+ * * onManagementStarting(ManagementContext)
+ * * onManagementStartingSubscriptions()
+ * * onManagementStartingSensorEmissions()
+ * * onManagementStartingExecutions()
+ * * onManagementStarted() - when all the above is said and done
+ * * onManagementStartingHere();
+ * <p>
+ * on unmanage it hits onManagementStoppingHere() then onManagementStopping().
+ * <p>
+ * When an entity's management migrates, it invoked onManagementStoppingHere() at the old location,
+ * then onManagementStartingHere() at the new location.
+ */
+public class EntityManagementSupport {
+
+    private static final Logger log = LoggerFactory.getLogger(EntityManagementSupport.class);
+    
+    public EntityManagementSupport(AbstractEntity entity) {
+        this.entity = entity;
+        nonDeploymentManagementContext = new NonDeploymentManagementContext(entity, NonDeploymentManagementContextMode.PRE_MANAGEMENT);
+    }
+
+    protected transient AbstractEntity entity;
+    NonDeploymentManagementContext nonDeploymentManagementContext;
+    
+    protected transient ManagementContext initialManagementContext;
+    protected transient ManagementContext managementContext;
+    protected transient SubscriptionContext subscriptionContext;
+    protected transient ExecutionContext executionContext;
+    
+    // TODO the application
+    // (elaborate or remove ^^^ ? -AH, Sept 2014)
+    
+    protected final AtomicBoolean managementContextUsable = new AtomicBoolean(false);
+    protected final AtomicBoolean currentlyDeployed = new AtomicBoolean(false);
+    protected final AtomicBoolean everDeployed = new AtomicBoolean(false);
+    protected Boolean readOnly = null;
+    protected final AtomicBoolean managementFailed = new AtomicBoolean(false);
+    
+    private volatile EntityChangeListener entityChangeListener = EntityChangeListener.NOOP;
+
+    /**
+     * Whether this entity is managed (i.e. "onManagementStarting" has been called, so the framework knows about it,
+     * and it has not been unmanaged).
+     */
+    public boolean isDeployed() { return currentlyDeployed.get(); }
+    public boolean isNoLongerManaged() {
+        return wasDeployed() && !isDeployed();
+    }
+    /** whether entity has ever been deployed (managed) */
+    public boolean wasDeployed() { return everDeployed.get(); }
+    
+    @Beta
+    public void setReadOnly(boolean isReadOnly) {
+        if (isDeployed())
+            throw new IllegalStateException("Cannot set read only after deployment");
+        this.readOnly = isReadOnly;
+    }
+
+    /** Whether the entity and its adjuncts should be treated as read-only;
+     * may be null briefly when initializing if RO status is unknown. */
+    @Beta
+    public Boolean isReadOnlyRaw() {
+        return readOnly;
+    }
+
+    /** Whether the entity and its adjuncts should be treated as read-only;
+     * error if initializing and RO status is unknown. */
+    @Beta
+    public boolean isReadOnly() {
+        Preconditions.checkNotNull(readOnly, "Read-only status of %s not yet known", entity);
+        return readOnly;
+    }
+
+    /**
+     * Whether the entity's management lifecycle is complete (i.e. both "onManagementStarting" and "onManagementStarted" have
+     * been called, and it is has not been unmanaged). 
+     */
+    public boolean isFullyManaged() {
+        return (nonDeploymentManagementContext == null) && currentlyDeployed.get();
+    }
+
+    public synchronized void setManagementContext(ManagementContextInternal val) {
+        if (initialManagementContext != null) {
+            throw new IllegalStateException("Initial management context is already set for "+entity+"; cannot change");
+        }
+        if (managementContext != null && !managementContext.equals(val)) {
+            throw new IllegalStateException("Management context is already set for "+entity+"; cannot change");
+        }
+        
+        this.initialManagementContext = checkNotNull(val, "managementContext");
+        if (nonDeploymentManagementContext != null) {
+            nonDeploymentManagementContext.setManagementContext(val);
+        }
+    }
+    
+    public void onRebind(ManagementTransitionInfo info) {
+        nonDeploymentManagementContext.setMode(NonDeploymentManagementContextMode.MANAGEMENT_REBINDING);
+    }
+    
+    public void onManagementStarting(ManagementTransitionInfo info) {
+        try {
+            synchronized (this) {
+                boolean alreadyManaging = isDeployed();
+                
+                if (alreadyManaging) {
+                    log.warn("Already managed: "+entity+" ("+nonDeploymentManagementContext+"); onManagementStarting is no-op");
+                } else if (nonDeploymentManagementContext == null || !nonDeploymentManagementContext.getMode().isPreManaged()) {
+                    throw new IllegalStateException("Not in expected pre-managed state: "+entity+" ("+nonDeploymentManagementContext+")");
+                }
+                if (managementContext != null && !managementContext.equals(info.getManagementContext())) {
+                    throw new IllegalStateException("Already has management context: "+managementContext+"; can't set "+info.getManagementContext());
+                }
+                if (initialManagementContext != null && !initialManagementContext.equals(info.getManagementContext())) {
+                    throw new IllegalStateException("Already has different initial management context: "+initialManagementContext+"; can't set "+info.getManagementContext());
+                }
+                if (alreadyManaging) {
+                    return;
+                }
+                
+                this.managementContext = info.getManagementContext();
+                nonDeploymentManagementContext.setMode(NonDeploymentManagementContextMode.MANAGEMENT_STARTING);
+                
+                if (!isReadOnly()) {
+                    nonDeploymentManagementContext.getSubscriptionManager().setDelegate((AbstractSubscriptionManager) managementContext.getSubscriptionManager());
+                    nonDeploymentManagementContext.getSubscriptionManager().startDelegatingForSubscribing();
+                }
+    
+                managementContextUsable.set(true);
+                currentlyDeployed.set(true);
+                everDeployed.set(true);
+                
+                entityChangeListener = new EntityChangeListenerImpl();
+            }
+            
+            /*
+             * TODO framework starting events - phase 1, including rebind
+             *  - establish hierarchy (child, groups, etc; construction if necessary on rebind)
+             *  - set location
+             *  - set local config values
+             *  - set saved sensor values
+             *  - register subscriptions -- BUT nothing is allowed to execute
+             *  [these operations may be done before we invoke starting also; above can happen in any order;
+             *  sensor _publications_ and executor submissions are queued]
+             *  then:  set the management context and the entity is "managed" from the perspective of external viewers (ManagementContext.isManaged(entity) returns true)
+             */
+            
+            if (!isReadOnly()) {
+                entity.onManagementStarting();
+            }
+        } catch (Throwable t) {
+            managementFailed.set(true);
+            throw Exceptions.propagate(t);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void onManagementStarted(ManagementTransitionInfo info) {
+        try {
+            synchronized (this) {
+                boolean alreadyManaged = isFullyManaged();
+                
+                if (alreadyManaged) {
+                    log.warn("Already managed: "+entity+" ("+nonDeploymentManagementContext+"); onManagementStarted is no-op");
+                } else if (nonDeploymentManagementContext == null || nonDeploymentManagementContext.getMode() != NonDeploymentManagementContextMode.MANAGEMENT_STARTING) {
+                    throw new IllegalStateException("Not in expected \"management starting\" state: "+entity+" ("+nonDeploymentManagementContext+")");
+                }
+                if (managementContext != info.getManagementContext()) {
+                    throw new IllegalStateException("Already has management context: "+managementContext+"; can't set "+info.getManagementContext());
+                }
+                if (alreadyManaged) {
+                    return;
+                }
+                
+                nonDeploymentManagementContext.setMode(NonDeploymentManagementContextMode.MANAGEMENT_STARTED);
+                
+                /*
+                 * - set derived/inherited config values
+                 * - publish all queued sensors
+                 * - start all queued executions (e.g. subscription delivery)
+                 * [above happens in exactly this order, at each entity]
+                 * then: the entity internally knows it fully managed (ManagementSupport.isManaged() returns true -- though not sure we need that);
+                 * subsequent sensor events and executions occur directly (no queueing)
+                 */
+                
+                if (!isReadOnly()) {
+                    nonDeploymentManagementContext.getSubscriptionManager().startDelegatingForPublishing();
+                }
+                
+                // TODO more of the above
+                // TODO custom started activities
+                // (elaborate or remove ^^^ ? -AH, Sept 2014)
+            }
+            
+            if (!isReadOnly()) {
+                entity.onManagementBecomingMaster();
+                entity.onManagementStarted();
+            }
+            
+            synchronized (this) {
+                nonDeploymentManagementContext = null;
+            }
+        } catch (Throwable t) {
+            managementFailed.set(true);
+            throw Exceptions.propagate(t);
+        }
+    }
+    
+    @SuppressWarnings("deprecation")
+    public void onManagementStopping(ManagementTransitionInfo info) {
+        synchronized (this) {
+            if (managementContext != info.getManagementContext()) {
+                throw new IllegalStateException("onManagementStopping encountered different management context for "+entity+
+                    (!wasDeployed() ? " (wasn't deployed)" : !isDeployed() ? " (no longer deployed)" : "")+
+                    ": "+managementContext+"; expected "+info.getManagementContext()+" (may be a pre-registered entity which was never properly managed)");
+            }
+            Stopwatch startTime = Stopwatch.createStarted();
+            while (!managementFailed.get() && nonDeploymentManagementContext!=null && 
+                    nonDeploymentManagementContext.getMode()==NonDeploymentManagementContextMode.MANAGEMENT_STARTING) {
+                // still becoming managed
+                try {
+                    if (startTime.elapsed(TimeUnit.SECONDS) > 30) {
+                        // emergency fix, 30s timeout for management starting
+                        log.error("Management stopping event "+info+" in "+this+" timed out waiting for start; proceeding to stopping");
+                        break;
+                    }
+                    wait(100);
+                } catch (InterruptedException e) {
+                    Exceptions.propagate(e);
+                }
+            }
+            if (nonDeploymentManagementContext==null) {
+                nonDeploymentManagementContext = new NonDeploymentManagementContext(entity, NonDeploymentManagementContextMode.MANAGEMENT_STOPPING);
+            } else {
+                // already stopped? or not started?
+                nonDeploymentManagementContext.setMode(NonDeploymentManagementContextMode.MANAGEMENT_STOPPING);
+            }
+        }
+        // TODO custom stopping activities
+        // TODO framework stopping events - no more sensors, executions, etc
+        // (elaborate or remove ^^^ ? -AH, Sept 2014)
+        
+        if (!isReadOnly() && info.getMode().isDestroying()) {
+            // if we support remote parent of local child, the following call will need to be properly remoted
+            if (entity.getParent()!=null) entity.getParent().removeChild(entity.getProxyIfAvailable());
+        }
+        // new subscriptions will be queued / not allowed
+        nonDeploymentManagementContext.getSubscriptionManager().stopDelegatingForSubscribing();
+        // new publications will be queued / not allowed
+        nonDeploymentManagementContext.getSubscriptionManager().stopDelegatingForPublishing();
+        
+        if (!isReadOnly()) {
+            entity.onManagementNoLongerMaster();
+            entity.onManagementStopped();
+        }
+    }
+    
+    public void onManagementStopped(ManagementTransitionInfo info) {
+        synchronized (this) {
+            if (managementContext == null && nonDeploymentManagementContext.getMode() == NonDeploymentManagementContextMode.MANAGEMENT_STOPPED) {
+                return;
+            }
+            if (managementContext != info.getManagementContext()) {
+                throw new IllegalStateException("Has different management context: "+managementContext+"; expected "+info.getManagementContext());
+            }
+            getSubscriptionContext().unsubscribeAll();
+            entityChangeListener = EntityChangeListener.NOOP;
+            managementContextUsable.set(false);
+            currentlyDeployed.set(false);
+            executionContext = null;
+            subscriptionContext = null;
+        }
+        
+        // TODO framework stopped activities, e.g. serialize state ?
+        entity.invalidateReferences();
+        
+        synchronized (this) {
+            managementContext = null;
+            nonDeploymentManagementContext.setMode(NonDeploymentManagementContextMode.MANAGEMENT_STOPPED);
+        }
+    }
+
+    @VisibleForTesting
+    @Beta
+    public boolean isManagementContextReal() {
+        return managementContextUsable.get();
+    }
+    
+    public synchronized ManagementContext getManagementContext() {
+        return (managementContextUsable.get()) ? managementContext : nonDeploymentManagementContext;
+    }    
+    
+    public synchronized ExecutionContext getExecutionContext() {
+        if (executionContext!=null) return executionContext;
+        if (managementContextUsable.get()) {
+            executionContext = managementContext.getExecutionContext(entity);
+            return executionContext;
+        }
+        return nonDeploymentManagementContext.getExecutionContext(entity);
+    }
+    public synchronized SubscriptionContext getSubscriptionContext() {
+        if (subscriptionContext!=null) return subscriptionContext;
+        if (managementContextUsable.get()) {
+            subscriptionContext = managementContext.getSubscriptionContext(entity);
+            return subscriptionContext;
+        }
+        return nonDeploymentManagementContext.getSubscriptionContext(entity);
+    }
+    public synchronized EntitlementManager getEntitlementManager() {
+        return getManagementContext().getEntitlementManager();
+    }
+
+    public void attemptLegacyAutodeployment(String effectorName) {
+        synchronized (this) {
+            if (managementContext != null) {
+                log.warn("Autodeployment suggested but not required for " + entity + "." + effectorName);
+                return;
+            }
+            if (entity instanceof Application) {
+                log.warn("Autodeployment with new management context triggered for " + entity + "." + effectorName + " -- will not be supported in future. Explicit manage call required.");
+                if (initialManagementContext != null) {
+                    initialManagementContext.getEntityManager().manage(entity);
+                } else {
+                    Entities.startManagement(entity);
+                }
+                return;
+            }
+        }
+        if ("start".equals(effectorName)) {
+            Entity e=entity;
+            if (e.getParent()!=null && ((EntityInternal)e.getParent()).getManagementSupport().isDeployed()) { 
+                log.warn("Autodeployment in parent's management context triggered for "+entity+"."+effectorName+" -- will not be supported in future. Explicit manage call required.");
+                ((EntityInternal)e.getParent()).getManagementContext().getEntityManager().manage(entity);
+                return;
+            }
+        }
+        log.warn("Autodeployment not available for "+entity+"."+effectorName);
+    }
+    
+    public EntityChangeListener getEntityChangeListener() {
+        return entityChangeListener;
+    }
+    
+    private class EntityChangeListenerImpl implements EntityChangeListener {
+        @Override
+        public void onChanged() {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+        }
+        @Override
+        public void onChildrenChanged() {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+        }
+        @Override
+        public void onLocationsChanged() {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+        }
+        @Override
+        public void onTagsChanged() {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+        }
+        @Override
+        public void onMembersChanged() {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+        }
+        @Override
+        public void onPolicyAdded(Policy policy) {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+            getManagementContext().getRebindManager().getChangeListener().onManaged(policy);
+        }
+        @Override
+        public void onEnricherAdded(Enricher enricher) {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+            getManagementContext().getRebindManager().getChangeListener().onManaged(enricher);
+        }
+        @Override
+        public void onFeedAdded(Feed feed) {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+            getManagementContext().getRebindManager().getChangeListener().onManaged(feed);
+        }
+        @Override
+        public void onPolicyRemoved(Policy policy) {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+            getManagementContext().getRebindManager().getChangeListener().onUnmanaged(policy);
+        }
+        @Override
+        public void onEnricherRemoved(Enricher enricher) {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+            getManagementContext().getRebindManager().getChangeListener().onUnmanaged(enricher);
+        }
+        @Override
+        public void onFeedRemoved(Feed feed) {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+            getManagementContext().getRebindManager().getChangeListener().onUnmanaged(feed);
+        }
+        @Override
+        public void onAttributeChanged(AttributeSensor<?> attribute) {
+            // TODO Could make this more efficient by inspecting the attribute to decide if needs persisted
+            // immediately, or not important, or transient (e.g. do we really need to persist 
+            // request-per-second count for rebind purposes?!)
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+        }
+        @Override
+        public void onConfigChanged(ConfigKey<?> key) {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+        }
+        @Override
+        public void onEffectorStarting(Effector<?> effector, Object parameters) {
+            Entitlements.checkEntitled(getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(entity, StringAndArgument.of(effector.getName(), parameters)));
+        }
+        @Override
+        public void onEffectorCompleted(Effector<?> effector) {
+            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return super.toString()+"["+(entity==null ? "null" : entity.getId())+"]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..3f59774
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagementUtils.java
@@ -0,0 +1,326 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import io.brooklyn.camp.CampPlatform;
+import io.brooklyn.camp.spi.Assembly;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
+
+import java.io.StringReader;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.entity.Application;
+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.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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator;
+import brooklyn.config.BrooklynServerConfig;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityFunctions;
+import brooklyn.entity.effector.Effectors;
+import brooklyn.entity.trait.Startable;
+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;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+/** Utility methods for working with entities and applications */
+public class EntityManagementUtils {
+
+    private static final Logger log = LoggerFactory.getLogger(EntityManagementUtils.class);
+
+    /**
+     * A marker config value which indicates that an application was created automatically
+     * to allow the management of a non-app entity.
+     */
+    public static final ConfigKey<Boolean> WRAPPER_APP_MARKER = ConfigKeys.newBooleanConfigKey("brooklyn.wrapper_app");
+
+    /** creates an application from the given app spec, managed by the given management context */
+    public static <T extends Application> T createUnstarted(ManagementContext mgmt, EntitySpec<T> spec) {
+        T app = mgmt.getEntityManager().createEntity(spec);
+        Entities.startManagement(app, mgmt);
+        return app;
+    }
+
+    /** convenience for accessing camp */
+    public static Maybe<CampPlatform> getCampPlatform(ManagementContext mgmt) {
+        return BrooklynServerConfig.getCampPlatform(mgmt);
+    }
+    
+    /** as {@link #createApplication(ManagementContext, EntitySpec)} but for a YAML spec */
+    public static <T extends Application> T createUnstarted(ManagementContext mgmt, String yaml) {
+        AssemblyTemplate at = getCampPlatform(mgmt).get().pdp().registerDeploymentPlan( new StringReader(yaml) );
+        return createUnstarted(mgmt, at);
+    }
+    
+    /** as {@link #createApplication(ManagementContext, EntitySpec)} but for an assembly template */
+    @SuppressWarnings("unchecked")
+    public static <T extends Application> T createUnstarted(ManagementContext mgmt, AssemblyTemplate at) {
+        CampPlatform camp = getCampPlatform(mgmt).get();
+        AssemblyTemplateInstantiator instantiator;
+        try {
+            instantiator = at.getInstantiator().newInstance();
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+        Assembly assembly;
+        if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
+            BrooklynClassLoadingContext loader = JavaBrooklynClassLoadingContext.create(mgmt);
+            
+            EntitySpec<?> spec = ((AssemblyTemplateSpecInstantiator) instantiator).createSpec(at, camp, loader, true);
+            Entity app = mgmt.getEntityManager().createEntity(spec);
+            Entities.startManagement((Application)app, mgmt);
+            return (T) app;
+        } else {
+            // currently, all brooklyn plans should produce the above; currently this will always throw Unsupported  
+            try {
+                assembly = instantiator.instantiate(at, camp);
+                return (T) mgmt.getEntityManager().getEntity(assembly.getId());
+            } catch (UnsupportedOperationException e) {
+                if (at.getPlatformComponentTemplates()==null || at.getPlatformComponentTemplates().isEmpty()) {
+                    if (at.getCustomAttributes().containsKey("brooklyn.catalog"))
+                        throw new IllegalArgumentException("Unrecognized application blueprint format: expected an application, not a brooklyn.catalog");
+                    throw new IllegalArgumentException("Unrecognized application blueprint format: no services defined");
+                }
+                // map this (expected) error to a nicer message
+                throw new IllegalArgumentException("Unrecognized application blueprint format");
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                throw new IllegalArgumentException("Invalid plan: "+at, e);
+            }
+        }
+    }
+    
+    /** container for operation which creates something and which wants to return both
+     * the items created and any pending create/start task */
+    public static class CreationResult<T,U> {
+        private final T thing;
+        @Nullable private final Task<U> task;
+        public CreationResult(T thing, Task<U> task) {
+            super();
+            this.thing = thing;
+            this.task = task;
+        }
+        
+        protected static <T,U> CreationResult<T,U> of(T thing, @Nullable Task<U> task) {
+            return new CreationResult<T,U>(thing, task);
+        }
+        
+        /** returns the thing/things created */
+        @Nullable public T get() { return thing; }
+        /** associated task, ie the one doing the creation/starting */
+        public Task<U> task() { return task; }
+        public CreationResult<T,U> blockUntilComplete(Duration timeout) { if (task!=null) task.blockUntilEnded(timeout); return this; }
+        public CreationResult<T,U> blockUntilComplete() { if (task!=null) task.blockUntilEnded(); return this; }
+    }
+
+    public static <T extends Application> CreationResult<T,Void> createStarting(ManagementContext mgmt, EntitySpec<T> appSpec) {
+        return start(createUnstarted(mgmt, appSpec));
+    }
+
+    public static CreationResult<? extends Application,Void> createStarting(ManagementContext mgmt, String appSpec) {
+        return start(createUnstarted(mgmt, appSpec));
+    }
+
+    public static CreationResult<? extends Application,Void> createStarting(ManagementContext mgmt, AssemblyTemplate at) {
+        return start(createUnstarted(mgmt, at));
+    }
+
+    public static <T extends Application> CreationResult<T,Void> start(T app) {
+        Task<Void> task = Entities.invokeEffector((EntityLocal)app, app, Startable.START,
+            // locations already set in the entities themselves;
+            // TODO make it so that this arg does not have to be supplied to START !
+            MutableMap.of("locations", MutableList.of()));
+        return CreationResult.of(app, task);
+    }
+    
+    public static CreationResult<List<Entity>, List<String>> addChildren(final EntityLocal parent, String yaml, Boolean start) {
+        if (Boolean.FALSE.equals(start))
+            return CreationResult.of(addChildrenUnstarted(parent, yaml), null);
+        return addChildrenStarting(parent, yaml);
+    }
+    
+    /** adds entities from the given yaml, under the given parent; but does not start them */
+    public static List<Entity> addChildrenUnstarted(final EntityLocal parent, String yaml) {
+        log.debug("Creating child of "+parent+" from yaml:\n{}", yaml);
+        
+        ManagementContext mgmt = parent.getApplication().getManagementContext();
+        CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get();
+        
+        AssemblyTemplate at = camp.pdp().registerDeploymentPlan( new StringReader(yaml) );
+
+        AssemblyTemplateInstantiator instantiator;
+        try {
+            instantiator = at.getInstantiator().newInstance();
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+        if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
+            BrooklynClassLoadingContext loader = JavaBrooklynClassLoadingContext.create(mgmt);
+            EntitySpec<?> specA = ((AssemblyTemplateSpecInstantiator) instantiator).createSpec(at, camp, loader, false);
+
+            // see whether we can promote children
+            List<EntitySpec<?>> specs = MutableList.of();
+            if (hasNoNameOrCustomKeysOrRoot(at, specA)) {
+                // we can promote
+                for (EntitySpec<?> specC: specA.getChildren()) {
+                    collapseSpec(specA, specC);
+                    specs.add(specC);
+                }
+            } else {
+                // if not promoting, set a nice name if needed
+                if (Strings.isEmpty(specA.getDisplayName())) {
+                    int size = specA.getChildren().size();
+                    String childrenCountString = size+" "+(size!=1 ? "children" : "child");
+                    specA.displayName("Dynamically added "+childrenCountString);
+                }
+                specs.add(specA);
+            }
+
+            final List<Entity> children = MutableList.of();
+            for (EntitySpec<?> spec: specs) {
+                Entity child = (Entity)parent.addChild(spec);
+                Entities.manage(child);
+                children.add(child);
+            }
+            
+            return children;
+        } else {
+            throw new IllegalStateException("Spec could not be parsed to supply a compatible instantiator");
+        }
+    }
+
+    public static CreationResult<List<Entity>,List<String>> addChildrenStarting(final EntityLocal parent, String yaml) {
+        final List<Entity> children = addChildrenUnstarted(parent, yaml);
+        String childrenCountString;
+
+        int size = children.size();
+        childrenCountString = size+" "+(size!=1 ? "children" : "child"); 
+
+        TaskBuilder<List<String>> taskM = Tasks.<List<String>>builder().name("add children")
+            .dynamic(true)
+            .tag(BrooklynTaskTags.NON_TRANSIENT_TASK_TAG)
+            .body(new Callable<List<String>>() {
+                @Override public List<String> call() throws Exception {
+                    return ImmutableList.copyOf(Iterables.transform(children, EntityFunctions.id()));
+                }})
+                .description("Add and start "+childrenCountString);
+
+        TaskBuilder<?> taskS = Tasks.builder().parallel(true).name("add (parallel)").description("Start each new entity");
+
+        // autostart if requested
+        for (Entity child: children) {
+            if (child instanceof Startable) {
+                taskS.add(Effectors.invocation(child, Startable.START, ImmutableMap.of("locations", ImmutableList.of())));
+            } else {
+                // include a task, just to give feedback in the GUI
+                taskS.add(Tasks.builder().name("create").description("Skipping start (not a Startable Entity)")
+                    .body(new Runnable() { public void run() {} })
+                    .tag(BrooklynTaskTags.tagForTargetEntity(child))
+                    .build());
+            }
+        }
+        taskM.add(taskS.build());
+        Task<List<String>> task = Entities.submit(parent, taskM.build());
+
+        return CreationResult.of(children, task);
+    }
+
+    /** worker method to combine specs */
+    @Beta //where should this live long-term?
+    public static void collapseSpec(EntitySpec<?> sourceToBeCollapsed, EntitySpec<?> targetToBeExpanded) {
+        if (Strings.isEmpty(targetToBeExpanded.getDisplayName()))
+            targetToBeExpanded.displayName(sourceToBeCollapsed.getDisplayName());
+        if (!sourceToBeCollapsed.getLocations().isEmpty())
+            targetToBeExpanded.locations(sourceToBeCollapsed.getLocations());
+
+        // NB: this clobbers child config; might prefer to deeply merge maps etc
+        // (but this should not be surprising, as unwrapping is often parameterising the nested blueprint, so outer config should dominate) 
+        targetToBeExpanded.configure(sourceToBeCollapsed.getConfig());
+        targetToBeExpanded.configure(sourceToBeCollapsed.getFlags());
+        
+        // TODO copying tags to all entities is not ideal;
+        // in particular the BrooklynTags.YAML_SPEC tag will show all entities if the root has multiple
+        targetToBeExpanded.tags(sourceToBeCollapsed.getTags());
+    }
+
+    /** worker method to help determine whether child/children can be promoted */
+    @Beta //where should this live long-term?
+    public static boolean hasNoNameOrCustomKeysOrRoot(AssemblyTemplate template, EntitySpec<?> spec) {
+        if (Strings.isNonEmpty(template.getName())) {
+            if (spec.getChildren().size()==1) {
+                String childName = Iterables.getOnlyElement(spec.getChildren()).getDisplayName();
+                if (Strings.isEmpty(childName) || childName.equals(template.getName())) {
+                    // if child has no name, or it's the same, could still promote
+                } else {
+                    return false;
+                }
+            } else {
+                // if name set at root and promoting children would be ambiguous, do not promote 
+                return false;
+            }
+        } else if (spec.getChildren().size()>1) {
+            // don't allow multiple children if a name is specified as a root
+            return false;
+        }
+        
+        Set<String> rootAttrs = template.getCustomAttributes().keySet();
+        for (String rootAttr: rootAttrs) {
+            if (rootAttr.equals("brooklyn.catalog") || rootAttr.equals("brooklyn.config")) {
+                // these do not block promotion
+                continue;
+            }
+            if (rootAttr.startsWith("brooklyn.")) {
+                // any others in 'brooklyn' namespace will block promotion
+                return false;
+            }
+            // location is allowed in both, and is copied on promotion
+            // (name also copied)
+            // others are root currently are ignored on promotion; they are usually metadata
+            // TODO might be nice to know what we are excluding
+        }
+        
+        return true;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagerInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagerInternal.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagerInternal.java
new file mode 100644
index 0000000..08f4069
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/EntityManagerInternal.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.management.internal;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.management.EntityManager;
+
+public interface EntityManagerInternal extends EntityManager, BrooklynObjectManagerInternal<Entity> {
+
+    /** gets all entities currently known to the application, including entities that are not yet managed */
+    Iterable<Entity> getAllEntitiesInApplication(Application application);
+
+    public Iterable<String> getEntityIds();
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/GroovyObservablesPropertyChangeToCollectionChangeAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/GroovyObservablesPropertyChangeToCollectionChangeAdapter.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/GroovyObservablesPropertyChangeToCollectionChangeAdapter.java
new file mode 100644
index 0000000..4e74904
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/GroovyObservablesPropertyChangeToCollectionChangeAdapter.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.management.internal;
+
+import groovy.util.ObservableList;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+public class GroovyObservablesPropertyChangeToCollectionChangeAdapter implements PropertyChangeListener {
+    @SuppressWarnings("rawtypes")
+    private final CollectionChangeListener delegate;
+
+    public GroovyObservablesPropertyChangeToCollectionChangeAdapter(@SuppressWarnings("rawtypes") CollectionChangeListener delegate) {
+        this.delegate = delegate;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (evt instanceof ObservableList.ElementAddedEvent) {
+            delegate.onItemAdded(evt.getNewValue());
+        } else if (evt instanceof ObservableList.ElementRemovedEvent) {
+            delegate.onItemRemoved(evt.getOldValue());
+        } else if (evt instanceof ObservableList.ElementUpdatedEvent) {
+            delegate.onItemRemoved(evt.getOldValue());
+            delegate.onItemAdded(evt.getNewValue());
+        } else if (evt instanceof ObservableList.ElementClearedEvent) {
+            for (Object value : ((ObservableList.ElementClearedEvent) evt).getValues()) {
+                delegate.onItemAdded(value);
+            }
+        } else if(evt instanceof ObservableList.MultiElementAddedEvent ) {
+            for(Object value: ((ObservableList.MultiElementAddedEvent)evt).getValues()){
+                delegate.onItemAdded(value);
+            }
+        }
+    }
+
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+    public boolean equals(Object other) {
+        if (other instanceof GroovyObservablesPropertyChangeToCollectionChangeAdapter)
+            return delegate.equals(((GroovyObservablesPropertyChangeToCollectionChangeAdapter) other).delegate);
+        if (other instanceof CollectionChangeListener)
+            return delegate.equals(other);
+        return false;
+    }
+} 
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalAccessManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalAccessManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalAccessManager.java
new file mode 100644
index 0000000..2699676
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalAccessManager.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.management.AccessController;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public class LocalAccessManager implements AccessManager {
+
+    private volatile boolean locationProvisioningAllowed = true;
+    private volatile boolean locationManagementAllowed = true;
+    private volatile boolean entityManagementAllowed = true;
+
+    private final AtomicReference<AccessControllerImpl> controller = new AtomicReference<AccessControllerImpl>();
+
+    public LocalAccessManager() {
+        updateAccessController();
+    }
+
+    @Override
+    public AccessController getAccessController() {
+        return controller.get();
+    }
+    
+    @Override
+    public boolean isLocationProvisioningAllowed() {
+        return locationProvisioningAllowed;
+    }
+
+    @Override
+    public boolean isLocationManagementAllowed() {
+        return locationManagementAllowed;
+    }
+
+    @Override
+    public boolean isEntityManagementAllowed() {
+        return entityManagementAllowed;
+    }
+
+    @Override
+    public void setLocationProvisioningAllowed(boolean allowed) {
+        locationProvisioningAllowed = allowed;
+        updateAccessController();
+    }
+
+    @Override
+    public void setLocationManagementAllowed(boolean allowed) {
+        locationManagementAllowed = allowed;
+        updateAccessController();
+    }
+
+    @Override
+    public void setEntityManagementAllowed(boolean allowed) {
+        entityManagementAllowed = allowed;
+        updateAccessController();
+    }
+
+    private void updateAccessController() {
+        controller.set(new AccessControllerImpl(locationProvisioningAllowed, locationManagementAllowed, entityManagementAllowed));
+    }
+    
+    private static class AccessControllerImpl implements AccessController {
+        private final boolean locationProvisioningAllowed;
+        private final boolean locationManagementAllowed;
+        private final boolean entityManagementAllowed;
+        
+        public AccessControllerImpl(boolean locationProvisioningAllowed, boolean locationManagementAllowed, 
+                boolean entityManagementAllowed) {
+            this.locationProvisioningAllowed = locationProvisioningAllowed;
+            this.locationManagementAllowed = locationManagementAllowed;
+            this.entityManagementAllowed = entityManagementAllowed;
+        }
+
+        @Override
+        public Response canProvisionLocation(Location provisioner) {
+            return (locationProvisioningAllowed ? Response.allowed() : Response.disallowed("location provisioning disabled"));
+        }
+        
+        @Override
+        public Response canManageLocation(Location loc) {
+            return (locationManagementAllowed ? Response.allowed() : Response.disallowed("location management disabled"));
+        }
+        
+        @Override
+        public Response canManageEntity(Entity entity) {
+            return (entityManagementAllowed ? Response.allowed() : Response.disallowed("entity management disabled"));
+        }
+    }
+}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java
deleted file mode 100644
index 32e5c1b..0000000
--- a/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.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.management.classloading;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.Collections;
-import java.util.Enumeration;
-
-import org.apache.brooklyn.api.management.ManagementContext;
-
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.guava.Maybe;
-
-import com.google.common.base.Objects;
-
-public class JavaBrooklynClassLoadingContext extends AbstractBrooklynClassLoadingContext {
-
-    // on deserialization this loader is replaced with the catalog's root loader;
-    // may cause problems for non-osgi catalog items, but that's a reasonable trade-off,
-    // should this be serialized (e.g. in SpecialFlagsTransformer) in such a case!
-    private final transient ClassLoader loader;
-
-    /**
-     * @deprecated since 0.7.0 only for legacy catalog items which provide a non-osgi loader; see {@link #newDefault(ManagementContext)}
-     */ @Deprecated
-    public static JavaBrooklynClassLoadingContext create(ClassLoader loader) {
-        return new JavaBrooklynClassLoadingContext(null, checkNotNull(loader, "loader"));
-    }
-    
-    /**
-     * At least one of mgmt or loader must not be null.
-     * @deprecated since 0.7.0 only for legacy catalog items which provide a non-osgi loader; see {@link #newDefault(ManagementContext)}
-     */ @Deprecated
-    public static JavaBrooklynClassLoadingContext create(ManagementContext mgmt, ClassLoader loader) {
-        checkState(mgmt != null || loader != null, "mgmt and loader must not both be null");
-        return new JavaBrooklynClassLoadingContext(mgmt, loader);
-    }
-    
-    public static JavaBrooklynClassLoadingContext create(ManagementContext mgmt) {
-        return new JavaBrooklynClassLoadingContext(checkNotNull(mgmt, "mgmt"), null);
-    }
-
-    @Deprecated /** @deprecated since 0.7.0 use {@link #create(ManagementContext)} */
-    public static JavaBrooklynClassLoadingContext newDefault(ManagementContext mgmt) {
-        return new JavaBrooklynClassLoadingContext(checkNotNull(mgmt, "mgmt"), null);
-    }
-
-    @Deprecated /** @deprecated since 0.7.0 will become private; use one of the static methods to instantiate */
-    public JavaBrooklynClassLoadingContext(ManagementContext mgmt, ClassLoader loader) {
-        super(mgmt);
-        this.loader = loader;
-    }
-    
-    private ClassLoader getClassLoader() {
-        if (loader != null) return loader;
-        if (mgmt!=null) return mgmt.getCatalogClassLoader();
-        return JavaBrooklynClassLoadingContext.class.getClassLoader();
-    }
-    
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public Maybe<Class<?>> tryLoadClass(String className) {
-        try {
-            return (Maybe) Maybe.of(getClassLoader().loadClass(className));
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            return Maybe.absent("Invalid class: "+className, e);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "java:"+getClassLoader();
-    }
-    
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(super.hashCode(), getClassLoader());
-    }
-    
-    @Override
-    public boolean equals(Object obj) {
-        if (!super.equals(obj)) return false;
-        if (!(obj instanceof JavaBrooklynClassLoadingContext)) return false;
-        if (!Objects.equal(getClassLoader(), ((JavaBrooklynClassLoadingContext)obj).getClassLoader())) return false;
-        return true;
-    }
-
-    @Override
-    public URL getResource(String name) {
-        return getClassLoader().getResource(name);
-    }
-
-    @Override
-    public Iterable<URL> getResources(String name) {
-        Enumeration<URL> resources;
-        try {
-            resources = getClassLoader().getResources(name);
-        } catch (IOException e) {
-            throw Exceptions.propagate(e);
-        }
-        return Collections.list(resources);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java
deleted file mode 100644
index 027ccc3..0000000
--- a/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java
+++ /dev/null
@@ -1,147 +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.management.classloading;
-
-import java.net.URL;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.entitlement.EntitlementClass;
-
-import brooklyn.catalog.internal.CatalogUtils;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.ha.OsgiManager;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.util.guava.Maybe;
-
-import com.google.common.base.Objects;
-
-public class OsgiBrooklynClassLoadingContext extends AbstractBrooklynClassLoadingContext {
-
-    private final String catalogItemId;
-    private boolean hasBundles = false;
-    private transient Collection<CatalogBundle> _bundles;
-
-    public OsgiBrooklynClassLoadingContext(ManagementContext mgmt, String catalogItemId, Collection<CatalogBundle> bundles) {
-        super(mgmt);
-        this._bundles = bundles;
-        this.hasBundles = bundles!=null && !bundles.isEmpty();
-        this.catalogItemId = catalogItemId;
-    }
-
-    public Collection<CatalogBundle> getBundles() {
-        if (_bundles!=null || !hasBundles) return _bundles;
-        CatalogItem<?, ?> cat = CatalogUtils.getCatalogItemOptionalVersion(mgmt, catalogItemId);
-        if (cat==null) {
-            throw new IllegalStateException("Catalog item not found for "+catalogItemId+"; cannot create loading context");
-        }
-        _bundles = cat.getLibraries();
-        return _bundles;
-    }
-    
-    @Override
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public Maybe<Class<?>> tryLoadClass(String className) {
-        Maybe<Class<Object>> clazz = null;
-        Maybe<OsgiManager> osgi = null;
-        if (mgmt!=null) {
-            osgi = ((ManagementContextInternal)mgmt).getOsgiManager();
-            if (osgi.isPresent() && getBundles()!=null && !getBundles().isEmpty()) {
-                if (!Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, catalogItemId))
-                    return Maybe.absent("Not entitled to use this catalog entry");
-                
-                clazz = osgi.get().tryResolveClass(className, getBundles());
-                if (clazz.isPresent())
-                    return (Maybe)clazz;
-            }
-        }
-        
-        if (clazz!=null) { 
-            // if OSGi bundles were defined and failed, then use its error message
-            return (Maybe)clazz;
-        }
-        // else determine best message
-        if (mgmt==null) return Maybe.absent("No mgmt context available for loading "+className);
-        if (osgi!=null && osgi.isAbsent()) return Maybe.absent("OSGi not available on mgmt for loading "+className);
-        if (!hasBundles)
-            return Maybe.absent("No bundles available for loading "+className);
-        return Maybe.absent("Inconsistent state ("+mgmt+"/"+osgi+"/"+getBundles()+" loading "+className);
-    }
-
-    @Override
-    public String toString() {
-        return "OSGi:"+catalogItemId+"["+getBundles()+"]";
-    }
-    
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(super.hashCode(), getBundles(), catalogItemId);
-    }
-    
-    @Override
-    public boolean equals(Object obj) {
-        if (!super.equals(obj)) return false;
-        if (!(obj instanceof OsgiBrooklynClassLoadingContext)) return false;
-
-        OsgiBrooklynClassLoadingContext other = (OsgiBrooklynClassLoadingContext)obj;
-        if (!catalogItemId.equals(other.catalogItemId)) return false;
-        if (!Objects.equal(getBundles(), other.getBundles())) return false;
-        return true;
-    }
-
-    @Override
-    public URL getResource(String name) {
-        if (mgmt != null && isEntitledToSeeCatalogItem()) {
-            Maybe<OsgiManager> osgi = ((ManagementContextInternal) mgmt).getOsgiManager();
-            if (osgi.isPresent() && hasBundles) {
-                return osgi.get().getResource(name, getBundles());
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public Iterable<URL> getResources(String name) {
-        if (mgmt != null && isEntitledToSeeCatalogItem()) {
-            Maybe<OsgiManager> osgi = ((ManagementContextInternal) mgmt).getOsgiManager();
-            if (osgi.isPresent() && hasBundles) {
-                return osgi.get().getResources(name, getBundles());
-            }
-        }
-        return Collections.emptyList();
-    }
-
-    public String getCatalogItemId() {
-        return catalogItemId;
-    }
-
-    /**
-     * @return true if the current entitlement context may {@link Entitlements#SEE_CATALOG_ITEM see}
-     * {@link #getCatalogItemId}.
-     */
-    private boolean isEntitledToSeeCatalogItem() {
-        return Entitlements.isEntitled(mgmt.getEntitlementManager(),
-                Entitlements.SEE_CATALOG_ITEM,
-                catalogItemId);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/entitlement/BasicEntitlementClassDefinition.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/entitlement/BasicEntitlementClassDefinition.java b/core/src/main/java/brooklyn/management/entitlement/BasicEntitlementClassDefinition.java
deleted file mode 100644
index dc41aa3..0000000
--- a/core/src/main/java/brooklyn/management/entitlement/BasicEntitlementClassDefinition.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.management.entitlement;
-
-import org.apache.brooklyn.api.management.entitlement.EntitlementClass;
-
-import com.google.common.base.Objects;
-import com.google.common.reflect.TypeToken;
-
-
-public class BasicEntitlementClassDefinition<T> implements EntitlementClass<T> {
-
-    private final String identifier;
-    private final TypeToken<T> argumentType;
-
-    public BasicEntitlementClassDefinition(String identifier, TypeToken<T> argumentType) {
-        this.identifier = identifier;
-        this.argumentType = argumentType;
-    }
-    
-    public BasicEntitlementClassDefinition(String identifier, Class<T> argumentType) {
-        this.identifier = identifier;
-        this.argumentType = TypeToken.of(argumentType);
-    }
-    
-    @Override
-    public String entitlementClassIdentifier() {
-        return identifier;
-    }
-
-    @Override
-    public TypeToken<T> entitlementClassArgumentType() {
-        return argumentType;
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(this).add("identitifier", identifier).add("argumentType", argumentType).toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/entitlement/EntitlementManagerAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/entitlement/EntitlementManagerAdapter.java b/core/src/main/java/brooklyn/management/entitlement/EntitlementManagerAdapter.java
deleted file mode 100644
index 0eeb4ba..0000000
--- a/core/src/main/java/brooklyn/management/entitlement/EntitlementManagerAdapter.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.management.entitlement;
-
-import org.apache.brooklyn.api.entity.Entity;
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.Beta;
-
-import brooklyn.management.entitlement.Entitlements.EntitlementClassesHandler;
-import brooklyn.management.entitlement.Entitlements.EntityAndItem;
-import brooklyn.management.entitlement.Entitlements.StringAndArgument;
-
-/**
- * provides an easy entry point to supplying entitlements, by providing the dispatch and defining the additional methods
- * which have to be supplied.
- * <p>
- * note that this class may change as versions change, deliberately breaking backwards compatibility
- * to ensure all permissions are used.
- * <p>
- * @since 0.7.0 */
-@Beta
-public abstract class EntitlementManagerAdapter implements EntitlementManager {
-
-    private static final Logger log = LoggerFactory.getLogger(EntitlementManagerAdapter.class);
-    
-    protected class Handler implements EntitlementClassesHandler<Boolean> {
-        final EntitlementContext context;
-        protected Handler(EntitlementContext context) {
-            this.context = context;
-        }
-        
-        @Override
-        public Boolean handleSeeCatalogItem(String catalogItemId) {
-            return isEntitledToSeeCatalogItem(context, catalogItemId);
-        }
-        @Override
-        public Boolean handleAddCatalogItem(Object catalogItemBeingAdded) {
-            return isEntitledToAddCatalogItem(context, catalogItemBeingAdded);
-        }
-        @Override
-        public Boolean handleModifyCatalogItem(StringAndArgument catalogItemIdAndModification) {
-            return isEntitledToModifyCatalogItem(context, catalogItemIdAndModification==null ? null : catalogItemIdAndModification.getString(),
-                catalogItemIdAndModification==null ? null : catalogItemIdAndModification.getArgument());
-        }
-        
-        @Override
-        public Boolean handleSeeEntity(Entity entity) {
-            return isEntitledToSeeEntity(context, entity);
-        }
-        @Override
-        public Boolean handleSeeSensor(EntityAndItem<String> sensorInfo) {
-            return isEntitledToSeeSensor(context, sensorInfo.getEntity(), sensorInfo.getItem());
-        }
-        @Override
-        public Boolean handleInvokeEffector(EntityAndItem<StringAndArgument> effectorInfo) {
-            StringAndArgument item = effectorInfo.getItem();
-            return isEntitledToInvokeEffector(context, effectorInfo.getEntity(), item==null ? null : item.getString(), item==null ? null : item.getArgument());
-        }
-        @Override
-        public Boolean handleModifyEntity(Entity entity) {
-            return isEntitledToModifyEntity(context, entity);
-        }
-
-        @Override
-        public Boolean handleDeployApplication(Object app) {
-            return isEntitledToDeployApplication(context, app);
-        }
-
-        @Override
-        public Boolean handleSeeAllServerInfo() {
-            return isEntitledToSeeAllServerInfo(context);
-        }
-
-        @Override
-        public Boolean handleSeeServerStatus() {
-            return isEntitledToSeeServerStatus(context);
-        }
-
-        @Override
-        public Boolean handleRoot() {
-            return isEntitledToRoot(context);
-        }
-    }
-    
-    @Override
-    public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
-        if (log.isTraceEnabled()) {
-            log.trace("Checking entitlement of "+context+" to "+entitlementClass+" "+entitlementClassArgument);
-        }
-        
-        if (isEntitledToRoot( context )) return true;
-        
-        Handler handler = new Handler(context);
-        return Entitlements.EntitlementClassesEnum.of(entitlementClass).handle(
-            handler, entitlementClassArgument);
-    }
-
-    protected abstract boolean isEntitledToSeeCatalogItem(EntitlementContext context, String catalogItemId);
-    /** passes item to be added, either yaml, or possibly null if any addition allowed (eg when resetting) */
-    protected abstract boolean isEntitledToAddCatalogItem(EntitlementContext context, Object catalogItemBeingAdded);
-    /** passes item being modified, as ID and description of modification, both possibly null if any modification is allowed (eg when resetting) */
-    protected abstract boolean isEntitledToModifyCatalogItem(EntitlementContext context, String catalogItemId, Object catalogItemModification);
-    protected abstract boolean isEntitledToSeeSensor(EntitlementContext context, Entity entity, String sensorName);
-    protected abstract boolean isEntitledToSeeEntity(EntitlementContext context, Entity entity);
-    /** arguments might be null, a map, or a list, depending how/where invoked */
-    protected abstract boolean isEntitledToInvokeEffector(EntitlementContext context, Entity entity, String effectorName, Object arguments);
-    protected abstract boolean isEntitledToModifyEntity(EntitlementContext context, Entity entity);
-    protected abstract boolean isEntitledToDeployApplication(EntitlementContext context, Object app);
-    protected abstract boolean isEntitledToSeeAllServerInfo(EntitlementContext context);
-    protected abstract boolean isEntitledToSeeServerStatus(EntitlementContext context);
-    protected abstract boolean isEntitledToRoot(EntitlementContext context);
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/entitlement/EntitlementPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/entitlement/EntitlementPredicates.java b/core/src/main/java/brooklyn/management/entitlement/EntitlementPredicates.java
deleted file mode 100644
index 75bc5dd..0000000
--- a/core/src/main/java/brooklyn/management/entitlement/EntitlementPredicates.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.management.entitlement;
-
-import javax.annotation.Nullable;
-
-import org.apache.brooklyn.api.management.entitlement.EntitlementClass;
-import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
-
-import com.google.common.base.Predicate;
-
-public class EntitlementPredicates {
-
-    public static <T> Predicate<T> isEntitled(final EntitlementManager entitlementManager, final EntitlementClass<T> entitlementClass) {
-
-        return new Predicate<T>() {
-            @Override
-            public boolean apply(@Nullable T t) {
-                return Entitlements.isEntitled(entitlementManager, entitlementClass, t);
-            }
-        };
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/entitlement/Entitlements.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/entitlement/Entitlements.java b/core/src/main/java/brooklyn/management/entitlement/Entitlements.java
deleted file mode 100644
index 28a9a84..0000000
--- a/core/src/main/java/brooklyn/management/entitlement/Entitlements.java
+++ /dev/null
@@ -1,418 +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.management.entitlement;
-
-import java.util.Arrays;
-import java.util.List;
-
-import javax.annotation.Nullable;
-
-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.api.management.entitlement.EntitlementClass;
-import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
-import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableList;
-import com.google.common.reflect.TypeToken;
-
-import brooklyn.config.BrooklynProperties;
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.javalang.Reflections;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.text.Strings;
-
-/** @since 0.7.0 */
-@Beta
-public class Entitlements {
-
-    private static final Logger log = LoggerFactory.getLogger(Entitlements.class);
-    
-    // ------------------- individual permissions
-    
-    public static EntitlementClass<String> SEE_CATALOG_ITEM = new BasicEntitlementClassDefinition<String>("catalog.see", String.class); 
-    public static EntitlementClass<Object> ADD_CATALOG_ITEM = new BasicEntitlementClassDefinition<Object>("catalog.add", Object.class); 
-    public static EntitlementClass<StringAndArgument> MODIFY_CATALOG_ITEM = new BasicEntitlementClassDefinition<StringAndArgument>("catalog.modify", StringAndArgument.class); 
-    
-    public static EntitlementClass<Entity> SEE_ENTITY = new BasicEntitlementClassDefinition<Entity>("entity.see", Entity.class);
-    public static EntitlementClass<EntityAndItem<String>> SEE_SENSOR = new BasicEntitlementClassDefinition<EntityAndItem<String>>("sensor.see", EntityAndItem. typeToken(String.class));
-    // string is effector name; argument may be a map or a list, depending how the args were supplied
-    public static EntitlementClass<EntityAndItem<StringAndArgument>> INVOKE_EFFECTOR = new BasicEntitlementClassDefinition<EntityAndItem<StringAndArgument>>("effector.invoke", EntityAndItem.typeToken(StringAndArgument.class));
-    public static EntitlementClass<Entity> MODIFY_ENTITY = new BasicEntitlementClassDefinition<Entity>("entity.modify", Entity.class);
-    
-    /** the permission to deploy an application, where parameter is some representation of the app to be deployed (spec instance or yaml plan) */
-    public static EntitlementClass<Object> DEPLOY_APPLICATION = new BasicEntitlementClassDefinition<Object>("app.deploy", Object.class);
-
-    /** catch-all for catalog, locations, scripting, usage, etc - exporting persistence, shutting down, etc;
-     * this is significantly more powerful than {@link #SERVER_STATUS}.
-     * NB: this may be refactored and deprecated in future */
-    public static EntitlementClass<Void> SEE_ALL_SERVER_INFO = new BasicEntitlementClassDefinition<Void>("server.info.all.see", Void.class);
-
-    /** permission to see general server status info: basically HA status; not nearly as much as {@link #SEE_ALL_SERVER_INFO} */
-    public static EntitlementClass<Void> SERVER_STATUS = new BasicEntitlementClassDefinition<Void>("server.status", Void.class);
-    
-    /** permission to run untrusted code or embedded scripts at the server; 
-     * secondary check required for any operation which could potentially grant root-level access */ 
-    public static EntitlementClass<Void> ROOT = new BasicEntitlementClassDefinition<Void>("root", Void.class);
-
-    @SuppressWarnings("unchecked")
-    public static enum EntitlementClassesEnum {
-        ENTITLEMENT_SEE_CATALOG_ITEM(SEE_CATALOG_ITEM) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleSeeCatalogItem((String)argument); } },
-        ENTITLEMENT_ADD_CATALOG_ITEM(ADD_CATALOG_ITEM) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleAddCatalogItem(argument); } },
-        ENTITLEMENT_MODIFY_CATALOG_ITEM(MODIFY_CATALOG_ITEM) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleModifyCatalogItem((StringAndArgument)argument); } },
-        
-        ENTITLEMENT_SEE_ENTITY(SEE_ENTITY) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleSeeEntity((Entity)argument); } },
-        ENTITLEMENT_SEE_SENSOR(SEE_SENSOR) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleSeeSensor((EntityAndItem<String>)argument); } },
-        ENTITLEMENT_INVOKE_EFFECTOR(INVOKE_EFFECTOR) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleInvokeEffector((EntityAndItem<StringAndArgument>)argument); } },
-        ENTITLEMENT_MODIFY_ENTITY(MODIFY_ENTITY) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleModifyEntity((Entity)argument); } },
-        
-        ENTITLEMENT_DEPLOY_APPLICATION(DEPLOY_APPLICATION) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleDeployApplication(argument); } },
-        
-        ENTITLEMENT_SEE_ALL_SERVER_INFO(SEE_ALL_SERVER_INFO) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleSeeAllServerInfo(); } },
-        ENTITLEMENT_SERVER_STATUS(SERVER_STATUS) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleSeeServerStatus(); } },
-        ENTITLEMENT_ROOT(ROOT) { public <T> T handle(EntitlementClassesHandler<T> handler, Object argument) { return handler.handleRoot(); } },
-        ;
-        
-        private EntitlementClass<?> entitlementClass;
-
-        private EntitlementClassesEnum(EntitlementClass<?> specificClass) {
-            this.entitlementClass = specificClass;
-        }
-        public EntitlementClass<?> getEntitlementClass() {
-            return entitlementClass;
-        }
-
-        public abstract <T> T handle(EntitlementClassesHandler<T> handler, Object argument);
-        
-        public static EntitlementClassesEnum of(EntitlementClass<?> entitlementClass) {
-            for (EntitlementClassesEnum x: values()) {
-                if (entitlementClass.equals(x.getEntitlementClass())) return x;
-            }
-            return null;
-        }
-    }
-    
-    public interface EntitlementClassesHandler<T> {
-        public T handleSeeCatalogItem(String catalogItemId);
-        public T handleSeeServerStatus();
-        public T handleAddCatalogItem(Object catalogItemBeingAdded);
-        public T handleModifyCatalogItem(StringAndArgument catalogItemIdAndModification);
-        public T handleSeeEntity(Entity entity);
-        public T handleSeeSensor(EntityAndItem<String> sensorInfo);
-        public T handleInvokeEffector(EntityAndItem<StringAndArgument> effectorInfo);
-        public T handleModifyEntity(Entity entity);
-        public T handleDeployApplication(Object app);
-        public T handleSeeAllServerInfo();
-        public T handleRoot();
-    }
-    
-    protected static class Pair<T1,T2> {
-        protected final T1 p1;
-        protected final T2 p2;
-        protected Pair(T1 p1, T2 p2) { this.p1 = p1; this.p2 = p2; }
-    }
-    public static class EntityAndItem<T> extends Pair<Entity,T> {
-        public static <TT> TypeToken<EntityAndItem<TT>> typeToken(Class<TT> type) {
-            return new TypeToken<Entitlements.EntityAndItem<TT>>() {
-                private static final long serialVersionUID = -738154831809025407L;
-            };
-        }
-        public EntityAndItem(Entity entity, T item) { super (entity, item); }
-        public Entity getEntity() { return p1; }
-        public T getItem() { return p2; }
-        public static <T> EntityAndItem<T> of(Entity entity, T item) {
-            return new EntityAndItem<T>(entity, item);
-        }
-    }
-    
-    public static class StringAndArgument extends Pair<String,Object> {
-        public StringAndArgument(String string, Object argument) { super(string, argument); }
-        public String getString() { return p1; }
-        public Object getArgument() { return p2; }
-        public static StringAndArgument of(String string, Object argument) {
-            return new StringAndArgument(string, argument);
-        }
-    }
-
-    /** 
-     * These lifecycle operations are currently treated as effectors. This may change in the future.
-     * @since 0.7.0 */
-    @Beta
-    public static class LifecycleEffectors {
-        public static final String DELETE = "delete";
-    }
-    
-    // ------------- permission sets -------------
-    
-    /** always ALLOW access to everything */
-    public static EntitlementManager root() {
-        return new EntitlementManager() {
-            @Override
-            public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> permission, T typeArgument) {
-                return true;
-            }
-            @Override
-            public String toString() {
-                return "Entitlements.root";
-            }
-        };
-    }
-
-    /** always DENY access to anything which requires entitlements */
-    public static EntitlementManager minimal() {
-        return new EntitlementManager() {
-            @Override
-            public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> permission, T typeArgument) {
-                return false;
-            }
-            @Override
-            public String toString() {
-                return "Entitlements.minimal";
-            }
-        };
-    }
-
-    public static class FineGrainedEntitlements {
-    
-        private static final Joiner COMMA_JOINER = Joiner.on(',');
-
-        public static EntitlementManager anyOf(final EntitlementManager... checkers) {
-            return anyOf(Arrays.asList(checkers));
-        }
-        
-        public static EntitlementManager anyOf(final Iterable<? extends EntitlementManager> checkers) {
-            return new EntitlementManager() {
-                @Override
-                public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> permission, T typeArgument) {
-                    for (EntitlementManager checker: checkers)
-                        if (checker.isEntitled(context, permission, typeArgument))
-                            return true;
-                    return false;
-                }
-                @Override
-                public String toString() {
-                    return "Entitlements.anyOf(" + COMMA_JOINER.join(checkers) + ")";
-                }
-            };
-        }
-        
-        public static EntitlementManager allOf(final EntitlementManager... checkers) {
-            return allOf(Arrays.asList(checkers));
-        }
-        
-        public static EntitlementManager allOf(final Iterable<? extends EntitlementManager> checkers) {
-            return new EntitlementManager() {
-                @Override
-                public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> permission, T typeArgument) {
-                    for (EntitlementManager checker: checkers)
-                        if (checker.isEntitled(context, permission, typeArgument))
-                            return true;
-                    return false;
-                }
-                @Override
-                public String toString() {
-                    return "Entitlements.allOf(" + COMMA_JOINER.join(checkers) + ")";
-                }
-            };
-        }
-
-        public static <U> EntitlementManager allowing(EntitlementClass<U> permission, Predicate<U> test) {
-            return new SinglePermissionEntitlementChecker<U>(permission, test);
-        }
-
-        public static <U> EntitlementManager allowing(EntitlementClass<U> permission) {
-            return new SinglePermissionEntitlementChecker<U>(permission, Predicates.<U>alwaysTrue());
-        }
-
-        public static class SinglePermissionEntitlementChecker<U> implements EntitlementManager {
-            final EntitlementClass<U> permission;
-            final Predicate<U> test;
-            
-            protected SinglePermissionEntitlementChecker(EntitlementClass<U> permission, Predicate<U> test) {
-                this.permission = permission;
-                this.test = test;
-            }
-            
-            @SuppressWarnings("unchecked")
-            @Override
-            public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> permission, T typeArgument) {
-                if (!Objects.equal(this.permission, permission)) return false;
-                return test.apply((U)typeArgument);
-            }
-            @Override
-            public String toString() {
-                return "Entitlements.allowing(" + permission + " -> " + test + ")";
-            }
-        }
-        public static EntitlementManager seeNonSecretSensors() {
-            return allowing(SEE_SENSOR, new Predicate<EntityAndItem<String>>() {
-                @Override
-                public boolean apply(EntityAndItem<String> input) {
-                    if (input == null) return false;
-                    return !Entities.isSecret(input.getItem());
-                }
-                @Override
-                public String toString() {
-                    return "Predicates.nonSecret";
-                }
-            });
-        }
-        
-    }
-    
-    /** allow read-only */
-    public static EntitlementManager readOnly() {
-        return FineGrainedEntitlements.anyOf(
-            FineGrainedEntitlements.allowing(SEE_ENTITY),
-            FineGrainedEntitlements.seeNonSecretSensors()
-        );
-    }
-
-    /** allow healthcheck */
-    public static EntitlementManager serverStatusOnly() {
-        return FineGrainedEntitlements.allowing(SERVER_STATUS);
-    }
-
-    // ------------- lookup conveniences -------------
-
-    private static class PerThreadEntitlementContextHolder {
-        public static final ThreadLocal<EntitlementContext> perThreadEntitlementsContextHolder = new ThreadLocal<EntitlementContext>();
-    }
-
-    /** 
-     * Finds the currently applicable {@link EntitlementContext} by examining the current thread
-     * then by investigating the current task, its submitter, etc. */
-    // NOTE: entitlements are propagated to tasks whenever they are created, as tags
-    // (see BrooklynTaskTags.tagForEntitlement and BasicExecutionContext.submitInternal).
-    // It might be cheaper to only do this lookup, not to propagate as tags, and to ensure
-    // all entitlement operations are wrapped in a task at source; but currently we do not
-    // do that so we need at least to set entitlement on the outermost task.
-    // Setting it on tasks submitted by a task is not strictly necessary (i.e. in BasicExecutionContext)
-    // but seems cheap enough, and means checking entitlements is fast, if we choose to do that more often.
-    public static EntitlementContext getEntitlementContext() {
-        EntitlementContext context;
-        context = PerThreadEntitlementContextHolder.perThreadEntitlementsContextHolder.get();
-        if (context!=null) return context;
-        
-        Task<?> task = Tasks.current();
-        while (task!=null) {
-            context = BrooklynTaskTags.getEntitlement(task);
-            if (context!=null) return context;
-            task = task.getSubmittedByTask();
-        }
-        
-        // no entitlements set -- assume entitlements not used, or system internal
-        return null;
-    }
-
-    public static void setEntitlementContext(EntitlementContext context) {
-        EntitlementContext oldContext = PerThreadEntitlementContextHolder.perThreadEntitlementsContextHolder.get();
-        if (oldContext!=null && context!=null) {
-            log.warn("Changing entitlement context from "+oldContext+" to "+context+"; context should have been reset or extended, not replaced");
-            log.debug("Trace for entitlement context duplicate overwrite", new Throwable("Trace for entitlement context overwrite"));
-        }
-        PerThreadEntitlementContextHolder.perThreadEntitlementsContextHolder.set(context);
-    }
-    
-    public static void clearEntitlementContext() {
-        PerThreadEntitlementContextHolder.perThreadEntitlementsContextHolder.set(null);
-    }
-    
-    public static <T> boolean isEntitled(EntitlementManager checker, EntitlementClass<T> permission, T typeArgument) {
-        return checker.isEntitled(getEntitlementContext(), permission, typeArgument);
-    }
-
-    /** throws {@link NotEntitledException} if entitlement not available for current {@link #getEntitlementContext()} */
-    public static <T> void checkEntitled(EntitlementManager checker, EntitlementClass<T> permission, T typeArgument) {
-        if (!isEntitled(checker, permission, typeArgument)) {
-            throw new NotEntitledException(getEntitlementContext(), permission, typeArgument);
-        }
-    }
-    /** throws {@link NotEntitledException} if entitlement not available for current {@link #getEntitlementContext()} 
-     * @since 0.7.0
-     * @deprecated since 0.7.0, use {@link #checkEntitled(EntitlementManager, EntitlementClass, Object)};
-     * kept briefly because there is some downstream usage*/
-    public static <T> void requireEntitled(EntitlementManager checker, EntitlementClass<T> permission, T typeArgument) {
-        checkEntitled(checker, permission, typeArgument);
-    }
-    
-    // ----------------- initialization ----------------
-
-    public final static String ENTITLEMENTS_CONFIG_PREFIX = "brooklyn.entitlements";
-    
-    public static ConfigKey<String> GLOBAL_ENTITLEMENT_MANAGER = ConfigKeys.newStringConfigKey(ENTITLEMENTS_CONFIG_PREFIX+".global", 
-        "Class for entitlements in effect globally; "
-        + "short names 'minimal', 'readonly', or 'root' are permitted here, with the default 'root' giving full access to all declared users; "
-        + "or supply the name of an "+EntitlementManager.class+" class to instantiate, taking a 1-arg BrooklynProperties constructor or a 0-arg constructor",
-        "root");
-    
-    public static EntitlementManager newManager(ManagementContext mgmt, BrooklynProperties brooklynProperties) {
-        return newGlobalManager(mgmt, brooklynProperties);
-    }
-    private static EntitlementManager newGlobalManager(ManagementContext mgmt, BrooklynProperties brooklynProperties) {
-        return load(mgmt, brooklynProperties, brooklynProperties.getConfig(GLOBAL_ENTITLEMENT_MANAGER));
-    }
-    
-    public static EntitlementManager load(@Nullable ManagementContext mgmt, BrooklynProperties brooklynProperties, String type) {
-        if ("root".equalsIgnoreCase(type)) return root();
-        if ("readonly".equalsIgnoreCase(type) || "read_only".equalsIgnoreCase(type)) return readOnly();
-        if ("minimal".equalsIgnoreCase(type)) return minimal();
-        if (Strings.isNonBlank(type)) {
-            try {
-                ClassLoader cl = mgmt==null ? null : ((ManagementContextInternal)mgmt).getCatalogClassLoader();
-                if (cl==null) cl = Entitlements.class.getClassLoader();
-                Class<?> clazz = cl.loadClass(type);
-                return (EntitlementManager) instantiate(clazz, ImmutableList.of(
-                        new Object[] {mgmt, brooklynProperties},
-                        new Object[] {mgmt},
-                        new Object[] {brooklynProperties},
-                        new Object[0]));
-            } catch (Exception e) { 
-                throw Exceptions.propagate(e); 
-            }
-        }
-        throw new IllegalStateException("Invalid entitlement manager specified: '"+type+"'");
-    }
-    
-    private static Object instantiate(Class<?> clazz, List<Object[]> constructorArgOptions) {
-        try {
-            for (Object[] constructorArgOption : constructorArgOptions) {
-                Optional<?> result = Reflections.invokeConstructorWithArgs(clazz, constructorArgOption);
-                if (result.isPresent()) return result.get();
-            }
-        } catch (Exception e) { 
-            throw Exceptions.propagate(e); 
-        }
-        throw new IllegalStateException("No matching constructor to instantiate "+clazz);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/entitlement/NotEntitledException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/entitlement/NotEntitledException.java b/core/src/main/java/brooklyn/management/entitlement/NotEntitledException.java
deleted file mode 100644
index f63b7e3..0000000
--- a/core/src/main/java/brooklyn/management/entitlement/NotEntitledException.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.management.entitlement;
-
-import org.apache.brooklyn.api.management.entitlement.EntitlementClass;
-import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
-
-
-public class NotEntitledException extends RuntimeException {
-
-    private static final long serialVersionUID = -4001882260980589181L;
-    
-    EntitlementContext entitlementContext;
-    EntitlementClass<?> permission;
-    Object typeArgument;
-    
-    public <T> NotEntitledException(EntitlementContext entitlementContext, EntitlementClass<T> permission, T typeArgument) {
-        this.entitlementContext = entitlementContext;
-        this.permission = permission;
-        this.typeArgument = typeArgument;
-    }
-    
-    @Override
-    public String toString() {
-        return super.toString()+"["+entitlementContext+":"+permission+(typeArgument!=null ? "("+typeArgument+")" : "")+"]";
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManager.java b/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManager.java
deleted file mode 100644
index ab9a90f..0000000
--- a/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManager.java
+++ /dev/null
@@ -1,100 +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.management.entitlement;
-
-import java.util.Map;
-
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.BrooklynProperties;
-import brooklyn.config.ConfigKey;
-import brooklyn.config.ConfigPredicates;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.Preconditions;
-
-public class PerUserEntitlementManager implements EntitlementManager {
-
-    private static final Logger log = LoggerFactory.getLogger(PerUserEntitlementManager.class);
-    
-    public final static String PER_USER_ENTITLEMENTS_CONFIG_PREFIX = Entitlements.ENTITLEMENTS_CONFIG_PREFIX+".perUser";
-    
-    public final static ConfigKey<String> DEFAULT_MANAGER = ConfigKeys.newStringConfigKey(PER_USER_ENTITLEMENTS_CONFIG_PREFIX+
-        ".default", "Default entitlements manager for users without further specification", "minimal");
-    
-    protected final EntitlementManager defaultManager;
-    protected final Map<String,EntitlementManager> perUserManagers = MutableMap.of();
-
-    private final static ThreadLocal<Boolean> ACTIVE = new ThreadLocal<Boolean>();
-    
-    private static EntitlementManager load(BrooklynProperties properties, String type) {
-        if (Boolean.TRUE.equals(ACTIVE.get())) {
-            // prevent infinite loop
-            throw new IllegalStateException("Cannot set "+PerUserEntitlementManager.class.getName()+" within config for itself");
-        }
-        try {
-            ACTIVE.set(true);
-            return Entitlements.load(null, properties, type);
-        } finally {
-            ACTIVE.remove();
-        }
-    }
-    
-    public PerUserEntitlementManager(BrooklynProperties properties) {
-        this(load(properties, properties.getConfig(DEFAULT_MANAGER)));
-        
-        BrooklynProperties users = properties.submap(ConfigPredicates.startingWith(PER_USER_ENTITLEMENTS_CONFIG_PREFIX+"."));
-        for (Map.Entry<ConfigKey<?>,?> key: users.getAllConfig().entrySet()) {
-            if (key.getKey().getName().equals(DEFAULT_MANAGER.getName())) continue;
-            String user = Strings.removeFromStart(key.getKey().getName(), PER_USER_ENTITLEMENTS_CONFIG_PREFIX+".");
-            addUser(user, load(properties, Strings.toString(key.getValue())));
-        }
-        
-        log.info(getClass().getSimpleName()+" created with "+perUserManagers.size()+" user"+Strings.s(perUserManagers)+" and "
-            + "default "+defaultManager+" (users: "+perUserManagers+")");
-    }
-    
-    public PerUserEntitlementManager(EntitlementManager defaultManager) {
-        this.defaultManager = Preconditions.checkNotNull(defaultManager);
-    }
-
-    public void addUser(String user, EntitlementManager managerForThisUser) {
-        perUserManagers.put(Preconditions.checkNotNull(user, "user"), Preconditions.checkNotNull(managerForThisUser, "managerForThisUser"));
-    }
-
-    @Override
-    public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
-        EntitlementManager entitlementInEffect = null;
-        if (context==null || context.user()==null) {
-            // no user means it is running as an internal process, always has root
-            entitlementInEffect = Entitlements.root(); 
-        } else {
-            if (context!=null) entitlementInEffect = perUserManagers.get(context.user());
-            if (entitlementInEffect==null) entitlementInEffect = defaultManager;
-        }
-        return entitlementInEffect.isEntitled(context, entitlementClass, entitlementClassArgument);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManagerWithDefault.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManagerWithDefault.java b/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManagerWithDefault.java
deleted file mode 100644
index 95f254f..0000000
--- a/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManagerWithDefault.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.management.entitlement;
-
-import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
-
-@Deprecated
-/** @deprecated since 0.7.0 use {@link PerUserEntitlementManager} */
-public class PerUserEntitlementManagerWithDefault extends PerUserEntitlementManager {
-
-    public PerUserEntitlementManagerWithDefault(EntitlementManager defaultManager) {
-        super(defaultManager);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/entitlement/WebEntitlementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/entitlement/WebEntitlementContext.java b/core/src/main/java/brooklyn/management/entitlement/WebEntitlementContext.java
deleted file mode 100644
index f6db50d..0000000
--- a/core/src/main/java/brooklyn/management/entitlement/WebEntitlementContext.java
+++ /dev/null
@@ -1,57 +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.management.entitlement;
-
-import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
-
-import brooklyn.util.javalang.JavaClassNames;
-
-/**
- * Indicates an authenticated web request as the entitlements context;
- * note user may still be null if no authentication was requested
- */
-public class WebEntitlementContext implements EntitlementContext {
-
-    final String user;
-    final String sourceIp;
-    final String requestUri;
-    
-    /**
-     * A mostly-unique identifier for the inbound request, to distinguish
-     * between duplicate requests and for cross-referencing with URIs
-     */
-    final String requestUniqueIdentifier;
-    
-    public WebEntitlementContext(String user, String sourceIp, String requestUri, String requestUniqueIdentifier) {
-        this.user = user;
-        this.sourceIp = sourceIp;
-        this.requestUri = requestUri;
-        this.requestUniqueIdentifier = requestUniqueIdentifier;
-    }
-    
-    @Override public String user() { return user; }
-    public String sourceIp() { return sourceIp; }
-    public String requestUri() { return requestUri; }
-    public String requestUniqueIdentifier() { return requestUniqueIdentifier; }
-
-    @Override
-    public String toString() {
-        return JavaClassNames.simpleClassName(getClass())+"["+user+"@"+sourceIp+":"+requestUniqueIdentifier+"]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/ha/BasicMasterChooser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/BasicMasterChooser.java b/core/src/main/java/brooklyn/management/ha/BasicMasterChooser.java
deleted file mode 100644
index a44c372..0000000
--- a/core/src/main/java/brooklyn/management/ha/BasicMasterChooser.java
+++ /dev/null
@@ -1,204 +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.management.ha;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.brooklyn.api.entity.trait.Identifiable;
-import org.apache.brooklyn.api.management.ha.ManagementNodeState;
-import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.text.NaturalOrderComparator;
-import brooklyn.util.time.Duration;
-
-import com.google.common.annotations.Beta;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.Ordering;
-
-/**
- * @since 0.7.0
- * 
- * @author aled
- */
-@Beta
-public abstract class BasicMasterChooser implements MasterChooser {
-
-    private static final Logger LOG = LoggerFactory.getLogger(BasicMasterChooser.class);
-
-    protected static class ScoredRecord<T extends Comparable<T>> implements Identifiable, Comparable<ScoredRecord<T>> {
-        String id;
-        ManagementNodeSyncRecord record;
-        T score;
-        
-        @Override
-        public String getId() {
-            return id;
-        }
-
-        @Override
-        public int compareTo(ScoredRecord<T> o) {
-            return score.compareTo(o.score);
-        }
-    }
-    
-    public ManagementNodeSyncRecord choose(ManagementPlaneSyncRecord memento, Duration heartbeatTimeout, String ownNodeId) {
-        if (LOG.isDebugEnabled()) LOG.debug("Choosing new master from "+memento.getManagementNodes());
-        ManagementNodeSyncRecord me = memento.getManagementNodes().get(ownNodeId);
-        if (me==null) {
-            LOG.warn("Management node details not known when choosing new master: "+memento+" / "+ownNodeId);
-            return null;
-        }
-        Long nowIsh = me.getRemoteTimestamp();
-        if (nowIsh==null) {
-            LOG.warn("Management node for self missing timestamp when choosing new master: "+memento);
-            return null;
-        }
-        
-        List<ScoredRecord<?>> contenders = filterHealthy(memento, heartbeatTimeout, nowIsh);
-        
-        if (!contenders.isEmpty()) {
-            return pick(contenders);
-        } else {
-            LOG.info("No valid management node found for choosing new master: contender="+memento.getManagementNodes());
-            return null;
-        }        
-    }
-
-    /** pick the best contender; argument guaranteed to be non-null and non-empty,
-     * filtered for health reasons */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    protected ManagementNodeSyncRecord pick(List<ScoredRecord<?>> contenders) {
-        ScoredRecord min = null;
-        for (ScoredRecord x: contenders) {
-            if (min==null || x.score.compareTo(min.score)<0) min = x;
-        }
-        return min.record;
-    }
-
-    public static class AlphabeticChooserScore implements Comparable<AlphabeticChooserScore> {
-        long priority;
-        int versionBias;
-        String brooklynVersion;
-        int statePriority;
-        String nodeId;
-        
-        @Override
-        public int compareTo(AlphabeticChooserScore o) {
-            if (o==null) return -1;
-            return ComparisonChain.start()
-                // invert the order where we prefer higher values
-                .compare(o.priority, this.priority)
-                .compare(o.versionBias, this.versionBias)
-                .compare(o.brooklynVersion, this.brooklynVersion, 
-                    Ordering.from(NaturalOrderComparator.INSTANCE).nullsFirst())
-                .compare(o.statePriority, this.statePriority)
-                .compare(this.nodeId, o.nodeId, Ordering.usingToString().nullsLast())
-                .result();
-        }
-    }
-    
-    /** comparator which prefers, in order:
-     * <li> higher explicit priority
-     * <li> non-snapshot Brooklyn version, then any Brooklyn version, and lastly null version 
-     *      (using {@link NaturalOrderComparator} so e.g. v10 > v3.20 > v3.9 )
-     * <li> higher version (null last)
-     * <li> node which reports it's master, hot standby, then standby 
-     * <li> finally (common case): lower (alphabetically) node id
-     */
-    public static class AlphabeticMasterChooser extends BasicMasterChooser {
-        final boolean preferHotStandby;
-        public AlphabeticMasterChooser(boolean preferHotStandby) { this.preferHotStandby = preferHotStandby; }
-        public AlphabeticMasterChooser() { this.preferHotStandby = true; }
-        @Override
-        protected AlphabeticChooserScore score(ManagementNodeSyncRecord contender) {
-            AlphabeticChooserScore score = new AlphabeticChooserScore();
-            score.priority = contender.getPriority()!=null ? contender.getPriority() : 0;
-            score.brooklynVersion = contender.getBrooklynVersion();
-            score.versionBias = contender.getBrooklynVersion()==null ? -2 :
-                contender.getBrooklynVersion().toLowerCase().indexOf("snapshot")>=0 ? -1 :
-                0;
-            if (preferHotStandby) {
-                // other master should be preferred before we get invoked, but including for good measure
-                score.statePriority = contender.getStatus()==ManagementNodeState.MASTER ? 3 :
-                    contender.getStatus()==ManagementNodeState.HOT_STANDBY ? 2 :
-                        contender.getStatus()==ManagementNodeState.STANDBY ? 1 : -1;
-            } else {
-                score.statePriority = 0;
-            }
-            score.nodeId = contender.getNodeId();
-            return score;
-        }
-    }
-    
-    /**
-     * Filters the {@link ManagementPlaneSyncRecord#getManagementNodes()} to only those in an appropriate state, 
-     * and with heartbeats that have not timed out.
-     */
-    protected List<ScoredRecord<?>> filterHealthy(ManagementPlaneSyncRecord memento, Duration heartbeatTimeout, long nowIsh) {
-        long oldestAcceptableTimestamp = nowIsh - heartbeatTimeout.toMilliseconds();
-        List<ScoredRecord<?>> contenders = MutableList.of();
-        for (ManagementNodeSyncRecord contender : memento.getManagementNodes().values()) {
-            boolean statusOk = (contender.getStatus() == ManagementNodeState.STANDBY || contender.getStatus() == ManagementNodeState.HOT_STANDBY || contender.getStatus() == ManagementNodeState.MASTER);
-            Long remoteTimestamp = contender.getRemoteTimestamp();
-            boolean heartbeatOk;
-            if (remoteTimestamp==null) {
-                throw new IllegalStateException("Missing remote timestamp when performing master election: "+this+" / "+contender);
-                // if the above exception happens in some contexts we could either fallback to local, or fail:
-//                remoteTimestamp = contender.getLocalTimestamp();
-                // or
-//                heartbeatOk=false;
-            } else {
-                heartbeatOk = remoteTimestamp >= oldestAcceptableTimestamp;
-            }
-            if (statusOk && heartbeatOk) {
-                contenders.add(newScoredRecord(contender));
-            }
-            if (LOG.isTraceEnabled()) LOG.trace("Filtering choices of new master: contender="+contender+"; statusOk="+statusOk+"; heartbeatOk="+heartbeatOk);
-        }
-        return contenders;
-    }
-
-    @VisibleForTesting
-    //Java 6 compiler workaround, using parameterized types fails
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    protected List<ScoredRecord<?>> sort(List<ScoredRecord<?>> input) {
-        ArrayList copy = new ArrayList<ScoredRecord<?>>(input);
-        Collections.sort(copy);
-        return copy;
-    }
-    
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    protected ScoredRecord<?> newScoredRecord(ManagementNodeSyncRecord contender) {
-        ScoredRecord r = new ScoredRecord();
-        r.id = contender.getNodeId();
-        r.record = contender;
-        r.score = score(contender);
-        return r;
-    }
-
-    protected abstract Comparable<?> score(ManagementNodeSyncRecord contender);
-    
-}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerSplitBrainTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerSplitBrainTest.java b/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerSplitBrainTest.java
deleted file mode 100644
index e747a82..0000000
--- a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerSplitBrainTest.java
+++ /dev/null
@@ -1,472 +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.management.ha;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.fail;
-
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.brooklyn.api.entity.proxying.EntitySpec;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
-import org.apache.brooklyn.api.management.ha.ManagementNodeState;
-import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-import org.apache.brooklyn.test.entity.TestApplication;
-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.entity.basic.ApplicationBuilder;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.rebind.PersistenceExceptionHandlerImpl;
-import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
-import brooklyn.entity.rebind.persister.InMemoryObjectStore;
-import brooklyn.entity.rebind.persister.ListeningObjectStore;
-import brooklyn.entity.rebind.persister.PersistMode;
-import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-import brooklyn.internal.BrooklynFeatureEnablement;
-import brooklyn.management.ha.TestEntityFailingRebind.RebindException;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.test.Asserts;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.base.Ticker;
-import com.google.common.collect.ImmutableList;
-
-@Test
-public class HighAvailabilityManagerSplitBrainTest {
-
-    private static final Logger log = LoggerFactory.getLogger(HighAvailabilityManagerSplitBrainTest.class);
-    
-    private List<HaMgmtNode> nodes = new MutableList<HighAvailabilityManagerSplitBrainTest.HaMgmtNode>();
-    Map<String,String> sharedBackingStore = MutableMap.of();
-    Map<String,Date> sharedBackingStoreDates = MutableMap.of();
-    private AtomicLong sharedTime; // used to set the ticker's return value
-    private ClassLoader classLoader = getClass().getClassLoader();
-    
-    public class HaMgmtNode {
-        // TODO share with HotStandbyTest and WarmStandbyTest and a few others (minor differences but worth it ultimately)
-
-        private ManagementContextInternal mgmt;
-        private String ownNodeId;
-        private String nodeName;
-        private ListeningObjectStore objectStore;
-        private ManagementPlaneSyncRecordPersister persister;
-        private HighAvailabilityManagerImpl ha;
-        private Ticker ticker;
-        private AtomicLong currentTime; // used to set the ticker's return value
-
-        public void setUp() throws Exception {
-            if (sharedTime==null)
-                currentTime = new AtomicLong(System.currentTimeMillis());
-            
-            ticker = new Ticker() {
-                // strictly not a ticker because returns millis UTC, but it works fine even so
-                @Override public long read() {
-                    if (sharedTime!=null) return sharedTime.get();
-                    return currentTime.get();
-                }
-            };
-            
-            nodeName = "node "+nodes.size();
-            mgmt = newLocalManagementContext();
-            ownNodeId = mgmt.getManagementNodeId();
-            objectStore = new ListeningObjectStore(newPersistenceObjectStore());
-            objectStore.injectManagementContext(mgmt);
-            objectStore.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);
-            persister = new ManagementPlaneSyncRecordPersisterToObjectStore(mgmt, objectStore, classLoader);
-            ((ManagementPlaneSyncRecordPersisterToObjectStore)persister).preferRemoteTimestampInMemento();
-            BrooklynMementoPersisterToObjectStore persisterObj = new BrooklynMementoPersisterToObjectStore(objectStore, mgmt.getBrooklynProperties(), classLoader);
-            mgmt.getRebindManager().setPersister(persisterObj, PersistenceExceptionHandlerImpl.builder().build());
-            ha = ((HighAvailabilityManagerImpl)mgmt.getHighAvailabilityManager())
-                .setPollPeriod(Duration.PRACTICALLY_FOREVER)
-                .setHeartbeatTimeout(Duration.THIRTY_SECONDS)
-                .setLocalTicker(ticker)
-                .setRemoteTicker(ticker)
-                .setPersister(persister);
-            log.info("Created "+nodeName+" "+ownNodeId);
-        }
-        
-        public void tearDown() throws Exception {
-            if (ha != null) ha.stop();
-            if (mgmt != null) Entities.destroyAll(mgmt);
-            if (objectStore != null) objectStore.deleteCompletely();
-        }
-        
-        private long tickerCurrentMillis() {
-            return ticker.read();
-        }
-        
-        private long tickerAdvance(Duration duration) {
-            if (sharedTime!=null)
-                throw new IllegalStateException("Using shared ticker; cannot advance private node clock");
-            currentTime.addAndGet(duration.toMilliseconds());
-            return tickerCurrentMillis();
-        }
-        
-        @Override
-        public String toString() {
-            return nodeName+" "+ownNodeId;
-        }
-    }
-    
-    private Boolean prevThrowOnRebind;
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        prevThrowOnRebind = TestEntityFailingRebind.getThrowOnRebind();
-        TestEntityFailingRebind.setThrowOnRebind(true);
-        nodes.clear();
-        sharedBackingStore.clear();
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        try {
-            for (HaMgmtNode n: nodes)
-                n.tearDown();
-        } finally {
-            if (prevThrowOnRebind != null) TestEntityFailingRebind.setThrowOnRebind(prevThrowOnRebind);
-        }
-    }
-
-    public HaMgmtNode newNode() throws Exception {
-        HaMgmtNode node = new HaMgmtNode();
-        node.setUp();
-        nodes.add(node);
-        return node;
-    }
-
-    private void sharedTickerAdvance(Duration duration) {
-        if (sharedTime==null) {
-            for (HaMgmtNode n: nodes)
-                n.tickerAdvance(duration);
-        } else {
-            sharedTime.addAndGet(duration.toMilliseconds());
-        }
-    }
-    
-    private long sharedTickerCurrentMillis() {
-        return sharedTime.get();
-    }
-    
-    protected void useSharedTime() {
-        if (!nodes.isEmpty())
-            throw new IllegalStateException("shared time must be set up before any nodes created");
-        sharedTime = new AtomicLong(System.currentTimeMillis());
-    }
-
-    protected ManagementContextInternal newLocalManagementContext() {
-        return new LocalManagementContextForTests();
-    }
-
-    protected PersistenceObjectStore newPersistenceObjectStore() {
-        return new InMemoryObjectStore(sharedBackingStore, sharedBackingStoreDates);
-    }
-    
-    @Test
-    public void testDoubleRebindFails() throws Exception {
-        useSharedTime();
-        HaMgmtNode n1 = newNode();
-        HaMgmtNode n2 = newNode();
-
-        // first auto should become master
-        n1.ha.start(HighAvailabilityMode.AUTO);
-        n2.ha.start(HighAvailabilityMode.AUTO);
-        assertEquals(n1.ha.getNodeState(), ManagementNodeState.MASTER);
-
-        TestApplication app = ApplicationBuilder.newManagedApp(
-                EntitySpec.create(TestApplication.class).impl(TestEntityFailingRebind.class), n1.mgmt);
-        app.start(ImmutableList.<Location>of());
-
-        n1.mgmt.getRebindManager().forcePersistNow(false, null);
-
-        //don't publish state for a while (i.e. long store delays, failures)
-        sharedTickerAdvance(Duration.ONE_MINUTE);
-
-        try {
-            n2.ha.publishAndCheck(false);
-            fail("n2 rebind failure expected");
-        } catch (Exception e) {
-            assertNestedRebindException(e);
-        }
-
-        // re-check should re-assert successfully, no rebind expected as he was previously master
-        n1.ha.publishAndCheck(false);
-        ManagementPlaneSyncRecord memento;
-        memento = n1.ha.loadManagementPlaneSyncRecord(true);
-        assertEquals(memento.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.MASTER);
-        assertEquals(memento.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.FAILED);
-
-        // hot backup permitted by the TestEntityFailingRebind
-        n1.ha.changeMode(HighAvailabilityMode.HOT_BACKUP);
-        memento = n1.ha.loadManagementPlaneSyncRecord(true);
-        assertEquals(memento.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.HOT_BACKUP);
-        try {
-            n1.ha.changeMode(HighAvailabilityMode.MASTER);
-            fail("n1 rebind failure expected");
-        } catch (Exception e) {
-            assertNestedRebindException(e);
-        }
-
-        memento = n1.ha.loadManagementPlaneSyncRecord(true);
-        assertEquals(memento.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.FAILED);
-        assertEquals(memento.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.FAILED);
-    }
-
-    @Test
-    public void testStandbyRebind() throws Exception {
-        useSharedTime();
-        HaMgmtNode n1 = newNode();
-        HaMgmtNode n2 = newNode();
-
-        // first auto should become master
-        n1.ha.start(HighAvailabilityMode.AUTO);
-        n2.ha.start(HighAvailabilityMode.AUTO);
-
-        TestApplication app = ApplicationBuilder.newManagedApp(
-                EntitySpec.create(TestApplication.class).impl(TestEntityFailingRebind.class), n1.mgmt);
-        app.start(ImmutableList.<Location>of());
-
-        n1.mgmt.getRebindManager().forcePersistNow(false, null);
-
-        //don't publish state for a while (i.e. long store delays, failures)
-        sharedTickerAdvance(Duration.ONE_MINUTE);
-
-        try {
-            n2.ha.publishAndCheck(false);
-            fail("n2 rebind failure expected");
-        } catch (Exception e) {
-            assertNestedRebindException(e);
-        }
-
-        TestEntityFailingRebind.setThrowOnRebind(false);
-        n1.ha.publishAndCheck(false);
-
-        ManagementPlaneSyncRecord memento = n1.ha.loadManagementPlaneSyncRecord(true);
-        assertEquals(memento.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.MASTER);
-        assertEquals(memento.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.FAILED);
-    }
-    
-    private void assertNestedRebindException(Throwable t) {
-        Throwable ptr = t;
-        while (ptr != null) {
-            if (ptr instanceof RebindException) {
-                return;
-            }
-            ptr = ptr.getCause();
-        }
-        Exceptions.propagate(t);
-    }
-    
-    @Test
-    public void testIfNodeStopsBeingAbleToWrite() throws Exception {
-        useSharedTime();
-        log.info("time at start "+sharedTickerCurrentMillis());
-        
-        HaMgmtNode n1 = newNode();
-        HaMgmtNode n2 = newNode();
-        
-        // first auto should become master
-        n1.ha.start(HighAvailabilityMode.AUTO);
-        ManagementPlaneSyncRecord memento1 = n1.ha.loadManagementPlaneSyncRecord(true);
-        
-        log.info(n1+" HA: "+memento1);
-        assertEquals(memento1.getMasterNodeId(), n1.ownNodeId);
-        Long time0 = sharedTickerCurrentMillis();
-        assertEquals(memento1.getManagementNodes().get(n1.ownNodeId).getRemoteTimestamp(), time0);
-        assertEquals(memento1.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.MASTER);
-
-        // second - make explicit hot; that's a strictly more complex case than cold standby, so provides pretty good coverage
-        n2.ha.start(HighAvailabilityMode.HOT_STANDBY);
-        ManagementPlaneSyncRecord memento2 = n2.ha.loadManagementPlaneSyncRecord(true);
-        
-        log.info(n2+" HA: "+memento2);
-        assertEquals(memento2.getMasterNodeId(), n1.ownNodeId);
-        assertEquals(memento2.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.MASTER);
-        assertEquals(memento2.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.HOT_STANDBY);
-        assertEquals(memento2.getManagementNodes().get(n1.ownNodeId).getRemoteTimestamp(), time0);
-        assertEquals(memento2.getManagementNodes().get(n2.ownNodeId).getRemoteTimestamp(), time0);
-        
-        // and no entities at either
-        assertEquals(n1.mgmt.getApplications().size(), 0);
-        assertEquals(n2.mgmt.getApplications().size(), 0);
-
-        // create
-        TestApplication app = ApplicationBuilder.newManagedApp(EntitySpec.create(TestApplication.class), n1.mgmt);
-        app.start(ImmutableList.<Location>of());
-        app.setAttribute(TestApplication.MY_ATTRIBUTE, "hello");
-        
-        assertEquals(n1.mgmt.getApplications().size(), 1);
-        assertEquals(n2.mgmt.getApplications().size(), 0);
-        log.info("persisting "+n1.ownNodeId);
-        n1.mgmt.getRebindManager().forcePersistNow(false, null);
-        
-        n1.objectStore.setWritesFailSilently(true);
-        log.info(n1+" writes off");
-        sharedTickerAdvance(Duration.ONE_MINUTE);
-        log.info("time now "+sharedTickerCurrentMillis());
-        Long time1 = sharedTickerCurrentMillis();
-        
-        log.info("publish "+n2.ownNodeId);
-        n2.ha.publishAndCheck(false);
-        ManagementPlaneSyncRecord memento2b = n2.ha.loadManagementPlaneSyncRecord(true);
-        log.info(n2+" HA now: "+memento2b);
-        
-        // n2 infers n1 as failed 
-        assertEquals(memento2b.getManagementNodes().get(n1.ownNodeId).getStatus(), ManagementNodeState.FAILED);
-        assertEquals(memento2b.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.MASTER);
-        assertEquals(memento2b.getMasterNodeId(), n2.ownNodeId);
-        assertEquals(memento2b.getManagementNodes().get(n1.ownNodeId).getRemoteTimestamp(), time0);
-        assertEquals(memento2b.getManagementNodes().get(n2.ownNodeId).getRemoteTimestamp(), time1);
-        
-        assertEquals(n1.mgmt.getApplications().size(), 1);
-        assertEquals(n2.mgmt.getApplications().size(), 1);
-        assertEquals(n1.mgmt.getApplications().iterator().next().getAttribute(TestApplication.MY_ATTRIBUTE), "hello");
-        
-        n1.objectStore.setWritesFailSilently(false);
-        log.info(n1+" writes on");
-        
-        sharedTickerAdvance(Duration.ONE_SECOND);
-        log.info("time now "+sharedTickerCurrentMillis());
-        Long time2 = sharedTickerCurrentMillis();
-        
-        log.info("publish "+n1.ownNodeId);
-        n1.ha.publishAndCheck(false);
-        ManagementPlaneSyncRecord memento1b = n1.ha.loadManagementPlaneSyncRecord(true);
-        log.info(n1+" HA now: "+memento1b);
-        
-        ManagementNodeState expectedStateAfterDemotion = BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY) ?
-            ManagementNodeState.HOT_STANDBY : ManagementNodeState.STANDBY;
-        
-        // n1 comes back and demotes himself 
-        assertEquals(memento1b.getManagementNodes().get(n1.ownNodeId).getStatus(), expectedStateAfterDemotion);
-        assertEquals(memento1b.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.MASTER);
-        assertEquals(memento1b.getMasterNodeId(), n2.ownNodeId);
-        assertEquals(memento1b.getManagementNodes().get(n1.ownNodeId).getRemoteTimestamp(), time2);
-        assertEquals(memento1b.getManagementNodes().get(n2.ownNodeId).getRemoteTimestamp(), time1);
-        
-        // n2 now sees itself as master, with n1 in standby again
-        ManagementPlaneSyncRecord memento2c = n2.ha.loadManagementPlaneSyncRecord(true);
-        log.info(n2+" HA now: "+memento2c);
-        assertEquals(memento2c.getManagementNodes().get(n1.ownNodeId).getStatus(), expectedStateAfterDemotion);
-        assertEquals(memento2c.getManagementNodes().get(n2.ownNodeId).getStatus(), ManagementNodeState.MASTER);
-        assertEquals(memento2c.getMasterNodeId(), n2.ownNodeId);
-        assertEquals(memento2c.getManagementNodes().get(n1.ownNodeId).getRemoteTimestamp(), time2);
-        assertEquals(memento2c.getManagementNodes().get(n2.ownNodeId).getRemoteTimestamp(), time2);
-
-        // right number of entities at n2; n1 may or may not depending whether hot standby is default
-        assertEquals(n2.mgmt.getApplications().size(), 1);
-        assertEquals(n1.mgmt.getApplications().size(), BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY) ? 1 : 0);
-    }
-    
-    @Test(invocationCount=50, groups="Integration")
-    public void testIfNodeStopsBeingAbleToWriteManyTimes() throws Exception {
-        testIfNodeStopsBeingAbleToWrite();
-    }
-    
-    @Test
-    public void testSimultaneousStartup() throws Exception {
-        doTestConcurrentStartup(5, null);
-    }
-
-    @Test
-    public void testNearSimultaneousStartup() throws Exception {
-        doTestConcurrentStartup(20, Duration.millis(20));
-    }
-
-    @Test(invocationCount=50, groups="Integration")
-    public void testNearSimultaneousStartupManyTimes() throws Exception {
-        doTestConcurrentStartup(20, Duration.millis(20));
-    }
-
-    protected void doTestConcurrentStartup(int size, final Duration staggerStart) throws Exception {
-        useSharedTime();
-        
-        List<Thread> spawned = MutableList.of();
-        for (int i=0; i<size; i++) {
-            final HaMgmtNode n = newNode();
-            Thread t = new Thread() { public void run() {
-                if (staggerStart!=null) Time.sleep(staggerStart.multiply(Math.random()));
-                n.ha.start(HighAvailabilityMode.AUTO);
-                n.ha.setPollPeriod(Duration.millis(20));
-            } };
-            spawned.add(t);
-            t.start();
-        }
-
-        try {
-            final Stopwatch timer = Stopwatch.createStarted();
-            Asserts.succeedsEventually(new Runnable() {
-                @Override public void run() {
-                    ManagementPlaneSyncRecord memento = nodes.get(0).ha.loadManagementPlaneSyncRecord(true);
-                    List<ManagementNodeState> counts = MutableList.of(), savedCounts = MutableList.of();
-                    for (HaMgmtNode n: nodes) {
-                        counts.add(n.ha.getNodeState());
-                        ManagementNodeSyncRecord m = memento.getManagementNodes().get(n.ownNodeId);
-                        if (m!=null) {
-                            savedCounts.add(m.getStatus());
-                        }
-                    }
-                    log.info("while starting "+nodes.size()+" nodes: "
-                        +Collections.frequency(counts, ManagementNodeState.MASTER)+" M + "
-                        +Collections.frequency(counts, ManagementNodeState.HOT_STANDBY)+" hot + "
-                        +Collections.frequency(counts, ManagementNodeState.STANDBY)+" warm + "
-                        +Collections.frequency(counts, ManagementNodeState.INITIALIZING)+" init; "
-                        + memento.getManagementNodes().size()+" saved, "
-                        +Collections.frequency(savedCounts, ManagementNodeState.MASTER)+" M + "
-                        +Collections.frequency(savedCounts, ManagementNodeState.HOT_STANDBY)+" hot + "
-                        +Collections.frequency(savedCounts, ManagementNodeState.STANDBY)+" warm + "
-                        +Collections.frequency(savedCounts, ManagementNodeState.INITIALIZING)+" init");
-
-                    if (timer.isRunning() && Duration.of(timer).compareTo(Duration.TEN_SECONDS)>0) {
-                        log.warn("we seem to have a problem stabilizing");  //handy place to set a suspend-VM breakpoint!
-                        timer.stop();
-                    }
-                    assertEquals(Collections.frequency(counts, ManagementNodeState.MASTER), 1);
-                    assertEquals(Collections.frequency(counts, ManagementNodeState.HOT_STANDBY)+Collections.frequency(counts, ManagementNodeState.STANDBY), nodes.size()-1);
-                    assertEquals(Collections.frequency(savedCounts, ManagementNodeState.MASTER), 1);
-                    assertEquals(Collections.frequency(savedCounts, ManagementNodeState.HOT_STANDBY)+Collections.frequency(savedCounts, ManagementNodeState.STANDBY), nodes.size()-1);
-                }});
-        } catch (Throwable t) {
-            log.warn("Failed to stabilize (rethrowing): "+t, t);
-            throw Exceptions.propagate(t);
-        }
-        
-        for (Thread t: spawned)
-            t.join(Duration.THIRTY_SECONDS.toMilliseconds());
-    }
-    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java b/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
deleted file mode 100644
index 57a5d2e..0000000
--- a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
+++ /dev/null
@@ -1,284 +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.management.ha;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
-import java.util.List;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
-import org.apache.brooklyn.api.management.ha.ManagementNodeState;
-import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-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.BrooklynVersion;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.rebind.PersistenceExceptionHandlerImpl;
-import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
-import brooklyn.entity.rebind.persister.PersistMode;
-import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord;
-import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord.Builder;
-import brooklyn.management.ha.HighAvailabilityManagerImpl.PromotionListener;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.test.Asserts;
-import brooklyn.util.time.Duration;
-
-import com.google.common.base.Ticker;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-
-@Test
-public abstract class HighAvailabilityManagerTestFixture {
-
-    @SuppressWarnings("unused")
-    private static final Logger log = LoggerFactory.getLogger(HighAvailabilityManagerTestFixture.class);
-    
-    private ManagementPlaneSyncRecordPersister persister;
-    protected ManagementContextInternal managementContext;
-    private String ownNodeId;
-    private HighAvailabilityManagerImpl manager;
-    private Ticker ticker;
-    private AtomicLong currentTime; // used to set the ticker's return value
-    private RecordingPromotionListener promotionListener;
-    private ClassLoader classLoader = getClass().getClassLoader();
-    private PersistenceObjectStore objectStore;
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        currentTime = new AtomicLong(1000000000L);
-        ticker = new Ticker() {
-            // strictly not a ticker because returns millis UTC, but it works fine even so
-            @Override public long read() {
-                return currentTime.get();
-            }
-        };
-        promotionListener = new RecordingPromotionListener();
-        managementContext = newLocalManagementContext();
-        ownNodeId = managementContext.getManagementNodeId();
-        objectStore = newPersistenceObjectStore();
-        objectStore.injectManagementContext(managementContext);
-        objectStore.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);
-        persister = new ManagementPlaneSyncRecordPersisterToObjectStore(managementContext, objectStore, classLoader);
-        ((ManagementPlaneSyncRecordPersisterToObjectStore)persister).preferRemoteTimestampInMemento();
-        BrooklynMementoPersisterToObjectStore persisterObj = new BrooklynMementoPersisterToObjectStore(
-                objectStore, 
-                managementContext.getBrooklynProperties(), 
-                classLoader);
-        managementContext.getRebindManager().setPersister(persisterObj, PersistenceExceptionHandlerImpl.builder().build());
-        manager = ((HighAvailabilityManagerImpl)managementContext.getHighAvailabilityManager())
-                .setPollPeriod(getPollPeriod())
-                .setHeartbeatTimeout(Duration.THIRTY_SECONDS)
-                .setPromotionListener(promotionListener)
-                .setLocalTicker(ticker)
-                .setRemoteTicker(getRemoteTicker())
-                .setPersister(persister);
-        persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
-            .node(newManagerMemento(ownNodeId, ManagementNodeState.HOT_STANDBY))
-            .build());
-
-    }
-
-    protected ManagementContextInternal newLocalManagementContext() {
-        return LocalManagementContextForTests.newInstance();
-    }
-
-    protected abstract PersistenceObjectStore newPersistenceObjectStore();
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (manager != null) manager.stop();
-        if (managementContext != null) Entities.destroyAll(managementContext);
-        if (objectStore != null) objectStore.deleteCompletely();
-    }
-    
-    // The web-console could still be polling (e.g. if have just restarted brooklyn), before the persister is set.
-    // Must not throw NPE, but instead return something sensible (e.g. an empty state record).
-    @Test
-    public void testGetManagementPlaneSyncStateDoesNotThrowNpeBeforePersisterSet() throws Exception {
-        HighAvailabilityManagerImpl manager2 = new HighAvailabilityManagerImpl(managementContext)
-            .setPollPeriod(Duration.millis(10))
-            .setHeartbeatTimeout(Duration.THIRTY_SECONDS)
-            .setPromotionListener(promotionListener)
-            .setLocalTicker(ticker)
-            .setRemoteTicker(ticker);
-        try {
-            ManagementPlaneSyncRecord state = manager2.loadManagementPlaneSyncRecord(true);
-            assertNotNull(state);
-        } finally {
-            manager2.stop();
-        }
-
-    }
-    // Can get a log.error about our management node's heartbeat being out of date. Caused by
-    // poller first writing a heartbeat record, and then the clock being incremented. But the
-    // next poll fixes it.
-    public void testPromotes() throws Exception {
-        persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
-                .node(newManagerMemento(ownNodeId, ManagementNodeState.HOT_STANDBY))
-                .node(newManagerMemento("node1", ManagementNodeState.MASTER))
-                .setMaster("node1")
-                .build());
-        
-        manager.start(HighAvailabilityMode.AUTO);
-        
-        // Simulate passage of time; ticker used by this HA-manager so it will "correctly" publish
-        // its own heartbeat with the new time; but node1's record is now out-of-date.
-        tickerAdvance(Duration.seconds(31));
-        
-        // Expect to be notified of our promotion, as the only other node
-        promotionListener.assertCalledEventually();
-    }
-
-    @Test(groups="Integration") // because one second wait in succeedsContinually
-    public void testDoesNotPromoteIfMasterTimeoutNotExpired() throws Exception {
-        persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
-                .node(newManagerMemento(ownNodeId, ManagementNodeState.HOT_STANDBY))
-                .node(newManagerMemento("node1", ManagementNodeState.MASTER))
-                .setMaster("node1")
-                .build());
-        
-        manager.start(HighAvailabilityMode.AUTO);
-        
-        tickerAdvance(Duration.seconds(25));
-        
-        // Expect not to be notified, as 25s < 30s timeout
-        // (it's normally a fake clock so won't hit 30, even waiting 1s below - but in "IntegrationTest" subclasses it is real!)
-        Asserts.succeedsContinually(new Runnable() {
-            @Override public void run() {
-                assertTrue(promotionListener.callTimestamps.isEmpty(), "calls="+promotionListener.callTimestamps);
-            }});
-    }
-
-    public void testGetManagementPlaneStatus() throws Exception {
-        // with the name zzzzz the mgr created here should never be promoted by the alphabetical strategy!
-        
-        tickerAdvance(Duration.FIVE_SECONDS);
-        persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
-                .node(newManagerMemento(ownNodeId, ManagementNodeState.STANDBY))
-                .node(newManagerMemento("zzzzzzz_node1", ManagementNodeState.STANDBY))
-                .build());
-        persister.loadSyncRecord();
-        long zzzTime = tickerCurrentMillis();
-        tickerAdvance(Duration.FIVE_SECONDS);
-        
-        manager.start(HighAvailabilityMode.AUTO);
-        ManagementPlaneSyncRecord memento = manager.loadManagementPlaneSyncRecord(true);
-        
-        // Note can assert timestamp because not "real" time; it's using our own Ticker
-        assertEquals(memento.getMasterNodeId(), ownNodeId);
-        assertEquals(memento.getManagementNodes().keySet(), ImmutableSet.of(ownNodeId, "zzzzzzz_node1"));
-        assertEquals(memento.getManagementNodes().get(ownNodeId).getNodeId(), ownNodeId);
-        assertEquals(memento.getManagementNodes().get(ownNodeId).getStatus(), ManagementNodeState.MASTER);
-        assertEquals(memento.getManagementNodes().get(ownNodeId).getLocalTimestamp(), tickerCurrentMillis());
-        assertEquals(memento.getManagementNodes().get("zzzzzzz_node1").getNodeId(), "zzzzzzz_node1");
-        assertEquals(memento.getManagementNodes().get("zzzzzzz_node1").getStatus(), ManagementNodeState.STANDBY);
-        assertEquals(memento.getManagementNodes().get("zzzzzzz_node1").getLocalTimestamp(), zzzTime);
-    }
-
-    @Test(groups="Integration", invocationCount=50) //because we have had non-deterministic failures 
-    public void testGetManagementPlaneStatusManyTimes() throws Exception {
-        testGetManagementPlaneStatus();
-    }
-    
-    @Test
-    public void testGetManagementPlaneSyncStateInfersTimedOutNodeAsFailed() throws Exception {
-        persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
-                .node(newManagerMemento(ownNodeId, ManagementNodeState.HOT_STANDBY))
-                .node(newManagerMemento("node1", ManagementNodeState.MASTER))
-                .setMaster("node1")
-                .build());
-        
-        manager.start(HighAvailabilityMode.HOT_STANDBY);
-        
-        ManagementPlaneSyncRecord state = manager.loadManagementPlaneSyncRecord(true);
-        assertEquals(state.getManagementNodes().get("node1").getStatus(), ManagementNodeState.MASTER);
-        assertEquals(state.getManagementNodes().get(ownNodeId).getStatus(), ManagementNodeState.HOT_STANDBY);
-        
-        // Simulate passage of time; ticker used by this HA-manager so it will "correctly" publish
-        // its own heartbeat with the new time; but node1's record is now out-of-date.
-        tickerAdvance(Duration.seconds(31));
-        
-        ManagementPlaneSyncRecord state2 = manager.loadManagementPlaneSyncRecord(true);
-        assertEquals(state2.getManagementNodes().get("node1").getStatus(), ManagementNodeState.FAILED);
-        assertNotEquals(state.getManagementNodes().get(ownNodeId).getStatus(), ManagementNodeState.FAILED);
-    }
-
-    protected Duration getPollPeriod() {
-        return Duration.millis(10);
-    }
-    
-    protected long tickerCurrentMillis() {
-        return ticker.read();
-    }
-    
-    protected long tickerAdvance(Duration duration) {
-        currentTime.addAndGet(duration.toMilliseconds());
-        return tickerCurrentMillis();
-    }
-
-    protected Ticker getRemoteTicker() {
-        return ticker;
-    }
-    
-    protected ManagementNodeSyncRecord newManagerMemento(String nodeId, ManagementNodeState status) {
-        Builder rb = BasicManagementNodeSyncRecord.builder();
-        rb.brooklynVersion(BrooklynVersion.get()).nodeId(nodeId).status(status);
-        rb.localTimestamp(tickerCurrentMillis());
-        if (getRemoteTicker()!=null)
-            rb.remoteTimestamp(getRemoteTicker().read());
-        return rb.build();
-    }
-    
-    public static class RecordingPromotionListener implements PromotionListener {
-        public final List<Long> callTimestamps = Lists.newCopyOnWriteArrayList();
-        
-        @Override
-        public void promotingToMaster() {
-            callTimestamps.add(System.currentTimeMillis());
-        }
-        
-        public void assertNotCalled() {
-            assertTrue(callTimestamps.isEmpty(), "calls="+callTimestamps);
-        }
-
-        public void assertCalled() {
-            assertFalse(callTimestamps.isEmpty(), "calls="+callTimestamps);
-        }
-
-        public void assertCalledEventually() {
-            Asserts.succeedsEventually(new Runnable() {
-                @Override public void run() {
-                    assertCalled();
-                }});
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/HotStandbyTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/HotStandbyTest.java b/core/src/test/java/brooklyn/management/ha/HotStandbyTest.java
deleted file mode 100644
index f061289..0000000
--- a/core/src/test/java/brooklyn/management/ha/HotStandbyTest.java
+++ /dev/null
@@ -1,665 +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.management.ha;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-
-import java.util.ArrayDeque;
-import java.util.Date;
-import java.util.Deque;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.Feed;
-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.ha.HighAvailabilityMode;
-import org.apache.brooklyn.api.management.ha.ManagementNodeState;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
-import org.apache.brooklyn.test.EntityTestUtils;
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-import org.apache.brooklyn.test.entity.TestApplication;
-import org.apache.brooklyn.test.entity.TestEntity;
-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 brooklyn.entity.rebind.PersistenceExceptionHandlerImpl;
-import brooklyn.entity.rebind.RebindFeedTest.MyEntityWithFunctionFeedImpl;
-import brooklyn.entity.rebind.RebindFeedTest.MyEntityWithNewFeedsEachTimeImpl;
-import brooklyn.entity.rebind.RebindManagerImpl;
-import brooklyn.entity.rebind.RebindTestFixture;
-import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
-import brooklyn.entity.rebind.persister.InMemoryObjectStore;
-import brooklyn.entity.rebind.persister.ListeningObjectStore;
-import brooklyn.entity.rebind.persister.PersistMode;
-import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-
-import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation.LocalhostMachine;
-
-import brooklyn.management.internal.AbstractManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.javalang.JavaClassNames;
-import brooklyn.util.repeat.Repeater;
-import brooklyn.util.text.ByteSizeStrings;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.collect.Iterables;
-
-public class HotStandbyTest {
-
-    private static final Logger log = LoggerFactory.getLogger(HotStandbyTest.class);
-    
-    private List<HaMgmtNode> nodes = new MutableList<HotStandbyTest.HaMgmtNode>();
-    Map<String,String> sharedBackingStore = MutableMap.of();
-    Map<String,Date> sharedBackingStoreDates = MutableMap.of();
-    private ClassLoader classLoader = getClass().getClassLoader();
-    
-    public class HaMgmtNode {
-        // TODO share with WarmStandbyTest and SplitBrainTest and a few others (minor differences but worth it ultimately)
-
-        private ManagementContextInternal mgmt;
-        private String ownNodeId;
-        private String nodeName;
-        private ListeningObjectStore objectStore;
-        private ManagementPlaneSyncRecordPersister persister;
-        private HighAvailabilityManagerImpl ha;
-        private Duration persistOrRebindPeriod = Duration.ONE_SECOND;
-
-        public void setUp() throws Exception {
-            nodeName = "node "+nodes.size();
-            mgmt = newLocalManagementContext();
-            ownNodeId = mgmt.getManagementNodeId();
-            objectStore = new ListeningObjectStore(newPersistenceObjectStore());
-            objectStore.injectManagementContext(mgmt);
-            objectStore.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);
-            persister = new ManagementPlaneSyncRecordPersisterToObjectStore(mgmt, objectStore, classLoader);
-            ((ManagementPlaneSyncRecordPersisterToObjectStore)persister).preferRemoteTimestampInMemento();
-            BrooklynMementoPersisterToObjectStore persisterObj = new BrooklynMementoPersisterToObjectStore(objectStore, mgmt.getBrooklynProperties(), classLoader);
-            ((RebindManagerImpl)mgmt.getRebindManager()).setPeriodicPersistPeriod(persistOrRebindPeriod);
-            mgmt.getRebindManager().setPersister(persisterObj, PersistenceExceptionHandlerImpl.builder().build());
-            ha = ((HighAvailabilityManagerImpl)mgmt.getHighAvailabilityManager())
-                .setPollPeriod(Duration.PRACTICALLY_FOREVER)
-                .setHeartbeatTimeout(Duration.THIRTY_SECONDS)
-                .setPersister(persister);
-            log.info("Created "+nodeName+" "+ownNodeId);
-        }
-        
-        public void tearDownThisOnly() throws Exception {
-            if (ha != null) ha.stop();
-            if (mgmt!=null) mgmt.getRebindManager().stop();
-            if (mgmt != null) Entities.destroyAll(mgmt);
-        }
-        
-        public void tearDownAll() throws Exception {
-            tearDownThisOnly();
-            // can't delete the object store until all being torn down
-            if (objectStore != null) objectStore.deleteCompletely();
-        }
-        
-        @Override
-        public String toString() {
-            return nodeName+" "+ownNodeId;
-        }
-
-        public RebindManagerImpl rebinder() {
-            return (RebindManagerImpl)mgmt.getRebindManager();
-        }
-    }
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        nodes.clear();
-        sharedBackingStore.clear();
-    }
-    
-    public HaMgmtNode newNode(Duration persistOrRebindPeriod) throws Exception {
-        HaMgmtNode node = new HaMgmtNode();
-        node.persistOrRebindPeriod = persistOrRebindPeriod;
-        node.setUp();
-        nodes.add(node);
-        return node;
-    }
-
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        for (HaMgmtNode n: nodes)
-            n.tearDownAll();
-    }
-
-    protected ManagementContextInternal newLocalManagementContext() {
-        return new LocalManagementContextForTests();
-    }
-
-    protected PersistenceObjectStore newPersistenceObjectStore() {
-        return new InMemoryObjectStore(sharedBackingStore, sharedBackingStoreDates);
-    }
-
-    private HaMgmtNode createMaster(Duration persistOrRebindPeriod) throws Exception {
-        HaMgmtNode n1 = newNode(persistOrRebindPeriod);
-        n1.ha.start(HighAvailabilityMode.AUTO);
-        assertEquals(n1.ha.getNodeState(), ManagementNodeState.MASTER);
-        return n1;
-    }
-    
-    private HaMgmtNode createHotStandby(Duration rebindPeriod) throws Exception {
-        HaMgmtNode n2 = newNode(rebindPeriod);
-        n2.ha.start(HighAvailabilityMode.HOT_STANDBY);
-        assertEquals(n2.ha.getNodeState(), ManagementNodeState.HOT_STANDBY);
-        return n2;
-    }
-
-    private TestApplication createFirstAppAndPersist(HaMgmtNode n1) throws Exception {
-        TestApplication app = TestApplication.Factory.newManagedInstanceForTests(n1.mgmt);
-        // for testing without enrichers, if desired:
-//        TestApplication app = ApplicationBuilder.newManagedApp(EntitySpec.create(TestApplication.class).impl(TestApplicationNoEnrichersImpl.class), n1.mgmt);
-        app.setDisplayName("First App");
-        app.start(MutableList.<Location>of());
-        app.config().set(TestEntity.CONF_NAME, "first-app");
-        app.setAttribute(TestEntity.SEQUENCE, 3);
-        
-        forcePersistNow(n1);
-        return app;
-    }
-
-    protected void forcePersistNow(HaMgmtNode n1) {
-        n1.mgmt.getRebindManager().forcePersistNow(false, null);
-    }
-    
-    private Application expectRebindSequenceNumber(HaMgmtNode master, HaMgmtNode hotStandby, Application app, int expectedSensorSequenceValue, boolean immediate) {
-        Application appRO = hotStandby.mgmt.lookup(app.getId(), Application.class);
-
-        if (immediate) {
-            forcePersistNow(master);
-            forceRebindNow(hotStandby);
-            EntityTestUtils.assertAttributeEquals(appRO, TestEntity.SEQUENCE, expectedSensorSequenceValue);
-        } else {
-            EntityTestUtils.assertAttributeEqualsEventually(appRO, TestEntity.SEQUENCE, expectedSensorSequenceValue);
-        }
-        
-        log.info("got sequence number "+expectedSensorSequenceValue+" from "+appRO);
-        
-        // make sure the instance (proxy) is unchanged
-        Application appRO2 = hotStandby.mgmt.lookup(app.getId(), Application.class);
-        Assert.assertTrue(appRO2==appRO);
-        
-        return appRO;
-    }
-
-    private void forceRebindNow(HaMgmtNode hotStandby) {
-        hotStandby.mgmt.getRebindManager().rebind(null, null, ManagementNodeState.HOT_STANDBY);
-    }
-    
-    @Test
-    public void testHotStandbySeesInitialCustomNameConfigAndSensorValueButDoesntAllowChange() throws Exception {
-        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
-        TestApplication app = createFirstAppAndPersist(n1);
-        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
-
-        assertEquals(n2.mgmt.getApplications().size(), 1);
-        Application appRO = n2.mgmt.lookup(app.getId(), Application.class);
-        Assert.assertNotNull(appRO);
-        Assert.assertTrue(appRO instanceof TestApplication);
-        assertEquals(appRO.getDisplayName(), "First App");
-        assertEquals(appRO.getConfig(TestEntity.CONF_NAME), "first-app");
-        assertEquals(appRO.getAttribute(TestEntity.SEQUENCE), (Integer)3);
-
-        try {
-            ((TestApplication)appRO).setAttribute(TestEntity.SEQUENCE, 4);
-            Assert.fail("Should not have allowed sensor to be set");
-        } catch (Exception e) {
-            Assert.assertTrue(e.toString().toLowerCase().contains("read-only"), "Error message did not contain expected text: "+e);
-        }
-        assertEquals(appRO.getAttribute(TestEntity.SEQUENCE), (Integer)3);
-    }
-
-    @Test
-    public void testHotStandbySeesChangesToNameConfigAndSensorValue() throws Exception {
-        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
-        TestApplication app = createFirstAppAndPersist(n1);
-        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
-
-        assertEquals(n2.mgmt.getApplications().size(), 1);
-        Application appRO = n2.mgmt.lookup(app.getId(), Application.class);
-        Assert.assertNotNull(appRO);
-        assertEquals(appRO.getChildren().size(), 0);
-        
-        // test changes
-
-        app.setDisplayName("First App Renamed");
-        app.config().set(TestEntity.CONF_NAME, "first-app-renamed");
-        app.setAttribute(TestEntity.SEQUENCE, 4);
-
-        appRO = expectRebindSequenceNumber(n1, n2, app, 4, true);
-        assertEquals(n2.mgmt.getEntityManager().getEntities().size(), 1);
-        assertEquals(appRO.getDisplayName(), "First App Renamed");
-        assertEquals(appRO.getConfig(TestEntity.CONF_NAME), "first-app-renamed");
-        
-        // and change again for good measure!
-
-        app.setDisplayName("First App");
-        app.config().set(TestEntity.CONF_NAME, "first-app-restored");
-        app.setAttribute(TestEntity.SEQUENCE, 5);
-        
-        appRO = expectRebindSequenceNumber(n1, n2, app, 5, true);
-        assertEquals(n2.mgmt.getEntityManager().getEntities().size(), 1);
-        assertEquals(appRO.getDisplayName(), "First App");
-        assertEquals(appRO.getConfig(TestEntity.CONF_NAME), "first-app-restored");
-    }
-
-
-    public void testHotStandbySeesStructuralChangesIncludingRemoval() throws Exception {
-        doTestHotStandbySeesStructuralChangesIncludingRemoval(true);
-    }
-    
-    @Test(groups="Integration") // due to time (it waits for background persistence)
-    public void testHotStandbyUnforcedSeesStructuralChangesIncludingRemoval() throws Exception {
-        doTestHotStandbySeesStructuralChangesIncludingRemoval(false);
-    }
-    
-    public void doTestHotStandbySeesStructuralChangesIncludingRemoval(boolean immediate) throws Exception {
-        HaMgmtNode n1 = createMaster(immediate ? Duration.PRACTICALLY_FOREVER : Duration.millis(200));
-        TestApplication app = createFirstAppAndPersist(n1);
-        HaMgmtNode n2 = createHotStandby(immediate ? Duration.PRACTICALLY_FOREVER : Duration.millis(200));
-
-        assertEquals(n2.mgmt.getApplications().size(), 1);
-        Application appRO = n2.mgmt.lookup(app.getId(), Application.class);
-        Assert.assertNotNull(appRO);
-        assertEquals(appRO.getChildren().size(), 0);
-        
-        // test additions - new child, new app
-        
-        TestEntity child = app.addChild(EntitySpec.create(TestEntity.class).configure(TestEntity.CONF_NAME, "first-child"));
-        Entities.manage(child);
-        TestApplication app2 = TestApplication.Factory.newManagedInstanceForTests(n1.mgmt);
-        app2.config().set(TestEntity.CONF_NAME, "second-app");
-        
-        app.setAttribute(TestEntity.SEQUENCE, 4);
-        appRO = expectRebindSequenceNumber(n1, n2, app, 4, immediate);
-        
-        assertEquals(appRO.getChildren().size(), 1);
-        Entity childRO = Iterables.getOnlyElement(appRO.getChildren());
-        assertEquals(childRO.getId(), child.getId());
-        assertEquals(childRO.getConfig(TestEntity.CONF_NAME), "first-child");
-        
-        assertEquals(n2.mgmt.getApplications().size(), 2);
-        Application app2RO = n2.mgmt.lookup(app2.getId(), Application.class);
-        Assert.assertNotNull(app2RO);
-        assertEquals(app2RO.getConfig(TestEntity.CONF_NAME), "second-app");
-        
-        assertEquals(n2.mgmt.getEntityManager().getEntities().size(), 3);
-        
-        // now test removals
-        
-        Entities.unmanage(child);
-        Entities.unmanage(app2);
-        
-        app.setAttribute(TestEntity.SEQUENCE, 5);
-        appRO = expectRebindSequenceNumber(n1, n2, app, 5, immediate);
-        
-        EntityTestUtils.assertAttributeEqualsEventually(appRO, TestEntity.SEQUENCE, 5);
-        assertEquals(n2.mgmt.getEntityManager().getEntities().size(), 1);
-        assertEquals(appRO.getChildren().size(), 0);
-        assertEquals(n2.mgmt.getApplications().size(), 1);
-        Assert.assertNull(n2.mgmt.lookup(app2.getId(), Application.class));
-        Assert.assertNull(n2.mgmt.lookup(child.getId(), Application.class));
-    }
-
-    @Test(groups="Integration", invocationCount=50)
-    public void testHotStandbySeesStructuralChangesIncludingRemovalManyTimes() throws Exception {
-        doTestHotStandbySeesStructuralChangesIncludingRemoval(true);
-    }
-
-    Deque<Long> usedMemory = new ArrayDeque<Long>();
-    protected long noteUsedMemory(String message) {
-        Time.sleep(Duration.millis(200));
-        for (HaMgmtNode n: nodes) {
-            ((AbstractManagementContext)n.mgmt).getGarbageCollector().gcIteration();
-        }
-        System.gc(); System.gc();
-        Time.sleep(Duration.millis(50));
-        System.gc(); System.gc();
-        long mem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
-        usedMemory.addLast(mem);
-        log.info("Memory used - "+message+": "+ByteSizeStrings.java().apply(mem));
-        return mem;
-    }
-    public void assertUsedMemoryLessThan(String event, long max) {
-        noteUsedMemory(event);
-        long nowUsed = usedMemory.peekLast();
-        if (nowUsed > max) {
-            // aggressively try to force GC
-            Time.sleep(Duration.ONE_SECOND);
-            usedMemory.removeLast();
-            noteUsedMemory(event+" (extra GC)");
-            nowUsed = usedMemory.peekLast();
-            if (nowUsed > max) {
-                Assert.fail("Too much memory used - "+ByteSizeStrings.java().apply(nowUsed)+" > max "+ByteSizeStrings.java().apply(max));
-            }
-        }
-    }
-    public void assertUsedMemoryMaxDelta(String event, long deltaMegabytes) {
-        assertUsedMemoryLessThan(event, usedMemory.peekLast() + deltaMegabytes*1024*1024);
-    }
-
-    @Test(groups="Integration")
-    public void testHotStandbyDoesNotLeakLotsOfRebinds() throws Exception {
-        log.info("Starting test "+JavaClassNames.niceClassAndMethod());
-        final int DELTA = 2;
-        
-        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
-        TestApplication app = createFirstAppAndPersist(n1);
-        long initialUsed = noteUsedMemory("Created app");
-        
-        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
-        assertUsedMemoryMaxDelta("Standby created", DELTA);
-        
-        forcePersistNow(n1);
-        forceRebindNow(n2);
-        assertUsedMemoryMaxDelta("Persisted and rebinded once", DELTA);
-        
-        for (int i=0; i<10; i++) {
-            forcePersistNow(n1);
-            forceRebindNow(n2);
-        }
-        assertUsedMemoryMaxDelta("Persisted and rebinded 10x", DELTA);
-        
-        for (int i=0; i<1000; i++) {
-            if ((i+1)%100==0) {
-                noteUsedMemory("iteration "+(i+1));
-                usedMemory.removeLast();
-            }
-            forcePersistNow(n1);
-            forceRebindNow(n2);
-        }
-        assertUsedMemoryMaxDelta("Persisted and rebinded 1000x", DELTA);
-        
-        Entities.unmanage(app);
-        forcePersistNow(n1);
-        forceRebindNow(n2);
-        
-        assertUsedMemoryLessThan("And now all unmanaged", initialUsed + DELTA*1000*1000);
-    }
-
-    static class BigObject {
-        public BigObject(int sizeBytes) { array = new byte[sizeBytes]; }
-        byte[] array;
-    }
-    
-    @Test(groups="Integration")
-    public void testHotStandbyDoesNotLeakBigObjects() throws Exception {
-        log.info("Starting test "+JavaClassNames.niceClassAndMethod());
-        final int SIZE = 5;
-        final int SIZE_UP_BOUND = SIZE+2;
-        final int SIZE_DOWN_BOUND = SIZE-1;
-        final int GRACE = 2;
-        // the XML persistence uses a lot of space, we approx at between 2x and 3c
-        final int SIZE_IN_XML = 3*SIZE;
-        final int SIZE_IN_XML_DOWN = 2*SIZE;
-        
-        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
-        TestApplication app = createFirstAppAndPersist(n1);        
-        noteUsedMemory("Finished seeding");
-        Long initialUsed = usedMemory.peekLast();
-        app.config().set(TestEntity.CONF_OBJECT, new BigObject(SIZE*1000*1000));
-        assertUsedMemoryMaxDelta("Set a big config object", SIZE_UP_BOUND);
-        forcePersistNow(n1);
-        assertUsedMemoryMaxDelta("Persisted a big config object", SIZE_IN_XML);
-        
-        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
-        forceRebindNow(n2);
-        assertUsedMemoryMaxDelta("Rebinded", SIZE_UP_BOUND);
-        
-        for (int i=0; i<10; i++)
-            forceRebindNow(n2);
-        assertUsedMemoryMaxDelta("Several more rebinds", GRACE);
-        for (int i=0; i<10; i++) {
-            forcePersistNow(n1);
-            forceRebindNow(n2);
-        }
-        assertUsedMemoryMaxDelta("And more rebinds and more persists", GRACE);
-        
-        app.config().set(TestEntity.CONF_OBJECT, "big is now small");
-        assertUsedMemoryMaxDelta("Big made small at primary", -SIZE_DOWN_BOUND);
-        forcePersistNow(n1);
-        assertUsedMemoryMaxDelta("And persisted", -SIZE_IN_XML_DOWN);
-        
-        forceRebindNow(n2);
-        assertUsedMemoryMaxDelta("And at secondary", -SIZE_DOWN_BOUND);
-        
-        Entities.unmanage(app);
-        forcePersistNow(n1);
-        forceRebindNow(n2);
-        
-        assertUsedMemoryLessThan("And now all unmanaged", initialUsed+GRACE*1000*1000);
-    }
-
-    @Test(groups="Integration") // because it's slow
-    // Sept 2014 - there is a small leak, of 200 bytes per child created and destroyed;
-    // but this goes away when the app is destroyed; it may be a benign record
-    public void testHotStandbyDoesNotLeakLotsOfRebindsCreatingAndDestroyingAChildEntity() throws Exception {
-        log.info("Starting test "+JavaClassNames.niceClassAndMethod());
-        final int DELTA = 2;
-        
-        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
-        TestApplication app = createFirstAppAndPersist(n1);
-        long initialUsed = noteUsedMemory("Created app");
-        
-        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
-        assertUsedMemoryMaxDelta("Standby created", DELTA);
-        
-        TestEntity lastChild = app.addChild(EntitySpec.create(TestEntity.class).configure(TestEntity.CONF_NAME, "first-child"));
-        Entities.manage(lastChild);
-        forcePersistNow(n1);
-        forceRebindNow(n2);
-        assertUsedMemoryMaxDelta("Child created and rebinded once", DELTA);
-        
-        for (int i=0; i<1000; i++) {
-            if (i==9 || (i+1)%100==0) {
-                noteUsedMemory("iteration "+(i+1));
-                usedMemory.removeLast();
-            }
-            TestEntity newChild = app.addChild(EntitySpec.create(TestEntity.class).configure(TestEntity.CONF_NAME, "first-child"));
-            Entities.manage(newChild);
-            Entities.unmanage(lastChild);
-            lastChild = newChild;
-            
-            forcePersistNow(n1);
-            forceRebindNow(n2);
-        }
-        assertUsedMemoryMaxDelta("Persisted and rebinded 1000x", DELTA);
-        
-        Entities.unmanage(app);
-        forcePersistNow(n1);
-        forceRebindNow(n2);
-        
-        assertUsedMemoryLessThan("And now all unmanaged", initialUsed + DELTA*1000*1000);
-    }
-    
-    protected void assertHotStandby(HaMgmtNode n1) {
-        assertEquals(n1.ha.getNodeState(), ManagementNodeState.HOT_STANDBY);
-        Assert.assertTrue(n1.rebinder().isReadOnlyRunning());
-        Assert.assertFalse(n1.rebinder().isPersistenceRunning());
-    }
-
-    protected void assertMaster(HaMgmtNode n1) {
-        assertEquals(n1.ha.getNodeState(), ManagementNodeState.MASTER);
-        Assert.assertFalse(n1.rebinder().isReadOnlyRunning());
-        Assert.assertTrue(n1.rebinder().isPersistenceRunning());
-    }
-
-    @Test
-    public void testChangeMode() throws Exception {
-        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
-        TestApplication app = createFirstAppAndPersist(n1);
-        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
-
-        TestEntity child = app.addChild(EntitySpec.create(TestEntity.class).configure(TestEntity.CONF_NAME, "first-child"));
-        Entities.manage(child);
-        TestApplication app2 = TestApplication.Factory.newManagedInstanceForTests(n1.mgmt);
-        app2.config().set(TestEntity.CONF_NAME, "second-app");
-
-        forcePersistNow(n1);
-        n2.ha.setPriority(1);
-        n1.ha.changeMode(HighAvailabilityMode.HOT_STANDBY);
-        
-        // both now hot standby
-        assertHotStandby(n1);
-        assertHotStandby(n2);
-        
-        assertEquals(n1.mgmt.getApplications().size(), 2);
-        Application app2RO = n1.mgmt.lookup(app2.getId(), Application.class);
-        Assert.assertNotNull(app2RO);
-        assertEquals(app2RO.getConfig(TestEntity.CONF_NAME), "second-app");
-        try {
-            ((TestApplication)app2RO).setAttribute(TestEntity.SEQUENCE, 4);
-            Assert.fail("Should not have allowed sensor to be set");
-        } catch (Exception e) {
-            Assert.assertTrue(e.toString().toLowerCase().contains("read-only"), "Error message did not contain expected text: "+e);
-        }
-
-        n1.ha.changeMode(HighAvailabilityMode.AUTO);
-        n2.ha.changeMode(HighAvailabilityMode.HOT_STANDBY, true, false);
-        // both still hot standby (n1 will defer to n2 as it has higher priority)
-        assertHotStandby(n1);
-        assertHotStandby(n2);
-        
-        // with priority 1, n2 will now be elected
-        n2.ha.changeMode(HighAvailabilityMode.AUTO);
-        assertHotStandby(n1);
-        assertMaster(n2);
-        
-        assertEquals(n2.mgmt.getApplications().size(), 2);
-        Application app2B = n2.mgmt.lookup(app2.getId(), Application.class);
-        Assert.assertNotNull(app2B);
-        assertEquals(app2B.getConfig(TestEntity.CONF_NAME), "second-app");
-        ((TestApplication)app2B).setAttribute(TestEntity.SEQUENCE, 4);
-        
-        forcePersistNow(n2);
-        forceRebindNow(n1);
-        Application app2BRO = n1.mgmt.lookup(app2.getId(), Application.class);
-        Assert.assertNotNull(app2BRO);
-        EntityTestUtils.assertAttributeEquals(app2BRO, TestEntity.SEQUENCE, 4);
-    }
-
-    @Test(groups="Integration", invocationCount=20)
-    public void testChangeModeManyTimes() throws Exception {
-        testChangeMode();
-    }
-
-    @Test
-    public void testChangeModeToDisabledAndBack() throws Exception {
-        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
-        n1.mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachine.class));
-        @SuppressWarnings("unused")
-        TestApplication app = createFirstAppAndPersist(n1);
-        
-        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
-        
-        // disabled n1 allows n2 to become master when next we tell it to check
-        n1.ha.changeMode(HighAvailabilityMode.DISABLED);
-        n2.ha.changeMode(HighAvailabilityMode.AUTO);
-        assertMaster(n2);
-        assertEquals(n1.ha.getNodeState(), ManagementNodeState.FAILED);
-        Assert.assertTrue(n1.mgmt.getApplications().isEmpty(), "n1 should have had no apps; instead had: "+n1.mgmt.getApplications());
-        Assert.assertTrue(n1.mgmt.getEntityManager().getEntities().isEmpty(), "n1 should have had no entities; instead had: "+n1.mgmt.getEntityManager().getEntities());
-        Assert.assertTrue(n1.mgmt.getLocationManager().getLocations().isEmpty(), "n1 should have had no locations; instead had: "+n1.mgmt.getLocationManager().getLocations());
-        
-        // we can now change n1 back to hot_standby
-        n1.ha.changeMode(HighAvailabilityMode.HOT_STANDBY);
-        assertHotStandby(n1);
-        // and it sees apps
-        Assert.assertFalse(n1.mgmt.getApplications().isEmpty(), "n1 should have had apps now");
-        Assert.assertFalse(n1.mgmt.getLocationManager().getLocations().isEmpty(), "n1 should have had locations now");
-        // and if n2 is disabled, n1 promotes
-        n2.ha.changeMode(HighAvailabilityMode.DISABLED);
-        n1.ha.changeMode(HighAvailabilityMode.AUTO);
-        assertMaster(n1);
-        assertEquals(n2.ha.getNodeState(), ManagementNodeState.FAILED);
-    }
-    
-    @Test
-    public void testHotStandbyDoesNotStartFeeds() throws Exception {
-        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
-        TestApplication app = createFirstAppAndPersist(n1);
-        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).impl(MyEntityWithFunctionFeedImpl.class));
-        forcePersistNow(n1);
-        Assert.assertTrue(entity.feeds().getFeeds().size() > 0, "Feeds: "+entity.feeds().getFeeds());
-        for (Feed feed : entity.feeds().getFeeds()) {
-            assertTrue(feed.isRunning(), "Feed expected running, but it is non-running");
-        }
-
-        HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
-        TestEntity entityRO = (TestEntity) n2.mgmt.lookup(entity.getId(), Entity.class);
-        Assert.assertTrue(entityRO.feeds().getFeeds().size() > 0, "Feeds: "+entity.feeds().getFeeds());
-        for (Feed feedRO : entityRO.feeds().getFeeds()) {
-            assertFalse(feedRO.isRunning(), "Feed expected non-active, but it is running");
-        }
-    }
-    
-    @Test(groups="Integration")
-    public void testHotStandbyDoesNotStartFeedsRebindingManyTimes() throws Exception {
-        testHotStandbyDoesNotStartFeeds();
-        final HaMgmtNode hsb = createHotStandby(Duration.millis(10));
-        Repeater.create("until 10 rebinds").every(Duration.millis(100)).until(
-            new Callable<Boolean>() {
-                @Override
-                public Boolean call() throws Exception {
-                    return ((RebindManagerImpl)hsb.mgmt.getRebindManager()).getReadOnlyRebindCount() >= 10;
-                }
-            }).runRequiringTrue();
-        // make sure not too many tasks (allowing 5 for rebind etc; currently just 2)
-        RebindTestFixture.waitForTaskCountToBecome(hsb.mgmt, 5);
-    }
-
-    @Test(groups="Integration")
-    public void testHotStandbyDoesNotStartFeedsRebindingManyTimesWithAnotherFeedGenerator() throws Exception {
-        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
-        TestApplication app = createFirstAppAndPersist(n1);
-        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).impl(MyEntityWithNewFeedsEachTimeImpl.class));
-        forcePersistNow(n1);
-        Assert.assertTrue(entity.feeds().getFeeds().size() == 4, "Feeds: "+entity.feeds().getFeeds());
-        
-        final HaMgmtNode hsb = createHotStandby(Duration.millis(10));
-        Repeater.create("until 10 rebinds").every(Duration.millis(100)).until(
-            new Callable<Boolean>() {
-                @Override
-                public Boolean call() throws Exception {
-                    return ((RebindManagerImpl)hsb.mgmt.getRebindManager()).getReadOnlyRebindCount() >= 10;
-                }
-            }).runRequiringTrue();
-        // make sure not too many tasks (allowing 5 for rebind etc; currently just 2)
-        RebindTestFixture.waitForTaskCountToBecome(hsb.mgmt, 5);
-    }
-
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/ImmutableManagementPlaneSyncRecord.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/ImmutableManagementPlaneSyncRecord.java b/core/src/test/java/brooklyn/management/ha/ImmutableManagementPlaneSyncRecord.java
deleted file mode 100644
index 10c47f9..0000000
--- a/core/src/test/java/brooklyn/management/ha/ImmutableManagementPlaneSyncRecord.java
+++ /dev/null
@@ -1,57 +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.management.ha;
-
-import java.util.Map;
-
-import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
-
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableMap;
-
-public class ImmutableManagementPlaneSyncRecord implements ManagementPlaneSyncRecord {
-    private final String masterNodeId;
-    private final Map<String, ManagementNodeSyncRecord> managementNodes;
-
-    ImmutableManagementPlaneSyncRecord(String masterNodeId, Map<String, ManagementNodeSyncRecord> nodes) {
-        this.masterNodeId = masterNodeId;
-        this.managementNodes = ImmutableMap.copyOf(nodes);
-    }
-    
-    @Override
-    public String getMasterNodeId() {
-        return masterNodeId;
-    }
-
-    @Override
-    public Map<String, ManagementNodeSyncRecord> getManagementNodes() {
-        return managementNodes;
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(this).add("master", masterNodeId).add("nodes", managementNodes.keySet()).toString();
-    }
-    
-    @Override
-    public String toVerboseString() {
-        return Objects.toStringHelper(this).add("master", masterNodeId).add("nodes", managementNodes).toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterInMemory.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterInMemory.java b/core/src/test/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterInMemory.java
deleted file mode 100644
index bc8b418..0000000
--- a/core/src/test/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterInMemory.java
+++ /dev/null
@@ -1,99 +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.management.ha;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.io.IOException;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.rebind.persister.InMemoryObjectStore;
-import brooklyn.util.time.Duration;
-
-import com.google.common.annotations.VisibleForTesting;
-
-/** @deprecated since 0.7.0 use {@link ManagementPlaneSyncRecordPersisterToObjectStore}
- * with {@link InMemoryObjectStore}
- * <code>
- * new ManagementPlaneSyncRecordPersisterToObjectStore(new InMemoryObjectStore(), classLoader)
- * </code> */
-@Deprecated
-public class ManagementPlaneSyncRecordPersisterInMemory implements ManagementPlaneSyncRecordPersister {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ManagementPlaneSyncRecordPersisterInMemory.class);
-
-    private final MutableManagementPlaneSyncRecord memento = new MutableManagementPlaneSyncRecord();
-    
-    private volatile boolean running = true;
-    
-    @Override
-    public synchronized void stop() {
-        running = false;
-    }
-
-    @Override
-    public ManagementPlaneSyncRecord loadSyncRecord() throws IOException {
-        if (!running) {
-            throw new IllegalStateException("Persister not running; cannot load memento");
-        }
-        
-        return memento.snapshot();
-    }
-    
-    @VisibleForTesting
-    @Override
-    public synchronized void waitForWritesCompleted(Duration timeout) throws InterruptedException, TimeoutException {
-        // The synchronized is sufficient - guarantee that no concurrent calls
-        return;
-    }
-
-    @Override
-    public synchronized void delta(Delta delta) {
-        if (!running) {
-            if (LOG.isDebugEnabled()) LOG.debug("Persister not running; ignoring checkpointed delta of manager-memento");
-            return;
-        }
-        
-        for (ManagementNodeSyncRecord m : delta.getNodes()) {
-            memento.addNode(m);
-        }
-        for (String id : delta.getRemovedNodeIds()) {
-            memento.deleteNode(id);
-        }
-        switch (delta.getMasterChange()) {
-        case NO_CHANGE:
-            break; // no-op
-        case SET_MASTER:
-            memento.setMasterNodeId(checkNotNull(delta.getNewMasterOrNull()));
-            break;
-        case CLEAR_MASTER:
-            memento.setMasterNodeId(null);
-            break; // no-op
-        default:
-            throw new IllegalStateException("Unknown state for master-change: "+delta.getMasterChange());
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/MasterChooserTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/MasterChooserTest.java b/core/src/test/java/brooklyn/management/ha/MasterChooserTest.java
deleted file mode 100644
index 14804b1..0000000
--- a/core/src/test/java/brooklyn/management/ha/MasterChooserTest.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.management.ha;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNull;
-
-import java.util.List;
-
-import org.apache.brooklyn.api.management.ha.ManagementNodeState;
-import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.BrooklynVersion;
-import brooklyn.entity.basic.EntityFunctions;
-import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord;
-import brooklyn.management.ha.BasicMasterChooser.AlphabeticMasterChooser;
-import brooklyn.management.ha.BasicMasterChooser.ScoredRecord;
-import brooklyn.util.time.Duration;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-
-public class MasterChooserTest {
-
-    private MutableManagementPlaneSyncRecord memento;
-    private AlphabeticMasterChooser chooser;
-    private long now;
-    
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        memento = new MutableManagementPlaneSyncRecord();
-        chooser = new AlphabeticMasterChooser();
-        now = System.currentTimeMillis();
-    }
-    
-    @Test
-    public void testChoosesFirstAlphanumeric() throws Exception {
-        memento.addNode(newManagerMemento("node1",  ManagementNodeState.STANDBY, now));
-        memento.addNode(newManagerMemento("node2",  ManagementNodeState.STANDBY, now));
-        memento.addNode(newManagerMemento("node3",  ManagementNodeState.STANDBY, now));
-        Duration heartbeatTimeout = Duration.THIRTY_SECONDS;
-        String ownNodeId = "node2";
-        assertEquals(chooser.choose(memento, heartbeatTimeout, ownNodeId).getNodeId(), "node1");
-    }
-    
-    @Test
-    public void testReturnsNullIfNoValid() throws Exception {
-        memento.addNode(newManagerMemento("node1", ManagementNodeState.STANDBY, now - 31*1000));
-        Duration heartbeatTimeout = Duration.THIRTY_SECONDS;
-        assertNull(chooser.choose(memento, heartbeatTimeout, "node2"));
-    }
-    
-    @Test
-    public void testFiltersOutByHeartbeat() throws Exception {
-        memento.addNode(newManagerMemento("node1", ManagementNodeState.STANDBY, now - 31*1000));
-        memento.addNode(newManagerMemento("node2", ManagementNodeState.STANDBY, now - 20*1000));
-        memento.addNode(newManagerMemento("node3", ManagementNodeState.STANDBY, now));
-        Duration heartbeatTimeout = Duration.THIRTY_SECONDS;
-        assertEquals(getIds(chooser.sort(chooser.filterHealthy(memento, heartbeatTimeout, now))), ImmutableList.of("node2", "node3"));
-    }
-    
-    protected static List<String> getIds(List<ScoredRecord<?>> filterHealthy) {
-        return ImmutableList.copyOf(Iterables.transform(filterHealthy, EntityFunctions.id()));
-    }
-
-    @Test
-    public void testFiltersOutByStatusNotPreferringMaster() throws Exception {
-        assertEquals(doTestFiltersOutByStatus(false, false), ImmutableList.of("node4", "node5"));
-    }
-    @Test
-    public void testFiltersOutByStatusPreferringMaster() throws Exception {
-        assertEquals(doTestFiltersOutByStatus(true, false), ImmutableList.of("node5", "node4"));
-    }
-    
-    @Test
-    public void testFiltersOutByStatusNotPreferringHot() throws Exception {
-        assertEquals(doTestFiltersOutByStatus(false, true), ImmutableList.of("node4", "node5", "node6"));
-    }
-    @Test
-    public void testFiltersOutByStatusPreferringHot() throws Exception {
-        assertEquals(doTestFiltersOutByStatus(true, true), ImmutableList.of("node5", "node6", "node4"));
-    }
-    
-    protected List<String> doTestFiltersOutByStatus(boolean preferHot, boolean includeHot) throws Exception {
-        chooser = new AlphabeticMasterChooser(preferHot);
-        memento.addNode(newManagerMemento("node1", ManagementNodeState.FAILED, now));
-        memento.addNode(newManagerMemento("node2", ManagementNodeState.TERMINATED, now));
-        memento.addNode(newManagerMemento("node3", null, now));
-        memento.addNode(newManagerMemento("node4",  ManagementNodeState.STANDBY, now));
-        memento.addNode(newManagerMemento("node5", ManagementNodeState.MASTER, now));
-        if (includeHot)
-            memento.addNode(newManagerMemento("node6",  ManagementNodeState.HOT_STANDBY, now));
-        return getIds(chooser.sort(chooser.filterHealthy(memento, Duration.THIRTY_SECONDS, now)));
-    }
-
-    @Test
-    public void testExplicityPriority() throws Exception {
-        chooser = new AlphabeticMasterChooser();
-        memento.addNode(newManagerMemento("node1", ManagementNodeState.STANDBY, now, BrooklynVersion.get(), 2L));
-        memento.addNode(newManagerMemento("node2", ManagementNodeState.STANDBY, now, BrooklynVersion.get(), -1L));
-        memento.addNode(newManagerMemento("node3", ManagementNodeState.STANDBY, now, BrooklynVersion.get(), null));
-        List<String> order = getIds(chooser.sort(chooser.filterHealthy(memento, Duration.THIRTY_SECONDS, now)));
-        assertEquals(order, ImmutableList.of("node1", "node3", "node2"));
-    }
-
-    @Test
-    public void testVersionsMaybeNull() throws Exception {
-        chooser = new AlphabeticMasterChooser();
-        memento.addNode(newManagerMemento("node1", ManagementNodeState.STANDBY, now, "v10", null));
-        memento.addNode(newManagerMemento("node2", ManagementNodeState.STANDBY, now, "v3-snapshot", null));
-        memento.addNode(newManagerMemento("node3", ManagementNodeState.STANDBY, now, "v3-snapshot", -1L));
-        memento.addNode(newManagerMemento("node4", ManagementNodeState.STANDBY, now, "v3-snapshot", null));
-        memento.addNode(newManagerMemento("node5", ManagementNodeState.STANDBY, now, "v3-stable", null));
-        memento.addNode(newManagerMemento("node6", ManagementNodeState.STANDBY, now, "v1", null));
-        memento.addNode(newManagerMemento("node7", ManagementNodeState.STANDBY, now, null, null));
-        List<String> order = getIds(chooser.sort(chooser.filterHealthy(memento, Duration.THIRTY_SECONDS, now)));
-        assertEquals(order, ImmutableList.of("node1", "node5", "node6", "node2", "node4", "node7", "node3"));
-    }
-
-    private ManagementNodeSyncRecord newManagerMemento(String nodeId, ManagementNodeState status, long timestamp) {
-        return newManagerMemento(nodeId, status, timestamp, BrooklynVersion.get(), null);
-    }
-    private ManagementNodeSyncRecord newManagerMemento(String nodeId, ManagementNodeState status, long timestamp,
-            String version, Long priority) {
-        return BasicManagementNodeSyncRecord.builder().brooklynVersion(version).nodeId(nodeId).status(status).localTimestamp(timestamp).remoteTimestamp(timestamp).
-            priority(priority).build();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/MutableManagementPlaneSyncRecord.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/MutableManagementPlaneSyncRecord.java b/core/src/test/java/brooklyn/management/ha/MutableManagementPlaneSyncRecord.java
deleted file mode 100644
index a68cac3..0000000
--- a/core/src/test/java/brooklyn/management/ha/MutableManagementPlaneSyncRecord.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.management.ha;
-
-import java.util.Map;
-
-import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
-
-import com.google.common.collect.Maps;
-
-public class MutableManagementPlaneSyncRecord implements ManagementPlaneSyncRecord {
-    private String masterNodeId;
-    private Map<String, ManagementNodeSyncRecord> managementNodes = Maps.newConcurrentMap();
-
-    @Override
-    public String getMasterNodeId() {
-        return masterNodeId;
-    }
-
-    @Override
-    public Map<String, ManagementNodeSyncRecord> getManagementNodes() {
-        return managementNodes;
-    }
-
-    @Override
-    public String toVerboseString() {
-        return toString();
-    }
-
-    public ImmutableManagementPlaneSyncRecord snapshot() {
-        return new ImmutableManagementPlaneSyncRecord(masterNodeId, managementNodes);
-    }
-    
-    public void setMasterNodeId(String masterNodeId) {
-        this.masterNodeId = masterNodeId;
-    }
-    
-    public void addNode(ManagementNodeSyncRecord memento) {
-        managementNodes.put(memento.getNodeId(), memento);
-    }
-    
-    public void deleteNode(String nodeId) {
-        managementNodes.remove(nodeId);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/TestEntityFailingRebind.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/TestEntityFailingRebind.java b/core/src/test/java/brooklyn/management/ha/TestEntityFailingRebind.java
deleted file mode 100644
index a8cfced..0000000
--- a/core/src/test/java/brooklyn/management/ha/TestEntityFailingRebind.java
+++ /dev/null
@@ -1,55 +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.management.ha;
-
-import org.apache.brooklyn.test.entity.TestApplicationImpl;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class TestEntityFailingRebind extends TestApplicationImpl {
-    
-    private static final Logger LOG = LoggerFactory.getLogger(TestEntityFailingRebind.class);
-
-    public static class RebindException extends RuntimeException {
-        private static final long serialVersionUID = 1L;
-
-        public RebindException(String message) {
-            super(message);
-        }
-    }
-    
-    private static boolean throwOnRebind = true;
-    
-    public static void setThrowOnRebind(boolean state) {
-        throwOnRebind = state;
-    }
-    
-    public static boolean getThrowOnRebind() {
-        return throwOnRebind;
-    }
-
-    @Override
-    public void rebind() {
-        if (throwOnRebind) {
-            LOG.warn("Throwing intentional exception to simulate failure of rebinding " + this);
-            throw new RebindException("Intentional exception thrown when rebinding " + this);
-        }
-    }
-
-}



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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/LocalSubscriptionManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/LocalSubscriptionManager.java b/core/src/main/java/brooklyn/management/internal/LocalSubscriptionManager.java
deleted file mode 100644
index c2c7905..0000000
--- a/core/src/main/java/brooklyn/management/internal/LocalSubscriptionManager.java
+++ /dev/null
@@ -1,292 +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.management.internal;
-
-import static brooklyn.util.JavaGroovyEquivalents.elvis;
-import static brooklyn.util.JavaGroovyEquivalents.groovyTruth;
-import static brooklyn.util.JavaGroovyEquivalents.join;
-import static brooklyn.util.JavaGroovyEquivalents.mapOf;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.brooklyn.api.entity.Entity;
-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.management.ExecutionManager;
-import org.apache.brooklyn.api.management.SubscriptionHandle;
-import org.apache.brooklyn.api.management.SubscriptionManager;
-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;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimaps;
-
-/**
- * A {@link SubscriptionManager} that stores subscription details locally.
- */
-public class LocalSubscriptionManager extends AbstractSubscriptionManager {
-    
-    private static final Logger LOG = LoggerFactory.getLogger(LocalSubscriptionManager.class);
-
-    protected final ExecutionManager em;
-    
-    private final String tostring = "SubscriptionContext("+Identifiers.getBase64IdFromValue(System.identityHashCode(this), 5)+")";
-
-    private final AtomicLong totalEventsPublishedCount = new AtomicLong();
-    private final AtomicLong totalEventsDeliveredCount = new AtomicLong();
-    
-    @SuppressWarnings("rawtypes")
-    protected final ConcurrentMap<String, Subscription> allSubscriptions = new ConcurrentHashMap<String, Subscription>();
-    @SuppressWarnings("rawtypes")
-    protected final ConcurrentMap<Object, Set<Subscription>> subscriptionsBySubscriber = new ConcurrentHashMap<Object, Set<Subscription>>();
-    @SuppressWarnings("rawtypes")
-    protected final ConcurrentMap<Object, Set<Subscription>> subscriptionsByToken = new ConcurrentHashMap<Object, Set<Subscription>>();
-    
-    public LocalSubscriptionManager(ExecutionManager m) {
-        this.em = m;
-    }
-        
-    public long getNumSubscriptions() {
-        return allSubscriptions.size();
-    }
-
-    public long getTotalEventsPublished() {
-        return totalEventsPublishedCount.get();
-    }
-    
-    public long getTotalEventsDelivered() {
-        return totalEventsDeliveredCount.get();
-    }
-    
-    @SuppressWarnings("unchecked")
-    protected synchronized <T> SubscriptionHandle subscribe(Map<String, Object> flags, Subscription<T> s) {
-        Entity producer = s.producer;
-        Sensor<T> sensor= s.sensor;
-        s.subscriber = getSubscriber(flags, s);
-        if (flags.containsKey("subscriberExecutionManagerTag")) {
-            s.subscriberExecutionManagerTag = flags.remove("subscriberExecutionManagerTag");
-            s.subscriberExecutionManagerTagSupplied = true;
-        } else {
-            s.subscriberExecutionManagerTag = 
-                s.subscriber instanceof Entity ? "subscription-delivery-entity-"+((Entity)s.subscriber).getId()+"["+s.subscriber+"]" : 
-                s.subscriber instanceof String ? "subscription-delivery-string["+s.subscriber+"]" : 
-                "subscription-delivery-object["+s.subscriber+"]";
-            s.subscriberExecutionManagerTagSupplied = false;
-        }
-        s.eventFilter = (Predicate<SensorEvent<T>>) flags.remove("eventFilter");
-        s.flags = flags;
-        
-        if (LOG.isDebugEnabled()) LOG.debug("Creating subscription {} for {} on {} {} in {}", new Object[] {s.id, s.subscriber, producer, sensor, this});
-        allSubscriptions.put(s.id, s);
-        addToMapOfSets(subscriptionsByToken, makeEntitySensorToken(s.producer, s.sensor), s);
-        if (s.subscriber!=null) {
-            addToMapOfSets(subscriptionsBySubscriber, s.subscriber, s);
-        }
-        if (!s.subscriberExecutionManagerTagSupplied && s.subscriberExecutionManagerTag!=null) {
-            ((BasicExecutionManager) em).setTaskSchedulerForTag(s.subscriberExecutionManagerTag, SingleThreadedScheduler.class);
-        }
-        return s;
-    }
-
-    @SuppressWarnings("unchecked")
-    public Set<SubscriptionHandle> getSubscriptionsForSubscriber(Object subscriber) {
-        return (Set<SubscriptionHandle>) ((Set<?>) elvis(subscriptionsBySubscriber.get(subscriber), Collections.emptySet()));
-    }
-
-    public synchronized Set<SubscriptionHandle> getSubscriptionsForEntitySensor(Entity source, Sensor<?> sensor) {
-        Set<SubscriptionHandle> subscriptions = new LinkedHashSet<SubscriptionHandle>();
-        subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(source, sensor)), Collections.emptySet()));
-        subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(null, sensor)), Collections.emptySet()));
-        subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(source, null)), Collections.emptySet()));
-        subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(null, null)), Collections.emptySet()));
-        return subscriptions;
-    }
-
-    /**
-     * Unsubscribe the given subscription id.
-     *
-     * @see #subscribe(Map, Entity, Sensor, SensorEventListener)
-     */
-    @SuppressWarnings("rawtypes")
-    public synchronized boolean unsubscribe(SubscriptionHandle sh) {
-        if (!(sh instanceof Subscription)) throw new IllegalArgumentException("Only subscription handles of type Subscription supported: sh="+sh+"; type="+(sh != null ? sh.getClass().getCanonicalName() : null));
-        Subscription s = (Subscription) sh;
-        boolean result = allSubscriptions.remove(s.id) != null;
-        boolean b2 = removeFromMapOfCollections(subscriptionsByToken, makeEntitySensorToken(s.producer, s.sensor), s);
-        assert result==b2;
-        if (s.subscriber!=null) {
-            boolean b3 = removeFromMapOfCollections(subscriptionsBySubscriber, s.subscriber, s);
-            assert b3 == b2;
-        }
-
-        // FIXME ALEX - this seems wrong
-        ((BasicExecutionManager) em).setTaskSchedulerForTag(s.subscriberExecutionManagerTag, SingleThreadedScheduler.class);
-        return result;
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public <T> void publish(final SensorEvent<T> event) {
-        // REVIEW 1459 - execution
-        
-        // delivery in parallel/background, using execution manager
-        
-        // subscriptions, should define SingleThreadedScheduler for any subscriber ID tag
-        // in order to ensure callbacks are invoked in the order they are submitted
-        // (recommend exactly one per subscription to prevent deadlock)
-        // this is done with:
-        // em.setTaskSchedulerForTag(subscriberId, SingleThreadedScheduler.class);
-        
-        //note, generating the notifications must be done in the calling thread to preserve order
-        //e.g. emit(A); emit(B); should cause onEvent(A); onEvent(B) in that order
-        if (LOG.isTraceEnabled()) LOG.trace("{} got event {}", this, event);
-        totalEventsPublishedCount.incrementAndGet();
-        
-        Set<Subscription> subs = (Set<Subscription>) ((Set<?>) getSubscriptionsForEntitySensor(event.getSource(), event.getSensor()));
-        if (groovyTruth(subs)) {
-            if (LOG.isTraceEnabled()) LOG.trace("sending {}, {} to {}", new Object[] {event.getSensor().getName(), event, join(subs, ",")});
-            for (Subscription s : subs) {
-                if (s.eventFilter!=null && !s.eventFilter.apply(event))
-                    continue;
-                final Subscription sAtClosureCreation = s;
-                
-//                Set<Object> tags = MutableSet.of();
-//                if (s.subscriberExecutionManagerTag!=null) tags.add(s.subscriberExecutionManagerTag);
-//                if (event.getSource()!=null) tags.add(BrooklynTaskTags.tagForContextEntity(event.getSource()));
-//                Map<String, Object> tagsMap = mapOf("tags", (Object)tags);
-                // use code above, instead of line below, if we want subscription deliveries associated with the entity;
-                // that will cause them to be cancelled when the entity is unmanaged
-                // (not sure that is useful, and likely NOT worth the expense, but it might be...) -Alex Oct 2014
-                Map<String, Object> tagsMap = mapOf("tag", s.subscriberExecutionManagerTag);
-                
-                em.submit(tagsMap, new Runnable() {
-                    @Override
-                    public String toString() {
-                        return "LSM.publish("+event+")";
-                    }
-                    public void run() {
-                        try {
-                            sAtClosureCreation.listener.onEvent(event);
-                        } catch (Throwable t) {
-                            if (event!=null && event.getSource()!=null && Entities.isNoLongerManaged(event.getSource())) {
-                                LOG.debug("Error processing subscriptions to "+this+", after entity unmanaged: "+t, t);
-                            } else {
-                                LOG.warn("Error processing subscriptions to "+this+": "+t, t);
-                            }
-                        }
-                    }});
-                totalEventsDeliveredCount.incrementAndGet();
-            }
-        }
-    }
-    
-    @Override
-    public String toString() {
-        return tostring;
-    }
-    
-    /**
-     * Copied from LanguageUtils.groovy, to remove dependency.
-     * 
-     * Adds the given value to a collection in the map under the key.
-     * 
-     * A collection (as {@link LinkedHashMap}) will be created if necessary,
-     * synchronized on map for map access/change and set for addition there
-     *
-     * @return the updated set (instance, not copy)
-     * 
-     * @deprecated since 0.5; use {@link HashMultimap}, and {@link Multimaps#synchronizedSetMultimap(com.google.common.collect.SetMultimap)}
-     */
-    @Deprecated
-    private static <K,V> Set<V> addToMapOfSets(Map<K,Set<V>> map, K key, V valueInCollection) {
-        Set<V> coll;
-        synchronized (map) {
-            coll = map.get(key);
-            if (coll==null) {
-                coll = new LinkedHashSet<V>();
-                map.put(key, coll);
-            }
-            if (coll.isEmpty()) {
-                synchronized (coll) {
-                    coll.add(valueInCollection);
-                }
-                //if collection was empty then add to the collection while holding the map lock, to prevent removal
-                return coll;
-            }
-        }
-        synchronized (coll) {
-            if (!coll.isEmpty()) {
-                coll.add(valueInCollection);
-                return coll;
-            }
-        }
-        //if was empty, recurse, because someone else might be removing the collection
-        return addToMapOfSets(map, key, valueInCollection);
-    }
-
-    /**
-     * Copied from LanguageUtils.groovy, to remove dependency.
-     * 
-     * Removes the given value from a collection in the map under the key.
-     *
-     * @return the updated set (instance, not copy)
-     * 
-     * @deprecated since 0.5; use {@link ArrayListMultimap} or {@link HashMultimap}, and {@link Multimaps#synchronizedListMultimap(com.google.common.collect.ListMultimap)} etc
-     */
-    @Deprecated
-    private static <K,V> boolean removeFromMapOfCollections(Map<K,? extends Collection<V>> map, K key, V valueInCollection) {
-        Collection<V> coll;
-        synchronized (map) {
-            coll = map.get(key);
-            if (coll==null) return false;
-        }
-        boolean result;
-        synchronized (coll) {
-            result = coll.remove(valueInCollection);
-        }
-        if (coll.isEmpty()) {
-            synchronized (map) {
-                synchronized (coll) {
-                    if (coll.isEmpty()) {
-                        //only remove from the map if no one is adding to the collection or to the map, and the collection is still in the map
-                        if (map.get(key)==coll) {
-                            map.remove(key);
-                        }
-                    }
-                }
-            }
-        }
-        return result;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/LocalUsageManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/LocalUsageManager.java b/core/src/main/java/brooklyn/management/internal/LocalUsageManager.java
deleted file mode 100644
index 6da41c8..0000000
--- a/core/src/main/java/brooklyn/management/internal/LocalUsageManager.java
+++ /dev/null
@@ -1,430 +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.management.internal;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.internal.storage.BrooklynStorage;
-
-import org.apache.brooklyn.location.basic.AbstractLocation;
-import org.apache.brooklyn.location.basic.LocationConfigKeys;
-import org.apache.brooklyn.location.basic.LocationInternal;
-
-import brooklyn.management.ManagementContextInjectable;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.usage.ApplicationUsage;
-import brooklyn.management.usage.LocationUsage;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.javalang.Reflections;
-import brooklyn.util.time.Duration;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-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;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-
-public class LocalUsageManager implements UsageManager {
-
-    // TODO Threading model needs revisited.
-    // Synchronizes on updates to storage; but if two Brooklyn nodes were both writing to the same
-    // ApplicationUsage or LocationUsage record there'd be a race. That currently won't happen
-    // (at least for ApplicationUsage?) because the app is mastered in just one node at a time,
-    // and because location events are just manage/unmanage which should be happening in just 
-    // one place at a time for a given location.
-    
-    private static final Logger log = LoggerFactory.getLogger(LocalUsageManager.class);
-
-    private static class ApplicationMetadataImpl implements brooklyn.management.internal.UsageListener.ApplicationMetadata {
-        private final Application app;
-        private String applicationId;
-        private String applicationName;
-        private String entityType;
-        private String catalogItemId;
-        private Map<String, String> metadata;
-
-        ApplicationMetadataImpl(Application app) {
-            this.app = checkNotNull(app, "app");
-            applicationId = app.getId();
-            applicationName = app.getDisplayName();
-            entityType = app.getEntityType().getName();
-            catalogItemId = app.getCatalogItemId();
-            metadata = ((EntityInternal)app).toMetadataRecord();
-        }
-        @Override public Application getApplication() {
-            return app;
-        }
-        @Override public String getApplicationId() {
-            return applicationId;
-        }
-        @Override public String getApplicationName() {
-            return applicationName;
-        }
-        @Override public String getEntityType() {
-            return entityType;
-        }
-        @Override public String getCatalogItemId() {
-            return catalogItemId;
-        }
-        @Override public Map<String, String> getMetadata() {
-            return metadata;
-        }
-    }
-    
-    private static class LocationMetadataImpl implements brooklyn.management.internal.UsageListener.LocationMetadata {
-        private final Location loc;
-        private String locationId;
-        private Map<String, String> metadata;
-
-        LocationMetadataImpl(Location loc) {
-            this.loc = checkNotNull(loc, "loc");
-            locationId = loc.getId();
-            metadata = ((LocationInternal)loc).toMetadataRecord();
-        }
-        @Override public Location getLocation() {
-            return loc;
-        }
-        @Override public String getLocationId() {
-            return locationId;
-        }
-        @Override public Map<String, String> getMetadata() {
-            return metadata;
-        }
-    }
-    
-    // Register a coercion from String->UsageListener, so that USAGE_LISTENERS defined in brooklyn.properties
-    // will be instantiated, given their class names.
-    static {
-        TypeCoercions.registerAdapter(String.class, brooklyn.management.internal.UsageListener.class, new Function<String, brooklyn.management.internal.UsageListener>() {
-            @Override public brooklyn.management.internal.UsageListener apply(String input) {
-                // TODO Want to use classLoader = mgmt.getCatalog().getRootClassLoader();
-                ClassLoader classLoader = LocalUsageManager.class.getClassLoader();
-                Optional<Object> result = Reflections.invokeConstructorWithArgs(classLoader, input);
-                if (result.isPresent()) {
-                    if (result.get() instanceof brooklyn.management.internal.UsageManager.UsageListener) {
-                        return new brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter((brooklyn.management.internal.UsageManager.UsageListener) result.get());
-                    } else {
-                        return (brooklyn.management.internal.UsageListener) result.get();
-                    }
-                } else {
-                    throw new IllegalStateException("Failed to create UsageListener from class name '"+input+"' using no-arg constructor");
-                }
-            }
-        });
-    }
-    
-    @VisibleForTesting
-    public static final String APPLICATION_USAGE_KEY = "usage-application";
-    
-    @VisibleForTesting
-    public static final String LOCATION_USAGE_KEY = "usage-location";
-
-    private final LocalManagementContext managementContext;
-    
-    private final Object mutex = new Object();
-
-    private final List<brooklyn.management.internal.UsageListener> listeners = Lists.newCopyOnWriteArrayList();
-    
-    private final AtomicInteger listenerQueueSize = new AtomicInteger();
-    
-    private ListeningExecutorService listenerExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
-            .setNameFormat("brooklyn-usagemanager-listener-%d")
-            .build()));
-
-    public LocalUsageManager(LocalManagementContext managementContext) {
-        this.managementContext = checkNotNull(managementContext, "managementContext");
-        
-        // TODO Once brooklyn.management.internal.UsageManager.UsageListener is deleted, restore this
-        // to normal generics!
-        Collection<?> listeners = managementContext.getBrooklynProperties().getConfig(UsageManager.USAGE_LISTENERS);
-        if (listeners != null) {
-            for (Object listener : listeners) {
-                if (listener instanceof ManagementContextInjectable) {
-                    ((ManagementContextInjectable)listener).injectManagementContext(managementContext);
-                }
-                if (listener instanceof brooklyn.management.internal.UsageManager.UsageListener) {
-                    addUsageListener((brooklyn.management.internal.UsageManager.UsageListener)listener);
-                } else if (listener instanceof brooklyn.management.internal.UsageListener) {
-                    addUsageListener((brooklyn.management.internal.UsageListener)listener);
-                } else if (listener == null) {
-                    throw new NullPointerException("null listener in config "+UsageManager.USAGE_LISTENERS);
-                } else {
-                    throw new ClassCastException("listener "+listener+" of type "+listener.getClass()+" is not of type "+brooklyn.management.internal.UsageListener.class.getName());
-                }
-            }
-        }
-    }
-
-    public void terminate() {
-        // Wait for the listeners to finish + close the listeners
-        Duration timeout = managementContext.getBrooklynProperties().getConfig(UsageManager.USAGE_LISTENER_TERMINATION_TIMEOUT);
-        if (listenerQueueSize.get() > 0) {
-            log.info("Usage manager waiting for "+listenerQueueSize+" listener events for up to "+timeout);
-        }
-        List<ListenableFuture<?>> futures = Lists.newArrayList();
-        for (final brooklyn.management.internal.UsageListener listener : listeners) {
-            ListenableFuture<?> future = listenerExecutor.submit(new Runnable() {
-                public void run() {
-                    if (listener instanceof Closeable) {
-                        try {
-                            ((Closeable)listener).close();
-                        } catch (IOException e) {
-                            log.warn("Problem closing usage listener "+listener+" (continuing)", e);
-                        }
-                    }
-                }});
-            futures.add(future);
-        }
-        try {
-            Futures.successfulAsList(futures).get(timeout.toMilliseconds(), TimeUnit.MILLISECONDS);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            log.warn("Problem terminiating usage listeners (continuing)", e);
-        } finally {
-            listenerExecutor.shutdownNow();
-        }
-    }
-
-    private void execOnListeners(final Function<brooklyn.management.internal.UsageListener, Void> job) {
-        for (final brooklyn.management.internal.UsageListener listener : listeners) {
-            listenerQueueSize.incrementAndGet();
-            listenerExecutor.execute(new Runnable() {
-                public void run() {
-                    try {
-                        job.apply(listener);
-                    } catch (RuntimeException e) {
-                        log.error("Problem notifying listener "+listener+" of "+job, e);
-                        Exceptions.propagateIfFatal(e);
-                    } finally {
-                        listenerQueueSize.decrementAndGet();
-                    }
-                }});
-        }
-    }
-    
-    @Override
-    public void recordApplicationEvent(final Application app, final Lifecycle state) {
-        log.debug("Storing application lifecycle usage event: application {} in state {}", new Object[] {app, state});
-        ConcurrentMap<String, ApplicationUsage> eventMap = managementContext.getStorage().getMap(APPLICATION_USAGE_KEY);
-        synchronized (mutex) {
-            ApplicationUsage usage = eventMap.get(app.getId());
-            if (usage == null) {
-                usage = new ApplicationUsage(app.getId(), app.getDisplayName(), app.getEntityType().getName(), ((EntityInternal)app).toMetadataRecord());
-            }
-            final ApplicationUsage.ApplicationEvent event = new ApplicationUsage.ApplicationEvent(state, getUser());
-            usage.addEvent(event);        
-            eventMap.put(app.getId(), usage);
-
-            execOnListeners(new Function<brooklyn.management.internal.UsageListener, Void>() {
-                    public Void apply(brooklyn.management.internal.UsageListener listener) {
-                        listener.onApplicationEvent(new ApplicationMetadataImpl(Entities.proxy(app)), event);
-                        return null;
-                    }
-                    public String toString() {
-                        return "applicationEvent("+app+", "+state+")";
-                    }});
-        }
-    }
-    
-    /**
-     * Adds this location event to the usage record for the given location (creating the usage 
-     * record if one does not already exist).
-     */
-    @Override
-    public void recordLocationEvent(final Location loc, final Lifecycle state) {
-        // TODO This approach (i.e. recording events on manage/unmanage would not work for
-        // locations that are reused. For example, in a FixedListMachineProvisioningLocation
-        // the ssh machine location is returned to the pool and handed back out again.
-        // But maybe the solution there is to hand out different instances so that one user
-        // can't change the config of the SshMachineLocation to subsequently affect the next 
-        // user.
-        //
-        // TODO Should perhaps extract the location storage methods into their own class,
-        // but no strong enough feelings yet...
-        
-        checkNotNull(loc, "location");
-        if (loc.getConfig(AbstractLocation.TEMPORARY_LOCATION)) {
-            log.info("Ignoring location lifecycle usage event for {} (state {}), because location is a temporary location", loc, state);
-            return;
-        }
-        checkNotNull(state, "state of location %s", loc);
-        if (loc.getId() == null) {
-            log.error("Ignoring location lifecycle usage event for {} (state {}), because location has no id", loc, state);
-            return;
-        }
-        if (managementContext.getStorage() == null) {
-            log.warn("Cannot store location lifecycle usage event for {} (state {}), because storage not available", loc, state);
-            return;
-        }
-        
-        Object callerContext = loc.getConfig(LocationConfigKeys.CALLER_CONTEXT);
-        
-        if (callerContext != null && callerContext instanceof Entity) {
-            log.debug("Storing location lifecycle usage event: location {} in state {}; caller context {}", new Object[] {loc, state, callerContext});
-            
-            Entity caller = (Entity) callerContext;
-            String entityTypeName = caller.getEntityType().getName();
-            String appId = caller.getApplicationId();
-
-            final LocationUsage.LocationEvent event = new LocationUsage.LocationEvent(state, caller.getId(), entityTypeName, appId, getUser());
-            
-            ConcurrentMap<String, LocationUsage> usageMap = managementContext.getStorage().<String, LocationUsage>getMap(LOCATION_USAGE_KEY);
-            synchronized (mutex) {
-                LocationUsage usage = usageMap.get(loc.getId());
-                if (usage == null) {
-                    usage = new LocationUsage(loc.getId(), ((LocationInternal)loc).toMetadataRecord());
-                }
-                usage.addEvent(event);
-                usageMap.put(loc.getId(), usage);
-                
-                execOnListeners(new Function<brooklyn.management.internal.UsageListener, Void>() {
-                        public Void apply(brooklyn.management.internal.UsageListener listener) {
-                            listener.onLocationEvent(new LocationMetadataImpl(loc), event);
-                            return null;
-                        }
-                        public String toString() {
-                            return "locationEvent("+loc+", "+state+")";
-                        }});
-            }
-        } else {
-            // normal for high-level locations
-            log.trace("Not recording location lifecycle usage event for {} in state {}, because no caller context", new Object[] {loc, state});
-        }
-    }
-
-    /**
-     * Returns the usage info for the location with the given id, or null if unknown.
-     */
-    @Override
-    public LocationUsage getLocationUsage(String locationId) {
-        BrooklynStorage storage = managementContext.getStorage();
-
-        Map<String, LocationUsage> usageMap = storage.getMap(LOCATION_USAGE_KEY);
-        return usageMap.get(locationId);
-    }
-    
-    /**
-     * Returns the usage info that matches the given predicate.
-     * For example, could be used to find locations used within a given time period.
-     */
-    @Override
-    public Set<LocationUsage> getLocationUsage(Predicate<? super LocationUsage> filter) {
-        // TODO could do more efficient indexing, to more easily find locations in use during a given period.
-        // But this is good enough for first-pass.
-
-        Map<String, LocationUsage> usageMap = managementContext.getStorage().getMap(LOCATION_USAGE_KEY);
-        Set<LocationUsage> result = Sets.newLinkedHashSet();
-        
-        for (LocationUsage usage : usageMap.values()) {
-            if (filter.apply(usage)) {
-                result.add(usage);
-            }
-        }
-        return result;
-    }
-    
-    /**
-     * Returns the usage info for the location with the given id, or null if unknown.
-     */
-    @Override
-    public ApplicationUsage getApplicationUsage(String appId) {
-        BrooklynStorage storage = managementContext.getStorage();
-
-        Map<String, ApplicationUsage> usageMap = storage.getMap(APPLICATION_USAGE_KEY);
-        return usageMap.get(appId);
-    }
-    
-    /**
-     * Returns the usage info that matches the given predicate.
-     * For example, could be used to find applications used within a given time period.
-     */
-    @Override
-    public Set<ApplicationUsage> getApplicationUsage(Predicate<? super ApplicationUsage> filter) {
-        // TODO could do more efficient indexing, to more easily find locations in use during a given period.
-        // But this is good enough for first-pass.
-
-        Map<String, ApplicationUsage> usageMap = managementContext.getStorage().getMap(APPLICATION_USAGE_KEY);
-        Set<ApplicationUsage> result = Sets.newLinkedHashSet();
-        
-        for (ApplicationUsage usage : usageMap.values()) {
-            if (filter.apply(usage)) {
-                result.add(usage);
-            }
-        }
-        return result;
-    }
-
-    @Override
-    @Deprecated
-    public void addUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener) {
-        addUsageListener(new brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener));
-    }
-
-    @Override
-    @Deprecated
-    public void removeUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener) {
-        removeUsageListener(new brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener));
-    }
-    
-    @Override
-    public void addUsageListener(brooklyn.management.internal.UsageListener listener) {
-        listeners.add(listener);
-    }
-
-    @Override
-    public void removeUsageListener(brooklyn.management.internal.UsageListener listener) {
-        listeners.remove(listener);
-    }
-
-    private String getUser() {
-        EntitlementContext entitlementContext = Entitlements.getEntitlementContext();
-        if (entitlementContext != null) {
-            return entitlementContext.user();
-        }
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/LocationManagerInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/LocationManagerInternal.java b/core/src/main/java/brooklyn/management/internal/LocationManagerInternal.java
deleted file mode 100644
index 17ecd5a..0000000
--- a/core/src/main/java/brooklyn/management/internal/LocationManagerInternal.java
+++ /dev/null
@@ -1,28 +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.management.internal;
-
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.management.LocationManager;
-
-public interface LocationManagerInternal extends LocationManager, BrooklynObjectManagerInternal<Location> {
-
-    public Iterable<String> getLocationIds();
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java b/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
deleted file mode 100644
index b21f73f..0000000
--- a/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
+++ /dev/null
@@ -1,122 +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.management.internal;
-
-import java.net.URI;
-import java.net.URL;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-
-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.ManagementContext;
-import org.apache.brooklyn.api.management.Task;
-
-import brooklyn.catalog.internal.CatalogInitialization;
-import brooklyn.config.BrooklynProperties;
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.proxying.InternalEntityFactory;
-import brooklyn.entity.proxying.InternalLocationFactory;
-import brooklyn.entity.proxying.InternalPolicyFactory;
-import brooklyn.internal.storage.BrooklynStorage;
-import brooklyn.management.ha.OsgiManager;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.task.TaskTags;
-
-import com.google.common.annotations.Beta;
-
-public interface ManagementContextInternal extends ManagementContext {
-
-    public static final String SUB_TASK_TAG = TaskTags.SUB_TASK_TAG;
-    
-    public static final String EFFECTOR_TAG = BrooklynTaskTags.EFFECTOR_TAG;
-    public static final String NON_TRANSIENT_TASK_TAG = BrooklynTaskTags.NON_TRANSIENT_TASK_TAG;
-    public static final String TRANSIENT_TASK_TAG = BrooklynTaskTags.TRANSIENT_TASK_TAG;
-
-    public static final String EMPTY_CATALOG_URL = "classpath://brooklyn/empty.catalog.bom";
-    
-    ClassLoader getBaseClassLoader();
-
-    Iterable<URL> getBaseClassPathForScanning();
-
-    void setBaseClassPathForScanning(Iterable<URL> urls);
-
-    void setManagementNodeUri(URI uri);
-
-    void addEntitySetListener(CollectionChangeListener<Entity> listener);
-
-    void removeEntitySetListener(CollectionChangeListener<Entity> listener);
-
-    void terminate();
-    
-    long getTotalEffectorInvocations();
-
-    <T> T invokeEffectorMethodSync(final Entity entity, final Effector<T> eff, final Object args) throws ExecutionException;
-    
-    <T> Task<T> invokeEffector(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters);
-
-    BrooklynStorage getStorage();
-    
-    BrooklynProperties getBrooklynProperties();
-    
-    AccessManager getAccessManager();
-
-    UsageManager getUsageManager();
-    
-    /**
-     * @return The OSGi manager, if available; may be absent if OSGi is not supported,
-     * e.g. in test contexts (but will be supported in all major contexts).
-     */
-    Maybe<OsgiManager> getOsgiManager();
-
-    InternalEntityFactory getEntityFactory();
-    
-    InternalLocationFactory getLocationFactory();
-    
-    InternalPolicyFactory getPolicyFactory();
-    
-    /**
-     * Registers an entity that has been created, but that has not yet begun to be managed.
-     * <p>
-     * This differs from the idea of "preManaged" where the entities are in the process of being
-     * managed, but where management is not yet complete.
-     */
-    // TODO would benefit from better naming! The name has percolated up from LocalEntityManager.
-    //      should we just rename here as register or preManage?
-    void prePreManage(Entity entity);
-
-    /**
-     * Registers a location that has been created, but that has not yet begun to be managed.
-     */
-    void prePreManage(Location location);
-
-    /** Object which allows adding, removing, and clearing errors.
-     * TODO In future this will change to a custom interface with a unique identifier for each error. */
-    @Beta
-    List<Throwable> errors();
-
-    @Beta
-    CatalogInitialization getCatalogInitialization();
-
-    @Beta
-    void setCatalogInitialization(CatalogInitialization catalogInitialization);
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/ManagementTransitionInfo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/ManagementTransitionInfo.java b/core/src/main/java/brooklyn/management/internal/ManagementTransitionInfo.java
deleted file mode 100644
index c16ff8e..0000000
--- a/core/src/main/java/brooklyn/management/internal/ManagementTransitionInfo.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.management.internal;
-
-import org.apache.brooklyn.api.management.ManagementContext;
-
-/** Stores a management transition mode, and the management context. */
-// TODO does this class really pull its weight?
-public class ManagementTransitionInfo {
-
-    final ManagementContext mgmtContext;
-    final ManagementTransitionMode mode;
-    
-    public ManagementTransitionInfo(ManagementContext mgmtContext, ManagementTransitionMode mode) {
-        this.mgmtContext = mgmtContext;
-        this.mode = mode;
-    }
-    
-    
-    public ManagementContext getManagementContext() {
-        return mgmtContext;
-    }
-
-    public ManagementTransitionMode getMode() {
-        return mode;
-    }
-    
-    @Override
-    public String toString() {
-        return super.toString()+"["+mgmtContext+";"+mode+"]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/ManagementTransitionMode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/ManagementTransitionMode.java b/core/src/main/java/brooklyn/management/internal/ManagementTransitionMode.java
deleted file mode 100644
index 1d25902..0000000
--- a/core/src/main/java/brooklyn/management/internal/ManagementTransitionMode.java
+++ /dev/null
@@ -1,127 +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.management.internal;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Preconditions;
-
-/**
- * Records details of a management transition, specifically the {@link BrooklynObjectManagementMode} before and after,
- * and allows easy checking of various aspects of that.
- * <p>
- * This helps make code readable and keep correct logic if we expand/change the management modes.
- */
-public class ManagementTransitionMode {
-
-    private static final Logger log = LoggerFactory.getLogger(ManagementTransitionMode.class);
-    
-    private final BrooklynObjectManagementMode modeBefore, modeAfter;
-
-    private ManagementTransitionMode(BrooklynObjectManagementMode modeBefore, BrooklynObjectManagementMode modeAfter) {
-        this.modeBefore = modeBefore;
-        this.modeAfter = modeAfter;
-    }
-    
-    public static ManagementTransitionMode transitioning(BrooklynObjectManagementMode modeBefore, BrooklynObjectManagementMode modeAfter) {
-        return new ManagementTransitionMode(Preconditions.checkNotNull(modeBefore, "modeBefore"), Preconditions.checkNotNull(modeAfter, "modeAfter"));
-    }
-
-    @Deprecated /** @deprecated marking places where we aren't sure */
-    public static ManagementTransitionMode guessing(BrooklynObjectManagementMode modeBefore, BrooklynObjectManagementMode modeAfter) {
-        return transitioning(modeBefore, modeAfter);
-    }
-
-    /** @return the mode this object was previously managed as */
-    public BrooklynObjectManagementMode getModeBefore() {
-        return modeBefore;
-    }
-    
-    /** @return the mode this object is now being managed as */
-    public BrooklynObjectManagementMode getModeAfter() {
-        return modeAfter;
-    }
-    
-    /** This management node was previously not loaded here, 
-     * either it did not exist (and is just being created) or it was in persisted state but
-     * not loaded at this node. */
-    public boolean wasNotLoaded() {
-        return getModeBefore()==BrooklynObjectManagementMode.NONEXISTENT || getModeBefore()==BrooklynObjectManagementMode.UNMANAGED_PERSISTED;
-    }
-
-    /** This management node is now not going to be loaded here, either it is being destroyed
-     * (not known anywhere, not even persisted) or simply forgotten here */
-    public boolean isNoLongerLoaded() {
-        return getModeAfter()==BrooklynObjectManagementMode.NONEXISTENT || getModeAfter()==BrooklynObjectManagementMode.UNMANAGED_PERSISTED;
-    }
-
-    /** This management node was the master for the given object */
-    public boolean wasPrimary() {
-        return getModeBefore()==BrooklynObjectManagementMode.MANAGED_PRIMARY;
-    }
-
-    /** This management node is now the master for the given object */
-    public boolean isPrimary() {
-        return getModeAfter()==BrooklynObjectManagementMode.MANAGED_PRIMARY;
-    }
-
-    /** Object was previously loaded as read-only at this management node;
-     * active management was occurring elsewhere (or not at all)
-     */
-    public boolean wasReadOnly() {
-        return getModeBefore()==BrooklynObjectManagementMode.LOADED_READ_ONLY;
-    }
-    
-    /** Object is now being loaded as read-only at this management node;
-     * expect active management to be occurring elsewhere
-     */
-    public boolean isReadOnly() {
-        return getModeAfter()==BrooklynObjectManagementMode.LOADED_READ_ONLY;
-    }
-    
-    /** Object is being created:
-     * previously did not exist (not even in persisted state);
-     * implies that we are the active manager creating it,
-     * i.e. {@link #getModeAfter()} should indicate {@link BrooklynObjectManagementMode#MANAGED_PRIMARY}.
-     * (if we're read-only and the manager has just created it, 
-     * {@link #getModeBefore()} should indicate {@link BrooklynObjectManagementMode#UNMANAGED_PERSISTED})
-     */
-    public boolean isCreating() {
-        if (getModeBefore()!=BrooklynObjectManagementMode.NONEXISTENT)
-            return false;
-        
-        if (getModeAfter()==BrooklynObjectManagementMode.LOADED_READ_ONLY) {
-            log.warn("isCreating set on RO object; highly irregular!");
-        }
-        return true;
-    }
-
-    /** Object is being destroyed:
-     * either destroyed elsewhere and we're catching up (in read-only mode),
-     * or we've been the active manager and are destroying it */
-    public boolean isDestroying() {
-        return getModeAfter()==BrooklynObjectManagementMode.NONEXISTENT;
-    }
-    
-    @Override
-    public String toString() {
-        return ManagementTransitionMode.class.getSimpleName()+"["+getModeBefore()+"->"+getModeAfter()+"]";
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/NonDeploymentAccessManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/NonDeploymentAccessManager.java b/core/src/main/java/brooklyn/management/internal/NonDeploymentAccessManager.java
deleted file mode 100644
index 4cc6bba..0000000
--- a/core/src/main/java/brooklyn/management/internal/NonDeploymentAccessManager.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.management.internal;
-
-import org.apache.brooklyn.api.management.AccessController;
-
-
-public class NonDeploymentAccessManager implements AccessManager {
-
-    private final ManagementContextInternal initialManagementContext;
-    
-    public NonDeploymentAccessManager(ManagementContextInternal initialManagementContext) {
-        this.initialManagementContext = initialManagementContext;
-    }
-    
-    private boolean isInitialManagementContextReal() {
-        return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext));
-    }
-
-    @Override
-    public AccessController getAccessController() {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getAccessManager().getAccessController();
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public boolean isLocationProvisioningAllowed() {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getAccessManager().isLocationProvisioningAllowed();
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public boolean isLocationManagementAllowed() {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getAccessManager().isLocationManagementAllowed();
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public boolean isEntityManagementAllowed() {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getAccessManager().isEntityManagementAllowed();
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public void setLocationProvisioningAllowed(boolean allowed) {
-        if (isInitialManagementContextReal()) {
-            initialManagementContext.getAccessManager().setLocationProvisioningAllowed(allowed);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public void setLocationManagementAllowed(boolean allowed) {
-        if (isInitialManagementContextReal()) {
-            initialManagementContext.getAccessManager().setLocationManagementAllowed(allowed);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public void setEntityManagementAllowed(boolean allowed) {
-        if (isInitialManagementContextReal()) {
-            initialManagementContext.getAccessManager().setEntityManagementAllowed(allowed);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/NonDeploymentEntityManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/NonDeploymentEntityManager.java b/core/src/main/java/brooklyn/management/internal/NonDeploymentEntityManager.java
deleted file mode 100644
index 8b85975..0000000
--- a/core/src/main/java/brooklyn/management/internal/NonDeploymentEntityManager.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.management.internal;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.proxying.EntitySpec;
-import org.apache.brooklyn.api.entity.proxying.EntityTypeRegistry;
-import org.apache.brooklyn.api.management.ManagementContext;
-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 com.google.common.base.Predicate;
-
-public class NonDeploymentEntityManager implements EntityManagerInternal {
-
-    private final ManagementContext initialManagementContext;
-    
-    public NonDeploymentEntityManager(ManagementContext initialManagementContext) {
-        this.initialManagementContext = initialManagementContext;
-    }
-    
-    @Override
-    public EntityTypeRegistry getEntityTypeRegistry() {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getEntityManager().getEntityTypeRegistry();
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation.");
-        }
-    }
-    
-    @Override
-    public <T extends Entity> T createEntity(EntitySpec<T> spec) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getEntityManager().createEntity(spec);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation.");
-        }
-    }
-    
-    @Override
-    public <T extends Entity> T createEntity(Map<?,?> config, Class<T> type) {
-        return createEntity(EntitySpec.create(type).configure(config));
-    }
-
-    @Override
-    public <T extends Policy> T createPolicy(PolicySpec<T> spec) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getEntityManager().createPolicy(spec);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation.");
-        }
-    }
-    
-    @Override
-    public <T extends Enricher> T createEnricher(EnricherSpec<T> spec) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getEntityManager().createEnricher(spec);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation.");
-        }
-    }
-    
-    @Override
-    public Collection<Entity> getEntities() {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getEntityManager().getEntities();
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    @Override
-    public Collection<Entity> getEntitiesInApplication(Application application) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getEntityManager().getEntitiesInApplication(application);
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    @Override
-    public Collection<Entity> findEntities(Predicate<? super Entity> filter) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getEntityManager().findEntities(filter);
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    @Override
-    public Collection<Entity> findEntitiesInApplication(Application application, Predicate<? super Entity> filter) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getEntityManager().findEntitiesInApplication(application, filter);
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    @Override
-    public Entity getEntity(String id) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getEntityManager().getEntity(id);
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public Iterable<String> getEntityIds() {
-        if (isInitialManagementContextReal()) {
-            return ((EntityManagerInternal)initialManagementContext.getEntityManager()).getEntityIds();
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    @Override
-    public ManagementTransitionMode getLastManagementTransitionMode(String itemId) {
-        if (isInitialManagementContextReal()) {
-            return ((EntityManagerInternal)initialManagementContext.getEntityManager()).getLastManagementTransitionMode(itemId);
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public void setManagementTransitionMode(Entity item, ManagementTransitionMode mode) {
-        if (isInitialManagementContextReal()) {
-            ((EntityManagerInternal)initialManagementContext.getEntityManager()).setManagementTransitionMode(item, mode);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-    
-    @Override
-    public boolean isManaged(Entity entity) {
-        return false;
-    }
-
-    @Override
-    public void manage(Entity e) {
-        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot manage "+e);
-    }
-
-    @Override
-    public void unmanage(Entity e, ManagementTransitionMode info) {
-        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-    }
-    
-    @Override
-    public void manageRebindedRoot(Entity item) {
-        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-    }
-
-    @Override
-    public void unmanage(Entity e) {
-        throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot unmanage "+e);
-    }
-    
-    private boolean isInitialManagementContextReal() {
-        return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext));
-    }
-
-    @Override
-    public Iterable<Entity> getAllEntitiesInApplication(Application application) {
-        if (isInitialManagementContextReal()) {
-            return ((EntityManagerInternal)initialManagementContext.getEntityManager()).getAllEntitiesInApplication(application);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation.");
-        }
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/NonDeploymentLocationManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/NonDeploymentLocationManager.java b/core/src/main/java/brooklyn/management/internal/NonDeploymentLocationManager.java
deleted file mode 100644
index 5d68db6..0000000
--- a/core/src/main/java/brooklyn/management/internal/NonDeploymentLocationManager.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.management.internal;
-
-import java.util.Collection;
-import java.util.Collections;
-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;
-
-public class NonDeploymentLocationManager implements LocationManagerInternal {
-
-    private final ManagementContext initialManagementContext;
-    
-    public NonDeploymentLocationManager(ManagementContext initialManagementContext) {
-        this.initialManagementContext = initialManagementContext;
-    }
-    
-    @Override
-    public <T extends Location> T createLocation(LocationSpec<T> spec) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getLocationManager().createLocation(spec);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot create "+spec);
-        }
-    }
-
-    @Override
-    public <T extends Location> T createLocation(Map<?, ?> config, Class<T> type) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getLocationManager().createLocation(config, type);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot create "+type);
-        }
-    }
-    
-    @Override
-    public Collection<Location> getLocations() {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getLocationManager().getLocations();
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    @Override
-    public Location getLocation(String id) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getLocationManager().getLocation(id);
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public Iterable<String> getLocationIds() {
-        if (isInitialManagementContextReal()) {
-            return ((LocationManagerInternal)initialManagementContext.getLocationManager()).getLocationIds();
-        } else {
-            return Collections.emptyList();
-        }
-    }
-    
-    @Override
-    public boolean isManaged(Location loc) {
-        return false;
-    }
-
-    @Override
-    public void manageRebindedRoot(Location loc) {
-        if (isInitialManagementContextReal()) {
-            ((LocationManagerInternal)initialManagementContext.getLocationManager()).manageRebindedRoot(loc);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot manage "+loc);
-        }
-    }
-
-    @Override
-    @Deprecated
-    public Location manage(Location loc) {
-        if (isInitialManagementContextReal()) {
-            return initialManagementContext.getLocationManager().manage(loc);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot manage "+loc);
-        }
-    }
-
-
-    @Override
-    public ManagementTransitionMode getLastManagementTransitionMode(String itemId) {
-        if (isInitialManagementContextReal()) {
-            return ((LocationManagerInternal)initialManagementContext.getLocationManager()).getLastManagementTransitionMode(itemId);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public void setManagementTransitionMode(Location item, ManagementTransitionMode mode) {
-        if (isInitialManagementContextReal()) {
-            ((LocationManagerInternal)initialManagementContext.getLocationManager()).setManagementTransitionMode(item, mode);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public void unmanage(Location item, ManagementTransitionMode info) {
-        if (isInitialManagementContextReal()) {
-            ((LocationManagerInternal)initialManagementContext.getLocationManager()).unmanage(item, info);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation");
-        }
-    }
-
-    @Override
-    public void unmanage(Location loc) {
-        if (isInitialManagementContextReal()) {
-            initialManagementContext.getLocationManager().unmanage(loc);
-        } else {
-            throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot unmanage "+loc);
-        }
-    }
-    
-    private boolean isInitialManagementContextReal() {
-        return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext));
-    }
-}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/TestEntityFailingRebind.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/TestEntityFailingRebind.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/TestEntityFailingRebind.java
new file mode 100644
index 0000000..974f5d7
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/TestEntityFailingRebind.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import org.apache.brooklyn.test.entity.TestApplicationImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TestEntityFailingRebind extends TestApplicationImpl {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(TestEntityFailingRebind.class);
+
+    public static class RebindException extends RuntimeException {
+        private static final long serialVersionUID = 1L;
+
+        public RebindException(String message) {
+            super(message);
+        }
+    }
+    
+    private static boolean throwOnRebind = true;
+    
+    public static void setThrowOnRebind(boolean state) {
+        throwOnRebind = state;
+    }
+    
+    public static boolean getThrowOnRebind() {
+        return throwOnRebind;
+    }
+
+    @Override
+    public void rebind() {
+        if (throwOnRebind) {
+            LOG.warn("Throwing intentional exception to simulate failure of rebinding " + this);
+            throw new RebindException("Intentional exception thrown when rebinding " + this);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/WarmStandbyTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/WarmStandbyTest.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/WarmStandbyTest.java
new file mode 100644
index 0000000..8324c16
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/WarmStandbyTest.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
+import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl;
+import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.test.entity.TestApplication;
+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.entity.basic.Entities;
+import brooklyn.entity.rebind.PersistenceExceptionHandlerImpl;
+import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
+import brooklyn.entity.rebind.persister.InMemoryObjectStore;
+import brooklyn.entity.rebind.persister.ListeningObjectStore;
+import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.time.Duration;
+
+@Test
+public class WarmStandbyTest {
+
+    private static final Logger log = LoggerFactory.getLogger(WarmStandbyTest.class);
+    
+    private List<HaMgmtNode> nodes = new MutableList<WarmStandbyTest.HaMgmtNode>();
+    Map<String,String> sharedBackingStore = MutableMap.of();
+    Map<String,Date> sharedBackingStoreDates = MutableMap.of();
+    private ClassLoader classLoader = getClass().getClassLoader();
+    
+    public class HaMgmtNode {
+        // TODO share with HotStandbyTest and SplitBrainTest and a few others (minor differences but worth it ultimately)
+        
+        private ManagementContextInternal mgmt;
+        private String ownNodeId;
+        private String nodeName;
+        private ListeningObjectStore objectStore;
+        private ManagementPlaneSyncRecordPersister persister;
+        private HighAvailabilityManagerImpl ha;
+
+        @BeforeMethod(alwaysRun=true)
+        public void setUp() throws Exception {
+            nodeName = "node "+nodes.size();
+            mgmt = newLocalManagementContext();
+            ownNodeId = mgmt.getManagementNodeId();
+            objectStore = new ListeningObjectStore(newPersistenceObjectStore());
+            objectStore.injectManagementContext(mgmt);
+            objectStore.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);
+            persister = new ManagementPlaneSyncRecordPersisterToObjectStore(mgmt, objectStore, classLoader);
+            ((ManagementPlaneSyncRecordPersisterToObjectStore)persister).preferRemoteTimestampInMemento();
+            BrooklynMementoPersisterToObjectStore persisterObj = new BrooklynMementoPersisterToObjectStore(objectStore, mgmt.getBrooklynProperties(), classLoader);
+            mgmt.getRebindManager().setPersister(persisterObj, PersistenceExceptionHandlerImpl.builder().build());
+            ha = ((HighAvailabilityManagerImpl)mgmt.getHighAvailabilityManager())
+                .setPollPeriod(Duration.PRACTICALLY_FOREVER)
+                .setHeartbeatTimeout(Duration.THIRTY_SECONDS)
+                .setPersister(persister);
+            log.info("Created "+nodeName+" "+ownNodeId);
+        }
+        
+        public void tearDown() throws Exception {
+            if (ha != null) ha.stop();
+            if (mgmt != null) Entities.destroyAll(mgmt);
+            if (objectStore != null) objectStore.deleteCompletely();
+        }
+        
+        @Override
+        public String toString() {
+            return nodeName+" "+ownNodeId;
+        }
+    }
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        nodes.clear();
+        sharedBackingStore.clear();
+    }
+    
+    public HaMgmtNode newNode() throws Exception {
+        HaMgmtNode node = new HaMgmtNode();
+        node.setUp();
+        nodes.add(node);
+        return node;
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        for (HaMgmtNode n: nodes)
+            n.tearDown();
+    }
+
+    protected ManagementContextInternal newLocalManagementContext() {
+        return new LocalManagementContextForTests();
+    }
+
+    protected PersistenceObjectStore newPersistenceObjectStore() {
+        return new InMemoryObjectStore(sharedBackingStore, sharedBackingStoreDates);
+    }
+
+    // TODO refactor above -- routines above this line are shared among HotStandbyTest and SplitBrainTest
+    
+    @Test
+    public void testWarmStandby() throws Exception {
+        HaMgmtNode n1 = newNode();
+        n1.ha.start(HighAvailabilityMode.AUTO);
+        assertEquals(n1.ha.getNodeState(), ManagementNodeState.MASTER);
+        
+        TestApplication app = TestApplication.Factory.newManagedInstanceForTests(n1.mgmt);
+        app.start(MutableList.<Location>of());
+        
+        n1.mgmt.getRebindManager().forcePersistNow(false, null);
+
+        HaMgmtNode n2 = newNode();
+        n2.ha.start(HighAvailabilityMode.STANDBY);
+        assertEquals(n2.ha.getNodeState(), ManagementNodeState.STANDBY);
+
+        assertEquals(n2.mgmt.getApplications().size(), 0);
+    }
+    
+    // TODO support forcible demotion, and check that a master forcibly demoted 
+    // to warm standby clears its apps, policies, and locations  
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/internal/AccessManagerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/internal/AccessManagerTest.java b/core/src/test/java/org/apache/brooklyn/core/management/internal/AccessManagerTest.java
new file mode 100644
index 0000000..891c3a4
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/internal/AccessManagerTest.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+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 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.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.test.entity.TestApplication;
+import org.apache.brooklyn.test.entity.TestEntity;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.Entities;
+
+import org.apache.brooklyn.location.basic.SimulatedLocation;
+
+import brooklyn.util.exceptions.Exceptions;
+
+import com.google.common.collect.ImmutableSet;
+
+public class AccessManagerTest {
+
+    private LocalManagementContext managementContext;
+    private TestApplication app;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        managementContext = new LocalManagementContextForTests();
+        app = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext);
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (managementContext != null) Entities.destroyAll(managementContext);
+        app = null;
+    }
+
+    @Test
+    public void testEntityManagementAllowed() throws Exception {
+        // default is allowed
+        TestEntity e1 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+
+        // when forbidden, should give error trying to create+manage new entity
+        managementContext.getAccessManager().setEntityManagementAllowed(false);
+        try {
+            app.createAndManageChild(EntitySpec.create(TestEntity.class));
+            fail();
+        } catch (Exception e) {
+            // expect it to be forbidden
+            if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) {
+                throw e;
+            }
+        }
+
+        // when forbidden, should refuse to create new app
+        try {
+            ApplicationBuilder.newManagedApp(TestApplication.class, managementContext);
+            fail();
+        } catch (Exception e) {
+            // expect it to be forbidden
+            if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) {
+                throw e;
+            }
+        }
+
+        // but when forbidden, still allowed to create locations
+        managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
+        
+        // when re-enabled, can create entities again
+        managementContext.getAccessManager().setEntityManagementAllowed(true);
+        TestEntity e3 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        assertEquals(ImmutableSet.copyOf(managementContext.getEntityManager().getEntities()), ImmutableSet.of(app, e1, e3));
+    }
+    
+    @Test
+    public void testLocationManagementAllowed() throws Exception {
+        // default is allowed
+        Location loc1 = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
+
+        // when forbidden, should give error
+        managementContext.getAccessManager().setLocationManagementAllowed(false);
+        try {
+            managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
+            fail();
+        } catch (Exception e) {
+            // expect it to be forbidden
+            if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) {
+                throw e;
+            }
+        }
+
+        // but when forbidden, still allowed to create entity
+        ApplicationBuilder.newManagedApp(TestApplication.class, managementContext);
+        
+        // when re-enabled, can create entities again
+        managementContext.getAccessManager().setLocationManagementAllowed(true);
+        Location loc3 = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
+        
+        assertEquals(ImmutableSet.copyOf(managementContext.getLocationManager().getLocations()), ImmutableSet.of(loc1, loc3));
+    }
+    
+    @Test
+    public void testLocationProvisioningAllowed() throws Exception {
+        SimulatedLocation loc = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
+        
+        // default is allowed
+        assertTrue(managementContext.getAccessController().canProvisionLocation(loc).isAllowed());
+
+        // when forbidden, should say so
+        managementContext.getAccessManager().setLocationProvisioningAllowed(false);
+        assertFalse(managementContext.getAccessController().canProvisionLocation(loc).isAllowed());
+
+        // but when forbidden, still allowed to create locations
+        managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
+        
+        // when re-enabled, can create entities again
+        managementContext.getAccessManager().setLocationProvisioningAllowed(true);
+        assertTrue(managementContext.getAccessController().canProvisionLocation(loc).isAllowed());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..7f35bba
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/internal/EntityExecutionManagerTest.java
@@ -0,0 +1,478 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Semaphore;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.api.management.ExecutionManager;
+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.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.test.entity.TestApplication;
+import org.apache.brooklyn.test.entity.TestEntity;
+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.config.BrooklynProperties;
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.BrooklynTaskTags.WrappedEntity;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.event.basic.BasicAttributeSensor;
+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;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.FluentIterable;
+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.collect.Lists;
+import com.google.common.util.concurrent.Callables;
+
+/** Includes many tests for {@link BrooklynGarbageCollector} */
+public class EntityExecutionManagerTest {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(EntityExecutionManagerTest.class);
+    
+    private static final Duration TIMEOUT_MS = Duration.TEN_SECONDS;
+    
+    private ManagementContextInternal mgmt;
+    private TestApplication app;
+    private TestEntity e;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+        app = null;
+        if (mgmt != null) Entities.destroyAll(mgmt);
+    }
+
+    @Test
+    public void testOnDoneCallback() throws InterruptedException {
+        mgmt = LocalManagementContextForTests.newInstance();
+        ExecutionManager em = mgmt.getExecutionManager();
+        BasicExecutionManager bem = (BasicExecutionManager)em;
+        final Map<Task<?>,Duration> completedTasks = MutableMap.of();
+        final Semaphore sema4 = new Semaphore(-1);
+        bem.addListener(new ExecutionListener() {
+            @Override
+            public void onTaskDone(Task<?> task) {
+                Assert.assertTrue(task.isDone());
+                Assert.assertEquals(task.getUnchecked(), "foo");
+                completedTasks.put(task, Duration.sinceUtc(task.getEndTimeUtc()));
+                sema4.release();
+            }
+        });
+        Task<String> t1 = em.submit( Tasks.<String>builder().name("t1").dynamic(false).body(Callables.returning("foo")).build() );
+        t1.getUnchecked();
+        Task<String> t2 = em.submit( Tasks.<String>builder().name("t2").dynamic(false).body(Callables.returning("foo")).build() );
+        sema4.acquire();
+        Assert.assertEquals(completedTasks.size(), 2, "completed tasks are: "+completedTasks);
+        completedTasks.get(t1).isShorterThan(Duration.TEN_SECONDS);
+        completedTasks.get(t2).isShorterThan(Duration.TEN_SECONDS);
+    }
+    
+    protected void forceGc() {
+        ((LocalManagementContext)app.getManagementContext()).getGarbageCollector().gcIteration();
+    }
+
+    protected static Task<?> runEmptyTaskWithNameAndTags(Entity target, String name, Object ...tags) {
+        TaskBuilder<Object> tb = newEmptyTask(name);
+        for (Object tag: tags) tb.tag(tag);
+        Task<?> task = ((EntityInternal)target).getExecutionContext().submit(tb.build());
+        task.getUnchecked();
+        return task;
+    }
+
+    protected static TaskBuilder<Object> newEmptyTask(String name) {
+        return Tasks.builder().name(name).dynamic(false).body(Callables.returning(null));
+    }
+    
+    protected void assertTaskCountForEntitySoon(final Entity entity, final int expectedCount) {
+        // Dead task (and initialization task) should have been GC'd on completion.
+        // However, the GC'ing happens in a listener, executed in a different thread - the task.get()
+        // doesn't block for it. Therefore can't always guarantee it will be GC'ed by now.
+        Repeater.create().backoff(Duration.millis(10), 2, Duration.millis(500)).limitTimeTo(Duration.TEN_SECONDS).until(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                forceGc();
+                Collection<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(((EntityInternal)entity).getManagementContext().getExecutionManager(), entity);
+                Assert.assertEquals(tasks.size(), expectedCount, "Tasks were "+tasks);
+                return true;
+            }
+        }).runRequiringTrue();
+    }
+
+    @Test
+    public void testGetTasksAndGcBoringTags() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        final Task<?> task = runEmptyTaskWithNameAndTags(e, "should-be-kept", ManagementContextInternal.NON_TRANSIENT_TASK_TAG);
+        runEmptyTaskWithNameAndTags(e, "should-be-gcd", ManagementContextInternal.TRANSIENT_TASK_TAG);
+        
+        assertTaskCountForEntitySoon(e, 1);
+        Collection<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(app.getManagementContext().getExecutionManager(), e);
+        assertEquals(tasks, ImmutableList.of(task), "Mismatched tasks, got: "+tasks);
+    }
+
+    @Test
+    public void testGcTaskAtNormalTagLimit() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
+            BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2);
+
+        for (int count=0; count<5; count++)
+            runEmptyTaskWithNameAndTags(e, "task"+count, ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
+
+        assertTaskCountForEntitySoon(e, 2);
+    }
+    
+    @Test
+    public void testGcTaskAtEntityLimit() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
+            BrooklynGarbageCollector.MAX_TASKS_PER_ENTITY, 2);
+        
+        for (int count=0; count<5; count++)
+            runEmptyTaskWithNameAndTags(e, "task-e-"+count, ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
+        for (int count=0; count<5; count++)
+            runEmptyTaskWithNameAndTags(app, "task-app-"+count, ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
+        
+        assertTaskCountForEntitySoon(app, 2);
+        assertTaskCountForEntitySoon(e, 2);
+    }
+    
+    @Test
+    public void testGcTaskWithTagAndEntityLimit() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
+            BrooklynGarbageCollector.MAX_TASKS_PER_ENTITY, 6);
+        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
+            BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2);
+
+        int count=0;
+        
+        runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
+        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
+        Time.sleep(Duration.ONE_MILLISECOND);
+        // should keep the 2 below, because all the other borings get grace, but delete the ones above
+        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
+        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag");
+        
+        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag", "another-tag-e");
+        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag", "another-tag-e");
+        // should keep both the above
+        
+        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag");
+        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag");
+        Time.sleep(Duration.ONE_MILLISECOND);
+        runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag");
+        // should keep the below since they have unique tags, but remove one of the e tasks above 
+        runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag", "and-another-tag");
+        runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag-app", "another-tag");
+        runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag-app", "another-tag");
+        
+        assertTaskCountForEntitySoon(e, 6);
+        assertTaskCountForEntitySoon(app, 3);
+        
+        // now with a lowered limit, we should remove one more e
+        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
+            BrooklynGarbageCollector.MAX_TASKS_PER_ENTITY, 5);
+        assertTaskCountForEntitySoon(e, 5);
+    }
+    
+    @Test
+    public void testGcDynamicTaskAtNormalTagLimit() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        ((BrooklynProperties)app.getManagementContext().getConfig()).put(
+            BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2);
+
+        for (int count=0; count<5; count++) {
+            TaskBuilder<Object> tb = Tasks.builder().name("task-"+count).dynamic(true).body(new Runnable() { @Override public void run() {}})
+                .tag(ManagementContextInternal.NON_TRANSIENT_TASK_TAG).tag("foo");
+            ((EntityInternal)e).getExecutionContext().submit(tb.build()).getUnchecked();
+        }
+
+        // might need an eventually here, if the internal job completion and GC is done in the background
+        // (if there are no test failures for a few months, since Sept 2014, then we can remove this comment)
+        assertTaskCountForEntitySoon(e, 2);
+    }
+    
+    @Test
+    public void testUnmanagedEntityCanBeGcedEvenIfPreviouslyTagged() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        String eId = e.getId();
+        
+        e.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of()).get();
+        Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(app.getManagementContext().getExecutionManager(), e);
+        Task<?> task = Iterables.get(tasks, 0);
+        assertTrue(task.getTags().contains(BrooklynTaskTags.tagForContextEntity(e)));
+
+        Set<Object> tags = app.getManagementContext().getExecutionManager().getTaskTags();
+        assertTrue(tags.contains(BrooklynTaskTags.tagForContextEntity(e)), "tags="+tags);
+        
+        Entities.destroy(e);
+        forceGc();
+        
+        Set<Object> tags2 = app.getManagementContext().getExecutionManager().getTaskTags();
+        for (Object tag : tags2) {
+            if (tag instanceof Entity && ((Entity)tag).getId().equals(eId)) {
+                fail("tags contains unmanaged entity "+tag);
+            }
+            if ((tag instanceof WrappedEntity) && ((WrappedEntity)tag).entity.getId().equals(eId) 
+                    && ((WrappedEntity)tag).wrappingType.equals(BrooklynTaskTags.CONTEXT_ENTITY)) {
+                fail("tags contains unmanaged entity (wrapped) "+tag);
+            }
+        }
+        return;
+    }
+    
+    @Test(groups="Integration")
+    public void testSubscriptionAndEffectorTasksGced() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        BasicExecutionManager em = (BasicExecutionManager) app.getManagementContext().getExecutionManager();
+        // allow background enrichers to complete
+        Time.sleep(Duration.ONE_SECOND);
+        forceGc();
+        List<Task<?>> t1 = em.getAllTasks();
+        
+        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        entity.setAttribute(TestEntity.NAME, "bob");
+        entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of()).get();
+        Entities.destroy(entity);
+        Time.sleep(Duration.ONE_SECOND);
+        forceGc();
+        List<Task<?>> t2 = em.getAllTasks();
+        
+        Assert.assertEquals(t1.size(), t2.size(), "lists are different:\n"+t1+"\n"+t2+"\n");
+    }
+
+    /**
+     * Invoke effector many times, where each would claim 10MB because it stores the return value.
+     * If it didn't gc the tasks promptly, it would consume 10GB ram (so would OOME before that).
+     */
+    @Test(groups="Integration")
+    public void testEffectorTasksGcedSoNoOome() throws Exception {
+        
+        BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty();
+        brooklynProperties.put(BrooklynGarbageCollector.GC_PERIOD, Duration.ONE_MILLISECOND);
+        brooklynProperties.put(BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2);
+        
+        app = ApplicationBuilder.newManagedApp(TestApplication.class, LocalManagementContextForTests.newInstance(brooklynProperties));
+        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        for (int i = 0; i < 1000; i++) {
+            if (i%100==0) LOG.info(JavaClassNames.niceClassAndMethod()+": iteration "+i);
+            try {
+                LOG.debug("testEffectorTasksGced: iteration="+i);
+                entity.invoke(TestEntity.IDENTITY_EFFECTOR, ImmutableMap.of("arg", new BigObject(10*1000*1000))).get();
+                
+                Time.sleep(Duration.ONE_MILLISECOND); // Give GC thread a chance to run
+                forceGc();
+            } catch (OutOfMemoryError e) {
+                LOG.warn(JavaClassNames.niceClassAndMethod()+": OOME at iteration="+i);
+                throw e;
+            }
+        }
+    }
+    
+    @Test(groups="Integration")
+    public void testUnmanagedEntityGcedOnUnmanageEvenIfEffectorInvoked() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        
+        BasicAttributeSensor<Object> byteArrayAttrib = new BasicAttributeSensor<Object>(Object.class, "test.byteArray", "");
+
+        for (int i = 0; i < 1000; i++) {
+            if (i<100 && i%10==0 || i%100==0) LOG.info(JavaClassNames.niceClassAndMethod()+": iteration "+i);
+            try {
+                LOG.debug(JavaClassNames.niceClassAndMethod()+": iteration="+i);
+                TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+                entity.setAttribute(byteArrayAttrib, new BigObject(10*1000*1000));
+                entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of()).get();
+                
+                // we get exceptions because tasks are still trying to publish after deployment;
+                // this should prevent them
+//                ((LocalEntityManager)app.getManagementContext().getEntityManager()).stopTasks(entity, Duration.ONE_SECOND);
+//                Entities.destroy(entity);
+                
+                // alternatively if we 'unmanage' instead of destroy, there are usually not errors
+                // (the errors come from the node transitioning to a 'stopping' state on destroy, 
+                // and publishing lots of info then)
+                Entities.unmanage(entity);
+                
+                forceGc();
+                // previously we did an extra GC but it was crazy slow, shouldn't be needed
+//                System.gc(); System.gc();
+            } catch (OutOfMemoryError e) {
+                LOG.warn(JavaClassNames.niceClassAndMethod()+": OOME at iteration="+i);
+                ExecutionManager em = app.getManagementContext().getExecutionManager();
+                Collection<Task<?>> tasks = ((BasicExecutionManager)em).getAllTasks();
+                LOG.info("TASKS count "+tasks.size()+": "+tasks);
+                throw e;
+            }
+        }
+    }
+
+    @Test(groups={"Integration"})
+    public void testEffectorTasksGcedForMaxPerTag() throws Exception {
+        int maxNumTasks = 2;
+        BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty();
+        brooklynProperties.put(BrooklynGarbageCollector.GC_PERIOD, Duration.ONE_SECOND);
+        brooklynProperties.put(BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2);
+        
+        app = ApplicationBuilder.newManagedApp(TestApplication.class, LocalManagementContextForTests.newInstance(brooklynProperties));
+        final TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        List<Task<?>> tasks = Lists.newArrayList();
+        
+        for (int i = 0; i < (maxNumTasks+1); i++) {
+            Task<?> task = entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of());
+            task.get();
+            tasks.add(task);
+            
+            // TASKS_OLDEST_FIRST_COMPARATOR is based on comparing EndTimeUtc; but two tasks executed in
+            // rapid succession could finish in same millisecond
+            // (especially when using System.currentTimeMillis, which can return the same time for several millisconds).
+            Thread.sleep(10);
+        }
+        
+        // Should initially have all tasks
+        Set<Task<?>> storedTasks = app.getManagementContext().getExecutionManager().getTasksWithAllTags(
+                ImmutableList.of(BrooklynTaskTags.tagForContextEntity(entity), ManagementContextInternal.EFFECTOR_TAG));
+        assertEquals(storedTasks, ImmutableSet.copyOf(tasks), "storedTasks="+storedTasks+"; expected="+tasks);
+        
+        // Then oldest should be GC'ed to leave only maxNumTasks
+        final List<Task<?>> recentTasks = tasks.subList(tasks.size()-maxNumTasks, tasks.size());
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+            @Override public void run() {
+                Set<Task<?>> storedTasks2 = app.getManagementContext().getExecutionManager().getTasksWithAllTags(
+                       ImmutableList.of(BrooklynTaskTags.tagForContextEntity(entity), ManagementContextInternal.EFFECTOR_TAG));
+                List<String> storedTasks2Str = FluentIterable
+                        .from(storedTasks2)
+                        .transform(new Function<Task<?>, String>() {
+                            @Override public String apply(Task<?> input) {
+                                return taskToVerboseString(input);
+                            }})
+                        .toList();
+                assertEquals(storedTasks2, ImmutableSet.copyOf(recentTasks), "storedTasks="+storedTasks2Str+"; expected="+recentTasks);
+            }});
+    }
+    
+    private String taskToVerboseString(Task t) {
+        return Objects.toStringHelper(t)
+                .add("id", t.getId())
+                .add("displayName", t.getDisplayName())
+                .add("submitTime", t.getSubmitTimeUtc())
+                .add("startTime", t.getStartTimeUtc())
+                .add("endTime", t.getEndTimeUtc())
+                .add("status", t.getStatusSummary())
+                .add("tags", t.getTags())
+                .toString();
+    }
+            
+    @Test(groups="Integration")
+    public void testEffectorTasksGcedForAge() throws Exception {
+        Duration maxTaskAge = Duration.millis(100);
+        Duration maxOverhead = Duration.millis(250);
+        Duration earlyReturnGrace = Duration.millis(10);
+        BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty();
+        brooklynProperties.put(BrooklynGarbageCollector.GC_PERIOD, Duration.ONE_MILLISECOND);
+        brooklynProperties.put(BrooklynGarbageCollector.MAX_TASK_AGE, maxTaskAge);
+        
+        app = ApplicationBuilder.newManagedApp(TestApplication.class, LocalManagementContextForTests.newInstance(brooklynProperties));
+        final TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        Task<?> oldTask = entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of());
+        oldTask.get();
+        
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+            @Override public void run() {
+                Set<Task<?>> storedTasks = app.getManagementContext().getExecutionManager().getTasksWithAllTags(ImmutableList.of(
+                        BrooklynTaskTags.tagForTargetEntity(entity), 
+                        ManagementContextInternal.EFFECTOR_TAG));
+                assertEquals(storedTasks, ImmutableSet.of(), "storedTasks="+storedTasks);
+            }});
+
+        Duration timeToGc = Duration.of(stopwatch);
+        assertTrue(timeToGc.isLongerThan(maxTaskAge.subtract(earlyReturnGrace)), "timeToGc="+timeToGc+"; maxTaskAge="+maxTaskAge);
+        assertTrue(timeToGc.isShorterThan(maxTaskAge.add(maxOverhead)), "timeToGc="+timeToGc+"; maxTaskAge="+maxTaskAge);
+    }
+    
+    private static class BigObject implements Serializable {
+        private static final long serialVersionUID = -4021304829674972215L;
+        private final int sizeBytes;
+        private final byte[] data;
+        
+        BigObject(int sizeBytes) {
+            this.sizeBytes = sizeBytes;
+            this.data = new byte[sizeBytes];
+        }
+        
+        @Override
+        public String toString() {
+            return "BigObject["+sizeBytes+"/"+data.length+"]";
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextInstancesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextInstancesTest.java b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextInstancesTest.java
new file mode 100644
index 0000000..973b795
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextInstancesTest.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests the {@link LocalManagementContext#terminateAll()} and {@link LocalManagementContext#getInstances()} behaviour.
+ * Note this test must NEVER be run in parallel with other tests, as it will terminate the ManagementContext of those
+ * other tests.
+ * 
+ * @author pveentjer
+ */
+public class LocalManagementContextInstancesTest {
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown(){
+         LocalManagementContext.terminateAll();
+    }
+
+    /** WIP group because other threads may be running in background, 
+     * creating management contexts at the same time as us (slim chance, but observed once);
+     * they shouldn't be, but cleaning that up is another matter! */
+    @Test(groups="WIP")
+    public void testGetInstances(){
+        LocalManagementContext.terminateAll();
+        LocalManagementContext context1 = new LocalManagementContext();
+        LocalManagementContext context2 = new LocalManagementContext();
+        LocalManagementContext context3 = new LocalManagementContext();
+
+        assertEquals(LocalManagementContext.getInstances(), ImmutableSet.of(context1, context2, context3));
+    }
+
+    /** WIP group because other threads may be running in background;
+     * they shouldn't be, but cleaning that up is another matter! */
+    @Test
+    public void terminateAll(){
+        LocalManagementContext.terminateAll();
+        
+        LocalManagementContext context1 = new LocalManagementContext();
+        LocalManagementContext context2 = new LocalManagementContext();
+
+        LocalManagementContext.terminateAll();
+
+        assertTrue(LocalManagementContext.getInstances().isEmpty());
+        assertFalse(context1.isRunning());
+        assertFalse(context2.isRunning());
+    }
+
+    @Test
+    public void terminateExplicitContext(){
+        LocalManagementContext context1 = new LocalManagementContext();
+        LocalManagementContext context2 = new LocalManagementContext();
+        LocalManagementContext context3 = new LocalManagementContext();
+
+        context2.terminate();
+
+        Assert.assertFalse(LocalManagementContext.getInstances().contains(context2));
+        Assert.assertTrue(LocalManagementContext.getInstances().contains(context1));
+        Assert.assertTrue(LocalManagementContext.getInstances().contains(context3));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextTest.java b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextTest.java
new file mode 100644
index 0000000..d88765c
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextTest.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.management.ManagementContext.PropertiesReloadListener;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+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.BrooklynProperties;
+import brooklyn.config.BrooklynProperties.Factory.Builder;
+import brooklyn.util.os.Os;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+public class LocalManagementContextTest {
+    
+    private LocalManagementContext context; 
+    private File globalPropertiesFile;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        context = null;
+        globalPropertiesFile = Os.newTempFile(getClass(), "global.properties");
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (context!=null) context.terminate();
+        if (globalPropertiesFile != null) globalPropertiesFile.delete();
+    }
+    
+    @Test
+    public void testReloadPropertiesFromBuilder() throws IOException {
+        String globalPropertiesContents = "brooklyn.location.localhost.displayName=myname";
+        Files.write(globalPropertiesContents, globalPropertiesFile, Charsets.UTF_8);
+        Builder propsBuilder = new BrooklynProperties.Factory.Builder()
+            .globalPropertiesFile(globalPropertiesFile.getAbsolutePath());
+        // no builder support in LocalManagementContextForTests (we are testing that the builder's files are reloaded so we need it here)
+        context = new LocalManagementContext(propsBuilder);
+        Location location = context.getLocationRegistry().resolve("localhost");
+        assertEquals(location.getDisplayName(), "myname");
+        String newGlobalPropertiesContents = "brooklyn.location.localhost.displayName=myname2";
+        Files.write(newGlobalPropertiesContents, globalPropertiesFile, Charsets.UTF_8);
+        context.reloadBrooklynProperties();
+        Location location2 = context.getLocationRegistry().resolve("localhost");
+        assertEquals(location.getDisplayName(), "myname");
+        assertEquals(location2.getDisplayName(), "myname2");
+    }
+    
+    @Test
+    public void testReloadPropertiesFromProperties() throws IOException {
+        String globalPropertiesContents = "brooklyn.location.localhost.displayName=myname";
+        Files.write(globalPropertiesContents, globalPropertiesFile, Charsets.UTF_8);
+        BrooklynProperties brooklynProperties = new BrooklynProperties.Factory.Builder()
+            .globalPropertiesFile(globalPropertiesFile.getAbsolutePath())
+            .build();
+        context = LocalManagementContextForTests.builder(true).useProperties(brooklynProperties).build();
+        Location location = context.getLocationRegistry().resolve("localhost");
+        assertEquals(location.getDisplayName(), "myname");
+        String newGlobalPropertiesContents = "brooklyn.location.localhost.displayName=myname2";
+        Files.write(newGlobalPropertiesContents, globalPropertiesFile, Charsets.UTF_8);
+        context.reloadBrooklynProperties();
+        Location location2 = context.getLocationRegistry().resolve("localhost");
+        assertEquals(location.getDisplayName(), "myname");
+        assertEquals(location2.getDisplayName(), "myname");
+    }
+    
+    @Test
+    public void testPropertiesModified() throws IOException {
+        BrooklynProperties properties = BrooklynProperties.Factory.newEmpty();
+        properties.put("myname", "myval");
+        context = LocalManagementContextForTests.builder(true).useProperties(properties).build();
+        assertEquals(context.getBrooklynProperties().get("myname"), "myval");
+        properties.put("myname", "newval");
+        assertEquals(properties.get("myname"), "newval");
+        // TODO: Should changes in the 'properties' collection be reflected in context.getBrooklynProperties()?
+        assertNotEquals(context.getBrooklynProperties().get("myname"), "newval");
+    }
+    
+    @Test
+    public void testAddAndRemoveReloadListener() {
+        final AtomicInteger reloadedCallbackCount = new AtomicInteger(0);
+        BrooklynProperties properties = BrooklynProperties.Factory.newEmpty();
+        properties.put("myname", "myval");
+        context = LocalManagementContextForTests.builder(true).useProperties(properties).build();
+        PropertiesReloadListener listener = new PropertiesReloadListener() {
+            public void reloaded() {
+                reloadedCallbackCount.incrementAndGet();
+            }
+        };
+        assertEquals(reloadedCallbackCount.get(), 0);
+        context.addPropertiesReloadListener(listener);
+        context.reloadBrooklynProperties();
+        assertEquals(reloadedCallbackCount.get(), 1);
+        context.removePropertiesReloadListener(listener);
+        context.reloadBrooklynProperties();
+        assertEquals(reloadedCallbackCount.get(), 1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManagerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManagerTest.java b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManagerTest.java
new file mode 100644
index 0000000..635e562
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManagerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.brooklyn.api.entity.Entity;
+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.management.SubscriptionHandle;
+import org.apache.brooklyn.api.management.SubscriptionManager;
+import org.apache.brooklyn.test.entity.TestEntity;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.BrooklynAppUnitTestSupport;
+import brooklyn.entity.basic.BasicGroup;
+import brooklyn.entity.basic.Entities;
+
+/**
+ * testing the {@link SubscriptionManager} and associated classes.
+ */
+public class LocalSubscriptionManagerTest extends BrooklynAppUnitTestSupport {
+    
+    private static final int TIMEOUT_MS = 5000;
+    
+    private TestEntity entity;
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+    }
+
+    private void manage(Entity ...entities) {
+        for (Entity e: entities)
+            Entities.manage(e);
+    }
+
+    @Test
+    public void testSubscribeToEntityAttributeChange() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        app.subscribe(entity, TestEntity.SEQUENCE, new SensorEventListener<Object>() {
+                @Override public void onEvent(SensorEvent<Object> event) {
+                    latch.countDown();
+                }});
+        entity.setSequenceValue(1234);
+        if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Timeout waiting for Event on TestEntity listener");
+        }
+    }
+    
+    @Test
+    public void testSubscribeToEntityWithAttributeWildcard() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        app.subscribe(entity, null, new SensorEventListener<Object>() {
+            @Override public void onEvent(SensorEvent<Object> event) {
+                latch.countDown();
+            }});
+        entity.setSequenceValue(1234);
+        if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Timeout waiting for Event on TestEntity listener");
+        }
+    }
+    
+    @Test
+    public void testSubscribeToAttributeChangeWithEntityWildcard() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        app.subscribe(null, TestEntity.SEQUENCE, new SensorEventListener<Object>() {
+                @Override public void onEvent(SensorEvent<Object> event) {
+                    latch.countDown();
+                }});
+        entity.setSequenceValue(1234);
+        if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Timeout waiting for Event on TestEntity listener");
+        }
+    }
+    
+    @Test
+    public void testSubscribeToChildAttributeChange() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        app.subscribeToChildren(app, TestEntity.SEQUENCE, new SensorEventListener<Object>() {
+            @Override public void onEvent(SensorEvent<Object> event) {
+                latch.countDown();
+            }});
+        entity.setSequenceValue(1234);
+        if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Timeout waiting for Event on child TestEntity listener");
+        }
+    }
+    
+    @Test
+    public void testSubscribeToMemberAttributeChange() throws Exception {
+        BasicGroup group = app.createAndManageChild(EntitySpec.create(BasicGroup.class));
+        TestEntity member = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        manage(group, member);
+        
+        group.addMember(member);
+
+        final List<SensorEvent<Integer>> events = new CopyOnWriteArrayList<SensorEvent<Integer>>();
+        final CountDownLatch latch = new CountDownLatch(1);
+        app.subscribeToMembers(group, TestEntity.SEQUENCE, new SensorEventListener<Integer>() {
+            @Override public void onEvent(SensorEvent<Integer> event) {
+                events.add(event);
+                latch.countDown();
+            }});
+        member.setAttribute(TestEntity.SEQUENCE, 123);
+
+        if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Timeout waiting for Event on parent TestEntity listener");
+        }
+        assertEquals(events.size(), 1);
+        assertEquals(events.get(0).getValue(), (Integer)123);
+        assertEquals(events.get(0).getSensor(), TestEntity.SEQUENCE);
+        assertEquals(events.get(0).getSource().getId(), member.getId());
+    }
+    
+    // Regression test for ConcurrentModificationException in issue #327
+    @Test(groups="Integration")
+    public void testConcurrentSubscribingAndPublishing() throws Exception {
+        final AtomicReference<Exception> threadException = new AtomicReference<Exception>();
+        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        // Repeatedly subscribe and unsubscribe, so listener-set constantly changing while publishing to it.
+        // First create a stable listener so it is always the same listener-set object.
+        Thread thread = new Thread() {
+            public void run() {
+                try {
+                    SensorEventListener<Object> noopListener = new SensorEventListener<Object>() {
+                        @Override public void onEvent(SensorEvent<Object> event) {
+                        }
+                    };
+                    app.subscribe(null, TestEntity.SEQUENCE, noopListener);
+                    while (!Thread.currentThread().isInterrupted()) {
+                        SubscriptionHandle handle = app.subscribe(null, TestEntity.SEQUENCE, noopListener);
+                        app.unsubscribe(null, handle);
+                    }
+                } catch (Exception e) {
+                    threadException.set(e);
+                }
+            }
+        };
+        
+        try {
+            thread.start();
+            for (int i = 0; i < 10000; i++) {
+                entity.setAttribute(TestEntity.SEQUENCE, i);
+            }
+        } finally {
+            thread.interrupt();
+        }
+
+        if (threadException.get() != null) throw threadException.get();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiPathTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiPathTest.java b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiPathTest.java
new file mode 100644
index 0000000..8dcbec2
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiPathTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.osgi;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+import org.osgi.framework.BundleException;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.BrooklynServerConfig;
+import brooklyn.config.BrooklynServerPaths;
+import brooklyn.entity.basic.Entities;
+import brooklyn.util.os.Os;
+import brooklyn.util.text.Identifiers;
+
+
+/** 
+ * Tests that OSGi entities load correctly and have the right catalog information set.
+ * Note further tests done elsewhere using CAMP YAML (referring to methods in this class).
+ */
+public class OsgiPathTest {
+   
+    protected LocalManagementContext mgmt = null;
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws BundleException, IOException, InterruptedException {
+        if (mgmt!=null) Entities.destroyAll(mgmt);
+    }
+    
+    @Test(groups="Integration") // integration only because OSGi takes ~200ms
+    public void testOsgiPathDefault() {
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+        String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath();
+        Assert.assertTrue(path.startsWith(BrooklynServerPaths.getMgmtBaseDir(mgmt)), path);
+        Assert.assertTrue(path.contains(mgmt.getManagementNodeId()), path);
+        
+        assertExistsThenIsCleaned(path);
+    }
+
+    @Test(groups="Integration") // integration only because OSGi takes ~200ms
+    public void testOsgiPathCustom() {
+        BrooklynProperties bp = BrooklynProperties.Factory.newEmpty();
+        String randomSeg = "osgi-test-"+Identifiers.makeRandomId(4);
+        bp.put(BrooklynServerConfig.OSGI_CACHE_DIR, "${brooklyn.os.tmpdir}"+"/"+randomSeg+"/"+"${brooklyn.mgmt.node.id}");
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).useProperties(bp).build();
+        String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath();
+        Os.deleteOnExitRecursivelyAndEmptyParentsUpTo(new File(path), new File(Os.tmp()+"/"+randomSeg));
+        
+        Assert.assertTrue(path.startsWith(Os.tmp()), path);
+        Assert.assertTrue(path.contains(mgmt.getManagementNodeId()), path);
+        
+        assertExistsThenIsCleaned(path);
+    }
+
+    @Test(groups="Integration") // integration only because OSGi takes ~200ms
+    public void testOsgiPathCustomWithoutNodeIdNotCleaned() {
+        BrooklynProperties bp = BrooklynProperties.Factory.newEmpty();
+        String randomSeg = "osgi-test-"+Identifiers.makeRandomId(4);
+        bp.put(BrooklynServerConfig.OSGI_CACHE_DIR, "${brooklyn.os.tmpdir}"+"/"+randomSeg+"/"+"sample");
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).useProperties(bp).build();
+        String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath();
+        Os.deleteOnExitRecursivelyAndEmptyParentsUpTo(new File(path), new File(Os.tmp()+"/"+randomSeg));
+        
+        Assert.assertTrue(path.startsWith(Os.tmp()), path);
+        Assert.assertFalse(path.contains(mgmt.getManagementNodeId()), path);
+        
+        assertExistsThenCorrectCleanedBehaviour(path, false);
+    }
+
+    private void assertExistsThenIsCleaned(String path) {
+        assertExistsThenCorrectCleanedBehaviour(path, true);
+    }
+    private void assertExistsThenCorrectCleanedBehaviour(String path, boolean shouldBeCleanAfterwards) {
+        Assert.assertTrue(new File(path).exists(), "OSGi cache "+path+" should exist when in use");
+        Entities.destroyAll(mgmt);
+        mgmt = null;
+        if (shouldBeCleanAfterwards)
+            Assert.assertFalse(new File(path).exists(), "OSGi cache "+path+" should be cleaned after");
+        else
+            Assert.assertTrue(new File(path).exists(), "OSGi cache "+path+" should NOT be cleaned after");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..7f1cc48
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiStandaloneTest.java
@@ -0,0 +1,259 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.osgi;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarInputStream;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.test.TestResourceUnavailableException;
+
+import brooklyn.util.exceptions.Exceptions;
+
+import org.apache.commons.io.FileUtils;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.launch.Framework;
+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.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;
+
+/** 
+ * Tests some assumptions about OSGi behaviour, in standalone mode (not part of brooklyn).
+ * See {@link OsgiTestResources} for description of test resources.
+ */
+public class OsgiStandaloneTest {
+
+    private static final Logger log = LoggerFactory.getLogger(OsgiStandaloneTest.class);
+
+    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_PATH = OsgiTestResources.BROOKLYN_OSGI_TEST_A_0_1_0_PATH;
+    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = "classpath:"+BROOKLYN_OSGI_TEST_A_0_1_0_PATH;
+
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_URL = "classpath:"+BROOKLYN_TEST_OSGI_ENTITIES_PATH;
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_NAME = "org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities";
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_VERSION = "0.1.0";
+
+    protected Framework framework = null;
+    private File storageTempDir;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        storageTempDir = Os.newTempDir("osgi-standalone");
+        framework = Osgis.newFrameworkStarted(storageTempDir.getAbsolutePath(), true, null);
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws BundleException, IOException, InterruptedException {
+        tearDownOsgiFramework(framework, storageTempDir);
+    }
+
+    public static void tearDownOsgiFramework(Framework framework, File storageTempDir) throws BundleException, InterruptedException, IOException {
+        if (framework!=null) {
+            framework.stop();
+            Assert.assertEquals(framework.waitForStop(1000).getType(), FrameworkEvent.STOPPED);
+            framework = null;
+        }
+        if (storageTempDir!=null) {
+            FileUtils.deleteDirectory(storageTempDir);
+            storageTempDir = null;
+        }
+    }
+
+    protected Bundle install(String url) throws BundleException {
+        try {
+            return Osgis.install(framework, url);
+        } catch (Exception e) {
+            throw new IllegalStateException("test resources not available; may be an IDE issue, so try a mvn rebuild of this project", e);
+        }
+    }
+
+    protected Bundle installFromClasspath(String resourceName) throws BundleException {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), resourceName);
+        try {
+            return Osgis.install(framework, String.format("classpath:%s", resourceName));
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    @Test
+    public void testInstallBundle() throws Exception {
+        Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
+        checkMath(bundle, 3, 6);
+    }
+
+    @Test
+    public void testBootBundle() throws Exception {
+        Bundle bundle = installFromClasspath(BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+        Class<?> bundleCls = bundle.loadClass("brooklyn.osgi.tests.SimpleEntity");
+        Assert.assertEquals(Entity.class,  bundle.loadClass(Entity.class.getName()));
+        Assert.assertEquals(Entity.class, bundleCls.getClassLoader().loadClass(Entity.class.getName()));
+    }
+
+    @Test
+    public void testDuplicateBundle() throws Exception {
+        MavenArtifact artifact = new MavenArtifact("org.apache.brooklyn", "brooklyn-api", "jar", "0.8.0-SNAPSHOT"); // BROOKLYN_VERSION
+        String localUrl = MavenRetriever.localUrl(artifact);
+        if ("file".equals(Urls.getProtocol(localUrl))) {
+            helperDuplicateBundle(localUrl);
+        } else {
+            log.warn("Skipping test OsgiStandaloneTest.testDuplicateBundle due to " + artifact + " not available in local repo.");
+        }
+    }
+
+    @Test(groups="Integration")
+    public void testRemoteDuplicateBundle() throws Exception {
+        helperDuplicateBundle(MavenRetriever.hostedUrl(new MavenArtifact("org.apache.brooklyn", "brooklyn-api", "jar", "0.8.0-SNAPSHOT"))); // BROOKLYN_VERSION
+    }
+
+    public void helperDuplicateBundle(String url) throws Exception {
+        //The bundle is already installed from the boot path.
+        //Make sure that we still get the initially loaded
+        //bundle after trying to install the same version.
+        Bundle bundle = install(url);
+        Assert.assertTrue(Osgis.isExtensionBundle(bundle));
+    }
+
+    @Test
+    public void testAMultiplier() throws Exception {
+        Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
+        checkMath(bundle, 3, 6);
+        setAMultiplier(bundle, 5);
+        checkMath(bundle, 3, 15);
+    }
+
+    /** run two multiplier tests to ensure that irrespective of order the tests run in, 
+     * on a fresh install the multiplier is reset */
+    @Test
+    public void testANOtherMultiple() throws Exception {
+        Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
+        checkMath(bundle, 3, 6);
+        setAMultiplier(bundle, 14);
+        checkMath(bundle, 3, 42);
+    }
+
+    @Test
+    public void testGetBundle() throws Exception {
+        Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
+        setAMultiplier(bundle, 3);
+
+        // can look it up based on the same location string (no other "location identifier" reference string seems to work here, however) 
+        Bundle bundle2 = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
+        checkMath(bundle2, 3, 9);
+    }
+
+    @Test
+    public void testUninstallAndReinstallBundle() throws Exception {
+        Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
+        checkMath(bundle, 3, 6);
+        setAMultiplier(bundle, 3);
+        checkMath(bundle, 3, 9);
+        bundle.uninstall();
+        
+        Bundle bundle2 = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
+        checkMath(bundle2, 3, 6);
+    }
+
+    protected void checkMath(Bundle bundle, int input, int output) throws Exception {
+        Assert.assertNotNull(bundle);
+        Class<?> aClass = bundle.loadClass("brooklyn.test.osgi.TestA");
+        Object aInst = aClass.newInstance();
+        Object result = aClass.getMethod("times", int.class).invoke(aInst, input);
+        Assert.assertEquals(result, output);
+    }
+
+    protected void setAMultiplier(Bundle bundle, int newMultiplier) throws Exception {
+        Assert.assertNotNull(bundle);
+        Class<?> aClass = bundle.loadClass("brooklyn.test.osgi.TestA");
+        aClass.getField("multiplier").set(null, newMultiplier);
+    }
+
+    @Test
+    public void testReadAManifest() throws Exception {
+        Enumeration<URL> manifests = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF");
+        log.info("Bundles and exported packages:");
+        MutableSet<String> allPackages = MutableSet.of();
+        while (manifests.hasMoreElements()) {
+            ManifestHelper mf = Osgis.ManifestHelper.forManifestContents(Streams.readFullyString( manifests.nextElement().openStream() ));
+            List<String> mfPackages = mf.getExportedPackages();
+            log.info("  "+mf.getSymbolicNameVersion()+": "+mfPackages);
+            allPackages.addAll(mfPackages);
+        }
+        log.info("Total export package count: "+allPackages.size());
+        Assert.assertTrue(allPackages.size()>20, "did not find enough packages"); // probably much larger
+        Assert.assertTrue(allPackages.contains(Osgis.class.getPackage().getName()));
+    }
+    
+    @Test
+    public void testReadKnownManifest() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+        InputStream in = this.getClass().getResourceAsStream(BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+        JarInputStream jarIn = new JarInputStream(in);
+        ManifestHelper helper = Osgis.ManifestHelper.forManifest(jarIn.getManifest());
+        jarIn.close();
+        Assert.assertEquals(helper.getVersion().toString(), "0.1.0");
+        Assert.assertTrue(helper.getExportedPackages().contains("brooklyn.osgi.tests"));
+    }
+    
+    @Test
+    public void testLoadOsgiBundleDependencies() throws Exception {
+        Bundle bundle = installFromClasspath(BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+        Assert.assertNotNull(bundle);
+        Class<?> aClass = bundle.loadClass("brooklyn.osgi.tests.SimpleApplicationImpl");
+        Object aInst = aClass.newInstance();
+        Assert.assertNotNull(aInst);
+    }
+    
+    @Test
+    public void testLoadAbsoluteWindowsResourceWithInstalledOSGi() {
+        //Felix installs an additional URL to the system classloader
+        //which throws an IllegalArgumentException when passed a
+        //windows path. See ExtensionManager.java static initializer.
+        String context = "mycontext";
+        String dummyPath = "C:\\dummypath";
+        ResourceUtils utils = ResourceUtils.create(this, context);
+        try {
+            utils.getResourceFromUrl(dummyPath);
+            Assert.fail("Non-reachable, should throw an exception for non-existing resource.");
+        } catch (RuntimeException e) {
+            Assert.assertTrue(e.getMessage().startsWith("Error getting resource '"+dummyPath+"' for "+context));
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiTestResources.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiTestResources.java b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiTestResources.java
new file mode 100644
index 0000000..b44da4d
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiTestResources.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.management.osgi;
+
+/**
+ * Many OSGi tests require OSGi bundles (of course). Test bundles have been collected here
+ * for convenience and clarity. Available bundles (on the classpath, with source code
+ * either embedded or in /src/dependencies) are described by the constants in this class.
+ * <p>
+ * Some of these bundles are also used in REST API tests, as that stretches catalog further
+ * (using CAMP) and that is one area where OSGi is heavily used. 
+ */
+public class OsgiTestResources {
+
+
+    /**
+     * brooklyn-osgi-test-a_0.1.0 -
+     * defines TestA which has a "times" method and a static multiplier field;
+     * we set the multiplier to determine when we are sharing versions and when not
+     */
+    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_PATH = "/brooklyn/osgi/brooklyn-osgi-test-a_0.1.0.jar";
+
+    /**
+     * brooklyn-test-osgi-entities (v 0.1.0) -
+     * defines an entity and an application, to confirm it can be read and used by brooklyn
+     */
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = "/brooklyn/osgi/brooklyn-test-osgi-entities.jar";
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_APPLICATION = "brooklyn.osgi.tests.SimpleApplication";
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY = "brooklyn.osgi.tests.SimpleEntity";
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY = "brooklyn.osgi.tests.SimplePolicy";
+
+    /**
+     * brooklyn-test-osgi-more-entities_0.1.0 -
+     * another bundle with a minimal sayHi effector, used to test versioning and dependencies
+     * (this one has no dependencies, but see also {@value #BROOKLYN_TEST_MORE_ENTITIES_V2_PATH})
+     */
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART = "brooklyn-test-osgi-more-entities";
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL = 
+        "org.apache.brooklyn.test.resources.osgi."+BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART;
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_V1_PATH = "/brooklyn/osgi/" + BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART + "_0.1.0.jar";
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY = "brooklyn.osgi.tests.more.MoreEntity";
+    
+    /**
+     * brooklyn-test-osgi-more-entities_0.2.0 -
+     * similar to {@link #BROOKLYN_TEST_MORE_ENTITIES_V1_PATH} but saying "HI NAME" rather than "Hi NAME",
+     * and declaring an explicit dependency on SimplePolicy from {@link #BROOKLYN_TEST_OSGI_ENTITIES_PATH}
+     */
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_PATH = "/brooklyn/osgi/" + BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART + "_0.2.0.jar";
+    
+    /**
+     * bundle with identical metadata (same symbolic name and version -- hence being an evil twin) 
+     * as {@link #BROOKLYN_TEST_MORE_ENTITIES_V2_PATH},
+     * but slightly different behaviour -- saying "HO NAME" -- in order to make sure we can differentiate two two
+     * at runtime.
+     */
+    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH = "/brooklyn/osgi/" + BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART + "_evil-twin_0.2.0.jar";
+    
+}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/base/src/test/java/org/apache/brooklyn/location/basic/WinRmMachineLocationLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/location/basic/WinRmMachineLocationLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/location/basic/WinRmMachineLocationLiveTest.java
index c089531..29eb71d 100644
--- a/software/base/src/test/java/org/apache/brooklyn/location/basic/WinRmMachineLocationLiveTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/location/basic/WinRmMachineLocationLiveTest.java
@@ -20,6 +20,7 @@ package org.apache.brooklyn.location.basic;
 
 import static org.testng.Assert.assertEquals;
 
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.location.jclouds.JcloudsLocation;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
@@ -35,8 +36,8 @@ import org.testng.annotations.Test;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.BrooklynAppLiveTestSupport;
 import brooklyn.entity.basic.Entities;
+
 import org.apache.brooklyn.location.jclouds.JcloudsWinRmMachineLocation;
-import brooklyn.management.internal.ManagementContextInternal;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/database/src/test/java/brooklyn/entity/database/mariadb/MariaDbIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/database/src/test/java/brooklyn/entity/database/mariadb/MariaDbIntegrationTest.java b/software/database/src/test/java/brooklyn/entity/database/mariadb/MariaDbIntegrationTest.java
index 67c8a51..1dc25df 100644
--- a/software/database/src/test/java/brooklyn/entity/database/mariadb/MariaDbIntegrationTest.java
+++ b/software/database/src/test/java/brooklyn/entity/database/mariadb/MariaDbIntegrationTest.java
@@ -23,6 +23,7 @@ import java.net.InetAddress;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,8 +37,9 @@ import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.database.DatastoreMixins.DatastoreCommon;
 import brooklyn.entity.database.VogellaExampleAccess;
+
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-import brooklyn.management.internal.LocalManagementContext;
+
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/database/src/test/java/brooklyn/entity/database/postgresql/PostgreSqlIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/database/src/test/java/brooklyn/entity/database/postgresql/PostgreSqlIntegrationTest.java b/software/database/src/test/java/brooklyn/entity/database/postgresql/PostgreSqlIntegrationTest.java
index 3d6ed4c..7cb5863 100644
--- a/software/database/src/test/java/brooklyn/entity/database/postgresql/PostgreSqlIntegrationTest.java
+++ b/software/database/src/test/java/brooklyn/entity/database/postgresql/PostgreSqlIntegrationTest.java
@@ -20,6 +20,7 @@ package brooklyn.entity.database.postgresql;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,8 +33,8 @@ import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.database.DatastoreMixins.DatastoreCommon;
 import brooklyn.entity.database.VogellaExampleAccess;
+
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-import brooklyn.management.internal.LocalManagementContext;
 
 import com.google.common.collect.ImmutableList;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/database/src/test/java/brooklyn/entity/database/rubyrep/RubyRepIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/database/src/test/java/brooklyn/entity/database/rubyrep/RubyRepIntegrationTest.java b/software/database/src/test/java/brooklyn/entity/database/rubyrep/RubyRepIntegrationTest.java
index e56b990..7028d1b 100644
--- a/software/database/src/test/java/brooklyn/entity/database/rubyrep/RubyRepIntegrationTest.java
+++ b/software/database/src/test/java/brooklyn/entity/database/rubyrep/RubyRepIntegrationTest.java
@@ -26,6 +26,7 @@ import java.util.List;
 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.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -46,8 +47,6 @@ import brooklyn.entity.database.postgresql.PostgreSqlNode;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.PortRanges;
 
-import brooklyn.management.internal.LocalManagementContext;
-
 public class RubyRepIntegrationTest {
 
     public static final Logger log = LoggerFactory.getLogger(RubyRepIntegrationTest.class);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 a2317ec..758ace7 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
@@ -30,6 +30,7 @@ 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.test.EntityTestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -51,7 +52,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.messaging.storm.topologies.ExclamationBolt;
 import brooklyn.entity.trait.Startable;
 import brooklyn.entity.zookeeper.ZooKeeperEnsemble;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.file.ArchiveBuilder;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
index e0053b6..8535876 100644
--- a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
+++ b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
@@ -25,9 +25,10 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Entities;
+
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.location.basic.BasicOsDetails;
 import org.apache.brooklyn.location.basic.BasicOsDetails.OsArchs;
-import brooklyn.management.internal.LocalManagementContext;
 
 public class CouchbaseOfflineTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java
index 01ad82f..be7ef17 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java
@@ -32,6 +32,7 @@ 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.management.internal.LocalManagementContext;
 import org.apache.brooklyn.entity.dns.AbstractGeoDnsService;
 import org.apache.brooklyn.entity.dns.AbstractGeoDnsServiceImpl;
 import org.apache.brooklyn.entity.dns.AbstractGeoDnsServiceTest;
@@ -63,7 +64,6 @@ import org.apache.brooklyn.location.basic.SimulatedLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 import org.apache.brooklyn.location.geo.HostGeoInfo;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.CollectionFunctionals;
 import brooklyn.util.exceptions.Exceptions;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/UrlMappingTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/UrlMappingTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/UrlMappingTest.java
index cfe99c3..2e894ea 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/UrlMappingTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/UrlMappingTest.java
@@ -28,6 +28,7 @@ import javax.annotation.Nullable;
 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.management.internal.LocalManagementContext;
 import org.apache.brooklyn.entity.proxy.nginx.UrlMapping;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
@@ -48,7 +49,6 @@ import brooklyn.entity.rebind.RebindTestUtils;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 a96a268..f18b414 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
@@ -30,6 +30,7 @@ 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.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.entity.proxy.nginx.NginxController;
 import org.apache.brooklyn.entity.webapp.tomcat.TomcatServer;
 import org.apache.brooklyn.test.EntityTestUtils;
@@ -55,7 +56,6 @@ import brooklyn.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocationReuseIntegrationTest.RecordingSshjTool;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.net.Networking;
 import brooklyn.util.repeat.Repeater;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jboss/ControlledDynamicWebAppClusterRebindIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jboss/ControlledDynamicWebAppClusterRebindIntegrationTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jboss/ControlledDynamicWebAppClusterRebindIntegrationTest.java
index fc3b0b4..588a890 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jboss/ControlledDynamicWebAppClusterRebindIntegrationTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jboss/ControlledDynamicWebAppClusterRebindIntegrationTest.java
@@ -31,6 +31,7 @@ import java.util.concurrent.Executors;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.entity.proxy.nginx.NginxController;
 import org.apache.brooklyn.entity.webapp.ControlledDynamicWebAppCluster;
 import org.apache.brooklyn.test.TestResourceUnavailableException;
@@ -46,8 +47,9 @@ import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.entity.rebind.RebindTestUtils;
+
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-import brooklyn.management.internal.LocalManagementContext;
+
 import brooklyn.util.internal.TimeExtras;
 
 import com.google.common.base.Predicates;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jboss/DynamicWebAppClusterRebindIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jboss/DynamicWebAppClusterRebindIntegrationTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jboss/DynamicWebAppClusterRebindIntegrationTest.java
index 8a3ab2e3..aabac25 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jboss/DynamicWebAppClusterRebindIntegrationTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jboss/DynamicWebAppClusterRebindIntegrationTest.java
@@ -31,6 +31,7 @@ import java.util.concurrent.Executors;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.entity.webapp.DynamicWebAppCluster;
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 import org.apache.brooklyn.test.WebAppMonitor;
@@ -44,8 +45,8 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.rebind.RebindTestUtils;
+
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-import brooklyn.management.internal.LocalManagementContext;
 
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/storage/hazelcast/src/main/java/org/apache/brooklyn/internal/storage/impl/hazelcast/HazelcastDataGrid.java
----------------------------------------------------------------------
diff --git a/storage/hazelcast/src/main/java/org/apache/brooklyn/internal/storage/impl/hazelcast/HazelcastDataGrid.java b/storage/hazelcast/src/main/java/org/apache/brooklyn/internal/storage/impl/hazelcast/HazelcastDataGrid.java
index 05d26c0..0b4dfd8 100644
--- a/storage/hazelcast/src/main/java/org/apache/brooklyn/internal/storage/impl/hazelcast/HazelcastDataGrid.java
+++ b/storage/hazelcast/src/main/java/org/apache/brooklyn/internal/storage/impl/hazelcast/HazelcastDataGrid.java
@@ -24,9 +24,9 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 
 import brooklyn.internal.storage.DataGrid;
-import brooklyn.management.internal.ManagementContextInternal;
 
 import com.google.common.collect.ImmutableMap;
 import com.hazelcast.config.Config;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/storage/hazelcast/src/main/java/org/apache/brooklyn/internal/storage/impl/hazelcast/HazelcastDataGridFactory.java
----------------------------------------------------------------------
diff --git a/storage/hazelcast/src/main/java/org/apache/brooklyn/internal/storage/impl/hazelcast/HazelcastDataGridFactory.java b/storage/hazelcast/src/main/java/org/apache/brooklyn/internal/storage/impl/hazelcast/HazelcastDataGridFactory.java
index 004bd16..c723f67 100644
--- a/storage/hazelcast/src/main/java/org/apache/brooklyn/internal/storage/impl/hazelcast/HazelcastDataGridFactory.java
+++ b/storage/hazelcast/src/main/java/org/apache/brooklyn/internal/storage/impl/hazelcast/HazelcastDataGridFactory.java
@@ -18,9 +18,10 @@
  */
 package org.apache.brooklyn.internal.storage.impl.hazelcast;
 
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+
 import brooklyn.internal.storage.DataGrid;
 import brooklyn.internal.storage.DataGridFactory;
-import brooklyn.management.internal.ManagementContextInternal;
 
 import com.hazelcast.core.HazelcastInstance;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/storage/hazelcast/src/test/java/org/apache/brooklyn/storage/impl/hazelcast/HazelcastStorageTest.java
----------------------------------------------------------------------
diff --git a/storage/hazelcast/src/test/java/org/apache/brooklyn/storage/impl/hazelcast/HazelcastStorageTest.java b/storage/hazelcast/src/test/java/org/apache/brooklyn/storage/impl/hazelcast/HazelcastStorageTest.java
index 426abd5..464117e 100644
--- a/storage/hazelcast/src/test/java/org/apache/brooklyn/storage/impl/hazelcast/HazelcastStorageTest.java
+++ b/storage/hazelcast/src/test/java/org/apache/brooklyn/storage/impl/hazelcast/HazelcastStorageTest.java
@@ -33,9 +33,11 @@ import brooklyn.internal.storage.BrooklynStorage;
 import brooklyn.internal.storage.DataGridFactory;
 import brooklyn.internal.storage.Reference;
 import brooklyn.internal.storage.impl.BrooklynStorageImpl;
+
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.internal.storage.impl.hazelcast.HazelcastDataGrid;
 import org.apache.brooklyn.internal.storage.impl.hazelcast.HazelcastDataGridFactory;
-import brooklyn.management.internal.LocalManagementContext;
+
 import brooklyn.util.collections.MutableList;
 
 import com.hazelcast.core.Hazelcast;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 d70392d..471d5e3 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
@@ -16,7 +16,7 @@ import brooklyn.entity.basic.StartableApplication;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.entity.webapp.JavaWebAppService;
 import org.apache.brooklyn.api.management.ManagementContext;
-import brooklyn.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.text.Strings;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/archetypes/quickstart/src/brooklyn-sample/src/test/java/com/acme/sample/brooklyn/sample/app/SampleUnitTest.java
----------------------------------------------------------------------
diff --git a/usage/archetypes/quickstart/src/brooklyn-sample/src/test/java/com/acme/sample/brooklyn/sample/app/SampleUnitTest.java b/usage/archetypes/quickstart/src/brooklyn-sample/src/test/java/com/acme/sample/brooklyn/sample/app/SampleUnitTest.java
index 9cc42ae..df15e0a 100644
--- a/usage/archetypes/quickstart/src/brooklyn-sample/src/test/java/com/acme/sample/brooklyn/sample/app/SampleUnitTest.java
+++ b/usage/archetypes/quickstart/src/brooklyn-sample/src/test/java/com/acme/sample/brooklyn/sample/app/SampleUnitTest.java
@@ -16,7 +16,7 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.entity.webapp.JavaWebAppService;
 import org.apache.brooklyn.entity.webapp.WebAppService;
 import org.apache.brooklyn.api.management.ManagementContext;
-import brooklyn.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 
 import com.google.common.base.Predicates;
 import com.google.common.collect.Iterables;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampPlatformLauncherAbstract.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampPlatformLauncherAbstract.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampPlatformLauncherAbstract.java
index 5167481..7adf958 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampPlatformLauncherAbstract.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampPlatformLauncherAbstract.java
@@ -19,9 +19,9 @@
 package org.apache.brooklyn.camp.brooklyn;
 
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 
 import io.brooklyn.camp.spi.PlatformRootSummary;
-import brooklyn.management.internal.LocalManagementContext;
 
 import com.google.common.annotations.Beta;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 fe22e89..7def6cf 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
@@ -47,13 +47,13 @@ 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.camp.brooklyn.BrooklynCampConstants;
+import org.apache.brooklyn.core.management.internal.EntityManagementUtils;
+import org.apache.brooklyn.core.management.internal.EntityManagementUtils.CreationResult;
 
 import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.basic.BasicApplicationImpl;
-import brooklyn.management.internal.EntityManagementUtils;
-import brooklyn.management.internal.EntityManagementUtils.CreationResult;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.flags.TypeCoercions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 d17740d..57d53da 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
@@ -46,6 +46,9 @@ import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.service.BrooklynServiceTypeResolver;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.service.ServiceTypeResolver;
+import org.apache.brooklyn.core.management.ManagementContextInjectable;
+import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.ConfigKey;
@@ -54,9 +57,6 @@ import brooklyn.entity.basic.BrooklynTags;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.proxying.InternalEntityFactory;
-import brooklyn.management.ManagementContextInjectable;
-import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableSet;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
index 94dd3d0..be18cc2 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
@@ -31,11 +31,11 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
+import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.catalog.internal.BasicBrooklynCatalog;
-import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.net.Urls;
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 086ddaf..601a46a 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
@@ -28,6 +28,7 @@ import org.apache.brooklyn.api.event.Sensor;
 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 brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.ConfigKeys;
@@ -36,7 +37,6 @@ import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.basic.EntityPredicates;
 import brooklyn.event.basic.DependentConfiguration;
 import brooklyn.event.basic.Sensors;
-import brooklyn.management.internal.EntityManagerInternal;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.task.TaskBuilder;
 import brooklyn.util.task.Tasks;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ApplicationsYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ApplicationsYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ApplicationsYamlTest.java
index 9d9e340..ddd54bf 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ApplicationsYamlTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ApplicationsYamlTest.java
@@ -24,13 +24,13 @@ import static org.testng.Assert.assertTrue;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
+import org.apache.brooklyn.core.management.internal.EntityManagementUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.BasicApplication;
 import brooklyn.entity.basic.BasicEntity;
-import brooklyn.management.internal.EntityManagementUtils;
 
 import com.google.common.collect.Iterables;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 36ce910..d2ada07 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
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatform;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
@@ -41,7 +42,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.StartableApplication;
 import brooklyn.entity.rebind.RebindOptions;
 import brooklyn.entity.rebind.RebindTestFixture;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.config.ConfigBag;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 a11bff6..df4b41b 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
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatform;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -39,7 +40,6 @@ import org.testng.annotations.BeforeMethod;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.config.ConfigBag;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/BrooklynYamlTypeInstantiatorTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/BrooklynYamlTypeInstantiatorTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/BrooklynYamlTypeInstantiatorTest.java
index d984db2..107b0b9 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/BrooklynYamlTypeInstantiatorTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/BrooklynYamlTypeInstantiatorTest.java
@@ -23,10 +23,10 @@ import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator.Factory;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator.InstantiatorFromKey;
+import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
 import brooklyn.policy.ha.ServiceRestarter;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.javalang.JavaClassNames;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 5e51b0e..87d014b 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
@@ -26,6 +26,7 @@ import org.apache.brooklyn.api.entity.Application;
 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.test.entity.TestEntity;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,7 +41,6 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.rebind.RebindTestUtils;
 import brooklyn.event.basic.Sensors;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.task.Tasks;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 6c803e1..06f596a 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
@@ -39,6 +39,7 @@ import org.apache.brooklyn.api.management.Task;
 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.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityImpl;
 import org.slf4j.Logger;
@@ -60,7 +61,6 @@ import brooklyn.entity.basic.SameServerEntity;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.event.basic.Sensors;
-import brooklyn.management.internal.EntityManagerInternal;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Functionals;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 343dd8f..e558288 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
@@ -28,6 +28,7 @@ import java.util.Set;
 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -37,7 +38,6 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.rebind.RebindTestUtils;
-import brooklyn.management.internal.LocalManagementContext;
 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/6602f694/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 a1b2ae0..ba29051 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
@@ -25,6 +25,7 @@ import org.apache.brooklyn.api.entity.Entity;
 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.entity.proxy.ProxySslConfig;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
@@ -36,7 +37,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.ManagementContextInjectable;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.flags.TypeCoercions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ReferencedYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ReferencedYamlTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ReferencedYamlTest.java
index 1c91cf1..535ac24 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ReferencedYamlTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ReferencedYamlTest.java
@@ -21,13 +21,13 @@ package org.apache.brooklyn.camp.brooklyn;
 import java.util.Collection;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.BasicApplication;
 import brooklyn.entity.basic.BasicEntity;
-import brooklyn.management.osgi.OsgiStandaloneTest;
 
 import com.google.common.collect.Iterables;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 7332af7..65bb7e0 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
@@ -20,6 +20,8 @@ package org.apache.brooklyn.camp.brooklyn.catalog;
 
 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.test.TestResourceUnavailableException;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 
@@ -31,8 +33,6 @@ import java.io.StringReader;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.osgi.OsgiTestResources;
 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/6602f694/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 0f326ad..1bf481d 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
@@ -37,9 +37,9 @@ import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityMatcher;
+import org.apache.brooklyn.core.management.osgi.OsgiVersionMoreEntityTest;
 
 import brooklyn.catalog.internal.CatalogUtils;
-import brooklyn.management.osgi.OsgiVersionMoreEntityTest;
 
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 111b180..c4579ff 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
@@ -32,11 +32,11 @@ import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
+import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
+import org.apache.brooklyn.core.management.osgi.OsgiTestResources;
 
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.BasicEntity;
-import brooklyn.management.osgi.OsgiStandaloneTest;
-import brooklyn.management.osgi.OsgiTestResources;
 
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 import org.apache.brooklyn.test.entity.TestEntity;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
index 7160657..70076f9 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
@@ -33,14 +33,12 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationDefinition;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
+import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
 
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.event.basic.BasicConfigKey;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-
-import brooklyn.management.osgi.OsgiStandaloneTest;
-
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 
 import brooklyn.util.text.StringFunctions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
index 10c60fb..6a44df3 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
@@ -25,10 +25,10 @@ import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
+import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
 
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.management.osgi.OsgiStandaloneTest;
 
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
index 2de6366..3a21fac 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
@@ -27,10 +27,8 @@ import org.testng.annotations.Test;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
-
-import brooklyn.management.osgi.OsgiStandaloneTest;
-import brooklyn.management.osgi.OsgiTestResources;
-
+import org.apache.brooklyn.core.management.osgi.OsgiStandaloneTest;
+import org.apache.brooklyn.core.management.osgi.OsgiTestResources;
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/cli/src/main/java/org/apache/brooklyn/cli/CloudExplorer.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/org/apache/brooklyn/cli/CloudExplorer.java b/usage/cli/src/main/java/org/apache/brooklyn/cli/CloudExplorer.java
index 32d2cec..32111e9 100644
--- a/usage/cli/src/main/java/org/apache/brooklyn/cli/CloudExplorer.java
+++ b/usage/cli/src/main/java/org/apache/brooklyn/cli/CloudExplorer.java
@@ -40,12 +40,12 @@ import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.options.TemplateOptions;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 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.management.internal.LocalManagementContext;
 import brooklyn.util.exceptions.FatalConfigurationRuntimeException;
 import brooklyn.util.stream.Streams;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 6162ad1..09cba24 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
@@ -79,6 +79,7 @@ import org.apache.brooklyn.cli.CloudExplorer.ComputeListImagesCommand;
 import org.apache.brooklyn.cli.CloudExplorer.ComputeListInstancesCommand;
 import org.apache.brooklyn.cli.CloudExplorer.ComputeTerminateInstancesCommand;
 import org.apache.brooklyn.cli.ItemLister.ListAllCommand;
+import org.apache.brooklyn.core.management.ha.OsgiManager;
 
 import brooklyn.entity.basic.AbstractApplication;
 import brooklyn.entity.basic.AbstractEntity;
@@ -93,9 +94,6 @@ import brooklyn.entity.trait.Startable;
 import org.apache.brooklyn.launcher.BrooklynLauncher;
 import org.apache.brooklyn.launcher.BrooklynServerDetails;
 import org.apache.brooklyn.launcher.config.StopWhichAppsOnShutdown;
-
-import brooklyn.management.ha.OsgiManager;
-
 import org.apache.brooklyn.rest.security.PasswordHasher;
 import org.apache.brooklyn.rest.util.ShutdownHandler;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynLauncher.java b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynLauncher.java
index b1efe6f..8267ca3 100644
--- a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynLauncher.java
+++ b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/BrooklynLauncher.java
@@ -49,6 +49,10 @@ import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
 import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynAssemblyTemplateInstantiator;
+import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl;
+import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -85,12 +89,6 @@ import brooklyn.entity.trait.Startable;
 import org.apache.brooklyn.launcher.config.StopWhichAppsOnShutdown;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation.LocalhostMachine;
 import org.apache.brooklyn.location.basic.PortRanges;
-
-import brooklyn.management.ha.HighAvailabilityManagerImpl;
-import brooklyn.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
-
 import org.apache.brooklyn.rest.BrooklynWebConfig;
 import org.apache.brooklyn.rest.filter.BrooklynPropertiesSecurityFilter;
 import org.apache.brooklyn.rest.security.provider.BrooklynUserWithRandomPasswordSecurityProvider;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 2b6c831..c9bf366 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
@@ -67,12 +67,10 @@ import brooklyn.internal.BrooklynInitialization;
 
 import org.apache.brooklyn.api.location.PortRange;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.launcher.config.CustomResourceLocator;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.PortRanges;
-
-import brooklyn.management.internal.ManagementContextInternal;
-
 import org.apache.brooklyn.rest.BrooklynRestApi;
 import org.apache.brooklyn.rest.BrooklynWebConfig;
 import org.apache.brooklyn.rest.filter.BrooklynPropertiesSecurityFilter;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/launcher/src/main/java/org/apache/brooklyn/launcher/camp/BrooklynCampPlatformLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/camp/BrooklynCampPlatformLauncher.java b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/camp/BrooklynCampPlatformLauncher.java
index fa9e722..461065e 100644
--- a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/camp/BrooklynCampPlatformLauncher.java
+++ b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/camp/BrooklynCampPlatformLauncher.java
@@ -23,12 +23,10 @@ import io.brooklyn.camp.spi.PlatformRootSummary;
 import brooklyn.entity.basic.BrooklynShutdownHooks;
 
 import org.apache.brooklyn.launcher.BrooklynLauncher;
-
-import brooklyn.management.internal.LocalManagementContext;
-
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatform;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherAbstract;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 
 import com.google.common.annotations.Beta;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/launcher/src/main/java/org/apache/brooklyn/launcher/config/BrooklynGlobalConfig.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/config/BrooklynGlobalConfig.java b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/config/BrooklynGlobalConfig.java
index 55ed04a..b65e3dd 100644
--- a/usage/launcher/src/main/java/org/apache/brooklyn/launcher/config/BrooklynGlobalConfig.java
+++ b/usage/launcher/src/main/java/org/apache/brooklyn/launcher/config/BrooklynGlobalConfig.java
@@ -21,9 +21,11 @@ package org.apache.brooklyn.launcher.config;
 import brooklyn.config.BrooklynServiceAttributes;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynConfigKeys;
+
+import org.apache.brooklyn.core.management.internal.BrooklynGarbageCollector;
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
-import brooklyn.management.internal.BrooklynGarbageCollector;
 import org.apache.brooklyn.rest.BrooklynWebConfig;
+
 import brooklyn.util.internal.BrooklynSystemProperties;
 import brooklyn.util.internal.StringSystemProperty;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
index e562399..1806aa9 100644
--- a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
@@ -23,6 +23,7 @@ import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.launcher.BrooklynLauncher;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
@@ -43,7 +44,6 @@ import org.testng.annotations.Test;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.rebind.RebindTestUtils;
 import brooklyn.entity.rebind.persister.PersistMode;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.test.Asserts;
 import brooklyn.util.os.Os;
 import brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java
index 9f11563..b0796db 100644
--- a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/BrooklynLauncherTest.java
@@ -22,6 +22,8 @@ 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.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.launcher.BrooklynLauncher;
 
 import static org.testng.Assert.assertEquals;
@@ -55,8 +57,6 @@ import brooklyn.entity.rebind.RebindTestUtils;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.exceptions.FatalRuntimeException;
 import brooklyn.util.io.FileUtil;
 import brooklyn.util.net.Urls;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 db4f5ff..8ee48f4 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
@@ -18,7 +18,9 @@
  */
 package org.apache.brooklyn.launcher;
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.launcher.BrooklynWebServer;
+
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
@@ -47,7 +49,7 @@ import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
+
 import org.apache.brooklyn.rest.BrooklynWebConfig;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/launcher/src/test/java/org/apache/brooklyn/launcher/WebAppRunnerTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/WebAppRunnerTest.java b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/WebAppRunnerTest.java
index 2c50716..d145906 100644
--- a/usage/launcher/src/test/java/org/apache/brooklyn/launcher/WebAppRunnerTest.java
+++ b/usage/launcher/src/test/java/org/apache/brooklyn/launcher/WebAppRunnerTest.java
@@ -18,9 +18,12 @@
  */
 package org.apache.brooklyn.launcher;
 
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.launcher.BrooklynWebServer;
 import org.apache.brooklyn.launcher.BrooklynLauncher;
 import org.apache.brooklyn.launcher.BrooklynServerDetails;
+
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.fail;
 
@@ -36,8 +39,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.net.Networking;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 f8ea8fb..4fcc63d 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
@@ -29,6 +29,7 @@ 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.camp.brooklyn.BrooklynCampPlatformLauncherAbstract;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -47,7 +48,6 @@ import org.apache.brooklyn.launcher.BrooklynLauncher;
 import org.apache.brooklyn.launcher.SimpleYamlLauncherForTests;
 import org.apache.brooklyn.launcher.camp.BrooklynCampPlatformLauncher;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.test.Asserts;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.os.Os;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/qa/src/test/java/org/apache/brooklyn/qa/load/LoadTest.java
----------------------------------------------------------------------
diff --git a/usage/qa/src/test/java/org/apache/brooklyn/qa/load/LoadTest.java b/usage/qa/src/test/java/org/apache/brooklyn/qa/load/LoadTest.java
index d692790..4b5b1b1 100644
--- a/usage/qa/src/test/java/org/apache/brooklyn/qa/load/LoadTest.java
+++ b/usage/qa/src/test/java/org/apache/brooklyn/qa/load/LoadTest.java
@@ -45,9 +45,9 @@ 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.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.launcher.BrooklynLauncher;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.os.Os;
 import brooklyn.util.time.Duration;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-client/src/test/java/org/apache/brooklyn/rest/client/ApplicationResourceIntegrationTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-client/src/test/java/org/apache/brooklyn/rest/client/ApplicationResourceIntegrationTest.java b/usage/rest-client/src/test/java/org/apache/brooklyn/rest/client/ApplicationResourceIntegrationTest.java
index be3a690..11847dd 100644
--- a/usage/rest-client/src/test/java/org/apache/brooklyn/rest/client/ApplicationResourceIntegrationTest.java
+++ b/usage/rest-client/src/test/java/org/apache/brooklyn/rest/client/ApplicationResourceIntegrationTest.java
@@ -36,11 +36,11 @@ import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.basic.StartableApplication;
-import org.apache.brooklyn.location.basic.BasicLocationRegistry;
-import brooklyn.management.internal.LocalManagementContext;
 
+import org.apache.brooklyn.location.basic.BasicLocationRegistry;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.rest.BrooklynRestApiLauncher;
 import org.apache.brooklyn.rest.BrooklynRestApiLauncherTest;
 import org.apache.brooklyn.rest.domain.ApplicationSpec;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-client/src/test/java/org/apache/brooklyn/rest/client/BrooklynApiRestClientTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-client/src/test/java/org/apache/brooklyn/rest/client/BrooklynApiRestClientTest.java b/usage/rest-client/src/test/java/org/apache/brooklyn/rest/client/BrooklynApiRestClientTest.java
index 9c5c20e..03bb6ea 100644
--- a/usage/rest-client/src/test/java/org/apache/brooklyn/rest/client/BrooklynApiRestClientTest.java
+++ b/usage/rest-client/src/test/java/org/apache/brooklyn/rest/client/BrooklynApiRestClientTest.java
@@ -35,11 +35,11 @@ import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.StartableApplication;
-import org.apache.brooklyn.location.basic.BasicLocationRegistry;
-import brooklyn.management.internal.LocalManagementContext;
 
+import org.apache.brooklyn.location.basic.BasicLocationRegistry;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.rest.BrooklynRestApiLauncher;
 import org.apache.brooklyn.rest.BrooklynRestApiLauncherTest;
 import org.apache.brooklyn.rest.domain.ApplicationSummary;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/main/java/org/apache/brooklyn/rest/filter/BrooklynPropertiesSecurityFilter.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/filter/BrooklynPropertiesSecurityFilter.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/filter/BrooklynPropertiesSecurityFilter.java
index 0094562..5f47438 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/filter/BrooklynPropertiesSecurityFilter.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/filter/BrooklynPropertiesSecurityFilter.java
@@ -35,10 +35,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.BrooklynServiceAttributes;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.entitlement.WebEntitlementContext;
 
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.management.entitlement.WebEntitlementContext;
 import org.apache.brooklyn.rest.security.provider.DelegatingSecurityProvider;
 
 import brooklyn.util.text.Strings;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 4cecb94..9313987 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
@@ -29,11 +29,11 @@ import org.codehaus.jackson.map.ObjectMapper;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.BrooklynServiceAttributes;
 import brooklyn.config.render.RendererHints;
-import brooklyn.management.ManagementContextInjectable;
 
 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.rest.util.BrooklynRestResourceUtils;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AccessResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AccessResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AccessResource.java
index 73a3d14..22f2765 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AccessResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/AccessResource.java
@@ -20,8 +20,8 @@ package org.apache.brooklyn.rest.resources;
 
 import javax.ws.rs.core.Response;
 
-import brooklyn.management.internal.AccessManager;
-import brooklyn.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.management.internal.AccessManager;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.rest.api.AccessApi;
 import org.apache.brooklyn.rest.domain.AccessSummary;
 import org.apache.brooklyn.rest.transform.AccessTransformer;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 9849ed2..8a54607 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
@@ -52,12 +52,6 @@ import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.basic.Sensors;
-import brooklyn.management.entitlement.EntitlementPredicates;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.entitlement.Entitlements.EntityAndItem;
-import brooklyn.management.entitlement.Entitlements.StringAndArgument;
-import brooklyn.management.internal.EntityManagementUtils;
-import brooklyn.management.internal.EntityManagementUtils.CreationResult;
 
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
@@ -66,6 +60,12 @@ import org.apache.brooklyn.api.event.AttributeSensor;
 import org.apache.brooklyn.api.event.Sensor;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.management.entitlement.EntitlementPredicates;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+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.EntityManagementUtils;
+import org.apache.brooklyn.core.management.internal.EntityManagementUtils.CreationResult;
 import org.apache.brooklyn.rest.api.ApplicationApi;
 import org.apache.brooklyn.rest.domain.ApplicationSpec;
 import org.apache.brooklyn.rest.domain.ApplicationSummary;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 218d729..e841b27 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
@@ -44,14 +44,14 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.management.entitlement.Entitlements.StringAndArgument;
 
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.catalog.internal.BasicBrooklynCatalog;
 import brooklyn.catalog.internal.CatalogDto;
 import brooklyn.catalog.internal.CatalogItemComparator;
 import brooklyn.catalog.internal.CatalogUtils;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.entitlement.Entitlements.StringAndArgument;
 
 import org.apache.brooklyn.rest.api.CatalogApi;
 import org.apache.brooklyn.rest.domain.ApiError;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EffectorResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EffectorResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EffectorResource.java
index ebc1276..1d6e961 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EffectorResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EffectorResource.java
@@ -33,13 +33,12 @@ import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.FluentIterable;
 
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.entitlement.Entitlements.StringAndArgument;
-import brooklyn.management.internal.EffectorUtils;
-
 import org.apache.brooklyn.api.entity.Effector;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.management.entitlement.Entitlements.StringAndArgument;
+import org.apache.brooklyn.core.management.internal.EffectorUtils;
 import org.apache.brooklyn.rest.api.EffectorApi;
 import org.apache.brooklyn.rest.domain.EffectorSummary;
 import org.apache.brooklyn.rest.domain.SummaryComparators;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 98917cd..5353f50 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
@@ -30,10 +30,10 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.management.entitlement.Entitlements;
 
 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.rest.api.EntityConfigApi;
 import org.apache.brooklyn.rest.domain.EntityConfigSummary;
 import org.apache.brooklyn.rest.filter.HaHotStateRequired;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 ec730a9..a50a86e 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
@@ -41,15 +41,15 @@ import org.slf4j.LoggerFactory;
 import brooklyn.entity.basic.BrooklynTags;
 import brooklyn.entity.basic.BrooklynTags.NamedStringTag;
 import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.management.entitlement.EntitlementPredicates;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.internal.EntityManagementUtils;
-import brooklyn.management.internal.EntityManagementUtils.CreationResult;
 
 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.Task;
+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.rest.api.EntityApi;
 import org.apache.brooklyn.rest.domain.EntitySummary;
 import org.apache.brooklyn.rest.domain.LocationSummary;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 b4aa5ce..655c20c 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
@@ -25,11 +25,11 @@ import javax.ws.rs.core.Response;
 
 import brooklyn.basic.BrooklynObjectInternal;
 import brooklyn.config.ConfigKey;
-import brooklyn.management.entitlement.Entitlements;
 
 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.rest.api.PolicyConfigApi;
 import org.apache.brooklyn.rest.domain.PolicyConfigSummary;
 import org.apache.brooklyn.rest.filter.HaHotStateRequired;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 e6c73db..e8b85ef 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
@@ -29,11 +29,11 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.event.basic.BasicAttributeSensor;
-import brooklyn.management.entitlement.Entitlements;
 
 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.rest.api.SensorApi;
 import org.apache.brooklyn.rest.domain.SensorSummary;
 import org.apache.brooklyn.rest.filter.HaHotStateRequired;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 8edc1d5..9c48ddf 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
@@ -50,8 +50,6 @@ import brooklyn.entity.basic.StartableApplication;
 import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils;
 import brooklyn.entity.rebind.persister.FileBasedObjectStore;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.internal.ManagementContextInternal;
 
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.basic.EntityLocal;
@@ -63,6 +61,8 @@ import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
 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.rest.api.ServerApi;
 import org.apache.brooklyn.rest.domain.BrooklynFeatureSummary;
 import org.apache.brooklyn.rest.domain.HighAvailabilitySummary;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/UsageResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/UsageResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/UsageResource.java
index 936de70..bf83a74 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/UsageResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/UsageResource.java
@@ -31,14 +31,16 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.Lifecycle;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.management.usage.ApplicationUsage;
-import brooklyn.management.usage.ApplicationUsage.ApplicationEvent;
-import brooklyn.management.usage.LocationUsage;
+
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.management.usage.ApplicationUsage;
+import org.apache.brooklyn.core.management.usage.LocationUsage;
+import org.apache.brooklyn.core.management.usage.ApplicationUsage.ApplicationEvent;
 import org.apache.brooklyn.rest.api.UsageApi;
 import org.apache.brooklyn.rest.domain.UsageStatistic;
 import org.apache.brooklyn.rest.domain.UsageStatistics;
 import org.apache.brooklyn.rest.transform.ApplicationTransformer;
+
 import brooklyn.util.exceptions.UserFacingException;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Time;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/AccessTransformer.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/AccessTransformer.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/AccessTransformer.java
index 4a0807f..31b0389 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/AccessTransformer.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/transform/AccessTransformer.java
@@ -20,7 +20,7 @@ package org.apache.brooklyn.rest.transform;
 
 import java.net.URI;
 
-import brooklyn.management.internal.AccessManager;
+import org.apache.brooklyn.core.management.internal.AccessManager;
 import org.apache.brooklyn.rest.domain.AccessSummary;
 
 import com.google.common.collect.ImmutableMap;



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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/drivers/downloads/DownloadProducerFromPropertiesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/drivers/downloads/DownloadProducerFromPropertiesTest.java b/core/src/test/java/brooklyn/entity/drivers/downloads/DownloadProducerFromPropertiesTest.java
index a8d6e31..8fa0f53 100644
--- a/core/src/test/java/brooklyn/entity/drivers/downloads/DownloadProducerFromPropertiesTest.java
+++ b/core/src/test/java/brooklyn/entity/drivers/downloads/DownloadProducerFromPropertiesTest.java
@@ -26,6 +26,7 @@ import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolverManager.
 import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolverManager.DownloadTargets;
 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.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.testng.annotations.AfterMethod;
@@ -39,8 +40,6 @@ import brooklyn.entity.basic.Entities;
 
 import org.apache.brooklyn.location.basic.SimulatedLocation;
 
-import brooklyn.management.internal.LocalManagementContext;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 ce3dffc..fa963c3 100644
--- a/core/src/test/java/brooklyn/entity/effector/EffectorBasicTest.java
+++ b/core/src/test/java/brooklyn/entity/effector/EffectorBasicTest.java
@@ -25,6 +25,7 @@ import org.apache.brooklyn.api.entity.basic.EntityLocal;
 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.test.TestUtils;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.slf4j.Logger;
@@ -37,8 +38,9 @@ import brooklyn.entity.BrooklynAppUnitTestSupport;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.trait.FailingEntity;
 import brooklyn.entity.trait.Startable;
+
 import org.apache.brooklyn.location.basic.SimulatedLocation;
-import brooklyn.management.internal.ManagementContextInternal;
+
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.task.Tasks;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/proxying/ApplicationBuilderOverridingTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/proxying/ApplicationBuilderOverridingTest.java b/core/src/test/java/brooklyn/entity/proxying/ApplicationBuilderOverridingTest.java
index 3d3573e..f8f6302 100644
--- a/core/src/test/java/brooklyn/entity/proxying/ApplicationBuilderOverridingTest.java
+++ b/core/src/test/java/brooklyn/entity/proxying/ApplicationBuilderOverridingTest.java
@@ -35,6 +35,7 @@ import org.apache.brooklyn.api.entity.Application;
 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.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -48,7 +49,6 @@ import brooklyn.entity.basic.BasicApplication;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.basic.StartableApplication;
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.exceptions.Exceptions;
 
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/proxying/EntityProxyTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/proxying/EntityProxyTest.java b/core/src/test/java/brooklyn/entity/proxying/EntityProxyTest.java
index fea7058..3ee866c 100644
--- a/core/src/test/java/brooklyn/entity/proxying/EntityProxyTest.java
+++ b/core/src/test/java/brooklyn/entity/proxying/EntityProxyTest.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.EntityManager;
 import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -40,7 +41,6 @@ import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.StartableApplication;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.collections.MutableMap;
 
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/proxying/InternalEntityFactoryTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/proxying/InternalEntityFactoryTest.java b/core/src/test/java/brooklyn/entity/proxying/InternalEntityFactoryTest.java
index 004d02d..8f0d3ec 100644
--- a/core/src/test/java/brooklyn/entity/proxying/InternalEntityFactoryTest.java
+++ b/core/src/test/java/brooklyn/entity/proxying/InternalEntityFactoryTest.java
@@ -26,6 +26,7 @@ import static org.testng.Assert.assertTrue;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestApplicationImpl;
@@ -37,7 +38,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.entity.basic.AbstractApplication;
 import brooklyn.entity.basic.Entities;
-import brooklyn.management.internal.ManagementContextInternal;
 
 public class InternalEntityFactoryTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/rebind/ActivePartialRebindVersionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/ActivePartialRebindVersionTest.java b/core/src/test/java/brooklyn/entity/rebind/ActivePartialRebindVersionTest.java
index 45a39cc..41917d4 100644
--- a/core/src/test/java/brooklyn/entity/rebind/ActivePartialRebindVersionTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/ActivePartialRebindVersionTest.java
@@ -25,12 +25,12 @@ import org.testng.annotations.Test;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.osgi.OsgiVersionMoreEntityTest;
 
 import brooklyn.catalog.internal.CatalogTestUtils;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.entity.rebind.transformer.CompoundTransformer;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.osgi.OsgiVersionMoreEntityTest;
 import brooklyn.util.collections.MutableList;
 
 public class ActivePartialRebindVersionTest extends RebindTestFixtureWithApp {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 7856be7..2b5cebf 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
@@ -39,12 +39,12 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.basic.StartableApplication;
 import brooklyn.event.basic.Sensors;
-import brooklyn.management.internal.LocalManagementContext;
 
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.TestResourceUnavailableException;
 
 import brooklyn.util.javalang.UrlClassLoader;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 ff37f74..5d95534 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
@@ -40,6 +40,7 @@ import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestEntity;
 
 import brooklyn.catalog.internal.BasicBrooklynCatalog;
@@ -47,8 +48,9 @@ import brooklyn.catalog.internal.CatalogDto;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.internal.BrooklynFeatureEnablement;
+
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-import brooklyn.management.internal.LocalManagementContext;
+
 import brooklyn.policy.basic.AbstractPolicy;
 
 import com.google.common.base.Joiner;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
index c04ef95..2c3890c 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
@@ -30,12 +30,12 @@ import brooklyn.camp.lite.CampPlatformWithJustBrooklynMgmt;
 
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestEntity;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.internal.BrooklynFeatureEnablement;
-import brooklyn.management.internal.LocalManagementContext;
 
 import com.google.common.collect.Iterables;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 75ba13e..992ca8e 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindEntityTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindEntityTest.java
@@ -52,6 +52,7 @@ import org.apache.brooklyn.api.location.LocationSpec;
 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.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityImpl;
@@ -74,7 +75,6 @@ import brooklyn.event.basic.Sensors;
 
 import org.apache.brooklyn.location.basic.LocationConfigTest.MyLocation;
 
-import brooklyn.management.internal.LocalManagementContext;
 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/6602f694/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 82d122a..ba168cf 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
@@ -39,6 +39,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.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -51,7 +52,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.management.internal.LocalManagementContext;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.flags.SetFromFlag;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 db51a91..c368a7a 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindFeedTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindFeedTest.java
@@ -28,6 +28,7 @@ import org.apache.brooklyn.api.entity.Feed;
 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.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestEntity;
 import org.apache.brooklyn.test.entity.TestEntityImpl.TestEntityWithoutEnrichers;
@@ -54,7 +55,6 @@ import brooklyn.event.feed.ssh.SshValueFunctions;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.management.internal.BrooklynGarbageCollector;
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.http.BetterMockWebServer;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 c3c27c2..868665f 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindFeedWithHaTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindFeedWithHaTest.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.management.Task;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -40,7 +41,6 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.http.BetterMockWebServer;
 import brooklyn.util.repeat.Repeater;
 import brooklyn.util.task.BasicExecutionManager;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 1522140..d563bea 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
@@ -40,6 +40,8 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.Task;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.mementos.BrooklynMementoManifest;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.Entities;
@@ -49,8 +51,6 @@ import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
 import brooklyn.entity.rebind.persister.FileBasedObjectStore;
 import brooklyn.entity.rebind.persister.PersistMode;
 import brooklyn.entity.trait.Startable;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.os.Os;
 import brooklyn.util.repeat.Repeater;
 import brooklyn.util.task.BasicExecutionManager;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java b/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
index 7541009..8e7ec46 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
@@ -38,6 +38,9 @@ import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.management.ha.ManagementNodeState;
 import org.apache.brooklyn.api.mementos.BrooklynMemento;
 import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
+import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -50,9 +53,6 @@ import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
 import brooklyn.entity.rebind.persister.FileBasedObjectStore;
 import brooklyn.entity.rebind.persister.PersistMode;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-import brooklyn.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.io.FileUtil;
 import brooklyn.util.javalang.Serializers;
 import brooklyn.util.javalang.Serializers.ObjectReplacer;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
index cd74827..3dcef6a 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
@@ -30,8 +30,6 @@ import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
 
-import brooklyn.management.osgi.OsgiTestResources;
-
 import org.apache.brooklyn.location.basic.SimulatedLocation;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
@@ -54,13 +52,14 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.mementos.BrooklynMementoPersister.LookupContext;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.management.osgi.OsgiTestResources;
+import org.apache.brooklyn.core.management.osgi.OsgiVersionMoreEntityTest;
 
 import brooklyn.catalog.internal.CatalogItemBuilder;
 import brooklyn.catalog.internal.CatalogItemDtoAbstract;
 import brooklyn.catalog.internal.CatalogTestUtils;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.group.DynamicCluster;
-import brooklyn.management.osgi.OsgiVersionMoreEntityTest;
 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/6602f694/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java b/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java
index 56896ca..0e00554 100644
--- a/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java
@@ -33,6 +33,7 @@ import org.apache.brooklyn.api.entity.rebind.RebindManager.RebindFailureMode;
 import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
+import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -50,7 +51,6 @@ import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
 import brooklyn.entity.rebind.persister.FileBasedObjectStore;
 import brooklyn.entity.rebind.persister.PersistMode;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.guava.SerializablePredicate;
 import brooklyn.util.os.Os;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/event/feed/ConfigToAttributesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/event/feed/ConfigToAttributesTest.java b/core/src/test/java/brooklyn/event/feed/ConfigToAttributesTest.java
index fb8f620..ff6ffab 100644
--- a/core/src/test/java/brooklyn/event/feed/ConfigToAttributesTest.java
+++ b/core/src/test/java/brooklyn/event/feed/ConfigToAttributesTest.java
@@ -21,6 +21,8 @@ package brooklyn.event.feed;
 import static org.testng.Assert.assertEquals;
 
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+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.annotations.AfterMethod;
@@ -30,8 +32,6 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.Entities;
 import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
 import brooklyn.event.basic.TemplatedStringAttributeSensorAndConfigKey;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
 
 public class ConfigToAttributesTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManager.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManager.java b/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManager.java
deleted file mode 100644
index 9674d93..0000000
--- a/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManager.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.management.entitlement;
-
-import org.apache.brooklyn.api.management.entitlement.EntitlementClass;
-import org.apache.brooklyn.api.management.entitlement.EntitlementContext;
-import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
-
-public class AcmeEntitlementManager extends PerUserEntitlementManager {
-
-    public AcmeEntitlementManager() {
-        // default mode (if no user specified) is root
-        super(Entitlements.root());
-        
-        super.addUser("admin", Entitlements.root());
-        
-        super.addUser("support", Entitlements.readOnly());
-        
-        // metrics can log in but can't really do much else
-        super.addUser("metrics", Entitlements.minimal());
-        
-        // 'navigator' defines a user with a custom entitlement manager allowing 
-        // access to see entities (in the tree) but not to do anything 
-        // or even see any sensor information on those entities
-        super.addUser("navigator", new EntitlementManager() {
-            @Override
-            public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
-                if (Entitlements.SEE_ENTITY.equals(entitlementClass)) return true;
-                return false;
-            }
-        });
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTest.java b/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTest.java
deleted file mode 100644
index e43fe4a..0000000
--- a/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTest.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.management.entitlement;
-
-import java.net.URI;
-
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import brooklyn.management.entitlement.Entitlements.EntityAndItem;
-import brooklyn.management.entitlement.Entitlements.StringAndArgument;
-
-public class AcmeEntitlementManagerTest extends AcmeEntitlementManagerTestFixture {
-
-    protected void addGlobalConfig() {
-        configBag.put(Entitlements.GLOBAL_ENTITLEMENT_MANAGER, AcmeEntitlementManager.class.getName());
-    }
-
-    @Test
-    public void testOtherAuthorizedUserHasAllPermissions() {
-        checkUserHasAllPermissions("other");
-    }
-
-    @Test
-    public void testNullUserHasAllPermissions() {
-        checkUserHasAllPermissions(null);
-    }
-
-    @Test
-    public void testNavigatorHasListPermissionsOnly() {
-        setup(configBag);
-        WebEntitlementContext entitlementContext = new WebEntitlementContext("navigator", "127.0.0.1", URI.create("/X").toString(), "X");
-        Entitlements.setEntitlementContext(entitlementContext);
-        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null));
-        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app));
-        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
-        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
-        // and cannot invoke methods
-        confirmEffectorEntitlement(false);
-    }
-
-}
-

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTestFixture.java b/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTestFixture.java
deleted file mode 100644
index 63ee8a0..0000000
--- a/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTestFixture.java
+++ /dev/null
@@ -1,155 +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.management.entitlement;
-
-import java.io.IOException;
-import java.net.URI;
-
-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.ManagementContext;
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.ImmutableList;
-
-import brooklyn.config.BrooklynProperties;
-import brooklyn.entity.basic.ApplicationBuilder;
-import brooklyn.entity.basic.BasicApplication;
-import brooklyn.entity.basic.Entities;
-import brooklyn.management.entitlement.Entitlements.EntityAndItem;
-import brooklyn.management.entitlement.Entitlements.StringAndArgument;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.exceptions.Exceptions;
-
-public abstract class AcmeEntitlementManagerTestFixture {
-
-    protected ManagementContext mgmt;
-    protected Application app;
-    protected ConfigBag configBag;
-    
-    public void setup(ConfigBag cfg) {
-        mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty().addFrom(cfg));
-        app = ApplicationBuilder.newManagedApp(EntitySpec.create(BasicApplication.class), mgmt);
-    }
-
-    @BeforeMethod(alwaysRun=true)
-    public void init() throws IOException {
-        Entitlements.clearEntitlementContext();
-        configBag = ConfigBag.newInstance();
-        addGlobalConfig();
-    }
-
-    protected abstract void addGlobalConfig() throws IOException;
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() {
-        Entitlements.clearEntitlementContext();
-        if (app != null) Entities.destroyAll(app.getManagementContext());
-        if (mgmt != null) Entities.destroyAll(mgmt);
-        app = null;
-        mgmt = null;
-    }
-
-    @Test
-    public void testMetricsHasMinimalPermissions() {
-        checkUserHasMinimalPermissions("metrics");
-    }
-    
-    public void checkUserHasMinimalPermissions(String username) {
-        setup(configBag);
-        WebEntitlementContext entitlementContext = new WebEntitlementContext(username, "127.0.0.1", URI.create("/applications").toString(), "H");
-        Entitlements.setEntitlementContext(entitlementContext);
-        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null));
-        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app));
-        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
-        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
-        // and can invoke methods
-        confirmEffectorEntitlement(false);
-    }
-
-    @Test
-    public void testSupportHasReadOnlyPermissions() {
-        checkUserHasReadOnlyPermissions("support");
-    }
-    
-    public void checkUserHasReadOnlyPermissions(String username) {
-        setup(configBag);
-        WebEntitlementContext entitlementContext = new WebEntitlementContext(username, "127.0.0.1", URI.create("/X").toString(), "B");
-        Entitlements.setEntitlementContext(entitlementContext);
-        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null));
-        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app));
-        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
-        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
-        // and cannot invoke methods
-        confirmEffectorEntitlement(false);
-    }
-
-    @Test
-    public void testAdminHasAllPermissions() {
-        checkUserHasAllPermissions("admin");
-    }
-    
-    public void checkUserHasAllPermissions(String user) {
-        setup(configBag);
-        WebEntitlementContext entitlementContext = new WebEntitlementContext(user, "127.0.0.1", URI.create("/X").toString(), "A");
-        Entitlements.setEntitlementContext(entitlementContext);
-        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null));
-        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app));
-        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
-        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
-        // and can invoke methods
-        confirmEffectorEntitlement(true);
-    }
-
-    protected void confirmEffectorEntitlement(boolean shouldSucceed) {
-        try {
-            ((BasicApplication)app).start(ImmutableList.<Location>of());
-            checkNoException(shouldSucceed);
-        } catch (Exception e) {
-            checkNotEntitledException(shouldSucceed, e);
-        }
-    }
-
-    private void checkNoException(boolean shouldBeEntitled) {
-        checkNotEntitledException(shouldBeEntitled, null);
-    }
-
-    private void checkNotEntitledException(boolean shouldBeEntitled, Exception e) {
-        if (e==null) {
-            if (shouldBeEntitled) return;
-            Assert.fail("entitlement should have been denied");
-        }
-        
-        Exception notEntitled = Exceptions.getFirstThrowableOfType(e, NotEntitledException.class);
-        if (notEntitled==null)
-            throw Exceptions.propagate(e);
-        if (!shouldBeEntitled) {
-            /* denied, as it should have been */ 
-            return;
-        }
-        Assert.fail("entitlement should have been granted, but was denied: "+e);
-    }
-
-}
-

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/entitlement/EntitlementsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/entitlement/EntitlementsTest.java b/core/src/test/java/brooklyn/management/entitlement/EntitlementsTest.java
deleted file mode 100644
index 901c5ae..0000000
--- a/core/src/test/java/brooklyn/management/entitlement/EntitlementsTest.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.management.entitlement;
-
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.proxying.EntitySpec;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
-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.BrooklynProperties;
-import brooklyn.entity.basic.ApplicationBuilder;
-import brooklyn.entity.basic.BasicApplication;
-import brooklyn.entity.basic.Entities;
-import brooklyn.management.entitlement.Entitlements.EntityAndItem;
-import brooklyn.management.entitlement.Entitlements.StringAndArgument;
-
-@Test
-public class EntitlementsTest {
-
-    private ManagementContext mgmt;
-    private Application app;
-
-    @BeforeMethod
-    public void setup() {
-        mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty());
-        app = ApplicationBuilder.newManagedApp(EntitySpec.create(BasicApplication.class), mgmt);
-    }
-
-    @AfterMethod
-    public void tearDown() {
-        if (app != null) Entities.destroyAll(app.getManagementContext());
-        if (mgmt != null) Entities.destroyAll(mgmt);
-        app = null;
-        mgmt = null;
-    }
-
-    // allowing
-    public void testAllowingRoot() {
-        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.ROOT);
-        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-    public void testAllowingSeeEntity() {
-        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_ENTITY);
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
-        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-    public void testAllowingInvokeEffector() {
-        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.INVOKE_EFFECTOR);
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
-        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-    public void testAllowingSeeSensor() {
-        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_SENSOR);
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
-        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-    public void testAllowingDeployApplication() {
-        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.DEPLOY_APPLICATION);
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
-        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-    public void testAllowingSeeAllServerInfo() {
-        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_ALL_SERVER_INFO);
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
-        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
-        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-
-    // nonSecret
-    public void testSeeNonSecretSensors() {
-        EntitlementManager seeNonSecretSensors = Entitlements.FineGrainedEntitlements.seeNonSecretSensors();
-        assertFalse(seeNonSecretSensors.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "password")));
-        assertTrue(seeNonSecretSensors.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "any-sensor")));
-    }
-
-    // allOf
-    public void testAllOfWithSeeEntityAndSeeSensors() {
-        EntitlementManager allOf = Entitlements.FineGrainedEntitlements.allOf(
-                Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_ENTITY),
-                Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_SENSOR));
-        assertFalse(allOf.isEntitled(null, Entitlements.ROOT, null));
-        assertTrue(allOf.isEntitled(null, Entitlements.SEE_ENTITY, null));
-        assertFalse(allOf.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
-        assertTrue(allOf.isEntitled(null, Entitlements.SEE_SENSOR, null));
-        assertFalse(allOf.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
-        assertFalse(allOf.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-
-    // anyOf
-    public void testAnyOfWithSeeEntityAndSeeSensors() {
-        EntitlementManager anyOf = Entitlements.FineGrainedEntitlements.anyOf(
-                Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_ENTITY),
-                Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_SENSOR));
-        assertFalse(anyOf.isEntitled(null, Entitlements.ROOT, null));
-        assertTrue(anyOf.isEntitled(null, Entitlements.SEE_ENTITY, null));
-        assertFalse(anyOf.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
-        assertTrue(anyOf.isEntitled(null, Entitlements.SEE_SENSOR, null));
-        assertFalse(anyOf.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
-        assertFalse(anyOf.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-
-    // root
-    public void testGlobalRootEntitlement() {
-        EntitlementManager root = Entitlements.root();
-        assertTrue(root.isEntitled(null, Entitlements.ROOT, null));
-        assertTrue(root.isEntitled(null, Entitlements.SEE_ENTITY, null));
-        assertTrue(root.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
-        assertTrue(root.isEntitled(null, Entitlements.SEE_SENSOR, null));
-        assertTrue(root.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
-        assertTrue(root.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-    public void testAppSpecificRootEntitlement() {
-        EntitlementManager root = Entitlements.root();
-        assertTrue(root.isEntitled(null, Entitlements.SEE_ENTITY, app));
-        assertTrue(root.isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
-        assertTrue(root.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "any-sensor")));
-        assertTrue(root.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "password")));
-        assertTrue(root.isEntitled(null, Entitlements.DEPLOY_APPLICATION, Entitlements.EntityAndItem.of(app, null)));
-        assertTrue(root.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-
-    // minimal
-    public void testGlobalMinimalEntitlement() {
-        EntitlementManager minimal = Entitlements.minimal();
-        assertFalse(minimal.isEntitled(null, Entitlements.ROOT, null));
-        assertFalse(minimal.isEntitled(null, Entitlements.SEE_ENTITY, null));
-        assertFalse(minimal.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
-        assertFalse(minimal.isEntitled(null, Entitlements.SEE_SENSOR, null));
-        assertFalse(minimal.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
-        assertFalse(minimal.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-    public void testAppSpecificMinimalEntitlement() {
-        EntitlementManager minimal = Entitlements.minimal();
-        assertFalse(minimal.isEntitled(null, Entitlements.SEE_ENTITY, app));
-        assertFalse(minimal.isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
-        assertFalse(minimal.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "any-sensor")));
-        assertFalse(minimal.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "password")));
-        assertFalse(minimal.isEntitled(null, Entitlements.DEPLOY_APPLICATION, Entitlements.EntityAndItem.of(app, null)));
-        assertFalse(minimal.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-
-    // readOnly
-    public void testGlobalReadOnlyEntitlement() {
-        EntitlementManager readOnly = Entitlements.readOnly();
-        assertFalse(readOnly.isEntitled(null, Entitlements.ROOT, null));
-        assertTrue(readOnly.isEntitled(null, Entitlements.SEE_ENTITY, null));
-        assertFalse(readOnly.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
-        assertFalse(readOnly.isEntitled(null, Entitlements.SEE_SENSOR, null));
-        assertFalse(readOnly.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
-        assertFalse(readOnly.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
-    }
-    public void testAppSpecificReadOnlyEntitlement() {
-        EntitlementManager readOnly = Entitlements.readOnly();
-        assertTrue(readOnly.isEntitled(null, Entitlements.SEE_ENTITY, app));
-        assertFalse(readOnly.isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
-        assertTrue(readOnly.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "any-sensor")));
-        assertFalse(readOnly.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "password")));
-        assertFalse(readOnly.isEntitled(null, Entitlements.DEPLOY_APPLICATION, Entitlements.EntityAndItem.of(app, null)));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/entitlement/EntityEntitlementTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/entitlement/EntityEntitlementTest.java b/core/src/test/java/brooklyn/management/entitlement/EntityEntitlementTest.java
deleted file mode 100644
index b6bf5c1..0000000
--- a/core/src/test/java/brooklyn/management/entitlement/EntityEntitlementTest.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.management.entitlement;
-
-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.ManagementContext;
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.ImmutableList;
-
-import brooklyn.config.BrooklynProperties;
-import brooklyn.entity.basic.ApplicationBuilder;
-import brooklyn.entity.basic.BasicApplication;
-import brooklyn.entity.basic.Entities;
-import brooklyn.management.entitlement.Entitlements.EntityAndItem;
-import brooklyn.management.entitlement.Entitlements.StringAndArgument;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.exceptions.Exceptions;
-
-public class EntityEntitlementTest {
-
-    private static final Logger log = LoggerFactory.getLogger(EntityEntitlementTest.class);
-    
-    ManagementContext mgmt;
-    Application app;
-    
-    public void setup(ConfigBag cfg) {
-        mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty().addFrom(cfg));
-        app = ApplicationBuilder.newManagedApp(EntitySpec.create(BasicApplication.class), mgmt);
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() {
-        if (app != null) Entities.destroyAll(app.getManagementContext());
-        if (mgmt != null) Entities.destroyAll(mgmt);
-        app = null;
-        mgmt = null;
-    }
-
-    @Test
-    public void testDefaultRootAllows() {
-        setup(ConfigBag.newInstance());
-        // default "root" access allows ROOT permission, and invoke effector, etc
-        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.ROOT, null));
-        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
-        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
-        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
-        // and can invoke methods, without any user/login registered
-        confirmEffectorEntitlement(true);
-        confirmSensorEntitlement(true);
-    }
-
-    @Test
-    public void testExplicitRootAllows() {
-        setup(ConfigBag.newInstance().configure(Entitlements.GLOBAL_ENTITLEMENT_MANAGER, "root"));
-        
-        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.ROOT, null));
-        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
-        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
-        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
-        
-        confirmEffectorEntitlement(true);
-        confirmSensorEntitlement(true);
-    }
-
-    @Test
-    public void testReadOnlyThrowsNotEntitled() {
-        try {
-            setup(ConfigBag.newInstance().configure(Entitlements.GLOBAL_ENTITLEMENT_MANAGER, "readonly"));
-            // eventually above call will fail in app creation, but for now the call below falls
-            ((BasicApplication)app).start(ImmutableList.<Location>of());
-            tearDown();
-            
-            Assert.fail("something should have been disallowed");
-        } catch (Exception e) {
-            checkNotEntitledException(e);
-        } finally {
-            app = null;
-            if (mgmt!=null) ((LocalManagementContext)mgmt).terminate();
-            mgmt = null;
-        }
-    }
-    
-    @Test(enabled=false)
-    // this test (and minimal below) currently work fine until tearDown, 
-    // but then of course they don't have entitlement to call 'stop';
-    // (as more entitlement checks are added app creation will fail).
-    // 
-    // TODO these tests should set up a user with the permission,
-    // e.g. so system has root, but 'bob' has read-only, then we can test read-only;
-    // TODO rest api tests, as some of the permissions will only be enforced at REST level
-    public void testReadOnlyAllowsSome() {
-        setup(ConfigBag.newInstance().configure(Entitlements.GLOBAL_ENTITLEMENT_MANAGER, "readonly"));
-        
-        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.ROOT, null));
-        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
-        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
-        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
-        
-        confirmEffectorEntitlement(false);
-        confirmSensorEntitlement(true);
-    }
-    
-    @Test(enabled=false)
-    public void testMinimalDisallows() {
-        setup(ConfigBag.newInstance().configure(Entitlements.GLOBAL_ENTITLEMENT_MANAGER, "minimal"));
-        
-        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.ROOT, null));
-        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
-        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
-        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
-        
-        confirmEffectorEntitlement(false);
-        confirmSensorEntitlement(false);
-    }
-    
-    protected void confirmSensorEntitlement(boolean shouldSucceed) {
-        // TODO... based on above
-        // (though maybe we should test against REST API classes rather than lower level programmatic?
-        // TBC...)
-        log.warn("confirmSensorEntitlement still required!");
-    }
-
-    // TODO specific users tests
-    
-    protected void confirmEffectorEntitlement(boolean shouldSucceed) {
-        try {
-            ((BasicApplication)app).start(ImmutableList.<Location>of());
-            checkNoException(shouldSucceed);
-        } catch (Exception e) {
-            checkNotEntitledException(shouldSucceed, e);
-        }
-    }
-
-    private void checkNoException(boolean shouldBeEntitled) {
-        checkNotEntitledException(shouldBeEntitled, null);
-    }
-
-    private void checkNotEntitledException(Exception e) {
-        checkNotEntitledException(false, e);
-    }
-
-    private void checkNotEntitledException(boolean shouldBeEntitled, Exception e) {
-        if (e==null) {
-            if (shouldBeEntitled) return;
-            Assert.fail("entitlement should have been denied");
-        }
-        
-        Exception notEntitled = Exceptions.getFirstThrowableOfType(e, NotEntitledException.class);
-        if (notEntitled==null)
-            throw Exceptions.propagate(e);
-        if (!shouldBeEntitled) {
-            /* denied, as it should have been */ 
-            return;
-        }
-        Assert.fail("entitlement should have been granted, but was denied: "+e);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/entitlement/PerUserEntitlementManagerPropertiesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/entitlement/PerUserEntitlementManagerPropertiesTest.java b/core/src/test/java/brooklyn/management/entitlement/PerUserEntitlementManagerPropertiesTest.java
deleted file mode 100644
index b583c08..0000000
--- a/core/src/test/java/brooklyn/management/entitlement/PerUserEntitlementManagerPropertiesTest.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.management.entitlement;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.Properties;
-
-import org.testng.annotations.Test;
-
-
-public class PerUserEntitlementManagerPropertiesTest extends AcmeEntitlementManagerTestFixture {
-
-    protected void addGlobalConfig() throws IOException {
-        Properties moreProps = new Properties();
-        moreProps.load(new StringReader(
-            "brooklyn.entitlements.global=brooklyn.management.entitlement.PerUserEntitlementManager\n"
-                + "brooklyn.entitlements.perUser.admin=root\n"
-                + "brooklyn.entitlements.perUser.support=readonly\n"
-                + "brooklyn.entitlements.perUser.metrics=minimal"));
-        configBag.putAll(moreProps);
-    }
-    
-    @Test
-    public void testOtherAuthorizedUserHasMinimalPermissions() {
-        checkUserHasMinimalPermissions("other");
-    }
-
-    @Test
-    public void testNullUserHasAllPermissions() {
-        checkUserHasAllPermissions(null);
-    }
-
-
-}
-

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerFileBasedTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerFileBasedTest.java b/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerFileBasedTest.java
deleted file mode 100644
index 7091f08..0000000
--- a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerFileBasedTest.java
+++ /dev/null
@@ -1,47 +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.management.ha;
-
-import java.io.File;
-
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.rebind.persister.FileBasedObjectStore;
-import brooklyn.util.os.Os;
-
-@Test
-public class HighAvailabilityManagerFileBasedTest extends HighAvailabilityManagerTestFixture {
-
-    private File dir;
-
-    protected FileBasedObjectStore newPersistenceObjectStore() {
-        if (dir!=null)
-            throw new IllegalStateException("Test does not support multiple object stores");
-        dir = Os.newTempDir(getClass());
-        return new FileBasedObjectStore(dir);
-    }
-
-    @Override
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        super.tearDown();
-        dir = Os.deleteRecursively(dir).asNullOrThrowing();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerInMemoryIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerInMemoryIntegrationTest.java b/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerInMemoryIntegrationTest.java
deleted file mode 100644
index a379faa..0000000
--- a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerInMemoryIntegrationTest.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.management.ha;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.rebind.persister.InMemoryObjectStore;
-import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-import brooklyn.util.time.Duration;
-import brooklyn.util.time.Time;
-
-import com.google.common.base.Ticker;
-
-@Test(groups="Integration")
-public class HighAvailabilityManagerInMemoryIntegrationTest extends HighAvailabilityManagerTestFixture {
-
-    private static final Logger log = LoggerFactory.getLogger(HighAvailabilityManagerInMemoryIntegrationTest.class);
-    
-    @Override
-    protected PersistenceObjectStore newPersistenceObjectStore() {
-        return new InMemoryObjectStore();
-    }
-
-    @Override
-    protected Duration getPollPeriod() {
-        return Duration.millis(100);
-    }
-    
-    @Override
-    protected long tickerAdvance(Duration duration) {
-        log.info("sleeping for "+duration);
-        // actually sleep, in order to advance the local time ticker
-        Time.sleep(duration);
-        return super.tickerAdvance(duration);
-    }
-
-    @Override
-    protected Ticker getRemoteTicker() {
-        // use real times
-        return null;
-    }
-
-    @Override
-    @Test(groups="Integration", enabled=false, invocationCount=50) 
-    public void testGetManagementPlaneStatusManyTimes() throws Exception {
-    }
-
-    @Test(groups="Integration")
-    @Override
-    public void testGetManagementPlaneStatus() throws Exception {
-        super.testGetManagementPlaneStatus();
-    }
-    
-    @Test(groups="Integration")
-    @Override
-    public void testDoesNotPromoteIfMasterTimeoutNotExpired() throws Exception {
-        super.testDoesNotPromoteIfMasterTimeoutNotExpired();
-    }
-    
-    @Test(groups="Integration")
-    @Override
-    public void testGetManagementPlaneSyncStateDoesNotThrowNpeBeforePersisterSet() throws Exception {
-        super.testGetManagementPlaneSyncStateDoesNotThrowNpeBeforePersisterSet();
-    }
-    
-    @Test(groups="Integration")
-    @Override
-    public void testGetManagementPlaneSyncStateInfersTimedOutNodeAsFailed() throws Exception {
-        super.testGetManagementPlaneSyncStateInfersTimedOutNodeAsFailed();
-    }
-    
-    @Test(groups="Integration")
-    @Override
-    public void testPromotes() throws Exception {
-        super.testPromotes();
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerInMemoryTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerInMemoryTest.java b/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerInMemoryTest.java
deleted file mode 100644
index ff86733..0000000
--- a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerInMemoryTest.java
+++ /dev/null
@@ -1,144 +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.management.ha;
-
-import java.util.Collection;
-import java.util.List;
-
-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.api.location.NoMachinesAvailableException;
-import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
-import org.apache.brooklyn.test.entity.TestApplication;
-import org.apache.brooklyn.test.entity.TestEntity;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.entity.rebind.persister.InMemoryObjectStore;
-import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-
-import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-import org.apache.brooklyn.location.basic.SshMachineLocation;
-
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.util.collections.MutableList;
-
-import com.google.common.collect.Iterables;
-
-@Test
-public class HighAvailabilityManagerInMemoryTest extends HighAvailabilityManagerTestFixture {
-
-    private static final Logger log = LoggerFactory.getLogger(HighAvailabilityManagerInMemoryTest.class);
-    
-    protected PersistenceObjectStore newPersistenceObjectStore() {
-        return new InMemoryObjectStore();
-    }
-    
-    public void testGetManagementPlaneStatus() throws Exception {
-        super.testGetManagementPlaneStatus();
-    }
-
-    // extra test that promoteToMaster doesn't interfere with what is managed
-    public void testLocationsStillManagedCorrectlyAfterDoublePromotion() throws NoMachinesAvailableException {
-        HighAvailabilityManagerImpl ha = (HighAvailabilityManagerImpl) managementContext.getHighAvailabilityManager();
-        ha.start(HighAvailabilityMode.MASTER);
-        
-        TestApplication app = TestApplication.Factory.newManagedInstanceForTests(managementContext);
-        
-        LocalhostMachineProvisioningLocation l = app.newLocalhostProvisioningLocation();
-        l.setConfig(TestEntity.CONF_NAME, "sample1");
-        Assert.assertEquals(l.getConfig(TestEntity.CONF_NAME), "sample1");
-        
-        SshMachineLocation l2 = l.obtain();
-        Assert.assertEquals(l2.getConfig(TestEntity.CONF_NAME), "sample1");
-        Assert.assertNotNull(l2.getParent(), "Parent not set after dodgy promoteToMaster");
-        Assert.assertEquals(l2.getParent().getConfig(TestEntity.CONF_NAME), "sample1");
-
-        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).location(l).location(l2));
-        log.info("Entities managed are: "+managementContext.getEntityManager().getEntities());
-        Collection<Location> le = entity.getLocations();
-        log.info("Locs at entity are: "+le);
-        Collection<Location> lm = managementContext.getLocationManager().getLocations();
-        log.info("Locs managed are: "+lm);
-        log.info("            objs: "+identities(lm));
-        Assert.assertNotNull(entity.getManagementSupport().getManagementContext());
-        Assert.assertNotNull( ((EntityInternal)app.getChildren().iterator().next()).getManagementSupport().getManagementContext());
-        Assert.assertTrue( ((EntityInternal)app.getChildren().iterator().next()).getManagementSupport().isDeployed());
-        checkEntitiesHealthy(app, entity);
-        
-        managementContext.getRebindManager().forcePersistNow(true, null);
-        log.info("Test deliberately doing unnecessary extra promoteToMaster");
-        ha.promoteToMaster();
-        
-        log.info("Entities managed are: "+managementContext.getEntityManager().getEntities());
-        Collection<Location> lle = entity.getLocations();
-        log.info("Locs at entity(old) are: "+lle);
-        log.info("                   objs: "+identities(lle));
-        // check entities -- the initial-full promotion previously re-created items, 
-        // and plugged them in as children, but only managed the roots
-        checkEntitiesHealthy(app, entity);
-        
-        // assert what's in the location manager is accurate
-        Collection<Location> llmm = managementContext.getLocationManager().getLocations();
-        log.info("Locs managed are: "+llmm);
-        log.info("            objs: "+identities(llmm));
-        Assert.assertEquals(llmm, lm);
-        SshMachineLocation ll2a = Iterables.getOnlyElement(Iterables.filter(llmm, SshMachineLocation.class));
-        Assert.assertEquals(ll2a.getConfig(TestEntity.CONF_NAME), "sample1");
-        Assert.assertNotNull(ll2a.getParent(), "Parent not set after dodgy promoteToMaster");
-        Assert.assertEquals(ll2a.getParent().getConfig(TestEntity.CONF_NAME), "sample1");
-        
-        // and what's in the location manager is accurate
-        Entity ee = (Entity)managementContext.lookup(entity.getId());
-        Collection<Location> llee = ee.getLocations();
-        log.info("Locs at entity(lookup) are: "+llee);
-        log.info("                      objs: "+identities(llee));
-        SshMachineLocation ll2b = Iterables.getOnlyElement(Iterables.filter(llee, SshMachineLocation.class));
-        Assert.assertEquals(ll2b.getConfig(TestEntity.CONF_NAME), "sample1");
-        Assert.assertNotNull(ll2b.getParent(), "Parent not set after dodgy promoteToMaster");
-        Assert.assertEquals(ll2b.getParent().getConfig(TestEntity.CONF_NAME), "sample1");
-    }
-
-    private void checkEntitiesHealthy(TestApplication app, TestEntity entity) {
-        Assert.assertNotNull(app.getManagementSupport().getManagementContext());
-        Assert.assertTrue( app.getManagementSupport().getManagementContext().isRunning() );
-        
-        Assert.assertNotNull(entity.getManagementSupport().getManagementContext());
-        Assert.assertNotNull( ((EntityInternal)app.getChildren().iterator().next()).getManagementSupport().getManagementContext() );
-        Assert.assertTrue( ((EntityInternal)app.getChildren().iterator().next()).getManagementSupport().isDeployed());
-        Assert.assertTrue( ((EntityInternal)app.getChildren().iterator().next()).getManagementSupport().getManagementContext() instanceof LocalManagementContext );
-    }
-
-    @Test(groups="Integration", invocationCount=50)
-    public void testLocationsStillManagedCorrectlyAfterDoublePromotionManyTimes() throws NoMachinesAvailableException {
-        testLocationsStillManagedCorrectlyAfterDoublePromotion();
-    }
-    
-    private List<String> identities(Collection<?> objs) {
-        List<String> result = MutableList.of();
-        for (Object obj: objs)
-            result.add(Integer.toHexString(System.identityHashCode(obj)));
-        return result;
-    }
-    
-}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
deleted file mode 100644
index 0877ed5..0000000
--- a/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
+++ /dev/null
@@ -1,459 +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.management.osgi;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.util.Arrays;
-import java.util.List;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.launch.Framework;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-import org.apache.brooklyn.api.basic.BrooklynObject;
-import org.apache.brooklyn.api.catalog.CatalogItem;
-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.api.management.classloading.BrooklynClassLoadingContext;
-import org.apache.brooklyn.api.policy.PolicySpec;
-
-import brooklyn.catalog.internal.CatalogEntityItemDto;
-import brooklyn.catalog.internal.CatalogItemBuilder;
-import brooklyn.catalog.internal.CatalogItemDtoAbstract;
-import brooklyn.catalog.internal.CatalogTestUtils;
-import brooklyn.catalog.internal.CatalogUtils;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.effector.Effectors;
-import brooklyn.entity.proxying.InternalEntityFactory;
-import brooklyn.entity.proxying.InternalPolicyFactory;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
-
-import org.apache.brooklyn.test.TestResourceUnavailableException;
-import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
-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;
-import com.google.common.collect.Iterables;
-
-
-/** 
- * Tests that OSGi entities load correctly and have the right catalog information set.
- * Note further tests done elsewhere using CAMP YAML (referring to methods in this class).
- */
-public class OsgiVersionMoreEntityTest {
-   
-    public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
-    public static final String BROOKLYN_TEST_OSGI_ENTITIES_URL = "classpath:"+OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
-
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_V1_PATH = OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_V1_PATH;
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_V1_URL = "classpath:"+BROOKLYN_TEST_MORE_ENTITIES_V1_PATH;
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_PATH = OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_V2_PATH;
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_URL = "classpath:"+BROOKLYN_TEST_MORE_ENTITIES_V2_PATH;
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH = OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH;
-    public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL = "classpath:"+BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH;
-    
-    public static final String TEST_VERSION = "0.1.0";
-
-    public static final String EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V1 = "Hi BROOKLYN from V1";
-    public static final String EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2 = "HI BROOKLYN FROM V2";
-    public static final String EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2_EVIL_TWIN = "HO BROOKLYN FROM V2 EVIL TWIN";
-    
-    protected LocalManagementContext mgmt;
-    protected TestApplication app;
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
-        app = TestApplication.Factory.newManagedInstanceForTests(mgmt);
-    }
-
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws BundleException, IOException, InterruptedException {
-        Entities.destroyAll(mgmt);
-    }
-    
-    /**
-     * Test fix for
-     * java.lang.NoClassDefFoundError: brooklyn.event.AttributeSensor not found by io.brooklyn.brooklyn-test-osgi-entities [41]
-     */
-    @Test
-    public void testEntityProxy() throws Exception {
-        File storageTempDir = Os.newTempDir("osgi-standalone");
-        Framework framework = Osgis.newFrameworkStarted(storageTempDir.getAbsolutePath(), true, null);
-        
-        try {
-            ManagementContextInternal managementContext;
-            InternalEntityFactory factory;
-
-            managementContext = new LocalManagementContextForTests();
-            InternalPolicyFactory policyFactory = new InternalPolicyFactory(managementContext);
-            factory = new InternalEntityFactory(managementContext, managementContext.getEntityManager().getEntityTypeRegistry(), policyFactory);
-
-            TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_ENTITIES_PATH);
-            Bundle bundle = Osgis.install(framework, BROOKLYN_TEST_OSGI_ENTITIES_PATH);
-            @SuppressWarnings("unchecked")
-            Class<? extends Entity> bundleCls = (Class<? extends Entity>) bundle.loadClass("brooklyn.osgi.tests.SimpleEntityImpl");
-            @SuppressWarnings("unchecked")
-            Class<? extends Entity> bundleInterface = (Class<? extends Entity>) bundle.loadClass("brooklyn.osgi.tests.SimpleEntity");
-
-            @SuppressWarnings("unchecked")
-            EntitySpec<Entity> spec = (EntitySpec<Entity>) (((EntitySpec<Entity>)EntitySpec.create(bundleInterface))).impl(bundleCls);
-            Entity entity = bundleCls.newInstance();
-            factory.createEntityProxy(spec, entity);
-
-            if (managementContext != null) Entities.destroyAll(managementContext);
-        } finally {
-            OsgiStandaloneTest.tearDownOsgiFramework(framework, storageTempDir);
-        }
-    }
-    
-    protected CatalogItem<?, ?> addCatalogItemWithTypeAsName(String type, String version, String ...libraries) {
-        return addCatalogItemWithNameAndType(type, version, type, libraries);
-    }
-    protected CatalogItem<?, ?> addCatalogItemWithNameAndType(String symName, String version, String type, String ...libraries) {
-        return addCatalogItemWithNameAndType(mgmt, symName, version, type, libraries);
-    }
-
-    @SuppressWarnings("deprecation")
-    static CatalogItem<?, ?> addCatalogItemWithNameAndType(ManagementContext mgmt, String symName, String version, String type, String ...libraries) {
-        CatalogEntityItemDto c1 = newCatalogItemWithNameAndType(symName, version, type, libraries);
-        mgmt.getCatalog().addItem(c1);
-        CatalogItem<?, ?> c2 = mgmt.getCatalog().getCatalogItem(symName, version);
-        Preconditions.checkNotNull(c2, "Item "+type+":"+version+" was not found after adding it");
-        return c2;
-    }
-
-    static CatalogEntityItemDto newCatalogItemWithTypeAsName(String type, String version, String ...libraries) {
-        return newCatalogItemWithNameAndType(type, version, type, libraries);
-    }
-    static CatalogEntityItemDto newCatalogItemWithNameAndType(String symName, String version, String type, String ...libraries) {
-        @SuppressWarnings("deprecation")
-        CatalogEntityItemDto c1 = CatalogItemBuilder.newEntity(symName, version)
-                .javaType(type)
-                .libraries(CatalogItemDtoAbstract.parseLibraries(Arrays.asList(libraries)))
-                .build();
-        return c1;
-    }
-
-    protected Entity addItemFromCatalog(CatalogItem<?, ?> c2) {
-        return addItemFromCatalog(mgmt, app, c2);
-    }
-    
-    public static Entity addItemFromCatalog(ManagementContext mgmt, TestApplication parent, CatalogItem<?, ?> c2) {
-        return parent.createAndManageChild( CatalogTestUtils.createEssentialEntitySpec(mgmt, c2) );
-    }
-
-    public static void assertV1MethodCall(Entity me) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        Assert.assertEquals(doMethodCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V1);
-    }
-    public static void assertV2MethodCall(BrooklynObject me) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        Assert.assertEquals(doMethodCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2);
-    }
-    public static void assertV2EvilTwinMethodCall(Entity me) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        Assert.assertEquals(doMethodCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2_EVIL_TWIN);
-    }
-
-    public static Object doMethodCallBrooklyn(BrooklynObject me) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        return me.getClass().getMethod("sayHI", String.class).invoke(me, "Brooklyn");
-    }
-
-    public static void assertV1EffectorCall(Entity me) {
-        Assert.assertEquals(doEffectorCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V1);
-    }
-    public static void assertV2EffectorCall(Entity me) {
-        Assert.assertEquals(doEffectorCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2);
-    }
-    public static void assertV2EvilTwinEffectorCall(Entity me) {
-        Assert.assertEquals(doEffectorCallBrooklyn(me), EXPECTED_SAY_HI_BROOKLYN_RESPONSE_FROM_V2_EVIL_TWIN);
-    }
-
-    public static String doEffectorCallBrooklyn(Entity me) {
-        return me.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), ImmutableMap.of("name", "brooklyn")).getUnchecked();
-    }
-
-    public static CatalogItem<?, ?> addMoreEntityV1(ManagementContext mgmt, String versionToRegister) {
-        TestResourceUnavailableException.throwIfResourceUnavailable(OsgiVersionMoreEntityTest.class, BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
-        return addCatalogItemWithNameAndType(mgmt,
-            OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-            versionToRegister,
-            OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-            BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
-    }
-    public static CatalogItem<?, ?> addMoreEntityV2(ManagementContext mgmt, String versionToRegister) {
-        TestResourceUnavailableException.throwIfResourceUnavailable(OsgiVersionMoreEntityTest.class, BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
-        return addCatalogItemWithNameAndType(mgmt,
-            OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-            versionToRegister,
-            OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-            BROOKLYN_TEST_MORE_ENTITIES_V2_URL,
-            BROOKLYN_TEST_OSGI_ENTITIES_URL);
-    }
-    
-    @Test
-    public void testMoreEntitiesV1() throws Exception {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
-
-        CatalogItem<?, ?> c2 = addMoreEntityV1(mgmt, TEST_VERSION);
-        
-        // test load and instantiate
-        Entity me = addItemFromCatalog(c2);
-        Assert.assertEquals(me.getCatalogItemId(), CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION));
-        
-        assertV1MethodCall(me);
-        assertV1EffectorCall(me);
-        
-        // test adding a child gets the right type; this time by entity parent hierarchy
-        BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, c2);
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        Entity me2 = me.addChild(EntitySpec.create( (Class)loader.loadClass(c2.getJavaType()) ));
-        Assert.assertEquals(me2.getCatalogItemId(), CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION));
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    protected PolicySpec<?> getPolicySpec(CatalogItem<?, ?> cp) {
-        BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, cp);
-        PolicySpec spec = PolicySpec.create( (Class)loader.loadClass(cp.getJavaType()) );
-        spec.catalogItemId(cp.getId());
-        return spec;
-    }
-
-    @Test
-    public void testMoreEntitiesV1Policy() throws Exception {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_ENTITIES_PATH);
-
-        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(
-                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-                TEST_VERSION,
-                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
-        
-        // test load and instantiate
-        Entity me = addItemFromCatalog(c2);
-
-        CatalogItem<?, ?> cp = addCatalogItemWithTypeAsName(
-                OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY,
-                TEST_VERSION,
-                BROOKLYN_TEST_OSGI_ENTITIES_URL);
-        me.addPolicy(getPolicySpec(cp));
-        
-        Assert.assertEquals(me.getPolicies().size(), 1, "Wrong number of policies: "+me.getPolicies());
-        
-        String catalogItemId = Iterables.getOnlyElement( me.getPolicies() ).getCatalogItemId();
-        Assert.assertNotNull(catalogItemId);
-        // must be the actual source bundle
-        Assert.assertFalse(catalogItemId.equals(me.getCatalogItemId()), "catalog item id is: "+catalogItemId);
-        Assert.assertTrue(catalogItemId.equals(CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY, TEST_VERSION)), "catalog item id is: "+catalogItemId);
-    }
-
-    @Test
-    public void testMoreEntitiesV2FailsWithoutBasicTestOsgiEntitiesBundle() throws Exception {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_PATH);
-
-        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(
-                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-                TEST_VERSION,
-                BROOKLYN_TEST_MORE_ENTITIES_V2_URL);
-        
-        // test load and instantiate
-        try {
-            Entity me = addItemFromCatalog(c2);
-            Assert.fail("Should have failed, with unresolved dependency; instead got "+me);
-        } catch (Exception e) {
-            Assert.assertTrue(e.toString().toLowerCase().contains("unresolved constraint"), "Missing expected text in error: "+e);
-            Assert.assertTrue(e.toString().toLowerCase().contains("wiring.package"), "Missing expected text in error: "+e);
-            Assert.assertTrue(e.toString().toLowerCase().contains("brooklyn.osgi.tests"), "Missing expected text in error: "+e);
-        }
-    }
-    
-    // V2 works with dependency declared, and can load
-    // and has policy, with policy item catalog ID is reasonable
-    @Test
-    public void testMoreEntitiesV2() throws Exception {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_PATH);
-
-        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(
-                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-                TEST_VERSION,
-                BROOKLYN_TEST_MORE_ENTITIES_V2_URL,
-                BROOKLYN_TEST_OSGI_ENTITIES_URL);
-        
-        // test load and instantiate
-        Entity me = addItemFromCatalog(c2);
-        Assert.assertEquals(me.getCatalogItemId(), CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION));
-        
-        assertV2MethodCall(me);
-        assertV2EffectorCall(me);
-        Assert.assertEquals(me.getPolicies().size(), 1, "Wrong number of policies: "+me.getPolicies());
-        
-        String catalogItemId = Iterables.getOnlyElement( me.getPolicies() ).getCatalogItemId();
-        Assert.assertNotNull(catalogItemId);
-        // allow either me's bundle (more) or the actual source bundle
-        Assert.assertTrue(catalogItemId.equals(me.getCatalogItemId()) || catalogItemId.startsWith("brooklyn-test-osgi-entities"));
-    }
-
-    @Test
-    public void testMoreEntitiesV1ThenV2GivesV2() throws Exception {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_PATH);
-
-        addCatalogItemWithTypeAsName(
-                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-                TEST_VERSION,
-                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
-        addCatalogItemWithTypeAsName(
-                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-                TEST_VERSION,
-                BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
-        
-        // test load and instantiate
-        Entity me = addItemFromCatalog( mgmt.getCatalog().getCatalogItem(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION) );
-        
-        assertV2MethodCall(me);
-        assertV2EffectorCall(me);
-        Assert.assertEquals(me.getPolicies().size(), 1, "Wrong number of policies: "+me.getPolicies());
-    }
-
-    @Test
-    public void testMoreEntitiesV2ThenV1GivesV1() throws Exception {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_PATH);
-
-        addCatalogItemWithTypeAsName(
-                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-                TEST_VERSION,
-                BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
-        addCatalogItemWithTypeAsName(
-                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-                TEST_VERSION,
-                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
-        
-        // test load and instantiate
-        Entity me = addItemFromCatalog( mgmt.getCatalog().getCatalogItem(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION) );
-        /*
-         * WARNING - Weird maven-bundle-plugin and OSGi behaviour.  Some caveats:
-         * <p>
-         * (1) When "import-package" declares a version, that is the *minimum* version;
-         *     it may be that semantic versioning is applied, so 1.3.1 = [1.3.1,4.0.0);
-         *     or it may be just a minimum 1.3.1 = [1.3.1,) ... i can't find good docs
-         *     [but see http://www.christianposta.com/blog/?p=241]
-         * <p>
-         * (2) Different versions of maven-bundle-plugin do wildly different things.
-         *     * v1.4.0 attaches the version to import-package (so you get the minimum
-         *       which can cause this test to fail);
-         *     * v2.x does not seem to declare the exported package at all in import-package
-         *       (violating the so-called best practice, see
-         *       http://blog.osgi.org/2007/04/importance-of-exporting-nd-importing.html )
-         *     * v2.4.0 gives a huge list in import/export package, with semver ranges;
-         *       but the other versions seem not to list much and they do NOT have versions
-         * <p>
-         * The tests are all working with 2.5.3 but if version dependencies become any
-         * more intertwined maven-bundle-plugin will almost certainly NOT do the wrong
-         * thing because packages do not have versions. (Ironically, 1.4.0 seems the
-         * best behaved, but for the minimum/semver package version behaviour, and
-         * even that wouldn't be so bad if you're doing semver, or if you figure out
-         * how to override with a _versionpolicy tag!)
-         */
-        assertV1MethodCall(me);
-        assertV1EffectorCall(me);
-        Assert.assertEquals(me.getPolicies().size(), 0, "Wrong number of policies: "+me.getPolicies());
-    }
-
-    @Test
-    public void testUnfazedByMoreEntitiesV1AndV2AndV2EvilTwin() throws Exception {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_PATH);
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH);
-
-        addCatalogItemWithNameAndType("v1",
-                TEST_VERSION,
-                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
-        addCatalogItemWithNameAndType("v2",
-                TEST_VERSION,
-                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-                BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
-        addCatalogItemWithNameAndType("v2-evil", 
-                TEST_VERSION,
-                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-                BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
-
-        // test osgi finding
-        
-        List<Bundle> bundles = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework())
-            .symbolicName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL).findAll();
-        Assert.assertEquals(bundles.size(), 3, "Wrong list of bundles: "+bundles);
-        
-        Maybe<Bundle> preferredVersion = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework())
-            .symbolicName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL).find();
-        Assert.assertTrue(preferredVersion.isPresent());
-        Assert.assertEquals(preferredVersion.get().getVersion().toString(), "0.2.0");
-        
-        Maybe<Bundle> evilVersion = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework()).
-            preferringFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL).find();
-        Assert.assertTrue(evilVersion.isPresent());
-        Assert.assertEquals(evilVersion.get().getVersion().toString(), "0.2.0");
-
-        // test preferredUrl vs requiredUrl
-
-        Maybe<Bundle> versionIgnoresInvalidPreferredUrl = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework())
-            .symbolicName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL)
-            .version(TEST_VERSION)
-            .preferringFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL).find();
-        Assert.assertTrue(versionIgnoresInvalidPreferredUrl.isPresent());
-        Assert.assertEquals(versionIgnoresInvalidPreferredUrl.get().getVersion().toString(), TEST_VERSION);
-
-        Maybe<Bundle> versionRespectsInvalidRequiredUrl = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework())
-            .symbolicName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL)
-            .version(TEST_VERSION)
-            .requiringFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL).find();
-        Assert.assertFalse(versionRespectsInvalidRequiredUrl.isPresent());
-
-        // test entity resolution
-        
-        Entity v2 = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v2", TEST_VERSION) );
-        assertV2MethodCall(v2);
-        assertV2EffectorCall(v2);
-        Assert.assertEquals(v2.getPolicies().size(), 1, "Wrong number of policies: "+v2.getPolicies());
-
-        Entity v2_evil = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v2-evil", TEST_VERSION) );
-        assertV2EvilTwinMethodCall(v2_evil);
-        assertV2EvilTwinEffectorCall(v2_evil);
-        Assert.assertEquals(v2_evil.getPolicies().size(), 1, "Wrong number of policies: "+v2_evil.getPolicies());
-
-        Entity v1 = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v1", TEST_VERSION) );
-        assertV1MethodCall(v1);
-        assertV1EffectorCall(v1);
-        Assert.assertEquals(v1.getPolicies().size(), 0, "Wrong number of policies: "+v1.getPolicies());
-    }
-
-    // TODO versioning (WIP until #92), install both V1 and V2 with version number, and test that both work
-        
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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 f419101..85eaf3d 100644
--- a/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java
+++ b/core/src/test/java/brooklyn/qa/longevity/EntityCleanupLongevityTestFixture.java
@@ -26,6 +26,9 @@ 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.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.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.entity.TestApplication;
 import org.apache.brooklyn.test.entity.TestEntity;
@@ -43,9 +46,6 @@ import brooklyn.internal.storage.impl.BrooklynStorageImpl;
 
 import org.apache.brooklyn.location.basic.SimulatedLocation;
 
-import brooklyn.management.internal.AbstractManagementContext;
-import brooklyn.management.internal.LocalManagementContext;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.task.BasicExecutionManager;
 import brooklyn.util.task.TaskScheduler;
 import brooklyn.util.text.Strings;
@@ -66,7 +66,7 @@ public abstract class EntityCleanupLongevityTestFixture {
     final static long MEMORY_MARGIN_OF_ERROR = 10*1024*1024;
 
     /** Iterations might currently leave behind:
-     * <li> brooklyn.management.usage.ApplicationUsage$ApplicationEvent (one each for started/stopped/destroyed, per app)
+     * <li> org.apache.brooklyn.core.management.usage.ApplicationUsage$ApplicationEvent (one each for started/stopped/destroyed, per app)
      * <li> SingleThreadedScheduler (subscription delivery tag for the entity)
      * <p>
      * Set at 2kb per iter for now. We'd like to drop this to 0 of course!

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
index f5e0052..578164f 100644
--- a/core/src/test/java/brooklyn/util/task/ssh/SshTasksTest.java
+++ b/core/src/test/java/brooklyn/util/task/ssh/SshTasksTest.java
@@ -23,6 +23,7 @@ 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;
@@ -37,7 +38,6 @@ import brooklyn.entity.basic.Entities;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 
-import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommandsIntegrationTest;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
index b19c389..e60310d 100644
--- a/core/src/test/java/brooklyn/util/task/system/SystemTasksTest.java
+++ b/core/src/test/java/brooklyn/util/task/system/SystemTasksTest.java
@@ -21,13 +21,13 @@ 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.management.internal.LocalManagementContext;
 import brooklyn.util.os.Os;
 import brooklyn.util.task.ssh.SshTasks;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
index a9143cc..bd8ef87 100644
--- a/core/src/test/java/brooklyn/util/text/TemplateProcessorTest.java
+++ b/core/src/test/java/brooklyn/util/text/TemplateProcessorTest.java
@@ -21,6 +21,7 @@ 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;
@@ -30,7 +31,6 @@ import org.testng.annotations.Test;
 
 import brooklyn.entity.BrooklynAppUnitTestSupport;
 import brooklyn.event.basic.DependentConfiguration;
-import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.test.FixedLocaleTest;
 
 import com.google.common.collect.ImmutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManager.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManager.java b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManager.java
new file mode 100644
index 0000000..0d0daa5
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManager.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.management.entitlement;
+
+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.entitlement.Entitlements;
+import org.apache.brooklyn.core.management.entitlement.PerUserEntitlementManager;
+
+public class AcmeEntitlementManager extends PerUserEntitlementManager {
+
+    public AcmeEntitlementManager() {
+        // default mode (if no user specified) is root
+        super(Entitlements.root());
+        
+        super.addUser("admin", Entitlements.root());
+        
+        super.addUser("support", Entitlements.readOnly());
+        
+        // metrics can log in but can't really do much else
+        super.addUser("metrics", Entitlements.minimal());
+        
+        // 'navigator' defines a user with a custom entitlement manager allowing 
+        // access to see entities (in the tree) but not to do anything 
+        // or even see any sensor information on those entities
+        super.addUser("navigator", new EntitlementManager() {
+            @Override
+            public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
+                if (Entitlements.SEE_ENTITY.equals(entitlementClass)) return true;
+                return false;
+            }
+        });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManagerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManagerTest.java b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManagerTest.java
new file mode 100644
index 0000000..82fa5bd
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManagerTest.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.management.entitlement;
+
+import java.net.URI;
+
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+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.testng.Assert;
+import org.testng.annotations.Test;
+
+public class AcmeEntitlementManagerTest extends AcmeEntitlementManagerTestFixture {
+
+    protected void addGlobalConfig() {
+        configBag.put(Entitlements.GLOBAL_ENTITLEMENT_MANAGER, AcmeEntitlementManager.class.getName());
+    }
+
+    @Test
+    public void testOtherAuthorizedUserHasAllPermissions() {
+        checkUserHasAllPermissions("other");
+    }
+
+    @Test
+    public void testNullUserHasAllPermissions() {
+        checkUserHasAllPermissions(null);
+    }
+
+    @Test
+    public void testNavigatorHasListPermissionsOnly() {
+        setup(configBag);
+        WebEntitlementContext entitlementContext = new WebEntitlementContext("navigator", "127.0.0.1", URI.create("/X").toString(), "X");
+        Entitlements.setEntitlementContext(entitlementContext);
+        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null));
+        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app));
+        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
+        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+        // and cannot invoke methods
+        confirmEffectorEntitlement(false);
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..5689da2
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/AcmeEntitlementManagerTestFixture.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.management.entitlement;
+
+import java.io.IOException;
+import java.net.URI;
+
+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.ManagementContext;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+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.test.entity.LocalManagementContextForTests;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+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 {
+
+    protected ManagementContext mgmt;
+    protected Application app;
+    protected ConfigBag configBag;
+    
+    public void setup(ConfigBag cfg) {
+        mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty().addFrom(cfg));
+        app = ApplicationBuilder.newManagedApp(EntitySpec.create(BasicApplication.class), mgmt);
+    }
+
+    @BeforeMethod(alwaysRun=true)
+    public void init() throws IOException {
+        Entitlements.clearEntitlementContext();
+        configBag = ConfigBag.newInstance();
+        addGlobalConfig();
+    }
+
+    protected abstract void addGlobalConfig() throws IOException;
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() {
+        Entitlements.clearEntitlementContext();
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+        if (mgmt != null) Entities.destroyAll(mgmt);
+        app = null;
+        mgmt = null;
+    }
+
+    @Test
+    public void testMetricsHasMinimalPermissions() {
+        checkUserHasMinimalPermissions("metrics");
+    }
+    
+    public void checkUserHasMinimalPermissions(String username) {
+        setup(configBag);
+        WebEntitlementContext entitlementContext = new WebEntitlementContext(username, "127.0.0.1", URI.create("/applications").toString(), "H");
+        Entitlements.setEntitlementContext(entitlementContext);
+        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null));
+        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app));
+        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
+        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+        // and can invoke methods
+        confirmEffectorEntitlement(false);
+    }
+
+    @Test
+    public void testSupportHasReadOnlyPermissions() {
+        checkUserHasReadOnlyPermissions("support");
+    }
+    
+    public void checkUserHasReadOnlyPermissions(String username) {
+        setup(configBag);
+        WebEntitlementContext entitlementContext = new WebEntitlementContext(username, "127.0.0.1", URI.create("/X").toString(), "B");
+        Entitlements.setEntitlementContext(entitlementContext);
+        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null));
+        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app));
+        Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
+        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+        // and cannot invoke methods
+        confirmEffectorEntitlement(false);
+    }
+
+    @Test
+    public void testAdminHasAllPermissions() {
+        checkUserHasAllPermissions("admin");
+    }
+    
+    public void checkUserHasAllPermissions(String user) {
+        setup(configBag);
+        WebEntitlementContext entitlementContext = new WebEntitlementContext(user, "127.0.0.1", URI.create("/X").toString(), "A");
+        Entitlements.setEntitlementContext(entitlementContext);
+        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null));
+        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app));
+        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
+        Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+        // and can invoke methods
+        confirmEffectorEntitlement(true);
+    }
+
+    protected void confirmEffectorEntitlement(boolean shouldSucceed) {
+        try {
+            ((BasicApplication)app).start(ImmutableList.<Location>of());
+            checkNoException(shouldSucceed);
+        } catch (Exception e) {
+            checkNotEntitledException(shouldSucceed, e);
+        }
+    }
+
+    private void checkNoException(boolean shouldBeEntitled) {
+        checkNotEntitledException(shouldBeEntitled, null);
+    }
+
+    private void checkNotEntitledException(boolean shouldBeEntitled, Exception e) {
+        if (e==null) {
+            if (shouldBeEntitled) return;
+            Assert.fail("entitlement should have been denied");
+        }
+        
+        Exception notEntitled = Exceptions.getFirstThrowableOfType(e, NotEntitledException.class);
+        if (notEntitled==null)
+            throw Exceptions.propagate(e);
+        if (!shouldBeEntitled) {
+            /* denied, as it should have been */ 
+            return;
+        }
+        Assert.fail("entitlement should have been granted, but was denied: "+e);
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/entitlement/EntitlementsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/entitlement/EntitlementsTest.java b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/EntitlementsTest.java
new file mode 100644
index 0000000..aac83f6
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/EntitlementsTest.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.entitlement;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+import org.apache.brooklyn.core.management.entitlement.Entitlements.EntityAndItem;
+import org.apache.brooklyn.core.management.entitlement.Entitlements.StringAndArgument;
+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.BrooklynProperties;
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.BasicApplication;
+import brooklyn.entity.basic.Entities;
+
+@Test
+public class EntitlementsTest {
+
+    private ManagementContext mgmt;
+    private Application app;
+
+    @BeforeMethod
+    public void setup() {
+        mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty());
+        app = ApplicationBuilder.newManagedApp(EntitySpec.create(BasicApplication.class), mgmt);
+    }
+
+    @AfterMethod
+    public void tearDown() {
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+        if (mgmt != null) Entities.destroyAll(mgmt);
+        app = null;
+        mgmt = null;
+    }
+
+    // allowing
+    public void testAllowingRoot() {
+        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.ROOT);
+        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+    public void testAllowingSeeEntity() {
+        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_ENTITY);
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
+        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+    public void testAllowingInvokeEffector() {
+        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.INVOKE_EFFECTOR);
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
+        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+    public void testAllowingSeeSensor() {
+        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_SENSOR);
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
+        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+    public void testAllowingDeployApplication() {
+        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.DEPLOY_APPLICATION);
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
+        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+    public void testAllowingSeeAllServerInfo() {
+        EntitlementManager allowSeeEntity = Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_ALL_SERVER_INFO);
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.ROOT, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_ENTITY, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.SEE_SENSOR, null));
+        assertFalse(allowSeeEntity.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
+        assertTrue(allowSeeEntity.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+
+    // nonSecret
+    public void testSeeNonSecretSensors() {
+        EntitlementManager seeNonSecretSensors = Entitlements.FineGrainedEntitlements.seeNonSecretSensors();
+        assertFalse(seeNonSecretSensors.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "password")));
+        assertTrue(seeNonSecretSensors.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "any-sensor")));
+    }
+
+    // allOf
+    public void testAllOfWithSeeEntityAndSeeSensors() {
+        EntitlementManager allOf = Entitlements.FineGrainedEntitlements.allOf(
+                Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_ENTITY),
+                Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_SENSOR));
+        assertFalse(allOf.isEntitled(null, Entitlements.ROOT, null));
+        assertTrue(allOf.isEntitled(null, Entitlements.SEE_ENTITY, null));
+        assertFalse(allOf.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
+        assertTrue(allOf.isEntitled(null, Entitlements.SEE_SENSOR, null));
+        assertFalse(allOf.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
+        assertFalse(allOf.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+
+    // anyOf
+    public void testAnyOfWithSeeEntityAndSeeSensors() {
+        EntitlementManager anyOf = Entitlements.FineGrainedEntitlements.anyOf(
+                Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_ENTITY),
+                Entitlements.FineGrainedEntitlements.allowing(Entitlements.SEE_SENSOR));
+        assertFalse(anyOf.isEntitled(null, Entitlements.ROOT, null));
+        assertTrue(anyOf.isEntitled(null, Entitlements.SEE_ENTITY, null));
+        assertFalse(anyOf.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
+        assertTrue(anyOf.isEntitled(null, Entitlements.SEE_SENSOR, null));
+        assertFalse(anyOf.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
+        assertFalse(anyOf.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+
+    // root
+    public void testGlobalRootEntitlement() {
+        EntitlementManager root = Entitlements.root();
+        assertTrue(root.isEntitled(null, Entitlements.ROOT, null));
+        assertTrue(root.isEntitled(null, Entitlements.SEE_ENTITY, null));
+        assertTrue(root.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
+        assertTrue(root.isEntitled(null, Entitlements.SEE_SENSOR, null));
+        assertTrue(root.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
+        assertTrue(root.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+    public void testAppSpecificRootEntitlement() {
+        EntitlementManager root = Entitlements.root();
+        assertTrue(root.isEntitled(null, Entitlements.SEE_ENTITY, app));
+        assertTrue(root.isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
+        assertTrue(root.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "any-sensor")));
+        assertTrue(root.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "password")));
+        assertTrue(root.isEntitled(null, Entitlements.DEPLOY_APPLICATION, Entitlements.EntityAndItem.of(app, null)));
+        assertTrue(root.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+
+    // minimal
+    public void testGlobalMinimalEntitlement() {
+        EntitlementManager minimal = Entitlements.minimal();
+        assertFalse(minimal.isEntitled(null, Entitlements.ROOT, null));
+        assertFalse(minimal.isEntitled(null, Entitlements.SEE_ENTITY, null));
+        assertFalse(minimal.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
+        assertFalse(minimal.isEntitled(null, Entitlements.SEE_SENSOR, null));
+        assertFalse(minimal.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
+        assertFalse(minimal.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+    public void testAppSpecificMinimalEntitlement() {
+        EntitlementManager minimal = Entitlements.minimal();
+        assertFalse(minimal.isEntitled(null, Entitlements.SEE_ENTITY, app));
+        assertFalse(minimal.isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
+        assertFalse(minimal.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "any-sensor")));
+        assertFalse(minimal.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "password")));
+        assertFalse(minimal.isEntitled(null, Entitlements.DEPLOY_APPLICATION, Entitlements.EntityAndItem.of(app, null)));
+        assertFalse(minimal.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+
+    // readOnly
+    public void testGlobalReadOnlyEntitlement() {
+        EntitlementManager readOnly = Entitlements.readOnly();
+        assertFalse(readOnly.isEntitled(null, Entitlements.ROOT, null));
+        assertTrue(readOnly.isEntitled(null, Entitlements.SEE_ENTITY, null));
+        assertFalse(readOnly.isEntitled(null, Entitlements.INVOKE_EFFECTOR, null));
+        assertFalse(readOnly.isEntitled(null, Entitlements.SEE_SENSOR, null));
+        assertFalse(readOnly.isEntitled(null, Entitlements.DEPLOY_APPLICATION, null));
+        assertFalse(readOnly.isEntitled(null, Entitlements.SEE_ALL_SERVER_INFO, null));
+    }
+    public void testAppSpecificReadOnlyEntitlement() {
+        EntitlementManager readOnly = Entitlements.readOnly();
+        assertTrue(readOnly.isEntitled(null, Entitlements.SEE_ENTITY, app));
+        assertFalse(readOnly.isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
+        assertTrue(readOnly.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "any-sensor")));
+        assertFalse(readOnly.isEntitled(null, Entitlements.SEE_SENSOR, Entitlements.EntityAndItem.of(app, "password")));
+        assertFalse(readOnly.isEntitled(null, Entitlements.DEPLOY_APPLICATION, Entitlements.EntityAndItem.of(app, null)));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..bb25740
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/EntityEntitlementTest.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.management.entitlement;
+
+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.ManagementContext;
+import org.apache.brooklyn.core.management.entitlement.Entitlements;
+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.test.entity.LocalManagementContextForTests;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+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 {
+
+    private static final Logger log = LoggerFactory.getLogger(EntityEntitlementTest.class);
+    
+    ManagementContext mgmt;
+    Application app;
+    
+    public void setup(ConfigBag cfg) {
+        mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty().addFrom(cfg));
+        app = ApplicationBuilder.newManagedApp(EntitySpec.create(BasicApplication.class), mgmt);
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() {
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+        if (mgmt != null) Entities.destroyAll(mgmt);
+        app = null;
+        mgmt = null;
+    }
+
+    @Test
+    public void testDefaultRootAllows() {
+        setup(ConfigBag.newInstance());
+        // default "root" access allows ROOT permission, and invoke effector, etc
+        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.ROOT, null));
+        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
+        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
+        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+        // and can invoke methods, without any user/login registered
+        confirmEffectorEntitlement(true);
+        confirmSensorEntitlement(true);
+    }
+
+    @Test
+    public void testExplicitRootAllows() {
+        setup(ConfigBag.newInstance().configure(Entitlements.GLOBAL_ENTITLEMENT_MANAGER, "root"));
+        
+        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.ROOT, null));
+        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
+        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
+        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+        
+        confirmEffectorEntitlement(true);
+        confirmSensorEntitlement(true);
+    }
+
+    @Test
+    public void testReadOnlyThrowsNotEntitled() {
+        try {
+            setup(ConfigBag.newInstance().configure(Entitlements.GLOBAL_ENTITLEMENT_MANAGER, "readonly"));
+            // eventually above call will fail in app creation, but for now the call below falls
+            ((BasicApplication)app).start(ImmutableList.<Location>of());
+            tearDown();
+            
+            Assert.fail("something should have been disallowed");
+        } catch (Exception e) {
+            checkNotEntitledException(e);
+        } finally {
+            app = null;
+            if (mgmt!=null) ((LocalManagementContext)mgmt).terminate();
+            mgmt = null;
+        }
+    }
+    
+    @Test(enabled=false)
+    // this test (and minimal below) currently work fine until tearDown, 
+    // but then of course they don't have entitlement to call 'stop';
+    // (as more entitlement checks are added app creation will fail).
+    // 
+    // TODO these tests should set up a user with the permission,
+    // e.g. so system has root, but 'bob' has read-only, then we can test read-only;
+    // TODO rest api tests, as some of the permissions will only be enforced at REST level
+    public void testReadOnlyAllowsSome() {
+        setup(ConfigBag.newInstance().configure(Entitlements.GLOBAL_ENTITLEMENT_MANAGER, "readonly"));
+        
+        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.ROOT, null));
+        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
+        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
+        Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+        
+        confirmEffectorEntitlement(false);
+        confirmSensorEntitlement(true);
+    }
+    
+    @Test(enabled=false)
+    public void testMinimalDisallows() {
+        setup(ConfigBag.newInstance().configure(Entitlements.GLOBAL_ENTITLEMENT_MANAGER, "minimal"));
+        
+        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.ROOT, null));
+        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
+        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
+        Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+        
+        confirmEffectorEntitlement(false);
+        confirmSensorEntitlement(false);
+    }
+    
+    protected void confirmSensorEntitlement(boolean shouldSucceed) {
+        // TODO... based on above
+        // (though maybe we should test against REST API classes rather than lower level programmatic?
+        // TBC...)
+        log.warn("confirmSensorEntitlement still required!");
+    }
+
+    // TODO specific users tests
+    
+    protected void confirmEffectorEntitlement(boolean shouldSucceed) {
+        try {
+            ((BasicApplication)app).start(ImmutableList.<Location>of());
+            checkNoException(shouldSucceed);
+        } catch (Exception e) {
+            checkNotEntitledException(shouldSucceed, e);
+        }
+    }
+
+    private void checkNoException(boolean shouldBeEntitled) {
+        checkNotEntitledException(shouldBeEntitled, null);
+    }
+
+    private void checkNotEntitledException(Exception e) {
+        checkNotEntitledException(false, e);
+    }
+
+    private void checkNotEntitledException(boolean shouldBeEntitled, Exception e) {
+        if (e==null) {
+            if (shouldBeEntitled) return;
+            Assert.fail("entitlement should have been denied");
+        }
+        
+        Exception notEntitled = Exceptions.getFirstThrowableOfType(e, NotEntitledException.class);
+        if (notEntitled==null)
+            throw Exceptions.propagate(e);
+        if (!shouldBeEntitled) {
+            /* denied, as it should have been */ 
+            return;
+        }
+        Assert.fail("entitlement should have been granted, but was denied: "+e);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManagerPropertiesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManagerPropertiesTest.java b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManagerPropertiesTest.java
new file mode 100644
index 0000000..f420019
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/entitlement/PerUserEntitlementManagerPropertiesTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.management.entitlement;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Properties;
+
+import org.testng.annotations.Test;
+
+
+public class PerUserEntitlementManagerPropertiesTest extends AcmeEntitlementManagerTestFixture {
+
+    protected void addGlobalConfig() throws IOException {
+        Properties moreProps = new Properties();
+        moreProps.load(new StringReader(
+            "brooklyn.entitlements.global=org.apache.brooklyn.core.management.entitlement.PerUserEntitlementManager\n"
+                + "brooklyn.entitlements.perUser.admin=root\n"
+                + "brooklyn.entitlements.perUser.support=readonly\n"
+                + "brooklyn.entitlements.perUser.metrics=minimal"));
+        configBag.putAll(moreProps);
+    }
+    
+    @Test
+    public void testOtherAuthorizedUserHasMinimalPermissions() {
+        checkUserHasMinimalPermissions("other");
+    }
+
+    @Test
+    public void testNullUserHasAllPermissions() {
+        checkUserHasAllPermissions(null);
+    }
+
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerFileBasedTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerFileBasedTest.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerFileBasedTest.java
new file mode 100644
index 0000000..99640b7
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerFileBasedTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import java.io.File;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.rebind.persister.FileBasedObjectStore;
+import brooklyn.util.os.Os;
+
+@Test
+public class HighAvailabilityManagerFileBasedTest extends HighAvailabilityManagerTestFixture {
+
+    private File dir;
+
+    protected FileBasedObjectStore newPersistenceObjectStore() {
+        if (dir!=null)
+            throw new IllegalStateException("Test does not support multiple object stores");
+        dir = Os.newTempDir(getClass());
+        return new FileBasedObjectStore(dir);
+    }
+
+    @Override
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        super.tearDown();
+        dir = Os.deleteRecursively(dir).asNullOrThrowing();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerInMemoryIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerInMemoryIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerInMemoryIntegrationTest.java
new file mode 100644
index 0000000..4b5a5cf
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerInMemoryIntegrationTest.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.rebind.persister.InMemoryObjectStore;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.base.Ticker;
+
+@Test(groups="Integration")
+public class HighAvailabilityManagerInMemoryIntegrationTest extends HighAvailabilityManagerTestFixture {
+
+    private static final Logger log = LoggerFactory.getLogger(HighAvailabilityManagerInMemoryIntegrationTest.class);
+    
+    @Override
+    protected PersistenceObjectStore newPersistenceObjectStore() {
+        return new InMemoryObjectStore();
+    }
+
+    @Override
+    protected Duration getPollPeriod() {
+        return Duration.millis(100);
+    }
+    
+    @Override
+    protected long tickerAdvance(Duration duration) {
+        log.info("sleeping for "+duration);
+        // actually sleep, in order to advance the local time ticker
+        Time.sleep(duration);
+        return super.tickerAdvance(duration);
+    }
+
+    @Override
+    protected Ticker getRemoteTicker() {
+        // use real times
+        return null;
+    }
+
+    @Override
+    @Test(groups="Integration", enabled=false, invocationCount=50) 
+    public void testGetManagementPlaneStatusManyTimes() throws Exception {
+    }
+
+    @Test(groups="Integration")
+    @Override
+    public void testGetManagementPlaneStatus() throws Exception {
+        super.testGetManagementPlaneStatus();
+    }
+    
+    @Test(groups="Integration")
+    @Override
+    public void testDoesNotPromoteIfMasterTimeoutNotExpired() throws Exception {
+        super.testDoesNotPromoteIfMasterTimeoutNotExpired();
+    }
+    
+    @Test(groups="Integration")
+    @Override
+    public void testGetManagementPlaneSyncStateDoesNotThrowNpeBeforePersisterSet() throws Exception {
+        super.testGetManagementPlaneSyncStateDoesNotThrowNpeBeforePersisterSet();
+    }
+    
+    @Test(groups="Integration")
+    @Override
+    public void testGetManagementPlaneSyncStateInfersTimedOutNodeAsFailed() throws Exception {
+        super.testGetManagementPlaneSyncStateInfersTimedOutNodeAsFailed();
+    }
+    
+    @Test(groups="Integration")
+    @Override
+    public void testPromotes() throws Exception {
+        super.testPromotes();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerInMemoryTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerInMemoryTest.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerInMemoryTest.java
new file mode 100644
index 0000000..6ae9dfb
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerInMemoryTest.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import java.util.Collection;
+import java.util.List;
+
+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.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl;
+import org.apache.brooklyn.core.management.internal.LocalManagementContext;
+import org.apache.brooklyn.test.entity.TestApplication;
+import org.apache.brooklyn.test.entity.TestEntity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.rebind.persister.InMemoryObjectStore;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import brooklyn.util.collections.MutableList;
+
+import com.google.common.collect.Iterables;
+
+@Test
+public class HighAvailabilityManagerInMemoryTest extends HighAvailabilityManagerTestFixture {
+
+    private static final Logger log = LoggerFactory.getLogger(HighAvailabilityManagerInMemoryTest.class);
+    
+    protected PersistenceObjectStore newPersistenceObjectStore() {
+        return new InMemoryObjectStore();
+    }
+    
+    public void testGetManagementPlaneStatus() throws Exception {
+        super.testGetManagementPlaneStatus();
+    }
+
+    // extra test that promoteToMaster doesn't interfere with what is managed
+    public void testLocationsStillManagedCorrectlyAfterDoublePromotion() throws NoMachinesAvailableException {
+        HighAvailabilityManagerImpl ha = (HighAvailabilityManagerImpl) managementContext.getHighAvailabilityManager();
+        ha.start(HighAvailabilityMode.MASTER);
+        
+        TestApplication app = TestApplication.Factory.newManagedInstanceForTests(managementContext);
+        
+        LocalhostMachineProvisioningLocation l = app.newLocalhostProvisioningLocation();
+        l.setConfig(TestEntity.CONF_NAME, "sample1");
+        Assert.assertEquals(l.getConfig(TestEntity.CONF_NAME), "sample1");
+        
+        SshMachineLocation l2 = l.obtain();
+        Assert.assertEquals(l2.getConfig(TestEntity.CONF_NAME), "sample1");
+        Assert.assertNotNull(l2.getParent(), "Parent not set after dodgy promoteToMaster");
+        Assert.assertEquals(l2.getParent().getConfig(TestEntity.CONF_NAME), "sample1");
+
+        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).location(l).location(l2));
+        log.info("Entities managed are: "+managementContext.getEntityManager().getEntities());
+        Collection<Location> le = entity.getLocations();
+        log.info("Locs at entity are: "+le);
+        Collection<Location> lm = managementContext.getLocationManager().getLocations();
+        log.info("Locs managed are: "+lm);
+        log.info("            objs: "+identities(lm));
+        Assert.assertNotNull(entity.getManagementSupport().getManagementContext());
+        Assert.assertNotNull( ((EntityInternal)app.getChildren().iterator().next()).getManagementSupport().getManagementContext());
+        Assert.assertTrue( ((EntityInternal)app.getChildren().iterator().next()).getManagementSupport().isDeployed());
+        checkEntitiesHealthy(app, entity);
+        
+        managementContext.getRebindManager().forcePersistNow(true, null);
+        log.info("Test deliberately doing unnecessary extra promoteToMaster");
+        ha.promoteToMaster();
+        
+        log.info("Entities managed are: "+managementContext.getEntityManager().getEntities());
+        Collection<Location> lle = entity.getLocations();
+        log.info("Locs at entity(old) are: "+lle);
+        log.info("                   objs: "+identities(lle));
+        // check entities -- the initial-full promotion previously re-created items, 
+        // and plugged them in as children, but only managed the roots
+        checkEntitiesHealthy(app, entity);
+        
+        // assert what's in the location manager is accurate
+        Collection<Location> llmm = managementContext.getLocationManager().getLocations();
+        log.info("Locs managed are: "+llmm);
+        log.info("            objs: "+identities(llmm));
+        Assert.assertEquals(llmm, lm);
+        SshMachineLocation ll2a = Iterables.getOnlyElement(Iterables.filter(llmm, SshMachineLocation.class));
+        Assert.assertEquals(ll2a.getConfig(TestEntity.CONF_NAME), "sample1");
+        Assert.assertNotNull(ll2a.getParent(), "Parent not set after dodgy promoteToMaster");
+        Assert.assertEquals(ll2a.getParent().getConfig(TestEntity.CONF_NAME), "sample1");
+        
+        // and what's in the location manager is accurate
+        Entity ee = (Entity)managementContext.lookup(entity.getId());
+        Collection<Location> llee = ee.getLocations();
+        log.info("Locs at entity(lookup) are: "+llee);
+        log.info("                      objs: "+identities(llee));
+        SshMachineLocation ll2b = Iterables.getOnlyElement(Iterables.filter(llee, SshMachineLocation.class));
+        Assert.assertEquals(ll2b.getConfig(TestEntity.CONF_NAME), "sample1");
+        Assert.assertNotNull(ll2b.getParent(), "Parent not set after dodgy promoteToMaster");
+        Assert.assertEquals(ll2b.getParent().getConfig(TestEntity.CONF_NAME), "sample1");
+    }
+
+    private void checkEntitiesHealthy(TestApplication app, TestEntity entity) {
+        Assert.assertNotNull(app.getManagementSupport().getManagementContext());
+        Assert.assertTrue( app.getManagementSupport().getManagementContext().isRunning() );
+        
+        Assert.assertNotNull(entity.getManagementSupport().getManagementContext());
+        Assert.assertNotNull( ((EntityInternal)app.getChildren().iterator().next()).getManagementSupport().getManagementContext() );
+        Assert.assertTrue( ((EntityInternal)app.getChildren().iterator().next()).getManagementSupport().isDeployed());
+        Assert.assertTrue( ((EntityInternal)app.getChildren().iterator().next()).getManagementSupport().getManagementContext() instanceof LocalManagementContext );
+    }
+
+    @Test(groups="Integration", invocationCount=50)
+    public void testLocationsStillManagedCorrectlyAfterDoublePromotionManyTimes() throws NoMachinesAvailableException {
+        testLocationsStillManagedCorrectlyAfterDoublePromotion();
+    }
+    
+    private List<String> identities(Collection<?> objs) {
+        List<String> result = MutableList.of();
+        for (Object obj: objs)
+            result.add(Integer.toHexString(System.identityHashCode(obj)));
+        return result;
+    }
+    
+}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/BrooklynObjectManagerInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/BrooklynObjectManagerInternal.java b/core/src/main/java/brooklyn/management/internal/BrooklynObjectManagerInternal.java
deleted file mode 100644
index 2af7e77..0000000
--- a/core/src/main/java/brooklyn/management/internal/BrooklynObjectManagerInternal.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.management.internal;
-
-import org.apache.brooklyn.api.basic.BrooklynObject;
-
-public interface BrooklynObjectManagerInternal<T extends BrooklynObject> {
-
-    ManagementTransitionMode getLastManagementTransitionMode(String itemId);
-    void setManagementTransitionMode(T item, ManagementTransitionMode mode);
-
-    /** 
-     * Begins management for the given rebinded root, recursively; 
-     * if rebinding as a read-only copy, {@link #setReadOnly(T, boolean)} should be called prior to this.
-     */
-    void manageRebindedRoot(T item);
-    
-    void unmanage(final T e, final ManagementTransitionMode info);
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/CollectionChangeListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/CollectionChangeListener.java b/core/src/main/java/brooklyn/management/internal/CollectionChangeListener.java
deleted file mode 100644
index 654debc..0000000
--- a/core/src/main/java/brooklyn/management/internal/CollectionChangeListener.java
+++ /dev/null
@@ -1,24 +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.management.internal;
-
-public interface CollectionChangeListener<Item> {
-    void onItemAdded(Item item);
-    void onItemRemoved(Item item);
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/EffectorUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/EffectorUtils.java b/core/src/main/java/brooklyn/management/internal/EffectorUtils.java
deleted file mode 100644
index fe38cb6..0000000
--- a/core/src/main/java/brooklyn/management/internal/EffectorUtils.java
+++ /dev/null
@@ -1,356 +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.management.internal;
-
-import static brooklyn.util.GroovyJavaMethods.truth;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.BasicParameterType;
-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;
-import com.google.common.collect.Maps;
-
-/**
- * Utility methods for invoking effectors.
- */
-public class EffectorUtils {
-
-    private static final Logger log = LoggerFactory.getLogger(EffectorUtils.class);
-
-    /** prepares arguments for an effector either accepting:
-     *  an array, which should contain the arguments in order, optionally omitting those which have defaults defined;
-     *  or a map, which should contain the arguments by name, again optionally omitting those which have defaults defined,
-     *  and in this case also performing type coercion.
-     */
-    public static Object[] prepareArgsForEffector(Effector<?> eff, Object args) {
-        if (args != null && args.getClass().isArray()) {
-            return prepareArgsForEffectorFromArray(eff, (Object[]) args);
-        }
-        if (args instanceof Map) {
-            return prepareArgsForEffectorFromMap(eff, (Map) args);
-        }
-        log.warn("Deprecated effector invocation style for call to "+eff+", expecting a map or an array, got: "+args);
-        if (log.isDebugEnabled()) {
-            log.debug("Deprecated effector invocation style for call to "+eff+", expecting a map or an array, got: "+args,
-                new Throwable("Trace for deprecated effector invocation style"));
-        }
-        return oldPrepareArgsForEffector(eff, args);
-    }
-
-    /** method used for calls such as   entity.effector(arg1, arg2)
-     * get routed here from AbstractEntity.invokeMethod */
-    private static Object[] prepareArgsForEffectorFromArray(Effector<?> eff, Object args[]) {
-        int newArgsNeeded = eff.getParameters().size();
-        if (args.length==1 && args[0] instanceof Map) {
-            if (newArgsNeeded!=1 || !eff.getParameters().get(0).getParameterClass().isAssignableFrom(args[0].getClass())) {
-                // treat a map in an array as a map passed directly (unless the method takes a single-arg map)
-                // this is to support   effector(param1: val1)
-                return prepareArgsForEffectorFromMap(eff, (Map) args[0]);
-            }
-        }
-        return prepareArgsForEffectorAsMapFromArray(eff, args).values().toArray(new Object[0]);
-    }
-
-    public static Map prepareArgsForEffectorAsMapFromArray(Effector<?> eff, Object args[]) {
-        int newArgsNeeded = eff.getParameters().size();
-        List l = Lists.newArrayList();
-        l.addAll(Arrays.asList(args));
-        Map newArgs = new LinkedHashMap();
-
-        for (int index = 0; index < eff.getParameters().size(); index++) {
-            ParameterType<?> it = eff.getParameters().get(index);
-
-            if (l.size() >= newArgsNeeded) {
-                //all supplied (unnamed) arguments must be used; ignore map
-                newArgs.put(it.getName(), l.remove(0));
-                // TODO do we ignore arguments in the same order that groovy does?
-            } else if (!l.isEmpty() && it.getParameterClass().isInstance(l.get(0))) {
-                //if there are parameters supplied, and type is correct, they get applied before default values
-                //(this is akin to groovy)
-                newArgs.put(it.getName(), l.remove(0));
-            } else if (it instanceof BasicParameterType && ((BasicParameterType)it).hasDefaultValue()) {
-                //finally, default values are used to make up for missing parameters
-                newArgs.put(it.getName(), ((BasicParameterType)it).getDefaultValue());
-            } else {
-                throw new IllegalArgumentException("Invalid arguments (count mismatch) for effector "+eff+": "+args);
-            }
-
-            newArgsNeeded--;
-        }
-        if (newArgsNeeded > 0) {
-            throw new IllegalArgumentException("Invalid arguments (missing "+newArgsNeeded+") for effector "+eff+": "+args);
-        }
-        if (!l.isEmpty()) {
-            throw new IllegalArgumentException("Invalid arguments ("+l.size()+" extra) for effector "+eff+": "+args);
-        }
-        return newArgs;
-    }
-
-    private static Object[] prepareArgsForEffectorFromMap(Effector<?> eff, Map m) {
-        m = Maps.newLinkedHashMap(m); //make editable copy
-        List newArgs = Lists.newArrayList();
-        int newArgsNeeded = eff.getParameters().size();
-
-        for (int index = 0; index < eff.getParameters().size(); index++) {
-            ParameterType<?> it = eff.getParameters().get(index);
-            Object v;
-            if (truth(it.getName()) && m.containsKey(it.getName())) {
-                // argument is in the map
-                v = m.remove(it.getName());
-            } else if (it instanceof BasicParameterType && ((BasicParameterType)it).hasDefaultValue()) {
-                //finally, default values are used to make up for missing parameters
-                v = ((BasicParameterType)it).getDefaultValue();
-            } else {
-                throw new IllegalArgumentException("Invalid arguments (missing argument "+it+") for effector "+eff+": "+m);
-            }
-
-            newArgs.add(TypeCoercions.coerce(v, it.getParameterClass()));
-            newArgsNeeded--;
-        }
-        if (newArgsNeeded>0)
-            throw new IllegalArgumentException("Invalid arguments (missing "+newArgsNeeded+") for effector "+eff+": "+m);
-        return newArgs.toArray(new Object[newArgs.size()]);
-    }
-
-    /**
-     * Takes arguments, and returns an array of arguments suitable for use by the Effector
-     * according to the ParameterTypes it exposes.
-     * <p>
-     * The args can be:
-     * <ol>
-     * <li>an array of ordered arguments
-     * <li>a collection (which will be automatically converted to an array)
-     * <li>a single argument (which will then be wrapped in an array)
-     * <li>a map containing the (named) arguments
-     * <li>an array or collection single entry of a map (treated same as 5 above)
-     * <li>a semi-populated array or collection that also containing a map as first arg -
-     *     uses ordered args in array, but uses named values from map in preference.
-     * <li>semi-populated array or collection, where default values will otherwise be used.
-     * </ol>
-     */
-    public static Object[] oldPrepareArgsForEffector(Effector<?> eff, Object args) {
-        //attempt to coerce unexpected types
-        Object[] argsArray;
-        if (args==null) {
-            argsArray = new Object[0];
-        } else if (args.getClass().isArray()) {
-            argsArray = (Object[]) args;
-        } else {
-            if (args instanceof Collection) {
-                argsArray = ((Collection) args).toArray(new Object[((Collection) args).size()]);
-            } else {
-                argsArray = new Object[] { args };
-            }
-        }
-
-        //if args starts with a map, assume it contains the named arguments
-        //(but only use it when we have insufficient supplied arguments)
-        List l = Lists.newArrayList();
-        l.addAll(Arrays.asList(argsArray));
-        Map m = (argsArray.length > 0 && argsArray[0] instanceof Map ? Maps.newLinkedHashMap((Map) l.remove(0)) : null);
-        List newArgs = Lists.newArrayList();
-        int newArgsNeeded = eff.getParameters().size();
-        boolean mapUsed = false;
-
-        for (int index = 0; index < eff.getParameters().size(); index++) {
-            ParameterType<?> it = eff.getParameters().get(index);
-
-            if (l.size() >= newArgsNeeded) {
-                //all supplied (unnamed) arguments must be used; ignore map
-                newArgs.add(l.remove(0));
-            } else if (truth(m) && truth(it.getName()) && m.containsKey(it.getName())) {
-                //some arguments were not supplied, and this one is in the map
-                newArgs.add(m.remove(it.getName()));
-            } else if (index == 0 && Map.class.isAssignableFrom(it.getParameterClass())) {
-                //if first arg is a map it takes the supplied map
-                newArgs.add(m);
-                mapUsed = true;
-            } else if (!l.isEmpty() && it.getParameterClass().isInstance(l.get(0))) {
-                //if there are parameters supplied, and type is correct, they get applied before default values
-                //(this is akin to groovy)
-                newArgs.add(l.remove(0));
-            } else if (it instanceof BasicParameterType && ((BasicParameterType)it).hasDefaultValue()) {
-                //finally, default values are used to make up for missing parameters
-                newArgs.add(((BasicParameterType)it).getDefaultValue());
-            } else {
-                throw new IllegalArgumentException("Invalid arguments (count mismatch) for effector "+eff+": "+args);
-            }
-
-            newArgsNeeded--;
-        }
-        if (newArgsNeeded > 0) {
-            throw new IllegalArgumentException("Invalid arguments (missing "+newArgsNeeded+") for effector "+eff+": "+args);
-        }
-        if (!l.isEmpty()) {
-            throw new IllegalArgumentException("Invalid arguments ("+l.size()+" extra) for effector "+eff+": "+args);
-        }
-        if (truth(m) && !mapUsed) {
-            throw new IllegalArgumentException("Invalid arguments ("+m.size()+" extra named) for effector "+eff+": "+args);
-        }
-        return newArgs.toArray(new Object[newArgs.size()]);
-    }
-
-    /**
-     * Invokes a method effector so that its progress is tracked. For internal use only, when we know the effector is backed by a method which is local.
-     */
-    public static <T> T invokeMethodEffector(Entity entity, Effector<T> eff, Object[] args) {
-        String name = eff.getName();
-
-        try {
-            if (log.isDebugEnabled()) log.debug("Invoking effector {} on {}", new Object[] {name, entity});
-            if (log.isTraceEnabled()) log.trace("Invoking effector {} on {} with args {}", new Object[] {name, entity, args});
-            EntityManagementSupport mgmtSupport = ((EntityInternal)entity).getManagementSupport();
-            if (!mgmtSupport.isDeployed()) {
-                mgmtSupport.attemptLegacyAutodeployment(name);
-            }
-            ManagementContextInternal mgmtContext = (ManagementContextInternal) ((EntityInternal) entity).getManagementContext();
-
-            mgmtSupport.getEntityChangeListener().onEffectorStarting(eff, args);
-            try {
-                return mgmtContext.invokeEffectorMethodSync(entity, eff, args);
-            } finally {
-                mgmtSupport.getEntityChangeListener().onEffectorCompleted(eff);
-            }
-        } catch (Exception e) {
-            handleEffectorException(entity, eff, e);
-            // (won't return below)
-            return null;
-        }
-    }
-
-    public static void handleEffectorException(Entity entity, Effector<?> effector, Throwable throwable) {
-        String message = "Error invoking " + effector.getName() + " at " + entity;
-        // Avoid throwing a PropagatedRuntimeException that just repeats the last PropagatedRuntimeException.
-        if (throwable instanceof PropagatedRuntimeException &&
-                throwable.getMessage() != null &&
-                throwable.getMessage().startsWith(message)) {
-            throw PropagatedRuntimeException.class.cast(throwable);
-        } else {
-            log.warn(message + ": " + Exceptions.collapseText(throwable));
-            throw new PropagatedRuntimeException(message, throwable);
-        }
-    }
-
-    public static <T> Task<T> invokeEffectorAsync(Entity entity, Effector<T> eff, Map<String,?> parameters) {
-        String name = eff.getName();
-
-        if (log.isDebugEnabled()) log.debug("Invoking-async effector {} on {}", new Object[] { name, entity });
-        if (log.isTraceEnabled()) log.trace("Invoking-async effector {} on {} with args {}", new Object[] { name, entity, parameters });
-        EntityManagementSupport mgmtSupport = ((EntityInternal)entity).getManagementSupport();
-        if (!mgmtSupport.isDeployed()) {
-            mgmtSupport.attemptLegacyAutodeployment(name);
-        }
-        ManagementContextInternal mgmtContext = (ManagementContextInternal) ((EntityInternal)entity).getManagementContext();
-
-        // FIXME seems brittle to have the listeners in the Utils method; better to move into the context.invokeEff
-        // (or whatever the last mile before invoking the effector is - though currently there is not such a canonical place!)
-        mgmtSupport.getEntityChangeListener().onEffectorStarting(eff, parameters);
-        try {
-            return mgmtContext.invokeEffector(entity, eff, parameters);
-        } finally {
-            // FIXME this is really Effector submitted
-            mgmtSupport.getEntityChangeListener().onEffectorCompleted(eff);
-        }
-    }
-
-    /** @deprecated since 0.7.0, not used */
-    @Deprecated
-    public static Effector<?> findEffectorMatching(Entity entity, Method method) {
-        outer: for (Effector<?> effector : entity.getEntityType().getEffectors()) {
-            if (!effector.getName().equals(entity)) continue;
-            if (effector.getParameters().size() != method.getParameterTypes().length) continue;
-            for (int i = 0; i < effector.getParameters().size(); i++) {
-                if (effector.getParameters().get(i).getParameterClass() != method.getParameterTypes()[i]) continue outer;
-            }
-            return effector;
-        }
-        return null;
-    }
-
-    /** @deprecated since 0.7.0, expects parameters but does not use them! */
-    @Deprecated
-    public static Effector<?> findEffectorMatching(Set<Effector<?>> effectors, String effectorName, Map<String, ?> parameters) {
-        // TODO Support overloading: check parameters as well
-        for (Effector<?> effector : effectors) {
-            if (effector.getName().equals(effectorName)) {
-                return effector;
-            }
-        }
-        return null;
-    }
-
-    /** matches effectors by name only (not parameters) */
-    public static Maybe<Effector<?>> findEffector(Collection<? extends Effector<?>> effectors, String effectorName) {
-        for (Effector<?> effector : effectors) {
-            if (effector.getName().equals(effectorName)) {
-                return Maybe.<Effector<?>>of(effector);
-            }
-        }
-        return Maybe.absent(new NoSuchElementException("No effector with name "+effectorName+" (contenders "+effectors+")"));
-    }
-
-    /** matches effectors by name only (not parameters), based on what is declared on the entity static type */
-    public static Maybe<Effector<?>> findEffectorDeclared(Entity entity, String effectorName) {
-        return findEffector(entity.getEntityType().getEffectors(), effectorName);
-    }
-
-    /** @deprecated since 0.7.0 use {@link #getTaskFlagsForEffectorInvocation(Entity, Effector, ConfigBag)} */
-    public static Map<Object,Object> getTaskFlagsForEffectorInvocation(Entity entity, Effector<?> effector) {
-        return getTaskFlagsForEffectorInvocation(entity, effector, null);
-    }
-    
-    /** returns a (mutable) map of the standard flags which should be placed on an effector */
-    public static Map<Object,Object> getTaskFlagsForEffectorInvocation(Entity entity, Effector<?> effector, ConfigBag parameters) {
-        return MutableMap.builder()
-                .put("description", "Invoking effector "+effector.getName()
-                    +" on "+entity.getDisplayName()
-                    +(parameters!=null ? " with parameters "+parameters.getAllConfig() : ""))
-                .put("displayName", effector.getName())
-                .put("tags", MutableList.of(
-                        BrooklynTaskTags.EFFECTOR_TAG, 
-                        BrooklynTaskTags.tagForEffectorCall(entity, effector.getName(), parameters), 
-                        BrooklynTaskTags.tagForTargetEntity(entity)))
-                .build();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/EntityChangeListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/EntityChangeListener.java b/core/src/main/java/brooklyn/management/internal/EntityChangeListener.java
deleted file mode 100644
index 1b67d2d..0000000
--- a/core/src/main/java/brooklyn/management/internal/EntityChangeListener.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.management.internal;
-
-import org.apache.brooklyn.api.entity.Effector;
-import org.apache.brooklyn.api.entity.Feed;
-import org.apache.brooklyn.api.event.AttributeSensor;
-import org.apache.brooklyn.api.policy.Enricher;
-import org.apache.brooklyn.api.policy.Policy;
-
-import brooklyn.config.ConfigKey;
-
-public interface EntityChangeListener {
-
-    // TODO for testing only!
-    public static final EntityChangeListener NOOP = new EntityChangeListener() {
-        @Override public void onChanged() {}
-        @Override public void onAttributeChanged(AttributeSensor<?> attribute) {}
-        @Override public void onConfigChanged(ConfigKey<?> key) {}
-        @Override public void onLocationsChanged() {}
-        @Override public void onMembersChanged() {}
-        @Override public void onTagsChanged() {}
-        @Override public void onChildrenChanged() {}
-        @Override public void onPolicyAdded(Policy policy) {}
-        @Override public void onPolicyRemoved(Policy policy) {}
-        @Override public void onEnricherAdded(Enricher enricher) {}
-        @Override public void onEnricherRemoved(Enricher enricher) {}
-        @Override public void onFeedAdded(Feed feed) {}
-        @Override public void onFeedRemoved(Feed feed) {}
-        @Override public void onEffectorStarting(Effector<?> effector, Object parameters) {}
-        @Override public void onEffectorCompleted(Effector<?> effector) {}
-    };
-    
-    void onChanged();
-
-    void onAttributeChanged(AttributeSensor<?> attribute);
-
-    void onConfigChanged(ConfigKey<?> key);
-
-    void onLocationsChanged();
-    
-    void onTagsChanged();
-
-    void onMembersChanged();
-
-    void onChildrenChanged();
-
-    void onPolicyAdded(Policy policy);
-
-    void onPolicyRemoved(Policy policy);
-
-    void onEnricherAdded(Enricher enricher);
-
-    void onEnricherRemoved(Enricher enricher);
-
-    void onFeedAdded(Feed feed);
-
-    void onFeedRemoved(Feed feed);
-
-    void onEffectorStarting(Effector<?> effector, Object parameters);
-    
-    void onEffectorCompleted(Effector<?> effector);
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/EntityManagementSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/EntityManagementSupport.java b/core/src/main/java/brooklyn/management/internal/EntityManagementSupport.java
deleted file mode 100644
index 596c8b2..0000000
--- a/core/src/main/java/brooklyn/management/internal/EntityManagementSupport.java
+++ /dev/null
@@ -1,478 +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.management.internal;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.Effector;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.Feed;
-import org.apache.brooklyn.api.event.AttributeSensor;
-import org.apache.brooklyn.api.management.ExecutionContext;
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.SubscriptionContext;
-import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
-import org.apache.brooklyn.api.policy.Enricher;
-import org.apache.brooklyn.api.policy.Policy;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.AbstractEntity;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.management.entitlement.Entitlements;
-import brooklyn.management.entitlement.Entitlements.EntityAndItem;
-import brooklyn.management.entitlement.Entitlements.StringAndArgument;
-import brooklyn.management.internal.NonDeploymentManagementContext.NonDeploymentManagementContextMode;
-import brooklyn.util.exceptions.Exceptions;
-
-import com.google.common.annotations.Beta;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Stopwatch;
-
-/**
- * Encapsulates management activities at an entity.
- * <p>
- * On entity deployment, ManagementContext.manage(entity) causes
- * <p>
- * * onManagementStarting(ManagementContext)
- * * onManagementStartingSubscriptions()
- * * onManagementStartingSensorEmissions()
- * * onManagementStartingExecutions()
- * * onManagementStarted() - when all the above is said and done
- * * onManagementStartingHere();
- * <p>
- * on unmanage it hits onManagementStoppingHere() then onManagementStopping().
- * <p>
- * When an entity's management migrates, it invoked onManagementStoppingHere() at the old location,
- * then onManagementStartingHere() at the new location.
- */
-public class EntityManagementSupport {
-
-    private static final Logger log = LoggerFactory.getLogger(EntityManagementSupport.class);
-    
-    public EntityManagementSupport(AbstractEntity entity) {
-        this.entity = entity;
-        nonDeploymentManagementContext = new NonDeploymentManagementContext(entity, NonDeploymentManagementContextMode.PRE_MANAGEMENT);
-    }
-
-    protected transient AbstractEntity entity;
-    NonDeploymentManagementContext nonDeploymentManagementContext;
-    
-    protected transient ManagementContext initialManagementContext;
-    protected transient ManagementContext managementContext;
-    protected transient SubscriptionContext subscriptionContext;
-    protected transient ExecutionContext executionContext;
-    
-    // TODO the application
-    // (elaborate or remove ^^^ ? -AH, Sept 2014)
-    
-    protected final AtomicBoolean managementContextUsable = new AtomicBoolean(false);
-    protected final AtomicBoolean currentlyDeployed = new AtomicBoolean(false);
-    protected final AtomicBoolean everDeployed = new AtomicBoolean(false);
-    protected Boolean readOnly = null;
-    protected final AtomicBoolean managementFailed = new AtomicBoolean(false);
-    
-    private volatile EntityChangeListener entityChangeListener = EntityChangeListener.NOOP;
-
-    /**
-     * Whether this entity is managed (i.e. "onManagementStarting" has been called, so the framework knows about it,
-     * and it has not been unmanaged).
-     */
-    public boolean isDeployed() { return currentlyDeployed.get(); }
-    public boolean isNoLongerManaged() {
-        return wasDeployed() && !isDeployed();
-    }
-    /** whether entity has ever been deployed (managed) */
-    public boolean wasDeployed() { return everDeployed.get(); }
-    
-    @Beta
-    public void setReadOnly(boolean isReadOnly) {
-        if (isDeployed())
-            throw new IllegalStateException("Cannot set read only after deployment");
-        this.readOnly = isReadOnly;
-    }
-
-    /** Whether the entity and its adjuncts should be treated as read-only;
-     * may be null briefly when initializing if RO status is unknown. */
-    @Beta
-    public Boolean isReadOnlyRaw() {
-        return readOnly;
-    }
-
-    /** Whether the entity and its adjuncts should be treated as read-only;
-     * error if initializing and RO status is unknown. */
-    @Beta
-    public boolean isReadOnly() {
-        Preconditions.checkNotNull(readOnly, "Read-only status of %s not yet known", entity);
-        return readOnly;
-    }
-
-    /**
-     * Whether the entity's management lifecycle is complete (i.e. both "onManagementStarting" and "onManagementStarted" have
-     * been called, and it is has not been unmanaged). 
-     */
-    public boolean isFullyManaged() {
-        return (nonDeploymentManagementContext == null) && currentlyDeployed.get();
-    }
-
-    public synchronized void setManagementContext(ManagementContextInternal val) {
-        if (initialManagementContext != null) {
-            throw new IllegalStateException("Initial management context is already set for "+entity+"; cannot change");
-        }
-        if (managementContext != null && !managementContext.equals(val)) {
-            throw new IllegalStateException("Management context is already set for "+entity+"; cannot change");
-        }
-        
-        this.initialManagementContext = checkNotNull(val, "managementContext");
-        if (nonDeploymentManagementContext != null) {
-            nonDeploymentManagementContext.setManagementContext(val);
-        }
-    }
-    
-    public void onRebind(ManagementTransitionInfo info) {
-        nonDeploymentManagementContext.setMode(NonDeploymentManagementContextMode.MANAGEMENT_REBINDING);
-    }
-    
-    public void onManagementStarting(ManagementTransitionInfo info) {
-        try {
-            synchronized (this) {
-                boolean alreadyManaging = isDeployed();
-                
-                if (alreadyManaging) {
-                    log.warn("Already managed: "+entity+" ("+nonDeploymentManagementContext+"); onManagementStarting is no-op");
-                } else if (nonDeploymentManagementContext == null || !nonDeploymentManagementContext.getMode().isPreManaged()) {
-                    throw new IllegalStateException("Not in expected pre-managed state: "+entity+" ("+nonDeploymentManagementContext+")");
-                }
-                if (managementContext != null && !managementContext.equals(info.getManagementContext())) {
-                    throw new IllegalStateException("Already has management context: "+managementContext+"; can't set "+info.getManagementContext());
-                }
-                if (initialManagementContext != null && !initialManagementContext.equals(info.getManagementContext())) {
-                    throw new IllegalStateException("Already has different initial management context: "+initialManagementContext+"; can't set "+info.getManagementContext());
-                }
-                if (alreadyManaging) {
-                    return;
-                }
-                
-                this.managementContext = info.getManagementContext();
-                nonDeploymentManagementContext.setMode(NonDeploymentManagementContextMode.MANAGEMENT_STARTING);
-                
-                if (!isReadOnly()) {
-                    nonDeploymentManagementContext.getSubscriptionManager().setDelegate((AbstractSubscriptionManager) managementContext.getSubscriptionManager());
-                    nonDeploymentManagementContext.getSubscriptionManager().startDelegatingForSubscribing();
-                }
-    
-                managementContextUsable.set(true);
-                currentlyDeployed.set(true);
-                everDeployed.set(true);
-                
-                entityChangeListener = new EntityChangeListenerImpl();
-            }
-            
-            /*
-             * TODO framework starting events - phase 1, including rebind
-             *  - establish hierarchy (child, groups, etc; construction if necessary on rebind)
-             *  - set location
-             *  - set local config values
-             *  - set saved sensor values
-             *  - register subscriptions -- BUT nothing is allowed to execute
-             *  [these operations may be done before we invoke starting also; above can happen in any order;
-             *  sensor _publications_ and executor submissions are queued]
-             *  then:  set the management context and the entity is "managed" from the perspective of external viewers (ManagementContext.isManaged(entity) returns true)
-             */
-            
-            if (!isReadOnly()) {
-                entity.onManagementStarting();
-            }
-        } catch (Throwable t) {
-            managementFailed.set(true);
-            throw Exceptions.propagate(t);
-        }
-    }
-
-    @SuppressWarnings("deprecation")
-    public void onManagementStarted(ManagementTransitionInfo info) {
-        try {
-            synchronized (this) {
-                boolean alreadyManaged = isFullyManaged();
-                
-                if (alreadyManaged) {
-                    log.warn("Already managed: "+entity+" ("+nonDeploymentManagementContext+"); onManagementStarted is no-op");
-                } else if (nonDeploymentManagementContext == null || nonDeploymentManagementContext.getMode() != NonDeploymentManagementContextMode.MANAGEMENT_STARTING) {
-                    throw new IllegalStateException("Not in expected \"management starting\" state: "+entity+" ("+nonDeploymentManagementContext+")");
-                }
-                if (managementContext != info.getManagementContext()) {
-                    throw new IllegalStateException("Already has management context: "+managementContext+"; can't set "+info.getManagementContext());
-                }
-                if (alreadyManaged) {
-                    return;
-                }
-                
-                nonDeploymentManagementContext.setMode(NonDeploymentManagementContextMode.MANAGEMENT_STARTED);
-                
-                /*
-                 * - set derived/inherited config values
-                 * - publish all queued sensors
-                 * - start all queued executions (e.g. subscription delivery)
-                 * [above happens in exactly this order, at each entity]
-                 * then: the entity internally knows it fully managed (ManagementSupport.isManaged() returns true -- though not sure we need that);
-                 * subsequent sensor events and executions occur directly (no queueing)
-                 */
-                
-                if (!isReadOnly()) {
-                    nonDeploymentManagementContext.getSubscriptionManager().startDelegatingForPublishing();
-                }
-                
-                // TODO more of the above
-                // TODO custom started activities
-                // (elaborate or remove ^^^ ? -AH, Sept 2014)
-            }
-            
-            if (!isReadOnly()) {
-                entity.onManagementBecomingMaster();
-                entity.onManagementStarted();
-            }
-            
-            synchronized (this) {
-                nonDeploymentManagementContext = null;
-            }
-        } catch (Throwable t) {
-            managementFailed.set(true);
-            throw Exceptions.propagate(t);
-        }
-    }
-    
-    @SuppressWarnings("deprecation")
-    public void onManagementStopping(ManagementTransitionInfo info) {
-        synchronized (this) {
-            if (managementContext != info.getManagementContext()) {
-                throw new IllegalStateException("onManagementStopping encountered different management context for "+entity+
-                    (!wasDeployed() ? " (wasn't deployed)" : !isDeployed() ? " (no longer deployed)" : "")+
-                    ": "+managementContext+"; expected "+info.getManagementContext()+" (may be a pre-registered entity which was never properly managed)");
-            }
-            Stopwatch startTime = Stopwatch.createStarted();
-            while (!managementFailed.get() && nonDeploymentManagementContext!=null && 
-                    nonDeploymentManagementContext.getMode()==NonDeploymentManagementContextMode.MANAGEMENT_STARTING) {
-                // still becoming managed
-                try {
-                    if (startTime.elapsed(TimeUnit.SECONDS) > 30) {
-                        // emergency fix, 30s timeout for management starting
-                        log.error("Management stopping event "+info+" in "+this+" timed out waiting for start; proceeding to stopping");
-                        break;
-                    }
-                    wait(100);
-                } catch (InterruptedException e) {
-                    Exceptions.propagate(e);
-                }
-            }
-            if (nonDeploymentManagementContext==null) {
-                nonDeploymentManagementContext = new NonDeploymentManagementContext(entity, NonDeploymentManagementContextMode.MANAGEMENT_STOPPING);
-            } else {
-                // already stopped? or not started?
-                nonDeploymentManagementContext.setMode(NonDeploymentManagementContextMode.MANAGEMENT_STOPPING);
-            }
-        }
-        // TODO custom stopping activities
-        // TODO framework stopping events - no more sensors, executions, etc
-        // (elaborate or remove ^^^ ? -AH, Sept 2014)
-        
-        if (!isReadOnly() && info.getMode().isDestroying()) {
-            // if we support remote parent of local child, the following call will need to be properly remoted
-            if (entity.getParent()!=null) entity.getParent().removeChild(entity.getProxyIfAvailable());
-        }
-        // new subscriptions will be queued / not allowed
-        nonDeploymentManagementContext.getSubscriptionManager().stopDelegatingForSubscribing();
-        // new publications will be queued / not allowed
-        nonDeploymentManagementContext.getSubscriptionManager().stopDelegatingForPublishing();
-        
-        if (!isReadOnly()) {
-            entity.onManagementNoLongerMaster();
-            entity.onManagementStopped();
-        }
-    }
-    
-    public void onManagementStopped(ManagementTransitionInfo info) {
-        synchronized (this) {
-            if (managementContext == null && nonDeploymentManagementContext.getMode() == NonDeploymentManagementContextMode.MANAGEMENT_STOPPED) {
-                return;
-            }
-            if (managementContext != info.getManagementContext()) {
-                throw new IllegalStateException("Has different management context: "+managementContext+"; expected "+info.getManagementContext());
-            }
-            getSubscriptionContext().unsubscribeAll();
-            entityChangeListener = EntityChangeListener.NOOP;
-            managementContextUsable.set(false);
-            currentlyDeployed.set(false);
-            executionContext = null;
-            subscriptionContext = null;
-        }
-        
-        // TODO framework stopped activities, e.g. serialize state ?
-        entity.invalidateReferences();
-        
-        synchronized (this) {
-            managementContext = null;
-            nonDeploymentManagementContext.setMode(NonDeploymentManagementContextMode.MANAGEMENT_STOPPED);
-        }
-    }
-
-    @VisibleForTesting
-    @Beta
-    public boolean isManagementContextReal() {
-        return managementContextUsable.get();
-    }
-    
-    public synchronized ManagementContext getManagementContext() {
-        return (managementContextUsable.get()) ? managementContext : nonDeploymentManagementContext;
-    }    
-    
-    public synchronized ExecutionContext getExecutionContext() {
-        if (executionContext!=null) return executionContext;
-        if (managementContextUsable.get()) {
-            executionContext = managementContext.getExecutionContext(entity);
-            return executionContext;
-        }
-        return nonDeploymentManagementContext.getExecutionContext(entity);
-    }
-    public synchronized SubscriptionContext getSubscriptionContext() {
-        if (subscriptionContext!=null) return subscriptionContext;
-        if (managementContextUsable.get()) {
-            subscriptionContext = managementContext.getSubscriptionContext(entity);
-            return subscriptionContext;
-        }
-        return nonDeploymentManagementContext.getSubscriptionContext(entity);
-    }
-    public synchronized EntitlementManager getEntitlementManager() {
-        return getManagementContext().getEntitlementManager();
-    }
-
-    public void attemptLegacyAutodeployment(String effectorName) {
-        synchronized (this) {
-            if (managementContext != null) {
-                log.warn("Autodeployment suggested but not required for " + entity + "." + effectorName);
-                return;
-            }
-            if (entity instanceof Application) {
-                log.warn("Autodeployment with new management context triggered for " + entity + "." + effectorName + " -- will not be supported in future. Explicit manage call required.");
-                if (initialManagementContext != null) {
-                    initialManagementContext.getEntityManager().manage(entity);
-                } else {
-                    Entities.startManagement(entity);
-                }
-                return;
-            }
-        }
-        if ("start".equals(effectorName)) {
-            Entity e=entity;
-            if (e.getParent()!=null && ((EntityInternal)e.getParent()).getManagementSupport().isDeployed()) { 
-                log.warn("Autodeployment in parent's management context triggered for "+entity+"."+effectorName+" -- will not be supported in future. Explicit manage call required.");
-                ((EntityInternal)e.getParent()).getManagementContext().getEntityManager().manage(entity);
-                return;
-            }
-        }
-        log.warn("Autodeployment not available for "+entity+"."+effectorName);
-    }
-    
-    public EntityChangeListener getEntityChangeListener() {
-        return entityChangeListener;
-    }
-    
-    private class EntityChangeListenerImpl implements EntityChangeListener {
-        @Override
-        public void onChanged() {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-        }
-        @Override
-        public void onChildrenChanged() {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-        }
-        @Override
-        public void onLocationsChanged() {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-        }
-        @Override
-        public void onTagsChanged() {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-        }
-        @Override
-        public void onMembersChanged() {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-        }
-        @Override
-        public void onPolicyAdded(Policy policy) {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-            getManagementContext().getRebindManager().getChangeListener().onManaged(policy);
-        }
-        @Override
-        public void onEnricherAdded(Enricher enricher) {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-            getManagementContext().getRebindManager().getChangeListener().onManaged(enricher);
-        }
-        @Override
-        public void onFeedAdded(Feed feed) {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-            getManagementContext().getRebindManager().getChangeListener().onManaged(feed);
-        }
-        @Override
-        public void onPolicyRemoved(Policy policy) {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-            getManagementContext().getRebindManager().getChangeListener().onUnmanaged(policy);
-        }
-        @Override
-        public void onEnricherRemoved(Enricher enricher) {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-            getManagementContext().getRebindManager().getChangeListener().onUnmanaged(enricher);
-        }
-        @Override
-        public void onFeedRemoved(Feed feed) {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-            getManagementContext().getRebindManager().getChangeListener().onUnmanaged(feed);
-        }
-        @Override
-        public void onAttributeChanged(AttributeSensor<?> attribute) {
-            // TODO Could make this more efficient by inspecting the attribute to decide if needs persisted
-            // immediately, or not important, or transient (e.g. do we really need to persist 
-            // request-per-second count for rebind purposes?!)
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-        }
-        @Override
-        public void onConfigChanged(ConfigKey<?> key) {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-        }
-        @Override
-        public void onEffectorStarting(Effector<?> effector, Object parameters) {
-            Entitlements.checkEntitled(getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(entity, StringAndArgument.of(effector.getName(), parameters)));
-        }
-        @Override
-        public void onEffectorCompleted(Effector<?> effector) {
-            getManagementContext().getRebindManager().getChangeListener().onChanged(entity);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return super.toString()+"["+(entity==null ? "null" : entity.getId())+"]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/EntityManagementUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/EntityManagementUtils.java b/core/src/main/java/brooklyn/management/internal/EntityManagementUtils.java
deleted file mode 100644
index 24ece98..0000000
--- a/core/src/main/java/brooklyn/management/internal/EntityManagementUtils.java
+++ /dev/null
@@ -1,326 +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.management.internal;
-
-import io.brooklyn.camp.CampPlatform;
-import io.brooklyn.camp.spi.Assembly;
-import io.brooklyn.camp.spi.AssemblyTemplate;
-import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
-
-import java.io.StringReader;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Callable;
-
-import javax.annotation.Nullable;
-
-import org.apache.brooklyn.api.entity.Application;
-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.api.management.ManagementContext;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator;
-import brooklyn.config.BrooklynServerConfig;
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityFunctions;
-import brooklyn.entity.effector.Effectors;
-import brooklyn.entity.trait.Startable;
-import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
-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;
-
-import com.google.common.annotations.Beta;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-
-/** Utility methods for working with entities and applications */
-public class EntityManagementUtils {
-
-    private static final Logger log = LoggerFactory.getLogger(EntityManagementUtils.class);
-
-    /**
-     * A marker config value which indicates that an application was created automatically
-     * to allow the management of a non-app entity.
-     */
-    public static final ConfigKey<Boolean> WRAPPER_APP_MARKER = ConfigKeys.newBooleanConfigKey("brooklyn.wrapper_app");
-
-    /** creates an application from the given app spec, managed by the given management context */
-    public static <T extends Application> T createUnstarted(ManagementContext mgmt, EntitySpec<T> spec) {
-        T app = mgmt.getEntityManager().createEntity(spec);
-        Entities.startManagement(app, mgmt);
-        return app;
-    }
-
-    /** convenience for accessing camp */
-    public static Maybe<CampPlatform> getCampPlatform(ManagementContext mgmt) {
-        return BrooklynServerConfig.getCampPlatform(mgmt);
-    }
-    
-    /** as {@link #createApplication(ManagementContext, EntitySpec)} but for a YAML spec */
-    public static <T extends Application> T createUnstarted(ManagementContext mgmt, String yaml) {
-        AssemblyTemplate at = getCampPlatform(mgmt).get().pdp().registerDeploymentPlan( new StringReader(yaml) );
-        return createUnstarted(mgmt, at);
-    }
-    
-    /** as {@link #createApplication(ManagementContext, EntitySpec)} but for an assembly template */
-    @SuppressWarnings("unchecked")
-    public static <T extends Application> T createUnstarted(ManagementContext mgmt, AssemblyTemplate at) {
-        CampPlatform camp = getCampPlatform(mgmt).get();
-        AssemblyTemplateInstantiator instantiator;
-        try {
-            instantiator = at.getInstantiator().newInstance();
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-        Assembly assembly;
-        if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
-            BrooklynClassLoadingContext loader = JavaBrooklynClassLoadingContext.create(mgmt);
-            
-            EntitySpec<?> spec = ((AssemblyTemplateSpecInstantiator) instantiator).createSpec(at, camp, loader, true);
-            Entity app = mgmt.getEntityManager().createEntity(spec);
-            Entities.startManagement((Application)app, mgmt);
-            return (T) app;
-        } else {
-            // currently, all brooklyn plans should produce the above; currently this will always throw Unsupported  
-            try {
-                assembly = instantiator.instantiate(at, camp);
-                return (T) mgmt.getEntityManager().getEntity(assembly.getId());
-            } catch (UnsupportedOperationException e) {
-                if (at.getPlatformComponentTemplates()==null || at.getPlatformComponentTemplates().isEmpty()) {
-                    if (at.getCustomAttributes().containsKey("brooklyn.catalog"))
-                        throw new IllegalArgumentException("Unrecognized application blueprint format: expected an application, not a brooklyn.catalog");
-                    throw new IllegalArgumentException("Unrecognized application blueprint format: no services defined");
-                }
-                // map this (expected) error to a nicer message
-                throw new IllegalArgumentException("Unrecognized application blueprint format");
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                throw new IllegalArgumentException("Invalid plan: "+at, e);
-            }
-        }
-    }
-    
-    /** container for operation which creates something and which wants to return both
-     * the items created and any pending create/start task */
-    public static class CreationResult<T,U> {
-        private final T thing;
-        @Nullable private final Task<U> task;
-        public CreationResult(T thing, Task<U> task) {
-            super();
-            this.thing = thing;
-            this.task = task;
-        }
-        
-        protected static <T,U> CreationResult<T,U> of(T thing, @Nullable Task<U> task) {
-            return new CreationResult<T,U>(thing, task);
-        }
-        
-        /** returns the thing/things created */
-        @Nullable public T get() { return thing; }
-        /** associated task, ie the one doing the creation/starting */
-        public Task<U> task() { return task; }
-        public CreationResult<T,U> blockUntilComplete(Duration timeout) { if (task!=null) task.blockUntilEnded(timeout); return this; }
-        public CreationResult<T,U> blockUntilComplete() { if (task!=null) task.blockUntilEnded(); return this; }
-    }
-
-    public static <T extends Application> CreationResult<T,Void> createStarting(ManagementContext mgmt, EntitySpec<T> appSpec) {
-        return start(createUnstarted(mgmt, appSpec));
-    }
-
-    public static CreationResult<? extends Application,Void> createStarting(ManagementContext mgmt, String appSpec) {
-        return start(createUnstarted(mgmt, appSpec));
-    }
-
-    public static CreationResult<? extends Application,Void> createStarting(ManagementContext mgmt, AssemblyTemplate at) {
-        return start(createUnstarted(mgmt, at));
-    }
-
-    public static <T extends Application> CreationResult<T,Void> start(T app) {
-        Task<Void> task = Entities.invokeEffector((EntityLocal)app, app, Startable.START,
-            // locations already set in the entities themselves;
-            // TODO make it so that this arg does not have to be supplied to START !
-            MutableMap.of("locations", MutableList.of()));
-        return CreationResult.of(app, task);
-    }
-    
-    public static CreationResult<List<Entity>, List<String>> addChildren(final EntityLocal parent, String yaml, Boolean start) {
-        if (Boolean.FALSE.equals(start))
-            return CreationResult.of(addChildrenUnstarted(parent, yaml), null);
-        return addChildrenStarting(parent, yaml);
-    }
-    
-    /** adds entities from the given yaml, under the given parent; but does not start them */
-    public static List<Entity> addChildrenUnstarted(final EntityLocal parent, String yaml) {
-        log.debug("Creating child of "+parent+" from yaml:\n{}", yaml);
-        
-        ManagementContext mgmt = parent.getApplication().getManagementContext();
-        CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get();
-        
-        AssemblyTemplate at = camp.pdp().registerDeploymentPlan( new StringReader(yaml) );
-
-        AssemblyTemplateInstantiator instantiator;
-        try {
-            instantiator = at.getInstantiator().newInstance();
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-        if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
-            BrooklynClassLoadingContext loader = JavaBrooklynClassLoadingContext.create(mgmt);
-            EntitySpec<?> specA = ((AssemblyTemplateSpecInstantiator) instantiator).createSpec(at, camp, loader, false);
-
-            // see whether we can promote children
-            List<EntitySpec<?>> specs = MutableList.of();
-            if (hasNoNameOrCustomKeysOrRoot(at, specA)) {
-                // we can promote
-                for (EntitySpec<?> specC: specA.getChildren()) {
-                    collapseSpec(specA, specC);
-                    specs.add(specC);
-                }
-            } else {
-                // if not promoting, set a nice name if needed
-                if (Strings.isEmpty(specA.getDisplayName())) {
-                    int size = specA.getChildren().size();
-                    String childrenCountString = size+" "+(size!=1 ? "children" : "child");
-                    specA.displayName("Dynamically added "+childrenCountString);
-                }
-                specs.add(specA);
-            }
-
-            final List<Entity> children = MutableList.of();
-            for (EntitySpec<?> spec: specs) {
-                Entity child = (Entity)parent.addChild(spec);
-                Entities.manage(child);
-                children.add(child);
-            }
-            
-            return children;
-        } else {
-            throw new IllegalStateException("Spec could not be parsed to supply a compatible instantiator");
-        }
-    }
-
-    public static CreationResult<List<Entity>,List<String>> addChildrenStarting(final EntityLocal parent, String yaml) {
-        final List<Entity> children = addChildrenUnstarted(parent, yaml);
-        String childrenCountString;
-
-        int size = children.size();
-        childrenCountString = size+" "+(size!=1 ? "children" : "child"); 
-
-        TaskBuilder<List<String>> taskM = Tasks.<List<String>>builder().name("add children")
-            .dynamic(true)
-            .tag(BrooklynTaskTags.NON_TRANSIENT_TASK_TAG)
-            .body(new Callable<List<String>>() {
-                @Override public List<String> call() throws Exception {
-                    return ImmutableList.copyOf(Iterables.transform(children, EntityFunctions.id()));
-                }})
-                .description("Add and start "+childrenCountString);
-
-        TaskBuilder<?> taskS = Tasks.builder().parallel(true).name("add (parallel)").description("Start each new entity");
-
-        // autostart if requested
-        for (Entity child: children) {
-            if (child instanceof Startable) {
-                taskS.add(Effectors.invocation(child, Startable.START, ImmutableMap.of("locations", ImmutableList.of())));
-            } else {
-                // include a task, just to give feedback in the GUI
-                taskS.add(Tasks.builder().name("create").description("Skipping start (not a Startable Entity)")
-                    .body(new Runnable() { public void run() {} })
-                    .tag(BrooklynTaskTags.tagForTargetEntity(child))
-                    .build());
-            }
-        }
-        taskM.add(taskS.build());
-        Task<List<String>> task = Entities.submit(parent, taskM.build());
-
-        return CreationResult.of(children, task);
-    }
-
-    /** worker method to combine specs */
-    @Beta //where should this live long-term?
-    public static void collapseSpec(EntitySpec<?> sourceToBeCollapsed, EntitySpec<?> targetToBeExpanded) {
-        if (Strings.isEmpty(targetToBeExpanded.getDisplayName()))
-            targetToBeExpanded.displayName(sourceToBeCollapsed.getDisplayName());
-        if (!sourceToBeCollapsed.getLocations().isEmpty())
-            targetToBeExpanded.locations(sourceToBeCollapsed.getLocations());
-
-        // NB: this clobbers child config; might prefer to deeply merge maps etc
-        // (but this should not be surprising, as unwrapping is often parameterising the nested blueprint, so outer config should dominate) 
-        targetToBeExpanded.configure(sourceToBeCollapsed.getConfig());
-        targetToBeExpanded.configure(sourceToBeCollapsed.getFlags());
-        
-        // TODO copying tags to all entities is not ideal;
-        // in particular the BrooklynTags.YAML_SPEC tag will show all entities if the root has multiple
-        targetToBeExpanded.tags(sourceToBeCollapsed.getTags());
-    }
-
-    /** worker method to help determine whether child/children can be promoted */
-    @Beta //where should this live long-term?
-    public static boolean hasNoNameOrCustomKeysOrRoot(AssemblyTemplate template, EntitySpec<?> spec) {
-        if (Strings.isNonEmpty(template.getName())) {
-            if (spec.getChildren().size()==1) {
-                String childName = Iterables.getOnlyElement(spec.getChildren()).getDisplayName();
-                if (Strings.isEmpty(childName) || childName.equals(template.getName())) {
-                    // if child has no name, or it's the same, could still promote
-                } else {
-                    return false;
-                }
-            } else {
-                // if name set at root and promoting children would be ambiguous, do not promote 
-                return false;
-            }
-        } else if (spec.getChildren().size()>1) {
-            // don't allow multiple children if a name is specified as a root
-            return false;
-        }
-        
-        Set<String> rootAttrs = template.getCustomAttributes().keySet();
-        for (String rootAttr: rootAttrs) {
-            if (rootAttr.equals("brooklyn.catalog") || rootAttr.equals("brooklyn.config")) {
-                // these do not block promotion
-                continue;
-            }
-            if (rootAttr.startsWith("brooklyn.")) {
-                // any others in 'brooklyn' namespace will block promotion
-                return false;
-            }
-            // location is allowed in both, and is copied on promotion
-            // (name also copied)
-            // others are root currently are ignored on promotion; they are usually metadata
-            // TODO might be nice to know what we are excluding
-        }
-        
-        return true;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/EntityManagerInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/EntityManagerInternal.java b/core/src/main/java/brooklyn/management/internal/EntityManagerInternal.java
deleted file mode 100644
index aa55b59..0000000
--- a/core/src/main/java/brooklyn/management/internal/EntityManagerInternal.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.management.internal;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.management.EntityManager;
-
-public interface EntityManagerInternal extends EntityManager, BrooklynObjectManagerInternal<Entity> {
-
-    /** gets all entities currently known to the application, including entities that are not yet managed */
-    Iterable<Entity> getAllEntitiesInApplication(Application application);
-
-    public Iterable<String> getEntityIds();
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/GroovyObservablesPropertyChangeToCollectionChangeAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/GroovyObservablesPropertyChangeToCollectionChangeAdapter.java b/core/src/main/java/brooklyn/management/internal/GroovyObservablesPropertyChangeToCollectionChangeAdapter.java
deleted file mode 100644
index 973e030..0000000
--- a/core/src/main/java/brooklyn/management/internal/GroovyObservablesPropertyChangeToCollectionChangeAdapter.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.management.internal;
-
-import groovy.util.ObservableList;
-
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-
-public class GroovyObservablesPropertyChangeToCollectionChangeAdapter implements PropertyChangeListener {
-    @SuppressWarnings("rawtypes")
-    private final CollectionChangeListener delegate;
-
-    public GroovyObservablesPropertyChangeToCollectionChangeAdapter(@SuppressWarnings("rawtypes") CollectionChangeListener delegate) {
-        this.delegate = delegate;
-    }
-
-    @SuppressWarnings("unchecked")
-    public void propertyChange(PropertyChangeEvent evt) {
-        if (evt instanceof ObservableList.ElementAddedEvent) {
-            delegate.onItemAdded(evt.getNewValue());
-        } else if (evt instanceof ObservableList.ElementRemovedEvent) {
-            delegate.onItemRemoved(evt.getOldValue());
-        } else if (evt instanceof ObservableList.ElementUpdatedEvent) {
-            delegate.onItemRemoved(evt.getOldValue());
-            delegate.onItemAdded(evt.getNewValue());
-        } else if (evt instanceof ObservableList.ElementClearedEvent) {
-            for (Object value : ((ObservableList.ElementClearedEvent) evt).getValues()) {
-                delegate.onItemAdded(value);
-            }
-        } else if(evt instanceof ObservableList.MultiElementAddedEvent ) {
-            for(Object value: ((ObservableList.MultiElementAddedEvent)evt).getValues()){
-                delegate.onItemAdded(value);
-            }
-        }
-    }
-
-    public int hashCode() {
-        return delegate.hashCode();
-    }
-
-    public boolean equals(Object other) {
-        if (other instanceof GroovyObservablesPropertyChangeToCollectionChangeAdapter)
-            return delegate.equals(((GroovyObservablesPropertyChangeToCollectionChangeAdapter) other).delegate);
-        if (other instanceof CollectionChangeListener)
-            return delegate.equals(other);
-        return false;
-    }
-} 
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/internal/LocalAccessManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/LocalAccessManager.java b/core/src/main/java/brooklyn/management/internal/LocalAccessManager.java
deleted file mode 100644
index 696d0f0..0000000
--- a/core/src/main/java/brooklyn/management/internal/LocalAccessManager.java
+++ /dev/null
@@ -1,111 +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.management.internal;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.management.AccessController;
-
-import com.google.common.annotations.Beta;
-
-@Beta
-public class LocalAccessManager implements AccessManager {
-
-    private volatile boolean locationProvisioningAllowed = true;
-    private volatile boolean locationManagementAllowed = true;
-    private volatile boolean entityManagementAllowed = true;
-
-    private final AtomicReference<AccessControllerImpl> controller = new AtomicReference<AccessControllerImpl>();
-
-    public LocalAccessManager() {
-        updateAccessController();
-    }
-
-    @Override
-    public AccessController getAccessController() {
-        return controller.get();
-    }
-    
-    @Override
-    public boolean isLocationProvisioningAllowed() {
-        return locationProvisioningAllowed;
-    }
-
-    @Override
-    public boolean isLocationManagementAllowed() {
-        return locationManagementAllowed;
-    }
-
-    @Override
-    public boolean isEntityManagementAllowed() {
-        return entityManagementAllowed;
-    }
-
-    @Override
-    public void setLocationProvisioningAllowed(boolean allowed) {
-        locationProvisioningAllowed = allowed;
-        updateAccessController();
-    }
-
-    @Override
-    public void setLocationManagementAllowed(boolean allowed) {
-        locationManagementAllowed = allowed;
-        updateAccessController();
-    }
-
-    @Override
-    public void setEntityManagementAllowed(boolean allowed) {
-        entityManagementAllowed = allowed;
-        updateAccessController();
-    }
-
-    private void updateAccessController() {
-        controller.set(new AccessControllerImpl(locationProvisioningAllowed, locationManagementAllowed, entityManagementAllowed));
-    }
-    
-    private static class AccessControllerImpl implements AccessController {
-        private final boolean locationProvisioningAllowed;
-        private final boolean locationManagementAllowed;
-        private final boolean entityManagementAllowed;
-        
-        public AccessControllerImpl(boolean locationProvisioningAllowed, boolean locationManagementAllowed, 
-                boolean entityManagementAllowed) {
-            this.locationProvisioningAllowed = locationProvisioningAllowed;
-            this.locationManagementAllowed = locationManagementAllowed;
-            this.entityManagementAllowed = entityManagementAllowed;
-        }
-
-        @Override
-        public Response canProvisionLocation(Location provisioner) {
-            return (locationProvisioningAllowed ? Response.allowed() : Response.disallowed("location provisioning disabled"));
-        }
-        
-        @Override
-        public Response canManageLocation(Location loc) {
-            return (locationManagementAllowed ? Response.allowed() : Response.disallowed("location management disabled"));
-        }
-        
-        @Override
-        public Response canManageEntity(Entity entity) {
-            return (entityManagementAllowed ? Response.allowed() : Response.disallowed("entity management disabled"));
-        }
-    }
-}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java b/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
deleted file mode 100644
index c4489fe..0000000
--- a/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
+++ /dev/null
@@ -1,1105 +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.management.ha;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.io.IOException;
-import java.net.URI;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-
-import javax.annotation.Nullable;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.rebind.RebindManager;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.management.Task;
-import org.apache.brooklyn.api.management.ha.HighAvailabilityManager;
-import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
-import org.apache.brooklyn.api.management.ha.ManagementNodeState;
-import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
-import org.apache.brooklyn.api.management.ha.MementoCopyMode;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister.Delta;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.BrooklynVersion;
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
-import brooklyn.catalog.internal.CatalogDto;
-import brooklyn.config.BrooklynServerConfig;
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.BrooklynTaskTags;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.entity.rebind.RebindManagerImpl;
-import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils;
-import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils.CreateBackupMode;
-import brooklyn.entity.rebind.persister.PersistenceActivityMetrics;
-import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord;
-import brooklyn.entity.rebind.plane.dto.ManagementPlaneSyncRecordImpl;
-import brooklyn.entity.rebind.plane.dto.ManagementPlaneSyncRecordImpl.Builder;
-import brooklyn.internal.BrooklynFeatureEnablement;
-import brooklyn.management.ha.BasicMasterChooser.AlphabeticMasterChooser;
-import brooklyn.management.internal.BrooklynObjectManagementMode;
-import brooklyn.management.internal.LocalEntityManager;
-import brooklyn.management.internal.LocationManagerInternal;
-import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.management.internal.ManagementTransitionMode;
-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;
-
-import com.google.common.annotations.Beta;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Stopwatch;
-import com.google.common.base.Ticker;
-import com.google.common.collect.Iterables;
-
-/**
- * This is the guts of the high-availability solution in Brooklyn.
- * <p>
- * Multiple brooklyn nodes can be started to form a single management plane, where one node is 
- * designated master and the others are "warm standbys". On termination or failure of the master,
- * the standbys deterministically decide which standby should become master (see {@link MasterChooser}).
- * That standby promotes itself.
- * <p>
- * The management nodes communicate their health/status via the {@link ManagementPlaneSyncRecordPersister}.
- * For example, if using {@link ManagementPlaneSyncRecordPersisterToObjectStore} with a shared blobstore or 
- * filesystem/NFS mount, then each management-node periodically writes its state. 
- * This acts as a heartbeat, being read by the other management-nodes.
- * <p>
- * Promotion to master involves:
- * <ol>
- *   <li>notifying the other management-nodes that it is now master
- *   <li>calling {@link RebindManager#rebind(ClassLoader, org.apache.brooklyn.api.entity.rebind.RebindExceptionHandler, ManagementNodeState)} to read all persisted entity state, and thus reconstitute the entities.
- * </ol>
- * <p>
- * Future improvements in this area will include brooklyn-managing-brooklyn to decide + promote
- * the standby.
- * 
- * @since 0.7.0
- * 
- * @author aled
- */
-@Beta
-public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
-
-    public final ConfigKey<Duration> POLL_PERIOD = ConfigKeys.newConfigKey(Duration.class, "brooklyn.ha.pollPeriod",
-        "How often nodes should poll to detect whether master is healthy", Duration.seconds(1));
-    public final ConfigKey<Duration> HEARTBEAT_TIMEOUT = ConfigKeys.newConfigKey(Duration.class, "brooklyn.ha.heartbeatTimeout",
-        "Maximum allowable time for detection of a peer's heartbeat; if no sign of master after this time, "
-        + "another node may promote itself", Duration.THIRTY_SECONDS);
-    
-    @VisibleForTesting /* only used in tests currently */
-    public static interface PromotionListener {
-        public void promotingToMaster();
-    }
-    
-    private static final Logger LOG = LoggerFactory.getLogger(HighAvailabilityManagerImpl.class);
-
-    private final ManagementContextInternal managementContext;
-    private volatile String ownNodeId;
-    private volatile ManagementPlaneSyncRecordPersister persister;
-    private volatile PromotionListener promotionListener;
-    private volatile MasterChooser masterChooser = new AlphabeticMasterChooser();
-    private volatile Ticker localTickerUtc = new Ticker() {
-        // strictly not a ticker because returns millis UTC, but it works fine even so
-        @Override
-        public long read() {
-            return System.currentTimeMillis();
-        }
-    };
-    private volatile Ticker optionalRemoteTickerUtc = null;
-    
-    private volatile Task<?> pollingTask;
-    private volatile boolean disabled;
-    private volatile boolean running;
-    private volatile ManagementNodeState nodeState = ManagementNodeState.INITIALIZING;
-    private volatile boolean nodeStateTransitionComplete = false;
-    private volatile long priority = 0;
-    
-    private final static int MAX_NODE_STATE_HISTORY = 200;
-    private final List<Map<String,Object>> nodeStateHistory = MutableList.of();
-    
-    private volatile transient Duration pollPeriodLocalOverride;
-    private volatile transient Duration heartbeatTimeoutOverride;
-
-    private volatile ManagementPlaneSyncRecord lastSyncRecord;
-    
-    private volatile PersistenceActivityMetrics managementStateWritePersistenceMetrics = new PersistenceActivityMetrics();
-    private volatile PersistenceActivityMetrics managementStateReadPersistenceMetrics = new PersistenceActivityMetrics();
-    private final long startTimeUtc;
-
-    public HighAvailabilityManagerImpl(ManagementContextInternal managementContext) {
-        this.managementContext = managementContext;
-        startTimeUtc = localTickerUtc.read();
-    }
-
-    @Override
-    public HighAvailabilityManagerImpl setPersister(ManagementPlaneSyncRecordPersister persister) {
-        this.persister = checkNotNull(persister, "persister");
-        return this;
-    }
-    
-    @Override
-    public ManagementPlaneSyncRecordPersister getPersister() {
-        return persister;
-    }
-    
-    protected synchronized Duration getPollPeriod() {
-        if (pollPeriodLocalOverride!=null) return pollPeriodLocalOverride;
-        return managementContext.getBrooklynProperties().getConfig(POLL_PERIOD);
-    }
-    
-    /** Overrides {@link #POLL_PERIOD} from brooklyn config, 
-     * including e.g. {@link Duration#PRACTICALLY_FOREVER} to disable polling;
-     * or <code>null</code> to clear a local override */
-    public HighAvailabilityManagerImpl setPollPeriod(Duration val) {
-        this.pollPeriodLocalOverride = val;
-        if (running) {
-            registerPollTask();
-        }
-        return this;
-    }
-
-    public HighAvailabilityManagerImpl setMasterChooser(MasterChooser val) {
-        this.masterChooser = checkNotNull(val, "masterChooser");
-        return this;
-    }
-
-    public synchronized Duration getHeartbeatTimeout() {
-        if (heartbeatTimeoutOverride!=null) return heartbeatTimeoutOverride;
-        return managementContext.getBrooklynProperties().getConfig(HEARTBEAT_TIMEOUT);
-    }
-    
-    /** Overrides {@link #HEARTBEAT_TIMEOUT} from brooklyn config, 
-     * including e.g. {@link Duration#PRACTICALLY_FOREVER} to prevent failover due to heartbeat absence;
-     * or <code>null</code> to clear a local override */
-    public HighAvailabilityManagerImpl setHeartbeatTimeout(Duration val) {
-        this.heartbeatTimeoutOverride = val;
-        return this;
-    }
-
-    /** A ticker that reads in milliseconds, for populating local timestamps.
-     * Defaults to System.currentTimeMillis(); may be overridden e.g. for testing. */
-    public HighAvailabilityManagerImpl setLocalTicker(Ticker val) {
-        this.localTickerUtc = checkNotNull(val);
-        return this;
-    }
-
-    /** A ticker that reads in milliseconds, for overriding remote timestamps.
-     * Defaults to null which means to use the remote timestamp. 
-     * Only for testing as this records the remote timestamp in the object.
-     * <p>
-     * If this is supplied, one must also set {@link ManagementPlaneSyncRecordPersisterToObjectStore#useRemoteTimestampInMemento()}. */
-    @VisibleForTesting
-    public HighAvailabilityManagerImpl setRemoteTicker(Ticker val) {
-        this.optionalRemoteTickerUtc = val;
-        return this;
-    }
-
-    public HighAvailabilityManagerImpl setPromotionListener(PromotionListener val) {
-        this.promotionListener = checkNotNull(val, "promotionListener");
-        return this;
-    }
-    
-    @Override
-    public boolean isRunning() {
-        return running;
-    }
-
-    @Override
-    public void disabled() {
-        disabled = true;
-        ownNodeId = managementContext.getManagementNodeId();
-        // this is notionally the master, just not running; see javadoc for more info
-        stop(ManagementNodeState.MASTER);
-        
-    }
-
-    @Override
-    public void start(HighAvailabilityMode startMode) {
-        nodeStateTransitionComplete = true;
-        disabled = false;
-        running = true;
-        changeMode(startMode, true, true);
-    }
-    
-    @Override
-    public void changeMode(HighAvailabilityMode startMode) {
-        changeMode(startMode, false, false);
-    }
-    
-    @VisibleForTesting
-    @Beta
-    public void changeMode(HighAvailabilityMode startMode, boolean preventElectionOnExplicitStandbyMode, boolean failOnExplicitModesIfUnusual) {
-        if (!running) {
-            // if was not running then start as disabled mode, then proceed as normal
-            LOG.info("HA changing mode to "+startMode+" from "+getInternalNodeState()+" when not running, forcing an intermediate start as DISABLED then will convert to "+startMode);
-            start(HighAvailabilityMode.DISABLED);
-        }
-        if (getNodeState()==ManagementNodeState.FAILED || getNodeState()==ManagementNodeState.INITIALIZING) {
-            if (startMode!=HighAvailabilityMode.DISABLED) {
-                // if coming from FAILED (or INITIALIZING because we skipped start call) then treat as initializing
-                setInternalNodeState(ManagementNodeState.INITIALIZING);
-            }
-        }
-        
-        ownNodeId = managementContext.getManagementNodeId();
-        // TODO Small race in that we first check, and then we'll do checkMaster() on first poll,
-        // so another node could have already become master or terminated in that window.
-        ManagementNodeSyncRecord existingMaster = hasHealthyMaster();
-        boolean weAreRecognisedAsMaster = existingMaster!=null && ownNodeId.equals(existingMaster.getNodeId());
-        boolean weAreMasterLocally = getInternalNodeState()==ManagementNodeState.MASTER;
-        
-        // catch error in some tests where mgmt context has a different mgmt context
-        if (managementContext.getHighAvailabilityManager()!=this)
-            throw new IllegalStateException("Cannot start an HA manager on a management context with a different HA manager!");
-        
-        if (weAreMasterLocally) {
-            // demotion may be required; do this before triggering an election
-            switch (startMode) {
-            case MASTER:
-            case AUTO:
-            case DISABLED:
-                // no action needed, will do anything necessary below (or above)
-                break;
-            case HOT_STANDBY: 
-            case HOT_BACKUP: 
-            case STANDBY: 
-                demoteTo(ManagementNodeState.of(startMode).get()); break;
-            default:
-                throw new IllegalStateException("Unexpected high availability mode "+startMode+" requested for "+this);
-            }
-        }
-        
-        ManagementNodeState oldState = getInternalNodeState();
-        
-        // now do election
-        switch (startMode) {
-        case AUTO:
-            // don't care; let's start and see if we promote ourselves
-            if (getInternalNodeState()==ManagementNodeState.INITIALIZING) {
-                setInternalNodeState(ManagementNodeState.STANDBY);
-            }
-            publishAndCheck(true);
-            switch (getInternalNodeState()) {
-            case HOT_BACKUP:
-                if (!nodeStateTransitionComplete) throw new IllegalStateException("Cannot switch to AUTO when in the middle of a transition to "+getInternalNodeState());
-                // else change us to standby, desiring to go to hot standby, and continue to below
-                setInternalNodeState(ManagementNodeState.STANDBY);
-                startMode = HighAvailabilityMode.HOT_BACKUP;
-            case HOT_STANDBY:
-            case STANDBY:
-                if (getInternalNodeState()==ManagementNodeState.STANDBY && oldState==ManagementNodeState.INITIALIZING && startMode!=HighAvailabilityMode.HOT_BACKUP
-                        && BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY)) {
-                    // auto requested; not promoted; so it should become hot standby
-                    startMode = HighAvailabilityMode.HOT_STANDBY;
-                }
-                ManagementPlaneSyncRecord newState = loadManagementPlaneSyncRecord(true);
-                String masterNodeId = newState.getMasterNodeId();
-                ManagementNodeSyncRecord masterNodeDetails = newState.getManagementNodes().get(masterNodeId);
-                LOG.info("Management node "+ownNodeId+" running as HA " + getInternalNodeState() + " autodetected"
-                        + (startMode == HighAvailabilityMode.HOT_STANDBY || startMode == HighAvailabilityMode.HOT_BACKUP ? 
-                            " (will change to "+startMode+")" : "")
-                        + ", " +
-                    (Strings.isBlank(masterNodeId) ? "no master currently (other node should promote itself soon)" : "master "
-                        + (existingMaster==null ? "(new) " : "")
-                        + "is "+masterNodeId +
-                        (masterNodeDetails==null || masterNodeDetails.getUri()==null ? " (no url)" : " at "+masterNodeDetails.getUri())));
-                break;
-            case MASTER:
-                LOG.info("Management node "+ownNodeId+" running as HA MASTER autodetected");
-                break;
-            default:
-                throw new IllegalStateException("Management node "+ownNodeId+" set to HA AUTO, encountered unexpected mode "+getInternalNodeState());
-            }
-            break;
-        case MASTER:
-            if (!failOnExplicitModesIfUnusual || existingMaster==null) {
-                promoteToMaster();
-                if (existingMaster!=null) {
-                    LOG.info("Management node "+ownNodeId+" running as HA MASTER explicitly");
-                } else {
-                    LOG.info("Management node "+ownNodeId+" running as HA MASTER explicitly, stealing from "+existingMaster);
-                }
-            } else if (!weAreRecognisedAsMaster) {
-                throw new IllegalStateException("Master already exists; cannot run as master (master "+existingMaster.toVerboseString()+"); "
-                    + "to trigger a promotion, set a priority and demote the current master");
-            } else {
-                LOG.info("Management node "+ownNodeId+" already running as HA MASTER, when set explicitly");
-            }
-            break;
-        case HOT_BACKUP:
-            setInternalNodeState(ManagementNodeState.HOT_BACKUP);
-            // then continue into next block
-        case STANDBY:
-        case HOT_STANDBY:
-            if (startMode!=HighAvailabilityMode.HOT_BACKUP) {
-                if (ManagementNodeState.isHotProxy(getInternalNodeState()) && startMode==HighAvailabilityMode.HOT_STANDBY) {
-                    // if was hot_backup, we can immediately go hot_standby
-                    setInternalNodeState(ManagementNodeState.HOT_STANDBY);
-                } else {
-                    // from any other state, set standby, then perhaps switch to hot_standby later on (or might become master in the next block)
-                    setInternalNodeState(ManagementNodeState.STANDBY);
-                }
-            }
-            if (ManagementNodeState.isStandby(getInternalNodeState())) {
-                if (!preventElectionOnExplicitStandbyMode) {
-                    publishAndCheck(true);
-                }
-                if (failOnExplicitModesIfUnusual && existingMaster==null) {
-                    LOG.error("Management node "+ownNodeId+" detected no master when "+startMode+" requested and existing master required; failing.");
-                    throw new IllegalStateException("No existing master; cannot start as "+startMode);
-                }
-            }
-            String message = "Management node "+ownNodeId+" running as HA "+getNodeState()+" (";
-            if (getNodeState().toString().equals(startMode.toString()))
-                message += "explicitly requested";
-            else if (startMode==HighAvailabilityMode.HOT_STANDBY && getNodeState()==ManagementNodeState.STANDBY)
-                message += "caller requested "+startMode+", will attempt rebind for HOT_STANDBY next";
-            else
-                message += "caller requested "+startMode;
-            
-            if (getNodeState()==ManagementNodeState.MASTER) {
-                message += " but election re-promoted this node)";
-            } else {
-                ManagementPlaneSyncRecord newState = loadManagementPlaneSyncRecord(true);
-                if (Strings.isBlank(newState.getMasterNodeId())) {
-                    message += "); no master currently"; 
-                    if (startMode != HighAvailabilityMode.HOT_BACKUP) message += " (subsequent election may repair)";
-                } else {
-                    message += "); master "+newState.getMasterNodeId();
-                }
-            }
-            LOG.info(message);
-            break;
-        case DISABLED:
-            // safe just to run even if we weren't master
-            LOG.info("Management node "+ownNodeId+" HA DISABLED (was "+getInternalNodeState()+")");
-            demoteTo(ManagementNodeState.FAILED);
-            if (pollingTask!=null) pollingTask.cancel(true);
-            break;
-        default:
-            throw new IllegalStateException("Unexpected high availability mode "+startMode+" requested for "+this);
-        }
-        
-        if ((startMode==HighAvailabilityMode.HOT_STANDBY || startMode==HighAvailabilityMode.HOT_BACKUP)) {
-            if (!ManagementNodeState.isHotProxy(oldState)) {
-                // now transition to hot proxy
-                nodeStateTransitionComplete = false;
-                if (startMode==HighAvailabilityMode.HOT_STANDBY) {
-                    // if it should be hot standby, then we may need to promote
-                    // inform the world that we are transitioning (but not eligible for promotion while going in to hot standby)
-                    // (no harm in doing this twice)
-                    publishHealth();
-                }
-                try {
-                    activateHotProxy(ManagementNodeState.of(startMode).get()).get();
-                    // error above now throws
-                    nodeStateTransitionComplete = true;
-                    publishHealth();
-
-                    if (getNodeState()==ManagementNodeState.HOT_STANDBY || getNodeState()==ManagementNodeState.HOT_BACKUP) {
-                        LOG.info("Management node "+ownNodeId+" now running as HA "+getNodeState()+"; "
-                            + managementContext.getApplications().size()+" application"+Strings.s(managementContext.getApplications().size())+" loaded");
-                    } else {
-                        // shouldn't come here, we should have gotten an error above
-                        LOG.warn("Management node "+ownNodeId+" unable to promote to "+startMode+" (currently "+getNodeState()+"); "
-                            + "(see log for further details)");
-                    }
-                } catch (Exception e) {
-                    LOG.warn("Management node "+ownNodeId+" unable to promote to "+startMode+" (currently "+getNodeState()+"); rethrowing: "+Exceptions.collapseText(e));
-                    nodeStateTransitionComplete = true;
-                    throw Exceptions.propagate(e);
-                }
-            } else {
-                // transitioning among hot proxy states - tell the rebind manager
-                managementContext.getRebindManager().stopReadOnly();
-                managementContext.getRebindManager().startReadOnly(ManagementNodeState.of(startMode).get());
-                nodeStateTransitionComplete = true;
-            }
-        } else {
-            nodeStateTransitionComplete = true;
-        }
-        if (startMode!=HighAvailabilityMode.DISABLED)
-            registerPollTask();
-    }
-
-    @Override
-    public void setPriority(long priority) {
-        this.priority = priority;
-        if (persister!=null) publishHealth();
-    }
-    
-    @Override
-    public long getPriority() {
-        return priority;
-    }
-    
-    @Override
-    public void stop() {
-        LOG.debug("Stopping "+this);
-        stop(ManagementNodeState.TERMINATED);
-    }
-    
-    private void stop(ManagementNodeState newState) {
-        boolean wasRunning = running;
-        
-        running = false;
-        setInternalNodeState(newState);
-        if (pollingTask != null) pollingTask.cancel(true);
-        
-        if (wasRunning) {
-            try {
-                publishHealth();
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                LOG.error("Problem publishing manager-node health on termination (continuing)", e);
-            }
-        }
-    }
-    
-    /** returns the node state this node is trying to be in */
-    public ManagementNodeState getTransitionTargetNodeState() {
-        return getInternalNodeState();
-    }
-    
-    protected ManagementNodeState getInternalNodeState() {
-        return nodeState;
-    }
-    
-    protected void setInternalNodeState(ManagementNodeState newState) {
-        ManagementNodeState oldState = getInternalNodeState();
-        synchronized (nodeStateHistory) {
-            if (this.nodeState != newState) {
-                nodeStateHistory.add(0, MutableMap.<String,Object>of("state", newState, "timestamp", currentTimeMillis()));
-                while (nodeStateHistory.size()>MAX_NODE_STATE_HISTORY) {
-                    nodeStateHistory.remove(nodeStateHistory.size()-1);
-                }
-            }
-            ((RebindManagerImpl)managementContext.getRebindManager()).setAwaitingInitialRebind(running &&
-                (ManagementNodeState.isHotProxy(newState) || newState==ManagementNodeState.MASTER));
-            this.nodeState = newState;
-        }
-        
-        if (ManagementNodeState.isHotProxy(oldState) && !ManagementNodeState.isHotProxy(newState)) {
-            // could perhaps promote standby items on some transitions; but for now we stop the old read-only and re-load them
-            // TODO ideally there'd be an incremental rebind as well as an incremental persist
-            managementContext.getRebindManager().stopReadOnly();
-            clearManagedItems(ManagementTransitionMode.transitioning(BrooklynObjectManagementMode.LOADED_READ_ONLY, BrooklynObjectManagementMode.UNMANAGED_PERSISTED));
-        }
-    }
-
-    @Override
-    public ManagementNodeState getNodeState() {
-        ManagementNodeState myNodeState = getInternalNodeState();
-        if (myNodeState==ManagementNodeState.FAILED) return getInternalNodeState();
-        // if target is master then we claim already being master, to prevent other nodes from taking it
-        // (we may fail subsequently of course)
-        if (myNodeState==ManagementNodeState.MASTER) return myNodeState;
-        
-        if (!nodeStateTransitionComplete) return ManagementNodeState.INITIALIZING;
-        return myNodeState;
-    }
-
-    public ManagementPlaneSyncRecord getLastManagementPlaneSyncRecord() {
-        return lastSyncRecord;
-    }
-    
-    @SuppressWarnings("unchecked")
-    protected void registerPollTask() {
-        final Runnable job = new Runnable() {
-            @Override public void run() {
-                try {
-                    publishAndCheck(false);
-                } catch (Exception e) {
-                    if (running) {
-                        LOG.error("Problem in HA-poller: "+e, e);
-                    } else {
-                        if (LOG.isDebugEnabled()) LOG.debug("Problem in HA-poller, but no longer running: "+e, e);
-                    }
-                } catch (Throwable t) {
-                    LOG.error("Problem in HA-poller: "+t, t);
-                    throw Exceptions.propagate(t);
-                }
-            }
-        };
-        Callable<Task<?>> taskFactory = new Callable<Task<?>>() {
-            @Override public Task<?> call() {
-                return Tasks.builder().dynamic(false).body(job).name("HA poller task").tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
-                    .description("polls HA status to see whether this node should promote").build();
-            }
-        };
-        
-        LOG.debug("Registering poll task for "+this+", period "+getPollPeriod());
-        if (getPollPeriod().equals(Duration.PRACTICALLY_FOREVER)) {
-            // don't schedule - used for tests
-            // (scheduling fires off one initial task in the background before the delay, 
-            // which affects tests that want to know exactly when publishing happens;
-            // TODO would be nice if scheduled task had a "no initial submission" flag )
-        } else {
-            if (pollingTask!=null) pollingTask.cancel(true);
-            
-            ScheduledTask task = new ScheduledTask(MutableMap.of("period", getPollPeriod(), "displayName", "scheduled:[HA poller task]"), taskFactory);
-            pollingTask = managementContext.getExecutionManager().submit(task);
-        }
-    }
-    
-    /** invoked manually when initializing, and periodically thereafter */
-    @VisibleForTesting
-    public synchronized void publishAndCheck(boolean initializing) {
-        publishHealth();
-        checkMaster(initializing);
-    }
-    
-    protected synchronized void publishHealth() {
-        if (persister == null) {
-            LOG.info("Cannot publish management-node health as no persister");
-            return;
-        }
-        
-        Stopwatch timer = Stopwatch.createStarted();
-        try {
-            ManagementNodeSyncRecord memento = createManagementNodeSyncRecord(false);
-            Delta delta = ManagementPlaneSyncRecordDeltaImpl.builder().node(memento).build();
-            persister.delta(delta);
-            managementStateWritePersistenceMetrics.noteSuccess(Duration.of(timer));
-            if (LOG.isTraceEnabled()) LOG.trace("Published management-node health: {}", memento);
-        } catch (Throwable t) {
-            managementStateWritePersistenceMetrics.noteFailure(Duration.of(timer));
-            managementStateWritePersistenceMetrics.noteError(t.toString());
-            LOG.debug("Error publishing management-node health (rethrowing): "+t);
-            throw Exceptions.propagate(t);
-        }
-    }
-    
-    public void publishClearNonMaster() {
-        ManagementPlaneSyncRecord plane = getLastManagementPlaneSyncRecord();
-        if (plane==null || persister==null) {
-            LOG.warn("Cannot clear HA node records; HA not active (or not yet loaded)");
-            return;
-        }
-        brooklyn.management.ha.ManagementPlaneSyncRecordDeltaImpl.Builder db = ManagementPlaneSyncRecordDeltaImpl.builder();
-        for (Map.Entry<String,ManagementNodeSyncRecord> node: plane.getManagementNodes().entrySet()) {
-            // only keep a node if it both claims master and is recognised as master;
-            // else ex-masters who died are kept around!
-            if (!ManagementNodeState.MASTER.equals(node.getValue().getStatus()) || 
-                    !Objects.equal(plane.getMasterNodeId(), node.getValue().getNodeId())) {
-                db.removedNodeId(node.getKey());
-            }
-        }
-        persister.delta(db.build());
-        // then get, so model is updated
-        loadManagementPlaneSyncRecord(true);
-    }
-    
-    protected synchronized void publishDemotion(boolean demotingFromMaster) {
-        checkState(getNodeState() != ManagementNodeState.MASTER, "node status must not be master when demoting", getNodeState());
-        
-        if (persister == null) {
-            LOG.info("Cannot publish management-node health as no persister");
-            return;
-        }
-        
-        ManagementNodeSyncRecord memento = createManagementNodeSyncRecord(false);
-        ManagementPlaneSyncRecordDeltaImpl.Builder deltaBuilder = ManagementPlaneSyncRecordDeltaImpl.builder()
-                .node(memento);
-        if (demotingFromMaster) {
-            deltaBuilder.clearMaster(ownNodeId);
-        }
-        
-        Delta delta = deltaBuilder.build();
-        persister.delta(delta);
-        if (LOG.isTraceEnabled()) LOG.trace("Published management-node health: {}", memento);
-    }
-    
-    /**
-     * Publishes (via {@link #persister}) the state of this management node with itself set to master.
-     */
-    protected synchronized void publishPromotionToMaster() {
-        checkState(getNodeState() == ManagementNodeState.MASTER, "node status must be master on publish, but is %s", getNodeState());
-        
-        if (persister == null) {
-            LOG.info("Cannot publish management-node health as no persister");
-            return;
-        }
-        
-        ManagementNodeSyncRecord memento = createManagementNodeSyncRecord(false);
-        Delta delta = ManagementPlaneSyncRecordDeltaImpl.builder()
-                .node(memento)
-                .setMaster(ownNodeId)
-                .build();
-        persister.delta(delta);
-        if (LOG.isTraceEnabled()) LOG.trace("Published management-node health: {}", memento);
-    }
-    
-    protected boolean isHeartbeatOk(ManagementNodeSyncRecord masterNode, ManagementNodeSyncRecord meNode) {
-        if (masterNode==null) return false;
-        if (meNode==null) {
-            // we can't confirm it's healthy, but it appears so as far as we can tell
-            return true;
-        }
-        Long timestampMaster = masterNode.getRemoteTimestamp();
-        Long timestampMe = meNode.getRemoteTimestamp();
-        if (timestampMaster==null || timestampMe==null) return false;
-        return (timestampMe - timestampMaster) <= getHeartbeatTimeout().toMilliseconds();
-    }
-    
-    protected ManagementNodeSyncRecord hasHealthyMaster() {
-        ManagementPlaneSyncRecord memento = loadManagementPlaneSyncRecord(false);
-        
-        String nodeId = memento.getMasterNodeId();
-        ManagementNodeSyncRecord masterMemento = (nodeId == null) ? null : memento.getManagementNodes().get(nodeId);
-        
-        ManagementNodeSyncRecord ourMemento = memento.getManagementNodes().get(ownNodeId);
-        boolean result = masterMemento != null && masterMemento.getStatus() == ManagementNodeState.MASTER
-                && isHeartbeatOk(masterMemento, ourMemento);
-        
-        if (LOG.isDebugEnabled()) LOG.debug("Healthy-master check result={}; masterId={}; masterMemento={}; ourMemento={}",
-                new Object[] {result, nodeId, (masterMemento == null ? "<none>" : masterMemento.toVerboseString()), (ourMemento == null ? "<none>" : ourMemento.toVerboseString())});
-        
-        return (result ? masterMemento : null);
-    }
-    
-    /**
-     * Looks up the state of all nodes in the management plane, and checks if the master is still ok.
-     * If it's not then determines which node should be promoted to master. If it is ourself, then promotes.
-     */
-    protected void checkMaster(boolean initializing) {
-        ManagementPlaneSyncRecord memento = loadManagementPlaneSyncRecord(false);
-        
-        if (getNodeState()==ManagementNodeState.FAILED || getNodeState()==ManagementNodeState.HOT_BACKUP) {
-            // if failed or hot backup then we can't promote ourselves, so no point in checking who is master
-            return;
-        }
-        
-        String currMasterNodeId = memento.getMasterNodeId();
-        ManagementNodeSyncRecord currMasterNodeRecord = memento.getManagementNodes().get(currMasterNodeId);
-        ManagementNodeSyncRecord ownNodeRecord = memento.getManagementNodes().get(ownNodeId);
-        
-        ManagementNodeSyncRecord newMasterNodeRecord = null;
-        boolean demotingSelfInFavourOfOtherMaster = false;
-        
-        if (currMasterNodeRecord != null && currMasterNodeRecord.getStatus() == ManagementNodeState.MASTER && isHeartbeatOk(currMasterNodeRecord, ownNodeRecord)) {
-            // master seems healthy
-            if (ownNodeId.equals(currMasterNodeId)) {
-                if (LOG.isTraceEnabled()) LOG.trace("Existing master healthy (us): master={}", currMasterNodeRecord.toVerboseString());
-                return;
-            } else {
-                if (ownNodeRecord!=null && ownNodeRecord.getStatus() == ManagementNodeState.MASTER) {
-                    LOG.error("Management node "+ownNodeId+" detected master change, stolen from us, deferring to "+currMasterNodeId);
-                    newMasterNodeRecord = currMasterNodeRecord;
-                    demotingSelfInFavourOfOtherMaster = true;
-                } else {
-                    if (LOG.isTraceEnabled()) LOG.trace("Existing master healthy (remote): master={}", currMasterNodeRecord.toVerboseString());
-                    return;
-                }
-            }
-        } else if (ownNodeRecord == null || !isHeartbeatOk(ownNodeRecord, ownNodeRecord)) {
-            // our heartbeats are also out-of-date! perhaps something wrong with persistence? just log, and don't over-react!
-            if (ownNodeRecord == null) {
-                LOG.error("No management node memento for self ("+ownNodeId+"); perhaps persister unwritable? "
-                        + "Master ("+currMasterNodeId+") reported failed but no-op as cannot tell conclusively");
-            } else {
-                LOG.error("This management node ("+ownNodeId+") memento heartbeats out-of-date; perhaps perister unwritable? "
-                        + "Master ("+currMasterNodeId+") reported failed but no-op as cannot tell conclusively"
-                        + ": self="+ownNodeRecord.toVerboseString());
-            }
-            return;
-        } else if (ownNodeId.equals(currMasterNodeId)) {
-            // we are supposed to be the master, but seem to be unhealthy!
-            LOG.warn("This management node ("+ownNodeId+") supposed to be master but reportedly unhealthy? "
-                    + "no-op as expect other node to fix: self="+ownNodeRecord.toVerboseString());
-            return;
-        }
-        
-        if (demotingSelfInFavourOfOtherMaster) {
-            LOG.debug("Master-change for this node only, demoting "+ownNodeRecord.toVerboseString()+" in favour of official master "+newMasterNodeRecord.toVerboseString());
-            demoteTo(
-                BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY) ?
-                    ManagementNodeState.HOT_STANDBY : ManagementNodeState.STANDBY);
-            return;
-        } else {
-            LOG.debug("Detected master heartbeat timeout. Initiating a new master election. Master was " + currMasterNodeRecord);
-        }
-        
-        // Need to choose a new master
-        newMasterNodeRecord = masterChooser.choose(memento, getHeartbeatTimeout(), ownNodeId);
-        
-        String newMasterNodeId = (newMasterNodeRecord == null) ? null : newMasterNodeRecord.getNodeId();
-        URI newMasterNodeUri = (newMasterNodeRecord == null) ? null : newMasterNodeRecord.getUri();
-        boolean weAreNewMaster = ownNodeId.equals(newMasterNodeId);
-        
-        if (LOG.isDebugEnabled()) {
-            LOG.debug("Management node master-change required: newMaster={}; oldMaster={}; plane={}, self={}; heartbeatTimeout={}", 
-                new Object[] {
-                    (newMasterNodeRecord == null ? "<none>" : newMasterNodeRecord.toVerboseString()),
-                    (currMasterNodeRecord == null ? currMasterNodeId+" (no memento)": currMasterNodeRecord.toVerboseString()),
-                    memento,
-                    ownNodeRecord.toVerboseString(), 
-                    getHeartbeatTimeout()
-                });
-        }
-        String message = "Management node "+ownNodeId+" detected ";
-        String currMasterSummary = currMasterNodeId + "(" + (currMasterNodeRecord==null ? "<none>" : timestampString(currMasterNodeRecord.getRemoteTimestamp())) + ")";
-        if (weAreNewMaster && (ownNodeRecord.getStatus() == ManagementNodeState.MASTER)) {
-            LOG.warn(message + "we must reassert master status, as was stolen and then failed at "+
-                (currMasterNodeRecord==null ? "a node which has gone away" : currMasterSummary));
-            publishPromotionToMaster();
-            publishHealth();
-            return;
-        }
-        
-        if (!initializing) {
-            if (weAreNewMaster) {
-                message += "we should be master, changing from ";
-            }
-            else if (currMasterNodeRecord==null && newMasterNodeId==null) message += "master change attempted but no candidates ";
-            else message += "master change, from ";
-            message += currMasterSummary + " to "
-                + (newMasterNodeId == null ? "<none>" :
-                    (weAreNewMaster ? "us " : "")
-                    + newMasterNodeId + " (" + timestampString(newMasterNodeRecord.getRemoteTimestamp()) + ")" 
-                    + (newMasterNodeUri!=null ? " "+newMasterNodeUri : "")  );
-            // always log, if you're looking at a standby node it's useful to see the new master's URL
-            LOG.info(message);
-        }
-
-        // New master is ourself: promote
-        if (weAreNewMaster) {
-            promoteToMaster();
-        }
-    }
-    
-    private static String timestampString(Long remoteTimestamp) {
-        if (remoteTimestamp==null) return null;
-        return remoteTimestamp+" / "+Time.makeTimeStringRounded( Duration.sinceUtc(remoteTimestamp))+" ago";
-    }
-
-    protected void promoteToMaster() {
-        if (!running) {
-            LOG.warn("Ignoring promote-to-master request, as HighAvailabilityManager is not running");
-            return;
-        }
-        
-        if (promotionListener != null) {
-            try {
-                promotionListener.promotingToMaster();
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                LOG.warn("Problem in promption-listener (continuing)", e);
-            }
-        }
-        setInternalNodeState(ManagementNodeState.MASTER);
-        publishPromotionToMaster();
-        try {
-            managementContext.getRebindManager().rebind(managementContext.getCatalogClassLoader(), null, getInternalNodeState());
-        } catch (Exception e) {
-            LOG.error("Management node "+managementContext.getManagementNodeId()+" enountered problem during rebind when promoting self to master; demoting to FAILED and rethrowing: "+e);
-            demoteTo(ManagementNodeState.FAILED);
-            throw Exceptions.propagate(e);
-        }
-        managementContext.getRebindManager().start();
-    }
-    
-    protected void backupOnDemotionIfNeeded() {
-        if (managementContext.getBrooklynProperties().getConfig(BrooklynServerConfig.PERSISTENCE_BACKUPS_REQUIRED_ON_DEMOTION)) {
-            BrooklynPersistenceUtils.createBackup(managementContext, CreateBackupMode.DEMOTION, MementoCopyMode.LOCAL);
-        }
-    }
-
-    /** @deprecated since 0.7.0, use {@link #demoteTo(ManagementNodeState)} */ @Deprecated
-    protected void demoteToFailed() {
-        demoteTo(ManagementNodeState.FAILED);
-    }
-    /** @deprecated since 0.7.0, use {@link #demoteTo(ManagementNodeState)} */ @Deprecated
-    protected void demoteToStandby(boolean hot) {
-        demoteTo(hot ? ManagementNodeState.HOT_STANDBY : ManagementNodeState.STANDBY);
-    }
-    
-    protected void demoteTo(ManagementNodeState toState) {
-        if (toState!=ManagementNodeState.FAILED && !running) {
-            LOG.warn("Ignoring demote-from-master request, as HighAvailabilityManager is no longer running");
-            return;
-        }
-        boolean wasMaster = (getInternalNodeState() == ManagementNodeState.MASTER);
-        if (wasMaster) backupOnDemotionIfNeeded();
-        // TODO target may be RO ?
-        ManagementTransitionMode mode = ManagementTransitionMode.transitioning(
-            wasMaster ? BrooklynObjectManagementMode.MANAGED_PRIMARY : BrooklynObjectManagementMode.LOADED_READ_ONLY,
-            BrooklynObjectManagementMode.UNMANAGED_PERSISTED);
-
-        nodeStateTransitionComplete = false;
-        
-        switch (toState) {
-        case FAILED: 
-        case HOT_BACKUP:
-        case STANDBY:
-            setInternalNodeState(toState); break;
-        case HOT_STANDBY:
-            setInternalNodeState(ManagementNodeState.STANDBY); break;
-        default:
-            throw new IllegalStateException("Illegal target state: "+toState);
-        }
-        onDemotionStopItems(mode);
-        nodeStateTransitionComplete = true;
-        publishDemotion(wasMaster);
-        
-        if (toState==ManagementNodeState.HOT_BACKUP || toState==ManagementNodeState.HOT_STANDBY) {
-            nodeStateTransitionComplete = false;
-            try {
-                activateHotProxy(toState).get();
-            } finally {
-                nodeStateTransitionComplete = true;
-            }
-            publishHealth();
-        }
-    }
-    
-    protected void onDemotionStopItems(ManagementTransitionMode mode) {
-        // stop persistence and remove all apps etc
-        managementContext.getRebindManager().stopPersistence();
-        managementContext.getRebindManager().stopReadOnly();
-        clearManagedItems(mode);
-        
-        // tasks are cleared as part of unmanaging entities above
-    }
-
-    /** clears all managed items from the management context; same items destroyed as in the course of a rebind cycle */
-    protected void clearManagedItems(ManagementTransitionMode mode) {
-        // start with the root applications
-        for (Application app: managementContext.getApplications()) {
-            if (((EntityInternal)app).getManagementSupport().isDeployed()) {
-                ((LocalEntityManager)((EntityInternal)app).getManagementContext().getEntityManager()).unmanage(app, mode);
-            }
-        }
-        // for active management, call above will remove recursively at present,
-        // but for read-only, and if we stop recursively, go through them all
-        for (Entity entity: managementContext.getEntityManager().getEntities()) {
-            ((LocalEntityManager)managementContext.getEntityManager()).unmanage(entity, mode);
-        }
-    
-        // again, for locations, call unmanage on parents first
-        for (Location loc: managementContext.getLocationManager().getLocations()) {
-            if (loc.getParent()==null)
-                ((LocationManagerInternal)managementContext.getLocationManager()).unmanage(loc, mode);
-        }
-        for (Location loc: managementContext.getLocationManager().getLocations()) {
-            ((LocationManagerInternal)managementContext.getLocationManager()).unmanage(loc, mode);
-        }
-        
-        ((BasicBrooklynCatalog)managementContext.getCatalog()).reset(CatalogDto.newEmptyInstance("<reset-by-ha-status-change>"));
-    }
-    
-    /** @deprecated since 0.7.0, use {@link #activateHotProxy(ManagementNodeState)} */ @Deprecated
-    protected boolean attemptHotStandby() {
-        return activateHotProxy(ManagementNodeState.HOT_STANDBY).getWithoutError();
-    }
-    
-    /** Starts hot standby or hot backup, in foreground
-     * <p>
-     * In the case of the former, the caller is responsible for publishing health afterwards,
-     * but if it fails, this method will {@link #demoteTo(ManagementNodeState)} {@link ManagementNodeState#FAILED}.
-     * <p>
-     * @return whether the requested {@link ManagementNodeState} was possible;
-     * (if not, errors should be stored elsewhere), callers may want to rethrow */
-    protected ReferenceWithError<Boolean> activateHotProxy(ManagementNodeState toState) {
-        try {
-            Preconditions.checkState(nodeStateTransitionComplete==false, "Must be in transitioning state to go into "+toState);
-            setInternalNodeState(toState);
-            managementContext.getRebindManager().startReadOnly(toState);
-            
-            return ReferenceWithError.newInstanceWithoutError(true);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            LOG.warn("Unable to change "+ownNodeId+" to "+toState+", switching to FAILED: "+e, e);
-            demoteTo(ManagementNodeState.FAILED);
-            return ReferenceWithError.newInstanceThrowingError(false, e);
-        }
-    }
-    
-    @Override
-    public ManagementPlaneSyncRecord loadManagementPlaneSyncRecord(boolean useLocalKnowledgeForThisNode) {
-        ManagementPlaneSyncRecord record = loadManagementPlaneSyncRecordInternal(useLocalKnowledgeForThisNode);
-        lastSyncRecord = record;
-        return record; 
-    }
-    
-    private ManagementPlaneSyncRecord loadManagementPlaneSyncRecordInternal(boolean useLocalKnowledgeForThisNode) {
-        if (disabled) {
-            // if HA is disabled, then we are the only node - no persistence; just load a memento to describe this node
-            Builder builder = ManagementPlaneSyncRecordImpl.builder()
-                .node(createManagementNodeSyncRecord(true));
-            if (getTransitionTargetNodeState() == ManagementNodeState.MASTER) {
-                builder.masterNodeId(ownNodeId);
-            }
-            return builder.build();
-        }
-        if (persister == null) {
-            // e.g. web-console may be polling before we've started up
-            LOG.debug("High availablity manager has no persister; returning empty record");
-            return ManagementPlaneSyncRecordImpl.builder().build();
-        }
-        
-        int maxLoadAttempts = 5;
-        Exception lastException = null;
-        Stopwatch timer = Stopwatch.createStarted();
-
-        for (int i = 0; i < maxLoadAttempts; i++) {
-            try {
-                ManagementPlaneSyncRecord result = persister.loadSyncRecord();
-                
-                if (useLocalKnowledgeForThisNode) {
-                    // Report this node's most recent state, and detect AWOL nodes
-                    ManagementNodeSyncRecord me = BasicManagementNodeSyncRecord.builder()
-                        .from(result.getManagementNodes().get(ownNodeId), true)
-                        .from(createManagementNodeSyncRecord(false), true)
-                        .build();
-                    Iterable<ManagementNodeSyncRecord> allNodes = result.getManagementNodes().values();
-                    if (me.getRemoteTimestamp()!=null)
-                        allNodes = Iterables.transform(allNodes, new MarkAwolNodes(me));
-                    Builder builder = ManagementPlaneSyncRecordImpl.builder()
-                        .masterNodeId(result.getMasterNodeId())
-                        .nodes(allNodes);
-                    builder.node(me);
-                    if (getTransitionTargetNodeState() == ManagementNodeState.MASTER) {
-                        builder.masterNodeId(ownNodeId);
-                    }
-                    result = builder.build();
-                }
-                
-                if (i>0) {
-                    managementStateReadPersistenceMetrics.noteError("Succeeded only on attempt "+(i+1)+": "+lastException);
-                }
-                managementStateReadPersistenceMetrics.noteSuccess(Duration.of(timer));
-                return result;
-            } catch (IOException e) {
-                if (i < (maxLoadAttempts - 1)) {
-                    if (LOG.isDebugEnabled()) LOG.debug("Problem loading mangement-plane memento attempt "+(i+1)+"/"+maxLoadAttempts+"; retrying", e);
-                }
-                lastException = e;
-            }
-        }
-        String message = "Failed to load mangement-plane memento "+maxLoadAttempts+" consecutive times";
-        managementStateReadPersistenceMetrics.noteError(message+": "+lastException);
-        managementStateReadPersistenceMetrics.noteFailure(Duration.of(timer));
-
-        throw new IllegalStateException(message, lastException);
-    }
-
-    protected ManagementNodeSyncRecord createManagementNodeSyncRecord(boolean useLocalTimestampAsRemoteTimestamp) {
-        long timestamp = currentTimeMillis();
-        brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord.Builder builder = BasicManagementNodeSyncRecord.builder()
-                .brooklynVersion(BrooklynVersion.get())
-                .nodeId(ownNodeId)
-                .status(getNodeState())
-                .priority(getPriority())
-                .localTimestamp(timestamp)
-                .uri(managementContext.getManagementNodeUri().orNull());
-        if (useLocalTimestampAsRemoteTimestamp)
-            builder.remoteTimestamp(timestamp);
-        else if (optionalRemoteTickerUtc!=null) {
-            builder.remoteTimestamp(optionalRemoteTickerUtc.read());
-        }
-        return builder.build();
-    }
-    
-    /**
-     * Gets the current time, using the {@link #localTickerUtc}. Normally this is equivalent of {@link System#currentTimeMillis()},
-     * but in test environments a custom {@link Ticker} can be injected via {@link #setLocalTicker(Ticker)} to allow testing of
-     * specific timing scenarios.
-     */
-    protected long currentTimeMillis() {
-        return localTickerUtc.read();
-    }
-
-    /**
-     * Infers the health of a node - if it last reported itself as healthy (standby or master), but we haven't heard 
-     * from it in a long time then report that node as failed; otherwise report its health as-is.
-     */
-    private class MarkAwolNodes implements Function<ManagementNodeSyncRecord, ManagementNodeSyncRecord> {
-        private final ManagementNodeSyncRecord referenceNode;
-        private MarkAwolNodes(ManagementNodeSyncRecord referenceNode) {
-            this.referenceNode = referenceNode;
-        }
-        @Nullable
-        @Override
-        public ManagementNodeSyncRecord apply(@Nullable ManagementNodeSyncRecord input) {
-            if (input == null) return null;
-            if (!(input.getStatus() == ManagementNodeState.STANDBY || input.getStatus() == ManagementNodeState.HOT_STANDBY || input.getStatus() == ManagementNodeState.MASTER || input.getStatus() == ManagementNodeState.HOT_BACKUP)) return input;
-            if (isHeartbeatOk(input, referenceNode)) return input;
-            return BasicManagementNodeSyncRecord.builder()
-                    .from(input)
-                    .status(ManagementNodeState.FAILED)
-                    .build();
-        }
-    }
-    
-    @Override
-    public String toString() {
-        return super.toString()+"[node:"+ownNodeId+";running="+running+"]";
-    }
-    
-    @Override
-    public Map<String,Object> getMetrics() {
-        Map<String,Object> result = MutableMap.of();
-        
-        result.put("state", getNodeState());
-        result.put("uptime", Time.makeTimeStringRounded(Duration.millis(currentTimeMillis()-startTimeUtc)));
-        result.put("currentTimeUtc", currentTimeMillis());
-        result.put("startTimeUtc", startTimeUtc);
-        result.put("highAvailability", MutableMap.<String,Object>of(
-            "priority", getPriority(),
-            "pollPeriod", getPollPeriod().toMilliseconds(),
-            "heartbeatTimeout", getHeartbeatTimeout().toMilliseconds(),
-            "history", nodeStateHistory));
-        
-        result.putAll(managementContext.getRebindManager().getMetrics());
-        result.put("managementStatePersistence", 
-            MutableMap.of("read", managementStateReadPersistenceMetrics, "write", managementStateWritePersistenceMetrics));
-        
-        return result;
-    }
-    
-    @Override
-    public long getLastStateChange() {
-        if (nodeStateHistory.size() > 0) {
-            return (Long)nodeStateHistory.get(0).get("timestamp");
-        } else {
-            return 0;
-        }
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordDeltaImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordDeltaImpl.java b/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordDeltaImpl.java
deleted file mode 100644
index 029ffb0..0000000
--- a/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordDeltaImpl.java
+++ /dev/null
@@ -1,122 +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.management.ha;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.util.Collection;
-
-import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister.Delta;
-
-import com.google.common.annotations.Beta;
-import com.google.common.collect.Sets;
-
-/**
- * @since 0.7.0
- * 
- * @author aled
- */
-@Beta
-public class ManagementPlaneSyncRecordDeltaImpl implements Delta {
-    
-    public static Builder builder() {
-        return new Builder();
-    }
-    
-    public static class Builder {
-        private Collection<ManagementNodeSyncRecord> nodes = Sets.newLinkedHashSet();
-        private Collection <String> removedNodeIds = Sets.newLinkedHashSet();
-        private MasterChange masterChange = MasterChange.NO_CHANGE;
-        private String master;
-        private String expectedOldMaster;
-        
-        public Builder node(ManagementNodeSyncRecord node) {
-            nodes.add(checkNotNull(node, "node")); return this;
-        }
-        public Builder removedNodeId(String id) {
-            removedNodeIds.add(checkNotNull(id, "id")); return this;
-        }
-        public Builder setMaster(String nodeId) {
-            masterChange = MasterChange.SET_MASTER;
-            master = checkNotNull(nodeId, "masterId");
-            return this;
-        }
-        public Builder clearMaster(String optionalExpectedNodeId) {
-            masterChange = MasterChange.CLEAR_MASTER;
-            this.expectedOldMaster = optionalExpectedNodeId;
-            return this;
-        }
-        public Delta build() {
-            return new ManagementPlaneSyncRecordDeltaImpl(this);
-        }
-    }
-    
-    private final Collection<ManagementNodeSyncRecord> nodes;
-    private final Collection <String> removedNodeIds;
-    private final MasterChange masterChange;
-    private String masterId;
-    private String expectedOldMaster;
-    
-    ManagementPlaneSyncRecordDeltaImpl(Builder builder) {
-        nodes = builder.nodes;
-        removedNodeIds = builder.removedNodeIds;
-        masterChange = builder.masterChange;
-        masterId = builder.master;
-        this.expectedOldMaster = builder.expectedOldMaster;
-        checkState((masterChange == MasterChange.SET_MASTER) ? (masterId != null) : (masterId == null), 
-                "invalid combination: change=%s; masterId=%s", masterChange, masterId);
-    }
-    
-    @Override
-    public Collection<ManagementNodeSyncRecord> getNodes() {
-        return nodes;
-    }
-
-    @Override
-    public Collection<String> getRemovedNodeIds() {
-        return removedNodeIds;
-    }
-
-    @Override
-    public MasterChange getMasterChange() {
-        return masterChange;
-    }
-
-    @Override
-    public String getNewMasterOrNull() {
-        return masterId;
-    }
-    
-    @Override
-    public String getExpectedMasterToClear() {
-        return expectedOldMaster;
-    }
-    
-    @Override
-    public String toString() {
-        return getClass().getCanonicalName()+"["+
-            (masterChange!=null && masterChange != MasterChange.NO_CHANGE ? 
-                masterChange+": "+expectedOldMaster+"->"+masterId+"; " : "")+
-            "nodes: "+nodes+
-            (removedNodeIds!=null && !removedNodeIds.isEmpty() ? "; removing: "+removedNodeIds : "")
-            +"]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java b/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java
deleted file mode 100644
index dff815e..0000000
--- a/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java
+++ /dev/null
@@ -1,355 +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.management.ha;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.io.IOException;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-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.api.management.ha.ManagementNodeSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.rebind.persister.MementoSerializer;
-import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessorWithLock;
-import brooklyn.entity.rebind.persister.RetryingMementoSerializer;
-import brooklyn.entity.rebind.persister.StoreObjectAccessorLocking;
-import brooklyn.entity.rebind.persister.XmlMementoSerializer;
-import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord;
-import brooklyn.entity.rebind.plane.dto.ManagementPlaneSyncRecordImpl;
-import brooklyn.util.exceptions.Exceptions;
-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.annotations.VisibleForTesting;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.Maps;
-
-/**
- * Structure of files is:
- * <ul>
- *   <li>{@code plane/} - top-level directory
- *     <ul>
- *       <li>{@code master} - contains the id of the management-node that is currently master
- *       <li>{@code change.log} - log of changes made
- *       <li>{@code nodes/} - sub-directory, containing one file per management-node
- *         <ul>
- *           <li>{@code a9WiuVKp} - file named after the management-node's id, containing the management node's current state
- *           <li>{@code E1eDXQF3}
- *         </ul>
- *     </ul>
- * </ul>
- * 
- * All writes are done synchronously.
- * 
- * @since 0.7.0
- * 
- * @author aled
- */
-@Beta
-public class ManagementPlaneSyncRecordPersisterToObjectStore implements ManagementPlaneSyncRecordPersister {
-
-    // TODO Multiple node appending to change.log could cause strange interleaving, or perhaps even data loss?
-    // But this file is not critical to functionality.
-
-    // TODO Should ManagementPlaneSyncRecordPersister.Delta be different so can tell what is a significant event,
-    // and thus log it in change.log - currently only subset of significant things being logged.
-
-    private static final Logger LOG = LoggerFactory.getLogger(ManagementPlaneSyncRecordPersisterToObjectStore.class);
-
-    private static final Duration SHUTDOWN_TIMEOUT = Duration.TEN_SECONDS;
-    private static final Duration SYNC_WRITE_TIMEOUT = Duration.TEN_SECONDS;
-    public static final String NODES_SUB_PATH = "nodes";
-
-    // TODO Leak if we go through lots of managers; but tiny!
-    private final ConcurrentMap<String, StoreObjectAccessorWithLock> nodeWriters = Maps.newConcurrentMap();
-
-    private StoreObjectAccessorWithLock masterWriter;
-    private StoreObjectAccessorWithLock changeLogWriter;
-
-    private ManagementContext mgmt;
-    private final PersistenceObjectStore objectStore;
-    private final MementoSerializer<Object> serializer;
-
-    private static final int MAX_SERIALIZATION_ATTEMPTS = 5;
-
-    private boolean started = false;
-    private volatile boolean running = true;
-    
-    @VisibleForTesting
-    /** allows, when testing, to be able to override file times / blobstore times with time from the ticker */
-    private boolean preferRemoteTimestampInMemento = false;
-
-    /**
-     * @param mgmt not used much at present but handy to ensure we know it so that obj store is prepared
-     * @param objectStore the objectStore use to read/write management-plane data;
-     *   this must have been {@link PersistenceObjectStore#prepareForSharedUse(brooklyn.entity.rebind.persister.PersistMode, HighAvailabilityMode)}
-     * @param classLoader ClassLoader to use when deserializing data
-     */
-    public ManagementPlaneSyncRecordPersisterToObjectStore(ManagementContext mgmt, PersistenceObjectStore objectStore, ClassLoader classLoader) {
-        this.mgmt = mgmt;
-        this.objectStore = checkNotNull(objectStore, "objectStore");
-
-        MementoSerializer<Object> rawSerializer = new XmlMementoSerializer<Object>(checkNotNull(classLoader, "classLoader"));
-        this.serializer = new RetryingMementoSerializer<Object>(rawSerializer, MAX_SERIALIZATION_ATTEMPTS);
-
-        objectStore.createSubPath(NODES_SUB_PATH);
-
-        LOG.debug("ManagementPlaneMemento-persister will use store "+objectStore);
-    }
-
-    protected synchronized void init() {
-        if (!started) {
-            started = true;
-            //Leading slash causes problems in SL, it's not a correct file name so remove it.
-            //But once removed we can't load the master file from existing persistence stores.
-            //Try to detect if the old file exists, if so use old-style names, otherwise use the correct names.
-            masterWriter = new StoreObjectAccessorLocking(objectStore.newAccessor("/master"));
-            if (masterWriter.get() != null) {
-                changeLogWriter = new StoreObjectAccessorLocking(objectStore.newAccessor("/change.log"));
-            } else {
-                masterWriter = new StoreObjectAccessorLocking(objectStore.newAccessor("master"));
-                changeLogWriter = new StoreObjectAccessorLocking(objectStore.newAccessor("change.log"));
-            }
-        }
-    }
-
-    @VisibleForTesting
-    public void preferRemoteTimestampInMemento() {
-        preferRemoteTimestampInMemento = true;
-    }
-    
-    @Override
-    public void stop() {
-        running = false;
-        try {
-            for (StoreObjectAccessorWithLock writer : nodeWriters.values()) {
-                try {
-                    writer.waitForCurrentWrites(SHUTDOWN_TIMEOUT);
-                } catch (TimeoutException e) {
-                    LOG.warn("Timeout during shutdown, waiting for write of "+writer+"; continuing");
-                }
-            }
-            try {
-                masterWriter.waitForCurrentWrites(SHUTDOWN_TIMEOUT);
-            } catch (TimeoutException e) {
-                LOG.warn("Timeout during shutdown, waiting for write of "+masterWriter+"; continuing");
-            }
-        } catch (InterruptedException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    @Override
-    public ManagementPlaneSyncRecord loadSyncRecord() throws IOException {
-        if (!running) {
-            throw new IllegalStateException("Persister not running; cannot load memento from "+ objectStore.getSummaryName());
-        }
-        init();
-        
-        // Note this is called a lot - every time we check the heartbeats
-        if (LOG.isTraceEnabled()) LOG.trace("Loading management-plane memento from {}", objectStore.getSummaryName());
-
-        Stopwatch stopwatch = Stopwatch.createStarted();
-
-        ManagementPlaneSyncRecordImpl.Builder builder = ManagementPlaneSyncRecordImpl.builder();
-
-        // Be careful about order: if the master-file says nodeX then nodeX's file must have an up-to-date timestamp.
-        // Therefore read master file first, followed by the other node-files.
-        String masterNodeId = masterWriter.get();
-        if (masterNodeId == null) {
-            LOG.debug("No master-memento deserialized from file "+masterWriter+"; ignoring and continuing (normal on startup, should cause an error later in live operation)");
-        } else {
-            builder.masterNodeId(masterNodeId);
-        }
-
-        // Load node-files
-        List<String> nodeFiles = objectStore.listContentsWithSubPath(NODES_SUB_PATH);
-        LOG.trace("Loading nodes from {}; {} nodes.",
-                new Object[]{objectStore.getSummaryName(), nodeFiles.size()});
-
-        for (String nodeFile : nodeFiles) {
-            PersistenceObjectStore.StoreObjectAccessor objectAccessor = objectStore.newAccessor(nodeFile);
-            String nodeContents = null;
-            Exception problem = null;
-            try {
-                nodeContents = objectAccessor.get();
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                problem = e;
-            }
-            if (problem!=null || Strings.isBlank(nodeContents)) {
-                // happens if node has gone away, or if FileBasedObjectStore.moveFile is not atomic, 
-                // i.e. it has deleted but not updated it yet
-                if (objectAccessor.exists()) {
-                    throw Exceptions.propagate(new IllegalStateException("Node record "+nodeFile+" could not be read when "+mgmt.getManagementNodeId()+" was scanning", problem));
-                } else {
-                    LOG.warn("Node record "+nodeFile+" went away while "+mgmt.getManagementNodeId()+" was scanning, ignoring (it has probably been terminated)");
-                    // if file was deleted, silently ignore
-                    continue;
-                }
-            }
-            ManagementNodeSyncRecord memento = (ManagementNodeSyncRecord) serializer.fromString(nodeContents);
-            if (memento == null) {
-                // shouldn't happen
-                throw Exceptions.propagate(new IllegalStateException("Node record "+nodeFile+" could not be deserialized when "+mgmt.getManagementNodeId()+" was scanning: "+nodeContents, problem));
-            } else {
-                if (memento.getRemoteTimestamp()!=null && preferRemoteTimestampInMemento) {
-                    // in test mode, the remote timestamp is stored in the file
-                } else {
-                    if (memento.getRemoteTimestamp()!=null) {
-                        LOG.debug("Ignoring remote timestamp in memento file ("+memento+"); looks like this data has been manually copied in");
-                    }
-                    Date lastModifiedDate = objectAccessor.getLastModifiedDate();
-                    ((BasicManagementNodeSyncRecord)memento).setRemoteTimestamp(lastModifiedDate!=null ? lastModifiedDate.getTime() : null);
-                }
-                builder.node(memento);
-            }
-        }
-
-        if (LOG.isDebugEnabled()) LOG.trace("Loaded management-plane memento; {} nodes, took {}",
-            nodeFiles.size(),
-            Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)));
-        return builder.build();
-    }
-    
-    @Override
-    public void delta(Delta delta) {
-        if (!running) {
-            if (LOG.isDebugEnabled()) LOG.debug("Persister not running; ignoring checkpointed delta of manager-memento");
-            return;
-        }
-        init();
-        
-        Stopwatch stopwatch = Stopwatch.createStarted();
-        if (LOG.isTraceEnabled()) LOG.trace("Checkpointing delta of manager-memento; updating {}", delta);
-        
-        for (ManagementNodeSyncRecord m : delta.getNodes()) {
-            persist(m);
-        }
-        for (String id : delta.getRemovedNodeIds()) {
-            deleteNode(id);
-        }
-        switch (delta.getMasterChange()) {
-        case NO_CHANGE:
-            break; // no-op
-        case SET_MASTER:
-            persistMaster(checkNotNull(delta.getNewMasterOrNull()), null);
-            break;
-        case CLEAR_MASTER:
-            persistMaster("", delta.getExpectedMasterToClear());
-            break; // no-op
-        default:
-            throw new IllegalStateException("Unknown state for master-change: "+delta.getMasterChange());
-        }
-        if (LOG.isDebugEnabled()) LOG.debug("Checkpointed delta of manager-memento in "+Time.makeTimeStringRounded(stopwatch)+": "+delta);
-    }
-
-    private void persistMaster(String nodeId, String optionalExpectedId) {
-        if (optionalExpectedId!=null) {
-            String currentRemoteMaster = masterWriter.get();
-            if (currentRemoteMaster==null) {
-                // okay to have nothing at remote
-            } else if (!currentRemoteMaster.trim().equals(optionalExpectedId.trim())) {
-                LOG.warn("Master at server is "+(Strings.isBlank(currentRemoteMaster) ? "<none>" : currentRemoteMaster)+"; expected "+optionalExpectedId+" "
-                    + (Strings.isNonBlank(nodeId) ? "and would set as "+nodeId : "and would clear") 
-                    + ", so not applying (yet)");
-                return;
-            }
-        }
-        masterWriter.put(nodeId);
-        try {
-            masterWriter.waitForCurrentWrites(SYNC_WRITE_TIMEOUT);
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-        changeLogWriter.append(Time.makeDateString() + ": set master to " + nodeId + "\n");
-        try {
-            changeLogWriter.waitForCurrentWrites(SYNC_WRITE_TIMEOUT);
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    @Override
-    @VisibleForTesting
-    public void waitForWritesCompleted(Duration timeout) throws InterruptedException, TimeoutException {
-        for (StoreObjectAccessorWithLock writer : nodeWriters.values()) {
-            writer.waitForCurrentWrites(timeout);
-        }
-        masterWriter.waitForCurrentWrites(timeout);
-    }
-
-    public void checkpoint(ManagementPlaneSyncRecord record) {
-        init();
-        for (ManagementNodeSyncRecord node : record.getManagementNodes().values()) {
-            // Check included in case the node in the memento is the one being initialised by
-            // BrooklynLauncher in the copy state command.
-            if (!ManagementNodeState.INITIALIZING.equals(node.getStatus()) && node.getNodeId() != null) {
-                persist(node);
-            }
-        }
-    }
-
-    private void persist(ManagementNodeSyncRecord node) {
-        StoreObjectAccessorWithLock writer = getOrCreateNodeWriter(node.getNodeId());
-        boolean fileExists = writer.exists();
-        writer.put(serializer.toString(node));
-        try {
-            writer.waitForCurrentWrites(SYNC_WRITE_TIMEOUT);
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-        if (!fileExists) {
-            changeLogWriter.append(Time.makeDateString()+": created node "+node.getNodeId()+"\n");
-        }
-        if (node.getStatus() == ManagementNodeState.TERMINATED || node.getStatus() == ManagementNodeState.FAILED) {
-            changeLogWriter.append(Time.makeDateString()+": set node "+node.getNodeId()+" status to "+node.getStatus()+"\n");
-        }
-    }
-    
-    private void deleteNode(String nodeId) {
-        getOrCreateNodeWriter(nodeId).delete();
-        changeLogWriter.append(Time.makeDateString()+": deleted node "+nodeId+"\n");
-    }
-
-    private StoreObjectAccessorWithLock getOrCreateNodeWriter(String nodeId) {
-        PersistenceObjectStore.StoreObjectAccessorWithLock writer = nodeWriters.get(nodeId);
-        if (writer == null) {
-            nodeWriters.putIfAbsent(nodeId, 
-                new StoreObjectAccessorLocking(objectStore.newAccessor(NODES_SUB_PATH+"/"+nodeId)));
-            writer = nodeWriters.get(nodeId);
-        }
-        return writer;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/brooklyn/management/ha/MasterChooser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/MasterChooser.java b/core/src/main/java/brooklyn/management/ha/MasterChooser.java
deleted file mode 100644
index fdde8b9..0000000
--- a/core/src/main/java/brooklyn/management/ha/MasterChooser.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.management.ha;
-
-import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
-import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
-
-import brooklyn.util.time.Duration;
-
-import com.google.common.annotations.Beta;
-
-/**
- * For choosing which management node to promote, when master detected as failed or stopped.
- * 
- * @since 0.7.0
- * 
- * @author aled
- */
-@Beta
-public interface MasterChooser {
-
-    ManagementNodeSyncRecord choose(ManagementPlaneSyncRecord memento, Duration heartbeatTimeout, String ownNodeId);
-    
-}


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

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..6a133fc
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/ha/OsgiManager.java
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.ha;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.Framework;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.BrooklynVersion;
+
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.api.management.ManagementContext;
+
+import brooklyn.config.BrooklynServerConfig;
+import brooklyn.config.BrooklynServerPaths;
+import brooklyn.config.ConfigKey;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.collections.MutableSet;
+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;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+public class OsgiManager {
+
+    private static final Logger log = LoggerFactory.getLogger(OsgiManager.class);
+    
+    public static final ConfigKey<Boolean> USE_OSGI = BrooklynServerConfig.USE_OSGI;
+    
+    /* see Osgis for info on starting framework etc */
+    
+    protected ManagementContext mgmt;
+    protected Framework framework;
+    protected File osgiCacheDir;
+
+    public OsgiManager(ManagementContext mgmt) {
+        this.mgmt = mgmt;
+    }
+
+    public void start() {
+        try {
+            osgiCacheDir = BrooklynServerPaths.getOsgiCacheDirCleanedIfNeeded(mgmt);
+            
+            // any extra OSGi startup args could go here
+            framework = Osgis.newFrameworkStarted(osgiCacheDir.getAbsolutePath(), false, MutableMap.of());
+            
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    public void stop() {
+        try {
+            if (framework!=null) {
+                framework.stop();
+                framework.waitForStop(0); // 0 means indefinite
+            }
+        } catch (BundleException e) {
+            throw Exceptions.propagate(e);
+        } catch (InterruptedException e) {
+            throw Exceptions.propagate(e);
+        }
+        if (BrooklynServerPaths.isOsgiCacheForCleaning(mgmt, osgiCacheDir)) {
+            // See exception reported in https://issues.apache.org/jira/browse/BROOKLYN-72
+            // We almost always fail to delete he OSGi temp directory due to a concurrent modification.
+            // Therefore keep trying.
+            final AtomicReference<DeletionResult> deletionResult = new AtomicReference<DeletionResult>();
+            Repeater.create("Delete OSGi cache dir")
+                    .until(new Callable<Boolean>() {
+                        public Boolean call() {
+                            deletionResult.set(Os.deleteRecursively(osgiCacheDir));
+                            return deletionResult.get().wasSuccessful();
+                        }})
+                    .limitTimeTo(Duration.ONE_SECOND)
+                    .backoffTo(Duration.millis(50))
+                    .run();
+            if (deletionResult.get().getThrowable()!=null) {
+                log.debug("Unable to delete "+osgiCacheDir+" (possibly being modified concurrently?): "+deletionResult.get().getThrowable());
+            }
+        }
+        osgiCacheDir = null;
+        framework = null;
+    }
+
+    public synchronized void registerBundle(CatalogBundle bundle) {
+        try {
+            if (checkBundleInstalledThrowIfInconsistent(bundle)) {
+                return;
+            }
+
+            Bundle b = Osgis.install(framework, bundle.getUrl());
+
+            checkCorrectlyInstalled(bundle, b);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            throw new IllegalStateException("Bundle from "+bundle.getUrl()+" failed to install: " + e.getMessage(), e);
+        }
+    }
+
+    private void checkCorrectlyInstalled(CatalogBundle bundle, Bundle b) {
+        String nv = b.getSymbolicName()+":"+b.getVersion().toString();
+
+        if (!isBundleNameEqualOrAbsent(bundle, b)) {
+            throw new IllegalStateException("Bundle already installed as "+nv+" but user explicitly requested "+bundle);
+        }
+
+        List<Bundle> matches = Osgis.bundleFinder(framework)
+                .symbolicName(b.getSymbolicName())
+                .version(b.getVersion().toString())
+                .findAll();
+        if (matches.isEmpty()) {
+            log.error("OSGi could not find bundle "+nv+" in search after installing it from "+bundle.getUrl());
+        } else if (matches.size()==1) {
+            log.debug("Bundle from "+bundle.getUrl()+" successfully installed as " + nv + " ("+b+")");
+        } else {
+            log.warn("OSGi has multiple bundles matching "+nv+", when just installed from "+bundle.getUrl()+": "+matches+"; "
+                + "brooklyn will prefer the URL-based bundle for top-level references but any dependencies or "
+                + "import-packages will be at the mercy of OSGi. "
+                + "It is recommended to use distinct versions for different bundles, and the same URL for the same bundles.");
+        }
+    }
+
+    private boolean checkBundleInstalledThrowIfInconsistent(CatalogBundle bundle) {
+        String bundleUrl = bundle.getUrl();
+        if (bundleUrl != null) {
+            Maybe<Bundle> installedBundle = Osgis.bundleFinder(framework).requiringFromUrl(bundleUrl).find();
+            if (installedBundle.isPresent()) {
+                Bundle b = installedBundle.get();
+                String nv = b.getSymbolicName()+":"+b.getVersion().toString();
+                if (!isBundleNameEqualOrAbsent(bundle, b)) {
+                    throw new IllegalStateException("User requested bundle " + bundle + " but already installed as "+nv);
+                } else {
+                    log.trace("Bundle from "+bundleUrl+" already installed as "+nv+"; not re-registering");
+                }
+                return true;
+            }
+        } else {
+            Maybe<Bundle> installedBundle = Osgis.bundleFinder(framework).symbolicName(bundle.getSymbolicName()).version(bundle.getVersion()).find();
+            if (installedBundle.isPresent()) {
+                log.trace("Bundle "+bundle+" installed from "+installedBundle.get().getLocation());
+            } else {
+                throw new IllegalStateException("Bundle "+bundle+" not previously registered, but URL is empty.");
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public static boolean isBundleNameEqualOrAbsent(CatalogBundle bundle, Bundle b) {
+        return !bundle.isNamed() ||
+                (bundle.getSymbolicName().equals(b.getSymbolicName()) &&
+                bundle.getVersion().equals(b.getVersion().toString()));
+    }
+
+    public <T> Maybe<Class<T>> tryResolveClass(String type, CatalogBundle... catalogBundles) {
+        return tryResolveClass(type, Arrays.asList(catalogBundles));
+    }
+    public <T> Maybe<Class<T>> tryResolveClass(String type, Iterable<CatalogBundle> catalogBundles) {
+        Map<CatalogBundle,Throwable> bundleProblems = MutableMap.of();
+        Set<String> extraMessages = MutableSet.of();
+        for (CatalogBundle catalogBundle: catalogBundles) {
+            try {
+                Maybe<Bundle> bundle = findBundle(catalogBundle);
+                if (bundle.isPresent()) {
+                    Bundle b = bundle.get();
+                    Class<T> clazz;
+                    //Extension bundles don't support loadClass.
+                    //Instead load from the app classpath.
+                    if (Osgis.isExtensionBundle(b)) {
+                        @SuppressWarnings("unchecked")
+                        Class<T> c = (Class<T>)Class.forName(type);
+                        clazz = c;
+                    } else {
+                        @SuppressWarnings("unchecked")
+                        Class<T> c = (Class<T>)b.loadClass(type);
+                        clazz = c;
+                    }
+                    return Maybe.of(clazz);
+                } else {
+                    bundleProblems.put(catalogBundle, ((Maybe.Absent<?>)bundle).getException());
+                }
+                
+            } catch (Exception e) {
+                // should come from classloading now; name formatting or missing bundle errors will be caught above 
+                Exceptions.propagateIfFatal(e);
+                bundleProblems.put(catalogBundle, e);
+
+                Throwable cause = e.getCause();
+                if (cause != null && cause.getMessage().contains("Unresolved constraint in bundle")) {
+                    if (BrooklynVersion.INSTANCE.getVersionFromOsgiManifest()==null) {
+                        extraMessages.add("No brooklyn-core OSGi manifest available. OSGi will not work.");
+                    }
+                    if (BrooklynVersion.isDevelopmentEnvironment()) {
+                        extraMessages.add("Your development environment may not have created necessary files. Doing a maven build then retrying may fix the issue.");
+                    }
+                    if (!extraMessages.isEmpty()) log.warn(Strings.join(extraMessages, " "));
+                    log.warn("Unresolved constraint resolving OSGi bundle "+catalogBundle+" to load "+type+": "+cause.getMessage());
+                    if (log.isDebugEnabled()) log.debug("Trace for OSGi resolution failure", e);
+                }
+            }
+        }
+        if (bundleProblems.size()==1) {
+            Throwable error = Iterables.getOnlyElement(bundleProblems.values());
+            if (error instanceof ClassNotFoundException && error.getCause()!=null && error.getCause().getMessage()!=null) {
+                error = Exceptions.collapseIncludingAllCausalMessages(error);
+            }
+            return Maybe.absent("Unable to resolve class "+type+" in "+Iterables.getOnlyElement(bundleProblems.keySet())
+                + (extraMessages.isEmpty() ? "" : " ("+Strings.join(extraMessages, " ")+")"), error);
+        } else {
+            return Maybe.absent(Exceptions.create("Unable to resolve class "+type+": "+bundleProblems
+                + (extraMessages.isEmpty() ? "" : " ("+Strings.join(extraMessages, " ")+")"), bundleProblems.values()));
+        }
+    }
+
+    public Maybe<Bundle> findBundle(CatalogBundle catalogBundle) {
+        //Either fail at install time when the user supplied name:version is different
+        //from the one reported from the bundle
+        //or
+        //Ignore user supplied name:version when URL is supplied to be able to find the
+        //bundle even if it's with a different version.
+        //
+        //For now we just log a warning if there's a version discrepancy at install time,
+        //so prefer URL if supplied.
+        BundleFinder bundleFinder = Osgis.bundleFinder(framework);
+        if (catalogBundle.getUrl() != null) {
+            bundleFinder.requiringFromUrl(catalogBundle.getUrl());
+        } else {
+            bundleFinder.symbolicName(catalogBundle.getSymbolicName()).version(catalogBundle.getVersion());
+        }
+        return bundleFinder.find();
+    }
+
+    /**
+     * Iterates through catalogBundles until one contains a resource with the given name.
+     */
+    public URL getResource(String name, Iterable<CatalogBundle> catalogBundles) {
+        for (CatalogBundle catalogBundle: catalogBundles) {
+            try {
+                Maybe<Bundle> bundle = findBundle(catalogBundle);
+                if (bundle.isPresent()) {
+                    URL result = bundle.get().getResource(name);
+                    if (result!=null) return result;
+                }
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return An iterable of all resources matching name in catalogBundles.
+     */
+    public Iterable<URL> getResources(String name, Iterable<CatalogBundle> catalogBundles) {
+        List<URL> resources = Lists.newArrayList();
+        for (CatalogBundle catalogBundle : catalogBundles) {
+            try {
+                Maybe<Bundle> bundle = findBundle(catalogBundle);
+                if (bundle.isPresent()) {
+                    Enumeration<URL> result = bundle.get().getResources(name);
+                    resources.addAll(Collections.list(result));
+                }
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+            }
+        }
+        return resources;
+    }
+
+    public Framework getFramework() {
+        return framework;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..1758846
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/AbstractManagementContext.java
@@ -0,0 +1,485 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
+
+import java.net.URI;
+import java.net.URL;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.api.basic.BrooklynObject;
+import org.apache.brooklyn.api.catalog.BrooklynCatalog;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.entity.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.drivers.EntityDriverManager;
+import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolverManager;
+import org.apache.brooklyn.api.entity.rebind.RebindManager;
+import org.apache.brooklyn.api.location.LocationRegistry;
+import org.apache.brooklyn.api.management.ExecutionContext;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.api.management.SubscriptionContext;
+import org.apache.brooklyn.api.management.Task;
+import org.apache.brooklyn.api.management.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.api.management.entitlement.EntitlementManager;
+import org.apache.brooklyn.api.management.ha.HighAvailabilityManager;
+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 brooklyn.catalog.internal.BasicBrooklynCatalog;
+import brooklyn.catalog.internal.CatalogInitialization;
+import brooklyn.catalog.internal.CatalogUtils;
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.StringConfigMap;
+import brooklyn.entity.basic.AbstractEntity;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.drivers.BasicEntityDriverManager;
+import brooklyn.entity.drivers.downloads.BasicDownloadsManager;
+import brooklyn.entity.rebind.RebindManagerImpl;
+import brooklyn.internal.storage.BrooklynStorage;
+import brooklyn.internal.storage.DataGrid;
+import brooklyn.internal.storage.DataGridFactory;
+import brooklyn.internal.storage.impl.BrooklynStorageImpl;
+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;
+import com.google.common.base.Preconditions;
+
+public abstract class AbstractManagementContext implements ManagementContextInternal {
+    private static final Logger log = LoggerFactory.getLogger(AbstractManagementContext.class);
+
+    private static DataGridFactory loadDataGridFactory(BrooklynProperties properties) {
+        String clazzName = properties.getFirst(DataGridFactory.class.getName());
+        if(clazzName == null){
+            clazzName = InMemoryDataGridFactory.class.getName();
+        }
+
+        Class<?> clazz;
+        try{
+            //todo: which classloader should we use?
+            clazz = LocalManagementContext.class.getClassLoader().loadClass(clazzName);
+        }catch(ClassNotFoundException e){
+            throw new IllegalStateException(format("Could not load class [%s]",clazzName),e);
+        }
+
+        Object instance;
+        try {
+            instance = clazz.newInstance();
+        } catch (InstantiationException e) {
+            throw new IllegalStateException(format("Could not instantiate class [%s]",clazzName),e);
+        } catch (IllegalAccessException e) {
+            throw new IllegalStateException(format("Could not instantiate class [%s]",clazzName),e);
+        }
+
+        if(!(instance instanceof DataGridFactory)){
+            throw new IllegalStateException(format("Class [%s] not an instantiate of class [%s]",clazzName, DataGridFactory.class.getName()));
+        }
+
+        return (DataGridFactory)instance;
+    }
+
+    static {
+        ResourceUtils.addClassLoaderProvider(new Function<Object, BrooklynClassLoadingContext>() {
+            @Override
+            public BrooklynClassLoadingContext apply(@Nullable Object input) {
+                if (input instanceof EntityInternal) {
+                    EntityInternal internal = (EntityInternal)input;
+                    if (internal.getCatalogItemId() != null) {
+                        CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(internal.getManagementContext(), internal.getCatalogItemId());
+                        if (item != null) {
+                            return CatalogUtils.newClassLoadingContext(internal.getManagementContext(), item);
+                        } else {
+                            log.error("Can't find catalog item " + internal.getCatalogItemId() +
+                                    " used for instantiating entity " + internal +
+                                    ". Falling back to application classpath.");
+                        }
+                    }
+                    return apply(internal.getManagementSupport());
+                }
+                
+                if (input instanceof EntityManagementSupport)
+                    return apply(((EntityManagementSupport)input).getManagementContext());
+                if (input instanceof ManagementContext)
+                    return JavaBrooklynClassLoadingContext.create((ManagementContext) input);
+                return null;
+            }
+        });
+    }
+
+    private final AtomicLong totalEffectorInvocationCount = new AtomicLong();
+
+    protected BrooklynProperties configMap;
+    protected BasicLocationRegistry locationRegistry;
+    protected final BasicBrooklynCatalog catalog;
+    protected ClassLoader baseClassLoader;
+    protected Iterable<URL> baseClassPathForScanning;
+
+    private final RebindManager rebindManager;
+    private final HighAvailabilityManager highAvailabilityManager;
+    
+    protected volatile BrooklynGarbageCollector gc;
+
+    private final EntityDriverManager entityDriverManager;
+    protected DownloadResolverManager downloadsManager;
+
+    protected EntitlementManager entitlementManager;
+
+    private final BrooklynStorage storage;
+
+    private volatile boolean running = true;
+    protected boolean startupComplete = false;
+    protected final List<Throwable> errors = Collections.synchronizedList(MutableList.<Throwable>of()); 
+
+    protected Maybe<URI> uri = Maybe.absent();
+    protected CatalogInitialization catalogInitialization;
+
+    public AbstractManagementContext(BrooklynProperties brooklynProperties){
+        this(brooklynProperties, null);
+    }
+
+    public AbstractManagementContext(BrooklynProperties brooklynProperties, DataGridFactory datagridFactory) {
+        this.configMap = brooklynProperties;
+        this.entityDriverManager = new BasicEntityDriverManager();
+        this.downloadsManager = BasicDownloadsManager.newDefault(configMap);
+        if (datagridFactory == null) {
+            datagridFactory = loadDataGridFactory(brooklynProperties);
+        }
+        DataGrid datagrid = datagridFactory.newDataGrid(this);
+
+        this.catalog = new BasicBrooklynCatalog(this);
+        
+        this.storage = new BrooklynStorageImpl(datagrid);
+        this.rebindManager = new RebindManagerImpl(this); // TODO leaking "this" reference; yuck
+        this.highAvailabilityManager = new HighAvailabilityManagerImpl(this); // TODO leaking "this" reference; yuck
+        
+        this.entitlementManager = Entitlements.newManager(this, brooklynProperties);
+    }
+
+    @Override
+    public void terminate() {
+        highAvailabilityManager.stop();
+        running = false;
+        rebindManager.stop();
+        storage.terminate();
+        // Don't unmanage everything; different entities get given their events at different times 
+        // so can cause problems (e.g. a group finds out that a member is unmanaged, before the
+        // group itself has been told that it is unmanaged).
+    }
+    
+    @Override
+    public boolean isRunning() {
+        return running;
+    }
+    
+    @Override
+    public boolean isStartupComplete() {
+        return startupComplete;
+    }
+
+    @Override
+    public BrooklynStorage getStorage() {
+        return storage;
+    }
+    
+    @Override
+    public RebindManager getRebindManager() {
+        return rebindManager;
+    }
+
+    @Override
+    public HighAvailabilityManager getHighAvailabilityManager() {
+        return highAvailabilityManager;
+    }
+
+    @Override
+    public long getTotalEffectorInvocations() {
+        return totalEffectorInvocationCount.get();
+    }
+    
+    @Override
+    public ExecutionContext getExecutionContext(Entity e) {
+        // BEC is a thin wrapper around EM so fine to create a new one here; but make sure it gets the real entity
+        if (e instanceof AbstractEntity) {
+            return new BasicExecutionContext(MutableMap.of("tag", BrooklynTaskTags.tagForContextEntity(e)), getExecutionManager());
+        } else {
+            return ((EntityInternal)e).getManagementSupport().getExecutionContext();
+        }
+    }
+
+    @Override
+    public ExecutionContext getServerExecutionContext() {
+        // BEC is a thin wrapper around EM so fine to create a new one here
+        return new BasicExecutionContext(MutableMap.of("tag", BrooklynTaskTags.BROOKLYN_SERVER_TASK_TAG), getExecutionManager());
+    }
+
+    @Override
+    public SubscriptionContext getSubscriptionContext(Entity e) {
+        // BSC is a thin wrapper around SM so fine to create a new one here
+        return new BasicSubscriptionContext(getSubscriptionManager(), e);
+    }
+
+    @Override
+    public EntityDriverManager getEntityDriverManager() {
+        return entityDriverManager;
+    }
+
+    @Override
+    public DownloadResolverManager getEntityDownloadsManager() {
+        return downloadsManager;
+    }
+    
+    @Override
+    public EntitlementManager getEntitlementManager() {
+        return entitlementManager;
+    }
+    
+    protected abstract void manageIfNecessary(Entity entity, Object context);
+
+    @Override
+    public <T> Task<T> invokeEffector(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters) {
+        return runAtEntity(entity, eff, parameters);
+    }
+    
+    protected <T> T invokeEffectorMethodLocal(Entity entity, Effector<T> eff, Object args) {
+        assert isManagedLocally(entity) : "cannot invoke effector method at "+this+" because it is not managed here";
+        totalEffectorInvocationCount.incrementAndGet();
+        Object[] transformedArgs = EffectorUtils.prepareArgsForEffector(eff, args);
+        return GroovyJavaMethods.invokeMethodOnMetaClass(entity, eff.getName(), transformedArgs);
+    }
+
+    /**
+     * Method for entity to make effector happen with correct semantics (right place, right task context),
+     * when a method is called on that entity.
+     * @throws ExecutionException 
+     */
+    @Override
+    public <T> T invokeEffectorMethodSync(final Entity entity, final Effector<T> eff, final Object args) throws ExecutionException {
+        try {
+            Task<?> current = Tasks.current();
+            if (current == null || !entity.equals(BrooklynTaskTags.getContextEntity(current)) || !isManagedLocally(entity)) {
+                manageIfNecessary(entity, eff.getName());
+                // Wrap in a task if we aren't already in a task that is tagged with this entity
+                Task<T> task = runAtEntity( EffectorUtils.getTaskFlagsForEffectorInvocation(entity, eff, 
+                            ConfigBag.newInstance().configureStringKey("args", args)),
+                        entity, 
+                        new Callable<T>() {
+                            public T call() {
+                                return invokeEffectorMethodLocal(entity, eff, args);
+                            }});
+                return task.get();
+            } else {
+                return invokeEffectorMethodLocal(entity, eff, args);
+            }
+        } catch (Exception e) {
+            // don't need to attach any message or warning because the Effector impl hierarchy does that (see calls to EffectorUtils.handleException)
+            throw new ExecutionException(e);
+        }
+    }
+
+    /**
+     * Whether the master entity record is local, and sensors and effectors can be properly accessed locally.
+     */ 
+    public abstract boolean isManagedLocally(Entity e);
+    
+    /**
+     * Causes the indicated runnable to be run at the right location for the given entity.
+     *
+     * Returns the actual task (if it is local) or a proxy task (if it is remote);
+     * if management for the entity has not yet started this may start it.
+     * 
+     * @deprecated since 0.6.0 use effectors (or support {@code runAtEntity(Entity, Effector, Map)} if something else is needed);
+     * (Callable with Map flags is too open-ended, bothersome to support, and not used much) 
+     */
+    @Deprecated
+    public abstract <T> Task<T> runAtEntity(@SuppressWarnings("rawtypes") Map flags, Entity entity, Callable<T> c);
+
+    /** Runs the given effector in the right place for the given entity.
+     * The task is immediately submitted in the background, but also recorded in the queueing context (if present)
+     * so it appears as a child, but marked inessential so it does not fail the parent task, who will ordinarily
+     * call {@link Task#get()} on the object and may do their own failure handling. 
+     */
+    protected abstract <T> Task<T> runAtEntity(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters);
+
+    @Override
+    public StringConfigMap getConfig() {
+        return configMap;
+    }
+
+    @Override
+    public BrooklynProperties getBrooklynProperties() {
+        return configMap;
+    }
+
+    @Override
+    public synchronized LocationRegistry getLocationRegistry() {
+        if (locationRegistry==null) locationRegistry = new BasicLocationRegistry(this);
+        return locationRegistry;
+    }
+
+    @Override
+    public BrooklynCatalog getCatalog() {
+        if (!getCatalogInitialization().hasRunAnyInitialization()) {
+            // catalog init is needed; normally this will be done from start sequence,
+            // but if accessed early -- and in tests -- we will load it here
+            getCatalogInitialization().injectManagementContext(this);
+            getCatalogInitialization().populateUnofficial(catalog);
+        }
+        return catalog;
+    }
+    
+    @Override
+    public ClassLoader getCatalogClassLoader() {
+        // catalog does not have to be initialized
+        return catalog.getRootClassLoader();
+    }
+
+    /**
+     * Optional class-loader that this management context should use as its base,
+     * as the first-resort in the catalog, and for scanning (if scanning the default in the catalog).
+     * In most instances the default classloader (ManagementContext.class.getClassLoader(), assuming
+     * this was in the JARs used at boot time) is fine, and in those cases this method normally returns null.
+     * (Surefire does some weird stuff, but the default classloader is fine for loading;
+     * however it requires a custom base classpath to be set for scanning.)
+     */
+    @Override
+    public ClassLoader getBaseClassLoader() {
+        return baseClassLoader;
+    }
+    
+    /** See {@link #getBaseClassLoader()}.  Only settable once and must be invoked before catalog is loaded. */
+    public void setBaseClassLoader(ClassLoader cl) {
+        if (baseClassLoader==cl) return;
+        if (baseClassLoader!=null) throw new IllegalStateException("Cannot change base class loader (in "+this+")");
+        if (catalog!=null) throw new IllegalStateException("Cannot set base class after catalog has been loaded (in "+this+")");
+        this.baseClassLoader = cl;
+    }
+    
+    /** Optional mechanism for setting the classpath which should be scanned by the catalog, if the catalog
+     * is scanning the default classpath.  Usually it infers the right thing, but some classloaders
+     * (e.g. surefire) do funny things which the underlying org.reflections.Reflections library can't see in to.
+     * <p>
+     * This should normally be invoked early in the server startup.  Setting it after the catalog is loaded will not
+     * take effect without an explicit internal call to do so.  Once set, it can be changed prior to catalog loading
+     * but it cannot be <i>changed</i> once the catalog is loaded.
+     * <p>
+     * ClasspathHelper.forJavaClassPath() is often a good argument to pass, and is used internally in some places
+     * when no items are found on the catalog. */
+    @Override
+    public void setBaseClassPathForScanning(Iterable<URL> urls) {
+        if (Objects.equal(baseClassPathForScanning, urls)) return;
+        if (baseClassPathForScanning != null) {
+            if (catalog==null)
+                log.warn("Changing scan classpath to "+urls+" from "+baseClassPathForScanning);
+            else
+                throw new IllegalStateException("Cannot change base class path for scanning (in "+this+")");
+        }
+        this.baseClassPathForScanning = urls;
+    }
+    /** 
+     * @see #setBaseClassPathForScanning(Iterable)
+     */
+    @Override
+    public Iterable<URL> getBaseClassPathForScanning() {
+        return baseClassPathForScanning;
+    }
+
+    public BrooklynGarbageCollector getGarbageCollector() {
+        return gc;
+    }
+
+    @Override
+    public void setManagementNodeUri(URI uri) {
+        this.uri = Maybe.of(checkNotNull(uri, "uri"));
+    }
+
+    @Override
+    public Maybe<URI> getManagementNodeUri() {
+        return uri;
+    }
+    
+    private Object catalogInitMutex = new Object();
+    @Override
+    public CatalogInitialization getCatalogInitialization() {
+        synchronized (catalogInitMutex) {
+            if (catalogInitialization!=null) return catalogInitialization;
+            CatalogInitialization ci = new CatalogInitialization();
+            setCatalogInitialization(ci);
+            return ci;
+        }
+    }
+    
+    @Override
+    public void setCatalogInitialization(CatalogInitialization catalogInitialization) {
+        synchronized (catalogInitMutex) {
+            Preconditions.checkNotNull(catalogInitialization, "initialization must not be null");
+            if (this.catalogInitialization!=null && this.catalogInitialization != catalogInitialization)
+                throw new IllegalStateException("Changing catalog init from "+this.catalogInitialization+" to "+catalogInitialization+"; changes not permitted");
+            catalogInitialization.injectManagementContext(this);
+            this.catalogInitialization = catalogInitialization;
+        }
+    }
+    
+    public BrooklynObject lookup(String id) {
+        return lookup(id, BrooklynObject.class);
+    }
+    
+    @SuppressWarnings("unchecked")
+    public <T extends BrooklynObject> T lookup(String id, Class<T> type) {
+        Object result;
+        result = getEntityManager().getEntity(id);
+        if (result!=null && type.isInstance(result)) return (T)result;
+        
+        result = getLocationManager().getLocation(id);
+        if (result!=null && type.isInstance(result)) return (T)result;
+
+        // TODO policies, enrichers, feeds
+        return null;
+    }
+
+    @Override
+    public List<Throwable> errors() {
+        return errors;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/AbstractSubscriptionManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/AbstractSubscriptionManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/AbstractSubscriptionManager.java
new file mode 100644
index 0000000..bec041d
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/AbstractSubscriptionManager.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.Group;
+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.management.SubscriptionHandle;
+import org.apache.brooklyn.api.management.SubscriptionManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+
+public abstract class AbstractSubscriptionManager implements SubscriptionManager {
+
+    // TODO Perhaps could use guava's SynchronizedSetMultimap? But need to check its synchronization guarantees.
+    //      That would replace the utils used for subscriptionsBySubscriber etc.
+    
+    @SuppressWarnings("unused")
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractSubscriptionManager.class);
+
+    /** performs the actual subscription; should return the subscription parameter as the handle */
+    protected abstract <T> SubscriptionHandle subscribe(Map<String, Object> flags, Subscription<T> s);
+    /** performs the actual publishing -- ie distribution to subscriptions */
+    public abstract <T> void publish(final SensorEvent<T> event);
+
+    public static class EntitySensorToken {
+        Entity e;
+        Sensor<?> s;
+        String sName;
+        public EntitySensorToken(Entity e, Sensor<?> s) {
+            this.e = e;
+            this.s = s;
+            this.sName = (s == null) ? null : checkNotNull(s.getName(), "sensor must have non-null name: %s", s);
+        }
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(e, sName);
+        }
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (!(obj instanceof EntitySensorToken)) return false;
+            if (!Objects.equal(e, ((EntitySensorToken)obj).e)) return false;
+            if (!Objects.equal(sName, ((EntitySensorToken)obj).sName)) return false;
+            return true;
+        }
+        @Override
+        public String toString() {
+            return (e != null ? e.getId() :  "*")+":"+(s != null ? sName : "*");
+        }
+    }
+    static Object makeEntitySensorToken(Entity e, Sensor<?> s) {
+        return new EntitySensorToken(e, s);
+    }
+    static Object makeEntitySensorToken(SensorEvent<?> se) {
+        return makeEntitySensorToken(se.getSource(), se.getSensor());
+    }
+    
+    /** @see SubscriptionManager#subscribe(Map, Entity, Sensor, SensorEventListener) */
+    public final <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        return subscribe(Collections.<String,Object>emptyMap(), producer, sensor, listener);
+    }
+ 
+    /**
+     * This implementation handles the following flags, in addition to those described in the {@link SubscriptionManager}
+     * interface:
+     * <ul>
+     * <li>subscriberExecutionManagerTag - a tag to pass to execution manager (without setting any execution semantics / TaskPreprocessor);
+     *      if not supplied and there is a subscriber, this will be inferred from the subscriber and set up with SingleThreadedScheduler
+     * <li>eventFilter - a Predicate&lt;SensorEvent&gt; instance to filter what events are delivered
+     * </ul>
+     * 
+     * @see SubscriptionManager#subscribe(Map, Entity, Sensor, SensorEventListener)
+     */
+    public final <T> SubscriptionHandle subscribe(Map<String, Object> flags, Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        return subscribe(flags, new Subscription<T>(producer, sensor, listener));
+    }
+        
+    /** @see SubscriptionManager#subscribeToChildren(Map, Entity, Sensor, SensorEventListener) */
+    public final <T> SubscriptionHandle subscribeToChildren(Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        return subscribeToChildren(Collections.<String,Object>emptyMap(), parent, sensor, listener);
+    }
+
+    /** @see SubscriptionManager#subscribe(Map, Entity, Sensor, SensorEventListener) */
+    public final  <T> SubscriptionHandle subscribeToChildren(Map<String, Object> flags, final Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        Predicate<SensorEvent<T>> eventFilter = new Predicate<SensorEvent<T>>() {
+            public boolean apply(SensorEvent<T> input) {
+                return parent != null && input.getSource() != null && parent.equals(input.getSource().getParent());
+            }
+        };
+        flags.put("eventFilter", eventFilter);
+        return subscribe(flags, null, sensor, listener);
+    }
+
+    /** @see SubscriptionManager#subscribeToChildren(Map, Entity, Sensor, SensorEventListener) */
+    public final <T> SubscriptionHandle subscribeToMembers(Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        return subscribeToMembers(Collections.<String,Object>emptyMap(), parent, sensor, listener);
+    }
+
+    /** @see SubscriptionManager#subscribe(Map, Entity, Sensor, SensorEventListener) */
+    public final  <T> SubscriptionHandle subscribeToMembers(Map<String, Object> flags, final Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        Predicate<SensorEvent<T>> eventFilter = new Predicate<SensorEvent<T>>() {
+            public boolean apply(SensorEvent<T> input) {
+                return parent.getMembers().contains(input.getSource());
+            }
+        };
+        flags.put("eventFilter", eventFilter);
+        return subscribe(flags, null, sensor, listener);
+    }
+
+    protected <T> Object getSubscriber(Map<String, Object> flags, Subscription<T> s) {
+        return s.subscriber!=null ? s.subscriber : flags.containsKey("subscriber") ? flags.remove("subscriber") : s.listener;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/AccessManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/AccessManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/AccessManager.java
new file mode 100644
index 0000000..5d6a057
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/AccessManager.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.management.internal;
+
+import org.apache.brooklyn.api.management.AccessController;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface AccessManager {
+
+    AccessController getAccessController();
+    
+    boolean isLocationProvisioningAllowed();
+
+    boolean isLocationManagementAllowed();
+
+    boolean isEntityManagementAllowed();
+
+    void setLocationProvisioningAllowed(boolean allowed);
+    
+    void setLocationManagementAllowed(boolean allowed);
+    
+    void setEntityManagementAllowed(boolean allowed);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..038ec90
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/AsyncCollectionChangeAdapter.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.brooklyn.api.management.ExecutionManager;
+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> {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(AsyncCollectionChangeAdapter.class);
+
+    private final ExecutionManager executor;
+    private final CollectionChangeListener<Item> delegate;
+    
+    public AsyncCollectionChangeAdapter(ExecutionManager executor, CollectionChangeListener<Item> delegate) {
+        this.executor = checkNotNull(executor, "executor");
+        this.delegate = checkNotNull(delegate, "delegate");
+        ((BasicExecutionManager) executor).setTaskSchedulerForTag(delegate, SingleThreadedScheduler.class);
+    }
+
+    @Override
+    public void onItemAdded(final Item item) {
+        executor.submit(MutableMap.of("tag", delegate), new Runnable() {
+            public void run() {
+                try {
+                    delegate.onItemAdded(item);
+                } catch (Throwable t) {
+                    LOG.warn("Error notifying listener of itemAdded("+item+")", t);
+                    Exceptions.propagate(t);
+                }
+            }
+        });
+    }
+    
+    @Override
+    public void onItemRemoved(final Item item) {
+        executor.submit(MutableMap.of("tag", delegate), new Runnable() {
+            public void run() {
+                try {
+                    delegate.onItemRemoved(item);
+                } catch (Throwable t) {
+                    LOG.warn("Error notifying listener of itemAdded("+item+")", t);
+                    Exceptions.propagate(t);
+                }
+            }
+        });
+    }
+
+    @Override
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        return (other instanceof AsyncCollectionChangeAdapter) && 
+                delegate.equals(((AsyncCollectionChangeAdapter<?>) other).delegate);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/BasicSubscriptionContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/BasicSubscriptionContext.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/BasicSubscriptionContext.java
new file mode 100644
index 0000000..04aa18f
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/BasicSubscriptionContext.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.management.internal;
+
+import static brooklyn.util.JavaGroovyEquivalents.mapOf;
+import groovy.lang.Closure;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.Group;
+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.management.SubscriptionContext;
+import org.apache.brooklyn.api.management.SubscriptionHandle;
+import org.apache.brooklyn.api.management.SubscriptionManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+
+/**
+ * A {@link SubscriptionContext} for an entity or other user of a {@link SubscriptionManager}.
+ */
+public class BasicSubscriptionContext implements SubscriptionContext {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(BasicSubscriptionContext.class);
+
+    private final SubscriptionManager manager;
+    private final Object subscriber;
+    private final Map<String,Object> flags;
+
+    public BasicSubscriptionContext(SubscriptionManager manager, Object subscriber) {
+        this(Collections.<String,Object>emptyMap(), manager, subscriber);
+    }
+    
+    public BasicSubscriptionContext(Map<String, ?> flags, SubscriptionManager manager, Object subscriber) {
+        this.manager = manager;
+        this.subscriber = subscriber;
+        this.flags = mapOf("subscriber", subscriber);
+        if (flags!=null) this.flags.putAll(flags);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, Closure c) {
+        return subscribe(Collections.<String,Object>emptyMap(), producer, sensor, c);
+    }
+    
+    @SuppressWarnings("rawtypes")
+    public <T> SubscriptionHandle subscribe(Map<String, Object> newFlags, Entity producer, Sensor<T> sensor, Closure c) {
+        return subscribe(newFlags, producer, sensor, toSensorEventListener(c));        
+    }
+
+    @Override
+    public <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        return subscribe(Collections.<String,Object>emptyMap(), producer, sensor, listener);
+    }
+    
+    @Override
+    public <T> SubscriptionHandle subscribe(Map<String, Object> newFlags, Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        Map<String,Object> subscriptionFlags = Maps.newLinkedHashMap(flags);
+        if (newFlags != null) subscriptionFlags.putAll(newFlags);
+        return manager.subscribe(subscriptionFlags, producer, sensor, listener);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public <T> SubscriptionHandle subscribeToChildren(Entity parent, Sensor<T> sensor, Closure c) {
+        return subscribeToChildren(Collections.<String,Object>emptyMap(), parent, sensor, c);
+    }
+    
+    @SuppressWarnings("rawtypes")
+    public <T> SubscriptionHandle subscribeToChildren(Map<String, Object> newFlags, Entity parent, Sensor<T> sensor, Closure c) {
+        return subscribeToChildren(newFlags, parent, sensor, toSensorEventListener(c));
+    }
+
+    @Override
+    public <T> SubscriptionHandle subscribeToChildren(Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        return subscribeToChildren(Collections.<String,Object>emptyMap(), parent, sensor, listener);
+    }
+    
+    @Override
+    public <T> SubscriptionHandle subscribeToChildren(Map<String, Object> newFlags, Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        Map<String,Object> subscriptionFlags = Maps.newLinkedHashMap(flags);
+        if (newFlags != null) subscriptionFlags.putAll(newFlags);
+        return manager.subscribeToChildren(subscriptionFlags, parent, sensor, listener);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public <T> SubscriptionHandle subscribeToMembers(Group parent, Sensor<T> sensor, Closure c) {
+        return subscribeToMembers(Collections.<String,Object>emptyMap(), parent, sensor, c);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public <T> SubscriptionHandle subscribeToMembers(Map<String, Object> newFlags, Group parent, Sensor<T> sensor, Closure c) {
+        return subscribeToMembers(newFlags, parent, sensor, toSensorEventListener(c));
+    }
+    
+    @Override
+    public <T> SubscriptionHandle subscribeToMembers(Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        return subscribeToMembers(Collections.<String,Object>emptyMap(), parent, sensor, listener);
+    }
+    
+    @Override
+    public <T> SubscriptionHandle subscribeToMembers(Map<String, Object> newFlags, Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        Map<String,Object> subscriptionFlags = Maps.newLinkedHashMap(flags);
+        if (newFlags != null) subscriptionFlags.putAll(newFlags);
+        return manager.subscribeToMembers(subscriptionFlags, parent, sensor, listener);
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Override
+    public boolean unsubscribe(SubscriptionHandle subscriptionId) {
+        Preconditions.checkNotNull(subscriptionId, "subscriptionId must not be null");
+        Preconditions.checkArgument(Objects.equal(subscriber, ((Subscription) subscriptionId).subscriber), "The subscriptionId is for a different "+subscriber+"; expected "+((Subscription) subscriptionId).subscriber);
+        return manager.unsubscribe(subscriptionId);
+    }
+
+    /** @see SubscriptionManager#publish(SensorEvent) */
+    @Override
+    public <T> void publish(SensorEvent<T> event) {
+        manager.publish(event);
+    }
+
+    /** Return the subscriptions associated with this context */
+    @Override
+    public Set<SubscriptionHandle> getSubscriptions() {
+        return manager.getSubscriptionsForSubscriber(subscriber);
+    }
+
+    @Override
+    public int unsubscribeAll() {
+        int count = 0;
+        
+        // To avoid ConcurrentModificationException when copying subscriptions, need to synchronize on it
+        Set<SubscriptionHandle> subscriptions = getSubscriptions();
+        Collection<SubscriptionHandle> subscriptionsCopy;
+        synchronized (subscriptions) {
+            subscriptionsCopy = ImmutableList.copyOf(subscriptions);
+        }
+        
+        for (SubscriptionHandle s : subscriptionsCopy) {
+            count++; 
+            boolean result = unsubscribe(s); 
+            if (!result) LOG.warn("When unsubscribing from all of {}, unsubscribe of {} return false", subscriber, s);
+        }
+        return count;
+    }
+    
+    @SuppressWarnings("rawtypes")
+    private <T> SensorEventListener<T> toSensorEventListener(final Closure c) {
+        return new SensorEventListener<T>() {
+            @Override public void onEvent(SensorEvent<T> event) {
+                c.call(event);
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/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
new file mode 100644
index 0000000..c02ff81
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynGarbageCollector.java
@@ -0,0 +1,626 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.management.internal;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.BrooklynTaskTags.WrappedEntity;
+import brooklyn.entity.basic.BrooklynTaskTags.WrappedStream;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.internal.storage.BrooklynStorage;
+import brooklyn.util.collections.MutableList;
+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;
+
+import com.google.common.base.Objects;
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Iterables;
+
+/**
+ * Deletes record of old tasks, to prevent space leaks and the eating up of more and more memory.
+ * 
+ * The deletion policy is configurable:
+ * <ul>
+ *   <li>Period - how frequently to look at the existing tasks to delete some, if required
+ *   <li>Max task age - the time after which a completed task will be automatically deleted
+ *       (i.e. any root task completed more than maxTaskAge ago will be deleted)
+ *   <li>Max tasks per <various categories> - the maximum number of tasks to be kept for a given tag,
+ *       split into categories based on what is seeming to be useful
+ * </ul>
+ * 
+ * The default is to check with a period of one minute, deleting tasks after 30 days, 
+ * and keeping at most 100000 tasks in the system,
+ * max 1000 tasks per entity, 50 per effector within that entity, and 50 per other non-effector tag
+ * within that entity (or global if not attached to an entity).
+ * 
+ * @author aled
+ */
+public class BrooklynGarbageCollector {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BrooklynGarbageCollector.class);
+
+    public static final ConfigKey<Duration> GC_PERIOD = ConfigKeys.newDurationConfigKey(
+            "brooklyn.gc.period", "the period for checking if any tasks need to be deleted", 
+            Duration.minutes(1));
+    
+    public static final ConfigKey<Boolean> DO_SYSTEM_GC = ConfigKeys.newBooleanConfigKey(
+            "brooklyn.gc.doSystemGc", "whether to periodically call System.gc()", false);
+    
+    /** 
+     * should we check for tasks which are submitted by another but backgrounded, i.e. not a child of that task?
+     * default to yes, despite it can be some extra loops, to make sure we GC them promptly.
+     * @since 0.7.0 */
+    // work offender is {@link DynamicSequentialTask} internal job tracker, but it is marked 
+    // transient so it is destroyed prompty; there may be others, however;
+    // but OTOH it might be expensive to check for these all the time!
+    // TODO probably we can set this false (remove this and related code),
+    // and just rely on usual GC to pick up background tasks; the lifecycle of background task
+    // should normally be independent of the submitter. (DST was the exception, and marking 
+    // transient there fixes the main problem, which is when the submitter is GC'd but the submitted is not,
+    // and we don't want the submitted to show up at the root in the GUI, which it will if its
+    // submitter has been GC'd)
+    @Beta
+    public static final ConfigKey<Boolean> CHECK_SUBTASK_SUBMITTERS = ConfigKeys.newBooleanConfigKey(
+        "brooklyn.gc.checkSubtaskSubmitters", "whether for subtasks to check the submitters", true);
+
+    public static final ConfigKey<Integer> MAX_TASKS_PER_TAG = ConfigKeys.newIntegerConfigKey(
+        "brooklyn.gc.maxTasksPerTag", 
+        "the maximum number of tasks to be kept for a given tag "
+        + "within an execution context (e.g. entity); "
+        + "some broad-brush tags are excluded, and if an entity has multiple tags all tag counts must be full",
+        50);
+    
+    public static final ConfigKey<Integer> MAX_TASKS_PER_ENTITY = ConfigKeys.newIntegerConfigKey(
+        "brooklyn.gc.maxTasksPerEntity", 
+        "the maximum number of tasks to be kept for a given entity",
+        1000);
+
+    public static final ConfigKey<Integer> MAX_TASKS_GLOBAL = ConfigKeys.newIntegerConfigKey(
+        "brooklyn.gc.maxTasksGlobal", 
+        "the maximum number of tasks to be kept across the entire system",
+        100000);
+
+    public static final ConfigKey<Duration> MAX_TASK_AGE = ConfigKeys.newDurationConfigKey(
+            "brooklyn.gc.maxTaskAge", 
+            "the duration after which a completed task will be automatically deleted", 
+            Duration.days(30));
+    
+    protected final static Comparator<Task<?>> TASKS_OLDEST_FIRST_COMPARATOR = new Comparator<Task<?>>() {
+        @Override public int compare(Task<?> t1, Task<?> t2) {
+            long end1 = t1.getEndTimeUtc();
+            long end2 = t2.getEndTimeUtc();
+            return (end1 < end2) ? -1 : ((end1 == end2) ? 0 : 1);
+        }
+    };
+    
+    private final BasicExecutionManager executionManager;
+    private final BrooklynStorage storage;
+    private final BrooklynProperties brooklynProperties;
+    private final ScheduledExecutorService executor;
+    private ScheduledFuture<?> activeCollector;
+    private Map<Entity,Task<?>> unmanagedEntitiesNeedingGc = new LinkedHashMap<Entity, Task<?>>();
+    
+    private Duration gcPeriod;
+    private final boolean doSystemGc;
+    private volatile boolean running = true;
+    
+    public BrooklynGarbageCollector(BrooklynProperties brooklynProperties, BasicExecutionManager executionManager, BrooklynStorage storage) {
+        this.executionManager = executionManager;
+        this.storage = storage;
+        this.brooklynProperties = brooklynProperties;
+
+        doSystemGc = brooklynProperties.getConfig(DO_SYSTEM_GC);
+        
+        executor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+                @Override public Thread newThread(Runnable r) {
+                    return new Thread(r, "brooklyn-gc");
+                }});
+        
+        executionManager.addListener(new ExecutionListener() {
+                @Override public void onTaskDone(Task<?> task) {
+                    BrooklynGarbageCollector.this.onTaskDone(task);
+                }});
+    
+        scheduleCollector(true);
+    }
+
+    protected synchronized void scheduleCollector(boolean canInterruptCurrent) {
+        if (activeCollector != null) activeCollector.cancel(canInterruptCurrent);
+        
+        gcPeriod = brooklynProperties.getConfig(GC_PERIOD);
+        if (gcPeriod!=null) {
+            activeCollector = executor.scheduleWithFixedDelay(
+                new Runnable() {
+                    @Override public void run() {
+                        gcIteration();
+                    }
+                }, 
+                gcPeriod.toMillisecondsRoundingUp(), 
+                gcPeriod.toMillisecondsRoundingUp(), 
+                TimeUnit.MILLISECONDS);
+        }
+    }
+
+    /** force a round of Brooklyn garbage collection */
+    public void gcIteration() {
+        try {
+            logUsage("brooklyn gc (before)");
+            gcTasks();
+            logUsage("brooklyn gc (after)");
+            
+            if (doSystemGc) {
+                // Can be very useful when tracking down OOMEs etc, where a lot of tasks are executing
+                // Empirically observed that (on OS X jvm at least) calling twice blocks - logs a significant
+                // amount of memory having been released, as though a full-gc had been run. But this is highly
+                // dependent on the JVM implementation.
+                System.gc(); System.gc();
+                logUsage("brooklyn gc (after system gc)");
+            }
+        } catch (Throwable t) {
+            Exceptions.propagateIfFatal(t);
+            LOG.warn("Error during management-context GC: "+t, t);
+            // previously we bailed on all errors, but I don't think we should do that -Alex
+        }
+    }
+
+    public void logUsage(String prefix) {
+        if (LOG.isDebugEnabled())
+            LOG.debug(prefix+" - using "+getUsageString());
+    }
+
+    public static String makeBasicUsageString() {
+        return Strings.makeSizeString(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())+" / "+
+            Strings.makeSizeString(Runtime.getRuntime().totalMemory()) + " memory" +
+            " ("+Strings.makeSizeString(MemoryUsageTracker.SOFT_REFERENCES.getBytesUsed()) + " soft); "+
+            Thread.activeCount()+" threads";
+    }
+    
+    public String getUsageString() {
+        return makeBasicUsageString()+"; "+
+            "storage: " + storage.getStorageMetrics() + "; " +
+            "tasks: " +
+            executionManager.getNumActiveTasks()+" active, "+
+            executionManager.getNumIncompleteTasks()+" unfinished; "+
+            executionManager.getNumInMemoryTasks()+" remembered, "+
+            executionManager.getTotalTasksSubmitted()+" total submitted)";
+    }
+    
+    public void shutdownNow() {
+        running = false;
+        if (activeCollector != null) activeCollector.cancel(true);
+        if (executor != null) executor.shutdownNow();
+    }
+    
+    public void onUnmanaged(Entity entity) {
+        // defer task deletions until the entity is completely unmanaged
+        // (this is usually invoked during the stop sequence)
+        synchronized (unmanagedEntitiesNeedingGc) {
+            unmanagedEntitiesNeedingGc.put(entity, Tasks.current());
+        }
+    }
+    
+    public void deleteTasksForEntity(Entity entity) {
+        // remove all references to this entity from tasks
+        executionManager.deleteTag(entity);
+        executionManager.deleteTag(BrooklynTaskTags.tagForContextEntity(entity));
+        executionManager.deleteTag(BrooklynTaskTags.tagForCallerEntity(entity));
+        executionManager.deleteTag(BrooklynTaskTags.tagForTargetEntity(entity));
+    }
+    
+    public void onUnmanaged(Location loc) {
+        // No-op currently; no tasks are tracked through their location
+    }
+    
+    public void onTaskDone(Task<?> task) {
+        if (shouldDeleteTaskImmediately(task)) {
+            executionManager.deleteTask(task);
+        }
+    }
+    
+    /** @deprecated since 0.7.0, method moved internal until semantics are clarified; see also {@link #shouldDeleteTaskImmediately(Task)} */
+    @Deprecated
+    public boolean shouldDeleteTask(Task<?> task) {
+        return shouldDeleteTaskImmediately(task);
+    }
+    /** whether this task should be deleted on completion,
+     * because it is transient, or because it is submitted background without much context information */
+    protected boolean shouldDeleteTaskImmediately(Task<?> task) {
+        if (!task.isDone()) return false;
+        
+        Set<Object> tags = task.getTags();
+        if (tags.contains(ManagementContextInternal.TRANSIENT_TASK_TAG))
+            return true;
+        if (tags.contains(ManagementContextInternal.EFFECTOR_TAG) || tags.contains(ManagementContextInternal.NON_TRANSIENT_TASK_TAG))
+            return false;
+        
+        if (task.getSubmittedByTask()!=null) {
+            Task<?> parent = task.getSubmittedByTask();
+            if (executionManager.getTask(parent.getId())==null) {
+                // parent is already cleaned up
+                return true;
+            }
+            if (parent instanceof HasTaskChildren && Iterables.contains(((HasTaskChildren)parent).getChildren(), task)) {
+                // it is a child, let the parent manage this task's death
+                return false;
+            }
+            Entity associatedEntity = BrooklynTaskTags.getTargetOrContextEntity(task);
+            if (associatedEntity!=null) {
+                // this is associated to an entity; destroy only if the entity is unmanaged
+                return !Entities.isManaged(associatedEntity);
+            }
+            // if not associated to an entity, then delete immediately
+            return true;
+        }
+        
+        // e.g. scheduled tasks, sensor events, etc
+        // TODO (in future may keep some of these with another limit, based on a new TagCategory)
+        // there may also be a server association for server-side tasks which should be kept
+        // (but be careful not to keep too many subscriptions!)
+        
+        return true;
+    }
+
+    /**
+     * Deletes old tasks. The age/number of tasks to keep is controlled by fields like 
+     * {@link #maxTasksPerTag} and {@link #maxTaskAge}.
+     */
+    protected synchronized int gcTasks() {
+        // TODO Must be careful with memory usage here: have seen OOME if we get crazy lots of tasks.
+        // hopefully the use new limits, filters, and use of live lists in some places (added Sep 2014) will help.
+        // 
+        // An option is for getTasksWithTag(tag) to return an ArrayList rather than a LinkedHashSet. That
+        // is a far more memory efficient data structure (e.g. 4 bytes overhead per object rather than 
+        // 32 bytes overhead per object for HashSet).
+        //
+        // More notes on optimization is in the history of this file.
+        
+        if (!running) return 0;
+        
+        Duration newPeriod = brooklynProperties.getConfig(GC_PERIOD);
+        if (!Objects.equal(gcPeriod, newPeriod)) {
+            // caller has changed period, reschedule on next run
+            scheduleCollector(false);
+        }
+    
+        expireUnmanagedEntityTasks();
+        expireAgedTasks();
+        expireTransientTasks();
+        
+        // now look at overcapacity tags, non-entity tags first
+        
+        Set<Object> taskTags = executionManager.getTaskTags();
+        
+        int maxTasksPerEntity = brooklynProperties.getConfig(MAX_TASKS_PER_ENTITY);
+        int maxTasksPerTag = brooklynProperties.getConfig(MAX_TASKS_PER_TAG);
+        
+        Map<Object,AtomicInteger> taskNonEntityTagsOverCapacity = MutableMap.of();
+        Map<Object,AtomicInteger> taskEntityTagsOverCapacity = MutableMap.of();
+        
+        Map<Object,AtomicInteger> taskAllTagsOverCapacity = MutableMap.of();
+        
+        for (Object tag : taskTags) {
+            if (isTagIgnoredForGc(tag)) continue;
+            
+            Set<Task<?>> tasksWithTag = executionManager.tasksWithTagLiveOrNull(tag);
+            if (tasksWithTag==null) continue;
+            AtomicInteger overA = null;
+            if (tag instanceof WrappedEntity) {
+                int over = tasksWithTag.size() - maxTasksPerEntity;
+                if (over>0) {
+                    overA = new AtomicInteger(over);
+                    taskEntityTagsOverCapacity.put(tag, overA);
+                }
+            } else {
+                int over = tasksWithTag.size() - maxTasksPerTag;
+                if (over>0) {
+                    overA = new AtomicInteger(over);
+                    taskNonEntityTagsOverCapacity.put(tag, overA);
+                }
+            }
+            if (overA!=null) {
+                taskAllTagsOverCapacity.put(tag, overA);
+            }
+        }
+        
+        int deletedCount = 0;
+        deletedCount += expireOverCapacityTagsInCategory(taskNonEntityTagsOverCapacity, taskAllTagsOverCapacity, TagCategory.NON_ENTITY_NORMAL, false);
+        deletedCount += expireOverCapacityTagsInCategory(taskEntityTagsOverCapacity, taskAllTagsOverCapacity, TagCategory.ENTITY, true);
+        deletedCount += expireSubTasksWhoseSubmitterIsExpired();
+        
+        int deletedGlobally = expireIfOverCapacityGlobally();
+        deletedCount += deletedGlobally;
+        if (deletedGlobally>0) deletedCount += expireSubTasksWhoseSubmitterIsExpired();
+        
+        return deletedCount;
+    }
+
+    protected static boolean isTagIgnoredForGc(Object tag) {
+        if (tag == null) return true;
+        if (tag.equals(ManagementContextInternal.EFFECTOR_TAG)) return true;
+        if (tag.equals(ManagementContextInternal.SUB_TASK_TAG)) return true;
+        if (tag.equals(ManagementContextInternal.NON_TRANSIENT_TASK_TAG)) return true;
+        if (tag.equals(ManagementContextInternal.TRANSIENT_TASK_TAG)) return true;
+        if (tag instanceof WrappedStream) {
+            return true;
+        }
+        
+        return false;
+    }
+    
+    protected void expireUnmanagedEntityTasks() {
+        Iterator<Entry<Entity, Task<?>>> ei;
+        synchronized (unmanagedEntitiesNeedingGc) {
+            ei = MutableSet.copyOf(unmanagedEntitiesNeedingGc.entrySet()).iterator();
+        }
+        while (ei.hasNext()) {
+            Entry<Entity, Task<?>> ee = ei.next();
+            if (Entities.isManaged(ee.getKey())) continue;
+            if (ee.getValue()!=null && !ee.getValue().isDone()) continue;
+            deleteTasksForEntity(ee.getKey());
+            synchronized (unmanagedEntitiesNeedingGc) {
+                unmanagedEntitiesNeedingGc.remove(ee.getKey());
+            }
+        }
+    }
+    
+    protected void expireAgedTasks() {
+        Duration maxTaskAge = brooklynProperties.getConfig(MAX_TASK_AGE);
+        
+        Collection<Task<?>> allTasks = executionManager.allTasksLive();
+        Collection<Task<?>> tasksToDelete = MutableList.of();
+
+        try {
+            for (Task<?> task: allTasks) {
+                if (!task.isDone()) continue;
+                if (BrooklynTaskTags.isSubTask(task)) continue;
+
+                if (maxTaskAge.isShorterThan(Duration.sinceUtc(task.getEndTimeUtc())))
+                    tasksToDelete.add(task);
+            }
+            
+        } catch (ConcurrentModificationException e) {
+            // delete what we've found so far
+            LOG.debug("Got CME inspecting aged tasks, with "+tasksToDelete.size()+" found for deletion: "+e);
+        }
+        
+        for (Task<?> task: tasksToDelete) {
+            executionManager.deleteTask(task);
+        }
+    }
+    
+    protected void expireTransientTasks() {
+        Set<Task<?>> transientTasks = executionManager.getTasksWithTag(BrooklynTaskTags.TRANSIENT_TASK_TAG);
+        for (Task<?> t: transientTasks) {
+            if (!t.isDone()) continue;
+            executionManager.deleteTask(t);
+        }
+    }
+    
+    protected int expireSubTasksWhoseSubmitterIsExpired() {
+        // ideally we wouldn't have this; see comments on CHECK_SUBTASK_SUBMITTERS
+        if (!brooklynProperties.getConfig(CHECK_SUBTASK_SUBMITTERS))
+            return 0;
+        
+        Collection<Task<?>> allTasks = executionManager.allTasksLive();
+        Collection<Task<?>> tasksToDelete = MutableList.of();
+        try {
+            for (Task<?> task: allTasks) {
+                if (!task.isDone()) continue;
+                Task<?> submitter = task.getSubmittedByTask();
+                // if we've leaked, ie a subtask which is not a child task, 
+                // and the submitter is GC'd, then delete this also
+                if (submitter!=null && submitter.isDone() && executionManager.getTask(submitter.getId())==null) {
+                    tasksToDelete.add(task);
+                }
+            }
+            
+        } catch (ConcurrentModificationException e) {
+            // delete what we've found so far
+            LOG.debug("Got CME inspecting aged tasks, with "+tasksToDelete.size()+" found for deletion: "+e);
+        }
+        
+        for (Task<?> task: tasksToDelete) {
+            executionManager.deleteTask(task);
+        }
+        return tasksToDelete.size();
+    }
+    
+    protected enum TagCategory { 
+        ENTITY, NON_ENTITY_NORMAL;
+        
+        public boolean acceptsTag(Object tag) {
+            if (isTagIgnoredForGc(tag)) return false;
+            if (tag instanceof WrappedEntity) return this==ENTITY;
+            if (this==ENTITY) return false;
+            return true;
+        }
+    } 
+
+
+    /** expires tasks which are over-capacity in all their non-entity tag categories, returned count */
+    protected int expireOverCapacityTagsInCategory(Map<Object, AtomicInteger> taskTagsInCategoryOverCapacity, Map<Object, AtomicInteger> taskAllTagsOverCapacity, TagCategory category, boolean emptyFilterNeeded) {
+        if (emptyFilterNeeded) {
+            // previous run may have decremented counts  
+            MutableList<Object> nowOkayTags = MutableList.of(); 
+            for (Map.Entry<Object,AtomicInteger> entry: taskTagsInCategoryOverCapacity.entrySet()) {
+                if (entry.getValue().get()<=0) nowOkayTags.add(entry.getKey());
+            }
+            for (Object tag: nowOkayTags) taskTagsInCategoryOverCapacity.remove(tag);
+        }
+        
+        if (taskTagsInCategoryOverCapacity.isEmpty())
+            return 0;
+        
+        Collection<Task<?>> tasks = executionManager.allTasksLive();
+        List<Task<?>> tasksToConsiderDeleting = MutableList.of();
+        try {
+            for (Task<?> task: tasks) {
+                if (!task.isDone()) continue;
+                
+                Set<Object> tags = task.getTags();
+
+                int categoryTags = 0, tooFullCategoryTags = 0;
+                for (Object tag: tags) {
+                    if (category.acceptsTag(tag)) {
+                        categoryTags++;
+                        if (taskTagsInCategoryOverCapacity.containsKey(tag))
+                            tooFullCategoryTags++;
+                    }
+                }
+                if (tooFullCategoryTags>0) {
+                    if (categoryTags==tooFullCategoryTags) {
+                        // all buckets are full, delete this one
+                        tasksToConsiderDeleting.add(task);
+                    } else {
+                        // if any bucket is under capacity, then give grace to the other buckets in this category
+                        for (Object tag: tags) {
+                            if (category.acceptsTag(tag)) {
+                                AtomicInteger over = taskTagsInCategoryOverCapacity.get(tag);
+                                if (over!=null) {
+                                    if (over.decrementAndGet()<=0) {
+                                        // and remove it from over-capacity if so
+                                        taskTagsInCategoryOverCapacity.remove(tag);
+                                        if (taskTagsInCategoryOverCapacity.isEmpty())
+                                            return 0;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+        } catch (ConcurrentModificationException e) {
+            // do CME's happen with these data structures?
+            // if so, let's just delete what we've found so far
+            LOG.debug("Got CME inspecting tasks, with "+tasksToConsiderDeleting.size()+" found for deletion: "+e);
+        }
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("brooklyn-gc detected "+taskTagsInCategoryOverCapacity.size()+" "+category+" "
+                    + "tags over capacity, expiring old tasks; "
+                    + tasksToConsiderDeleting.size()+" tasks under consideration; categories are: "
+                    + taskTagsInCategoryOverCapacity);
+
+        Collections.sort(tasksToConsiderDeleting, TASKS_OLDEST_FIRST_COMPARATOR);
+        // now try deleting tasks which are overcapacity for each (non-entity) tag
+        int deleted = 0;
+        for (Task<?> task: tasksToConsiderDeleting) {
+            boolean delete = true;
+            for (Object tag: task.getTags()) {
+                if (!category.acceptsTag(tag))
+                    continue;
+                if (taskTagsInCategoryOverCapacity.get(tag)==null) {
+                    // no longer over capacity in this tag
+                    delete = false;
+                    break;
+                }
+            }
+            if (delete) {
+                // delete this and update overcapacity info
+                deleted++;
+                executionManager.deleteTask(task);
+                for (Object tag: task.getTags()) {
+                    AtomicInteger counter = taskAllTagsOverCapacity.get(tag);
+                    if (counter!=null && counter.decrementAndGet()<=0)
+                        taskTagsInCategoryOverCapacity.remove(tag);
+                }
+                if (LOG.isTraceEnabled())
+                    LOG.trace("brooklyn-gc deleted "+task+", buckets now "+taskTagsInCategoryOverCapacity);
+                if (taskTagsInCategoryOverCapacity.isEmpty())
+                    break;
+            }
+        }
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("brooklyn-gc deleted "+deleted+" tasks in over-capacity " + category+" tag categories; "
+                    + "capacities now: " + taskTagsInCategoryOverCapacity);
+        return deleted;
+    }
+
+    protected int expireIfOverCapacityGlobally() {
+        Collection<Task<?>> tasksLive = executionManager.allTasksLive();
+        if (tasksLive.size() <= brooklynProperties.getConfig(MAX_TASKS_GLOBAL))
+            return 0;
+        LOG.debug("brooklyn-gc detected "+tasksLive.size()+" tasks in memory, over global limit, looking at deleting some");
+        
+        try {
+            tasksLive = MutableList.copyOf(tasksLive);
+        } catch (ConcurrentModificationException e) {
+            tasksLive = executionManager.getTasksWithAllTags(MutableList.of());
+        }
+
+        MutableList<Task<?>> tasks = MutableList.of();
+        for (Task<?> task: tasksLive) {
+            if (task.isDone()) {
+                tasks.add(task);
+            }
+        }
+        
+        int numToDelete = tasks.size() - brooklynProperties.getConfig(MAX_TASKS_GLOBAL);
+        if (numToDelete <= 0) {
+            LOG.debug("brooklyn-gc detected only "+tasks.size()+" completed tasks in memory, not over global limit, so not deleting any");
+            return 0;
+        }
+            
+        Collections.sort(tasks, TASKS_OLDEST_FIRST_COMPARATOR);
+        
+        int numDeleted = 0;
+        while (numDeleted < numToDelete && tasks.size()>numDeleted) {
+            executionManager.deleteTask( tasks.get(numDeleted++) );
+        }
+        if (LOG.isDebugEnabled())
+            LOG.debug("brooklyn-gc deleted "+numDeleted+" tasks as was over global limit, now have "+executionManager.allTasksLive().size());
+        return numDeleted;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynObjectManagementMode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynObjectManagementMode.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynObjectManagementMode.java
new file mode 100644
index 0000000..49afe27
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/BrooklynObjectManagementMode.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.management.internal;
+
+/** Indicates how an entity/location/adjunct is treated at a given {@link ManagementContext} */
+public enum BrooklynObjectManagementMode {
+    /** item does not exist, not in memory, nor persisted (e.g. creating for first time, or finally destroying) */
+    NONEXISTENT, 
+    /** item exists or existed elsewhere, i.e. there is persisted state, but is not loaded here */
+    UNMANAGED_PERSISTED, 
+    /** item is loaded but read-only (ie not actively managed here) */
+    LOADED_READ_ONLY, 
+    /** item is actively managed here */
+    MANAGED_PRIMARY 
+}
\ No newline at end of file



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

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


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

Branch: refs/heads/master
Commit: 9cf456dca3110c308c11bd4b5fdedbbe74b4236d
Parents: 6602f69
Author: Hadrian Zbarcea <ha...@apache.org>
Authored: Sat Aug 15 00:05:56 2015 -0400
Committer: Hadrian Zbarcea <ha...@apache.org>
Committed: Sat Aug 15 00:05:56 2015 -0400

----------------------------------------------------------------------
 .../brooklyn/entity/basic/AbstractEntity.java   |   4 +-
 .../entity/basic/AbstractGroupImpl.java         |   2 +-
 .../rebind/PeriodicDeltaChangeListener.java     |   2 +-
 .../brooklyn/entity/rebind/RebindIteration.java |   2 +-
 .../entity/rebind/RebindManagerImpl.java        |   2 +-
 .../basic/PortAttributeSensorAndConfigKey.java  |   2 +-
 .../java/brooklyn/event/feed/AbstractFeed.java  |   2 +-
 .../internal/BrooklynFeatureEnablement.java     | 210 -------------------
 .../internal/BrooklynInitialization.java        |  81 -------
 .../util/BrooklynLanguageExtensions.java        |   3 +-
 .../main/java/brooklyn/util/ResourceUtils.java  |   2 +-
 .../brooklyn/util/crypto/FluentKeySigner.java   |   2 +-
 .../java/brooklyn/util/crypto/SecureKeys.java   |   2 +-
 .../java/brooklyn/util/flags/TypeCoercions.java |   2 +-
 .../util/internal/ssh/sshj/SshjTool.java        |   2 +-
 .../util/task/BasicExecutionManager.java        |   2 +-
 .../internal/BrooklynFeatureEnablement.java     | 210 +++++++++++++++++++
 .../core/internal/BrooklynInitialization.java   |  81 +++++++
 .../ha/HighAvailabilityManagerImpl.java         |   2 +-
 .../internal/LocalManagementContext.java        |   2 +-
 .../location/basic/AbstractLocation.java        |   2 +-
 .../location/basic/LocationConfigUtils.java     |   4 +-
 .../entity/rebind/RebindCatalogItemTest.java    |   2 +-
 ...talogWhenCatalogPersistenceDisabledTest.java |   2 +-
 .../internal/BrooklynFeatureEnablementTest.java | 118 -----------
 .../sshj/SshjToolAsyncStubIntegrationTest.java  |   2 +-
 .../ssh/sshj/SshjToolIntegrationTest.java       |   2 +-
 .../internal/BrooklynFeatureEnablementTest.java | 119 +++++++++++
 .../HighAvailabilityManagerSplitBrainTest.java  |   2 +-
 .../nginx/NginxRebindWithHaIntegrationTest.java |   2 +-
 .../brooklyn/catalog/CatalogYamlRebindTest.java |   2 +-
 .../brooklyn/launcher/BrooklynWebServer.java    |   2 +-
 32 files changed, 440 insertions(+), 436 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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 dc005d8..2860b82 100644
--- a/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
+++ b/core/src/main/java/brooklyn/entity/basic/AbstractEntity.java
@@ -53,6 +53,8 @@ import org.apache.brooklyn.api.policy.EnricherSpec;
 import org.apache.brooklyn.api.policy.EntityAdjunct;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
+import org.apache.brooklyn.core.internal.BrooklynInitialization;
 import org.apache.brooklyn.core.management.internal.EffectorUtils;
 import org.apache.brooklyn.core.management.internal.EntityManagementSupport;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
@@ -76,8 +78,6 @@ import brooklyn.event.basic.BasicNotificationSensor;
 import brooklyn.event.basic.Sensors;
 import brooklyn.event.feed.AbstractFeed;
 import brooklyn.event.feed.ConfigToAttributes;
-import brooklyn.internal.BrooklynFeatureEnablement;
-import brooklyn.internal.BrooklynInitialization;
 import brooklyn.internal.storage.BrooklynStorage;
 import brooklyn.internal.storage.Reference;
 import brooklyn.internal.storage.impl.BasicReference;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/core/src/main/java/brooklyn/entity/basic/AbstractGroupImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/AbstractGroupImpl.java b/core/src/main/java/brooklyn/entity/basic/AbstractGroupImpl.java
index 79abdb0..a300f39 100644
--- a/core/src/main/java/brooklyn/entity/basic/AbstractGroupImpl.java
+++ b/core/src/main/java/brooklyn/entity/basic/AbstractGroupImpl.java
@@ -28,11 +28,11 @@ 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.proxying.EntitySpec;
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.internal.ManagementContextInternal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.internal.BrooklynFeatureEnablement;
 import brooklyn.util.collections.SetFromLiveMap;
 
 import com.google.common.base.Optional;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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 98906e9..d204ecc 100644
--- a/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java
+++ b/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java
@@ -45,12 +45,12 @@ import org.apache.brooklyn.api.management.Task;
 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 brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils;
 import brooklyn.entity.rebind.persister.PersistenceActivityMetrics;
-import brooklyn.internal.BrooklynFeatureEnablement;
 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/9cf456dc/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 30803e2..8c6a402 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
@@ -64,6 +64,7 @@ import org.apache.brooklyn.api.mementos.TreeNode;
 import org.apache.brooklyn.api.mementos.BrooklynMementoManifest.EntityMementoManifest;
 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.management.internal.BrooklynObjectManagementMode;
 import org.apache.brooklyn.core.management.internal.BrooklynObjectManagerInternal;
 import org.apache.brooklyn.core.management.internal.EntityManagerInternal;
@@ -86,7 +87,6 @@ import brooklyn.entity.proxying.InternalPolicyFactory;
 import brooklyn.entity.rebind.RebindManagerImpl.RebindTracker;
 import brooklyn.entity.rebind.persister.PersistenceActivityMetrics;
 import brooklyn.event.feed.AbstractFeed;
-import brooklyn.internal.BrooklynFeatureEnablement;
 
 import org.apache.brooklyn.location.basic.AbstractLocation;
 import org.apache.brooklyn.location.basic.LocationInternal;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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 8221472..862c86b 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
@@ -43,6 +43,7 @@ import org.apache.brooklyn.api.management.ha.MementoCopyMode;
 import org.apache.brooklyn.api.mementos.BrooklynMementoPersister;
 import org.apache.brooklyn.api.mementos.BrooklynMementoRawData;
 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.slf4j.Logger;
@@ -58,7 +59,6 @@ import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils;
 import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils.CreateBackupMode;
 import brooklyn.entity.rebind.persister.PersistenceActivityMetrics;
 import brooklyn.entity.rebind.transformer.CompoundTransformer;
-import brooklyn.internal.BrooklynFeatureEnablement;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.QuorumCheck;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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 b086b98..fd7100a 100644
--- a/core/src/main/java/brooklyn/event/basic/PortAttributeSensorAndConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/PortAttributeSensorAndConfigKey.java
@@ -28,12 +28,12 @@ import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.BrooklynConfigKeys;
-import brooklyn.internal.BrooklynInitialization;
 
 import org.apache.brooklyn.location.basic.Locations;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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 5f5229c..23037a7 100644
--- a/core/src/main/java/brooklyn/event/feed/AbstractFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/AbstractFeed.java
@@ -26,6 +26,7 @@ import org.apache.brooklyn.api.entity.Feed;
 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,7 +34,6 @@ import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.rebind.BasicFeedRebindSupport;
-import brooklyn.internal.BrooklynFeatureEnablement;
 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/9cf456dc/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java b/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
deleted file mode 100644
index 10a6e4a..0000000
--- a/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
+++ /dev/null
@@ -1,210 +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.internal;
-
-import java.util.Map;
-
-import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
-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;
-
-/**
- * For enabling/disabling experimental features.
- * They can be enabled via java system properties, or by explicitly calling {@link #setEnablement(String, boolean)}.
- * <p>
- * For example, start brooklyn with {@code -Dbrooklyn.experimental.feature.policyPersistence=true}
- * 
- * @author aled
- */
-@Beta
-public class BrooklynFeatureEnablement {
-
-    private static final Logger LOG = LoggerFactory.getLogger(BrooklynFeatureEnablement.class);
-
-    public static final String FEATURE_PROPERTY_PREFIX = "brooklyn.experimental.feature";
-    
-    public static final String FEATURE_POLICY_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".policyPersistence";
-    
-    public static final String FEATURE_ENRICHER_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".enricherPersistence";
-
-    public static final String FEATURE_FEED_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".feedPersistence";
-    
-    /** whether feeds are automatically registered when set on entities, so that they are persisted */
-    public static final String FEATURE_FEED_REGISTRATION_PROPERTY = FEATURE_PROPERTY_PREFIX+".feedRegistration";
-
-    public static final String FEATURE_CATALOG_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".catalogPersistence";
-    
-    /** whether the default standby mode is {@link HighAvailabilityMode#HOT_STANDBY} or falling back to the traditional
-     * {@link HighAvailabilityMode#STANDBY} */
-    public static final String FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY = FEATURE_PROPERTY_PREFIX+".defaultStandbyIsHot";
-    
-    /** whether to attempt to use {@link BrooklynStorage} (datagrid) as a backing store for data;
-     * note this is <b>not</b> compatible with {@link #FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY} 
-     * which uses a blob/file store and a larger-granularity rebind process than was intended with the datagrid */
-    /* not sure if we still even need this? now the rebind/read-only feature reloads on demand from the persistence store;
-     * the data-grid backing  */
-    public static final String FEATURE_USE_BROOKLYN_LIVE_OBJECTS_DATAGRID_STORAGE = FEATURE_PROPERTY_PREFIX+".useBrooklynLiveObjectsDatagridStorage";
-
-    /**
-     * Renaming threads can really helps with debugging etc; however it's a massive performance hit (2x)
-     * <p>
-     * We get 55000 tasks per sec with this off, 28k/s with this on.
-     * <p>
-     * Defaults to false if system property is not set.
-     */
-    public static final String FEATURE_RENAME_THREADS = "brooklyn.executionManager.renameThreads";
-
-    /**
-     * When rebinding to state created from very old versions, the catalogItemId properties will be missing which
-     * results in errors when OSGi bundles are used. When enabled the code tries to infer the catalogItemId from
-     * <ul>
-     *   <li> parent entities
-     *   <li> catalog items matching the type that needs to be deserialized
-     *   <li> iterating through all catalog items and checking if they can provide the needed type
-     * </ul>
-     */
-    public static final String FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND = "brooklyn.backwardCompatibility.feature.inferCatalogItemOnRebind";
-    
-    /**
-     * When rebinding, an entity could reference a catalog item that no longer exists. This option 
-     * will automatically update the catalog item reference to what is inferred as the most 
-     * suitable catalog symbolicName:version.
-     */
-    public static final String FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND = "brooklyn.quickfix.fixDanglingCatalogItemOnRebind";
-    
-    /**
-     * When executing over ssh, whether to support the "async exec" approach, or only the classic approach.
-     * 
-     * If this feature is disabled, then even if the {@link ShellTool#PROP_EXEC_ASYNC} is configured it
-     * will still use the classic ssh approach.
-     */
-    public static final String FEATURE_SSH_ASYNC_EXEC = FEATURE_PROPERTY_PREFIX+".ssh.asyncExec";
-
-    public static final String FEATURE_VALIDATE_LOCATION_SSH_KEYS = "brooklyn.validate.locationSshKeys";
-    
-    private static final Map<String, Boolean> FEATURE_ENABLEMENTS = Maps.newLinkedHashMap();
-
-    private static final Object MUTEX = new Object();
-
-    static void setDefaults() {
-        // Idea is here one can put experimental features that are *enabled* by default, but 
-        // that can be turned off via system properties, or vice versa.
-        // Typically this is useful where a feature is deemed risky!
-        
-        setDefault(FEATURE_POLICY_PERSISTENCE_PROPERTY, true);
-        setDefault(FEATURE_ENRICHER_PERSISTENCE_PROPERTY, true);
-        setDefault(FEATURE_FEED_PERSISTENCE_PROPERTY, true);
-        setDefault(FEATURE_FEED_REGISTRATION_PROPERTY, false);
-        setDefault(FEATURE_CATALOG_PERSISTENCE_PROPERTY, true);
-        setDefault(FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY, false);
-        setDefault(FEATURE_USE_BROOKLYN_LIVE_OBJECTS_DATAGRID_STORAGE, false);
-        setDefault(FEATURE_RENAME_THREADS, false);
-        setDefault(FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND, true);
-        setDefault(FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND, false);
-        setDefault(FEATURE_SSH_ASYNC_EXEC, false);
-        setDefault(FEATURE_VALIDATE_LOCATION_SSH_KEYS, true);
-    }
-    
-    static {
-        setDefaults();
-    }
-    
-    /**
-     * Initialises the feature-enablement from brooklyn properties. For each
-     * property, prefer a system-property if present; otherwise use the value 
-     * from brooklyn properties.
-     */
-    public static void init(BrooklynProperties props) {
-        boolean changed = false;
-        for (Map.Entry<String, Object> entry : props.asMapWithStringKeys().entrySet()) {
-            String property = entry.getKey();
-            if (property.startsWith(FEATURE_PROPERTY_PREFIX)) {
-                if (!FEATURE_ENABLEMENTS.containsKey(property)) {
-                    Object rawVal = System.getProperty(property);
-                    if (rawVal == null) {
-                        rawVal = entry.getValue();
-                    }
-                    boolean val = Boolean.parseBoolean(""+rawVal);
-                    FEATURE_ENABLEMENTS.put(property, val);
-                    
-                    changed = true;
-                    LOG.debug("Init feature enablement of "+property+" set to "+val);
-                }
-            }
-        }
-        if (!changed) {
-            LOG.debug("Init feature enablement did nothing, as no settings in brooklyn properties");
-        }
-    }
-    
-    public static boolean isEnabled(String property) {
-        synchronized (MUTEX) {
-            if (!FEATURE_ENABLEMENTS.containsKey(property)) {
-                String rawVal = System.getProperty(property);
-                boolean val = Boolean.parseBoolean(rawVal);
-                FEATURE_ENABLEMENTS.put(property, val);
-            }
-            return FEATURE_ENABLEMENTS.get(property);
-        }
-    }
-
-    public static boolean enable(String property) {
-        return setEnablement(property, true);
-    }
-    
-    public static boolean disable(String property) {
-        return setEnablement(property, false);
-    }
-    
-    public static boolean setEnablement(String property, boolean val) {
-        synchronized (MUTEX) {
-            boolean oldVal = isEnabled(property);
-            FEATURE_ENABLEMENTS.put(property, val);
-            return oldVal;
-        }
-    }
-    
-    static void setDefault(String property, boolean val) {
-        synchronized (MUTEX) {
-            if (!FEATURE_ENABLEMENTS.containsKey(property)) {
-                String rawVal = System.getProperty(property);
-                if (rawVal == null) {
-                    FEATURE_ENABLEMENTS.put(property, val);
-                    LOG.debug("Default enablement of "+property+" set to "+val);
-                } else {
-                    LOG.debug("Not setting default enablement of "+property+" to "+val+", because system property is "+rawVal);
-                }
-            }
-        }
-    }
-    
-    static void clearCache() {
-        synchronized (MUTEX) {
-            FEATURE_ENABLEMENTS.clear();
-            setDefaults();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/core/src/main/java/brooklyn/internal/BrooklynInitialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/internal/BrooklynInitialization.java b/core/src/main/java/brooklyn/internal/BrooklynInitialization.java
deleted file mode 100644
index 70eea9f..0000000
--- a/core/src/main/java/brooklyn/internal/BrooklynInitialization.java
+++ /dev/null
@@ -1,81 +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.internal;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-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;
-
-/** Various static initialization tasks are routed through this class,
- * to give us better traceability of their invocation. */ 
-@Beta
-public class BrooklynInitialization {
-
-    private static AtomicBoolean done = new AtomicBoolean(false);
-    
-    public static void initTypeCoercionStandardAdapters() {
-        TypeCoercions.initStandardAdapters();
-    }
-
-    public static void initSecureKeysBouncyCastleProvider() {
-        SecureKeys.initBouncyCastleProvider();
-    }
-
-    public static void initNetworking() {
-        Networking.init();
-    }
-    
-    public static void initPortRanges() {
-        PortRanges.init();
-    }
-
-    @SuppressWarnings("deprecation")
-    public static void initLegacyLanguageExtensions() {
-        brooklyn.util.BrooklynLanguageExtensions.init();
-    }
-
-    /* other things:
-     * 
-     * RendererHints - done by the entity classes which need them, including Sensors
-     * 
-     */
-    
-    public synchronized static void initAll() {
-        if (done.get()) return;
-        initTypeCoercionStandardAdapters();
-        initSecureKeysBouncyCastleProvider();
-        initNetworking();
-        initPortRanges();
-        initLegacyLanguageExtensions();
-        done.set(true);
-    }
-
-    @SuppressWarnings("deprecation")
-    public synchronized static void reinitAll() {
-        done.set(false);
-        brooklyn.util.BrooklynLanguageExtensions.reinit();
-        initAll();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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
index dd25090..86aac7e 100644
--- a/core/src/main/java/brooklyn/util/BrooklynLanguageExtensions.java
+++ b/core/src/main/java/brooklyn/util/BrooklynLanguageExtensions.java
@@ -20,7 +20,8 @@ package brooklyn.util;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import brooklyn.internal.BrooklynInitialization;
+import org.apache.brooklyn.core.internal.BrooklynInitialization;
+
 import brooklyn.util.internal.TimeExtras;
 
 /** @deprecated since 0.7.0 use {@link BrooklynInitialization} */

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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
index 53264e3..9b23b7a 100644
--- a/core/src/main/java/brooklyn/util/ResourceUtils.java
+++ b/core/src/main/java/brooklyn/util/ResourceUtils.java
@@ -40,6 +40,7 @@ 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.internal.BrooklynInitialization;
 import org.apache.brooklyn.core.management.classloading.JavaBrooklynClassLoadingContext;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
@@ -53,7 +54,6 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
 import brooklyn.catalog.internal.CatalogUtils;
-import brooklyn.internal.BrooklynInitialization;
 
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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
index 983a3d6..a1bc125 100644
--- a/core/src/main/java/brooklyn/util/crypto/FluentKeySigner.java
+++ b/core/src/main/java/brooklyn/util/crypto/FluentKeySigner.java
@@ -28,11 +28,11 @@ 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.internal.BrooklynInitialization;
 import brooklyn.util.exceptions.Exceptions;
 
 /** A fluent API which simplifies generating certificates (signed keys) */

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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
index 23a46d0..29fcf32 100644
--- a/core/src/main/java/brooklyn/util/crypto/SecureKeys.java
+++ b/core/src/main/java/brooklyn/util/crypto/SecureKeys.java
@@ -28,6 +28,7 @@ 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;
@@ -42,7 +43,6 @@ import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.internal.BrooklynInitialization;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.stream.Streams;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
index 7bfb770..31f27be 100644
--- a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
+++ b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
@@ -45,6 +45,7 @@ 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;
 
@@ -53,7 +54,6 @@ import brooklyn.entity.basic.ClosureEntityFactory;
 import brooklyn.entity.basic.ConfigurableEntityFactory;
 import brooklyn.entity.basic.ConfigurableEntityFactoryFromEntityFactory;
 import brooklyn.event.basic.Sensors;
-import brooklyn.internal.BrooklynInitialization;
 import brooklyn.util.JavaGroovyEquivalents;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.collections.QuorumCheck;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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
index 8fa38ad..e069aa9 100644
--- a/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjTool.java
+++ b/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjTool.java
@@ -49,11 +49,11 @@ 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.internal.BrooklynFeatureEnablement;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.RuntimeTimeoutException;
 import brooklyn.util.internal.ssh.BackoffLimitedRetryHandler;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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
index c472132..13d035b 100644
--- a/core/src/main/java/brooklyn/util/task/BasicExecutionManager.java
+++ b/core/src/main/java/brooklyn/util/task/BasicExecutionManager.java
@@ -49,10 +49,10 @@ 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.internal.BrooklynFeatureEnablement;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.text.Identifiers;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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
new file mode 100644
index 0000000..24d138b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynFeatureEnablement.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.internal;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.management.ha.HighAvailabilityMode;
+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;
+
+/**
+ * For enabling/disabling experimental features.
+ * They can be enabled via java system properties, or by explicitly calling {@link #setEnablement(String, boolean)}.
+ * <p>
+ * For example, start brooklyn with {@code -Dbrooklyn.experimental.feature.policyPersistence=true}
+ * 
+ * @author aled
+ */
+@Beta
+public class BrooklynFeatureEnablement {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BrooklynFeatureEnablement.class);
+
+    public static final String FEATURE_PROPERTY_PREFIX = "brooklyn.experimental.feature";
+    
+    public static final String FEATURE_POLICY_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".policyPersistence";
+    
+    public static final String FEATURE_ENRICHER_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".enricherPersistence";
+
+    public static final String FEATURE_FEED_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".feedPersistence";
+    
+    /** whether feeds are automatically registered when set on entities, so that they are persisted */
+    public static final String FEATURE_FEED_REGISTRATION_PROPERTY = FEATURE_PROPERTY_PREFIX+".feedRegistration";
+
+    public static final String FEATURE_CATALOG_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".catalogPersistence";
+    
+    /** whether the default standby mode is {@link HighAvailabilityMode#HOT_STANDBY} or falling back to the traditional
+     * {@link HighAvailabilityMode#STANDBY} */
+    public static final String FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY = FEATURE_PROPERTY_PREFIX+".defaultStandbyIsHot";
+    
+    /** whether to attempt to use {@link BrooklynStorage} (datagrid) as a backing store for data;
+     * note this is <b>not</b> compatible with {@link #FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY} 
+     * which uses a blob/file store and a larger-granularity rebind process than was intended with the datagrid */
+    /* not sure if we still even need this? now the rebind/read-only feature reloads on demand from the persistence store;
+     * the data-grid backing  */
+    public static final String FEATURE_USE_BROOKLYN_LIVE_OBJECTS_DATAGRID_STORAGE = FEATURE_PROPERTY_PREFIX+".useBrooklynLiveObjectsDatagridStorage";
+
+    /**
+     * Renaming threads can really helps with debugging etc; however it's a massive performance hit (2x)
+     * <p>
+     * We get 55000 tasks per sec with this off, 28k/s with this on.
+     * <p>
+     * Defaults to false if system property is not set.
+     */
+    public static final String FEATURE_RENAME_THREADS = "brooklyn.executionManager.renameThreads";
+
+    /**
+     * When rebinding to state created from very old versions, the catalogItemId properties will be missing which
+     * results in errors when OSGi bundles are used. When enabled the code tries to infer the catalogItemId from
+     * <ul>
+     *   <li> parent entities
+     *   <li> catalog items matching the type that needs to be deserialized
+     *   <li> iterating through all catalog items and checking if they can provide the needed type
+     * </ul>
+     */
+    public static final String FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND = "brooklyn.backwardCompatibility.feature.inferCatalogItemOnRebind";
+    
+    /**
+     * When rebinding, an entity could reference a catalog item that no longer exists. This option 
+     * will automatically update the catalog item reference to what is inferred as the most 
+     * suitable catalog symbolicName:version.
+     */
+    public static final String FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND = "brooklyn.quickfix.fixDanglingCatalogItemOnRebind";
+    
+    /**
+     * When executing over ssh, whether to support the "async exec" approach, or only the classic approach.
+     * 
+     * If this feature is disabled, then even if the {@link ShellTool#PROP_EXEC_ASYNC} is configured it
+     * will still use the classic ssh approach.
+     */
+    public static final String FEATURE_SSH_ASYNC_EXEC = FEATURE_PROPERTY_PREFIX+".ssh.asyncExec";
+
+    public static final String FEATURE_VALIDATE_LOCATION_SSH_KEYS = "brooklyn.validate.locationSshKeys";
+    
+    private static final Map<String, Boolean> FEATURE_ENABLEMENTS = Maps.newLinkedHashMap();
+
+    private static final Object MUTEX = new Object();
+
+    static void setDefaults() {
+        // Idea is here one can put experimental features that are *enabled* by default, but 
+        // that can be turned off via system properties, or vice versa.
+        // Typically this is useful where a feature is deemed risky!
+        
+        setDefault(FEATURE_POLICY_PERSISTENCE_PROPERTY, true);
+        setDefault(FEATURE_ENRICHER_PERSISTENCE_PROPERTY, true);
+        setDefault(FEATURE_FEED_PERSISTENCE_PROPERTY, true);
+        setDefault(FEATURE_FEED_REGISTRATION_PROPERTY, false);
+        setDefault(FEATURE_CATALOG_PERSISTENCE_PROPERTY, true);
+        setDefault(FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY, false);
+        setDefault(FEATURE_USE_BROOKLYN_LIVE_OBJECTS_DATAGRID_STORAGE, false);
+        setDefault(FEATURE_RENAME_THREADS, false);
+        setDefault(FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND, true);
+        setDefault(FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND, false);
+        setDefault(FEATURE_SSH_ASYNC_EXEC, false);
+        setDefault(FEATURE_VALIDATE_LOCATION_SSH_KEYS, true);
+    }
+    
+    static {
+        setDefaults();
+    }
+    
+    /**
+     * Initialises the feature-enablement from brooklyn properties. For each
+     * property, prefer a system-property if present; otherwise use the value 
+     * from brooklyn properties.
+     */
+    public static void init(BrooklynProperties props) {
+        boolean changed = false;
+        for (Map.Entry<String, Object> entry : props.asMapWithStringKeys().entrySet()) {
+            String property = entry.getKey();
+            if (property.startsWith(FEATURE_PROPERTY_PREFIX)) {
+                if (!FEATURE_ENABLEMENTS.containsKey(property)) {
+                    Object rawVal = System.getProperty(property);
+                    if (rawVal == null) {
+                        rawVal = entry.getValue();
+                    }
+                    boolean val = Boolean.parseBoolean(""+rawVal);
+                    FEATURE_ENABLEMENTS.put(property, val);
+                    
+                    changed = true;
+                    LOG.debug("Init feature enablement of "+property+" set to "+val);
+                }
+            }
+        }
+        if (!changed) {
+            LOG.debug("Init feature enablement did nothing, as no settings in brooklyn properties");
+        }
+    }
+    
+    public static boolean isEnabled(String property) {
+        synchronized (MUTEX) {
+            if (!FEATURE_ENABLEMENTS.containsKey(property)) {
+                String rawVal = System.getProperty(property);
+                boolean val = Boolean.parseBoolean(rawVal);
+                FEATURE_ENABLEMENTS.put(property, val);
+            }
+            return FEATURE_ENABLEMENTS.get(property);
+        }
+    }
+
+    public static boolean enable(String property) {
+        return setEnablement(property, true);
+    }
+    
+    public static boolean disable(String property) {
+        return setEnablement(property, false);
+    }
+    
+    public static boolean setEnablement(String property, boolean val) {
+        synchronized (MUTEX) {
+            boolean oldVal = isEnabled(property);
+            FEATURE_ENABLEMENTS.put(property, val);
+            return oldVal;
+        }
+    }
+    
+    static void setDefault(String property, boolean val) {
+        synchronized (MUTEX) {
+            if (!FEATURE_ENABLEMENTS.containsKey(property)) {
+                String rawVal = System.getProperty(property);
+                if (rawVal == null) {
+                    FEATURE_ENABLEMENTS.put(property, val);
+                    LOG.debug("Default enablement of "+property+" set to "+val);
+                } else {
+                    LOG.debug("Not setting default enablement of "+property+" to "+val+", because system property is "+rawVal);
+                }
+            }
+        }
+    }
+    
+    static void clearCache() {
+        synchronized (MUTEX) {
+            FEATURE_ENABLEMENTS.clear();
+            setDefaults();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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
new file mode 100644
index 0000000..1858306
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynInitialization.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.internal;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+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;
+
+/** Various static initialization tasks are routed through this class,
+ * to give us better traceability of their invocation. */ 
+@Beta
+public class BrooklynInitialization {
+
+    private static AtomicBoolean done = new AtomicBoolean(false);
+    
+    public static void initTypeCoercionStandardAdapters() {
+        TypeCoercions.initStandardAdapters();
+    }
+
+    public static void initSecureKeysBouncyCastleProvider() {
+        SecureKeys.initBouncyCastleProvider();
+    }
+
+    public static void initNetworking() {
+        Networking.init();
+    }
+    
+    public static void initPortRanges() {
+        PortRanges.init();
+    }
+
+    @SuppressWarnings("deprecation")
+    public static void initLegacyLanguageExtensions() {
+        brooklyn.util.BrooklynLanguageExtensions.init();
+    }
+
+    /* other things:
+     * 
+     * RendererHints - done by the entity classes which need them, including Sensors
+     * 
+     */
+    
+    public synchronized static void initAll() {
+        if (done.get()) return;
+        initTypeCoercionStandardAdapters();
+        initSecureKeysBouncyCastleProvider();
+        initNetworking();
+        initPortRanges();
+        initLegacyLanguageExtensions();
+        done.set(true);
+    }
+
+    @SuppressWarnings("deprecation")
+    public synchronized static void reinitAll() {
+        done.set(false);
+        brooklyn.util.BrooklynLanguageExtensions.reinit();
+        initAll();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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 8a02fcf..25d8281 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
@@ -42,6 +42,7 @@ import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
 import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
 import org.apache.brooklyn.api.management.ha.MementoCopyMode;
 import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister.Delta;
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.ha.BasicMasterChooser.AlphabeticMasterChooser;
 import org.apache.brooklyn.core.management.internal.BrooklynObjectManagementMode;
 import org.apache.brooklyn.core.management.internal.LocalEntityManager;
@@ -66,7 +67,6 @@ import brooklyn.entity.rebind.persister.PersistenceActivityMetrics;
 import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord;
 import brooklyn.entity.rebind.plane.dto.ManagementPlaneSyncRecordImpl;
 import brooklyn.entity.rebind.plane.dto.ManagementPlaneSyncRecordImpl.Builder;
-import brooklyn.internal.BrooklynFeatureEnablement;
 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/9cf456dc/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 306dcd8..3c353e2 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
@@ -42,6 +42,7 @@ import org.apache.brooklyn.api.management.ManagementContext;
 import org.apache.brooklyn.api.management.SubscriptionManager;
 import org.apache.brooklyn.api.management.Task;
 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.slf4j.Logger;
@@ -54,7 +55,6 @@ import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.proxying.InternalEntityFactory;
 import brooklyn.entity.proxying.InternalLocationFactory;
 import brooklyn.entity.proxying.InternalPolicyFactory;
-import brooklyn.internal.BrooklynFeatureEnablement;
 import brooklyn.internal.storage.DataGridFactory;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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 c5a74a0..554a781 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
@@ -35,6 +35,7 @@ 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.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.slf4j.Logger;
@@ -47,7 +48,6 @@ import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.rebind.BasicLocationRebindSupport;
 import brooklyn.event.basic.BasicConfigKey;
-import brooklyn.internal.BrooklynFeatureEnablement;
 import brooklyn.internal.storage.BrooklynStorage;
 import brooklyn.internal.storage.Reference;
 import brooklyn.internal.storage.impl.BasicReference;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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 ce3c444..d34a199 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
@@ -31,13 +31,15 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.internal.BrooklynFeatureEnablement;
+
 import org.apache.brooklyn.location.cloud.CloudLocationConfig;
+
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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 5d95534..f46df75 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
@@ -40,6 +40,7 @@ import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
 import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestEntity;
 
@@ -47,7 +48,6 @@ import brooklyn.catalog.internal.BasicBrooklynCatalog;
 import brooklyn.catalog.internal.CatalogDto;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
-import brooklyn.internal.BrooklynFeatureEnablement;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
index 2c3890c..7654759 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
@@ -30,12 +30,12 @@ import brooklyn.camp.lite.CampPlatformWithJustBrooklynMgmt;
 
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.proxying.EntitySpec;
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.test.entity.TestEntity;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
-import brooklyn.internal.BrooklynFeatureEnablement;
 
 import com.google.common.collect.Iterables;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/core/src/test/java/brooklyn/internal/BrooklynFeatureEnablementTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/internal/BrooklynFeatureEnablementTest.java b/core/src/test/java/brooklyn/internal/BrooklynFeatureEnablementTest.java
deleted file mode 100644
index ea0fed6..0000000
--- a/core/src/test/java/brooklyn/internal/BrooklynFeatureEnablementTest.java
+++ /dev/null
@@ -1,118 +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.internal;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.config.BrooklynProperties;
-
-public class BrooklynFeatureEnablementTest {
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        BrooklynFeatureEnablement.clearCache();
-    }
-    
-    @Test
-    public void testDefaultIsNotEnabled() throws Exception {
-        assertFalse(BrooklynFeatureEnablement.isEnabled("feature.not.referenced.anywhere"));
-    }
-    
-    @Test
-    public void testCanSetPropertyEnablement() throws Exception {
-        String featureProperty = "brooklyn.experimental.feature.testCanSetPropertyEnablement";
-        boolean preTestVal = BrooklynFeatureEnablement.isEnabled(featureProperty);
-        try {
-            boolean oldVal = BrooklynFeatureEnablement.enable(featureProperty);
-            assertEquals(oldVal, preTestVal);
-            assertTrue(BrooklynFeatureEnablement.isEnabled(featureProperty));
-            
-            boolean oldVal2 = BrooklynFeatureEnablement.disable(featureProperty);
-            assertTrue(oldVal2);
-            assertFalse(BrooklynFeatureEnablement.isEnabled(featureProperty));
-        } finally {
-            BrooklynFeatureEnablement.setEnablement(featureProperty, preTestVal);
-        }
-    }
-    
-    @Test
-    public void testReadsEnablementFromProperties() throws Exception {
-        String featureProperty = "brooklyn.experimental.feature.testReadsEnablementFromProperties";
-        System.setProperty(featureProperty, "true");
-        try {
-            assertTrue(BrooklynFeatureEnablement.isEnabled(featureProperty));
-        } finally {
-            System.clearProperty(featureProperty);
-        }
-    }
-    
-    @Test
-    public void testCanSetDefaultWhichTakesEffectIfNoSystemProperty() throws Exception {
-        String featureProperty = "brooklyn.experimental.feature.testCanSetDefaultWhichTakesEffectIfNoSystemProperty";
-        BrooklynFeatureEnablement.setDefault(featureProperty, true);
-        assertTrue(BrooklynFeatureEnablement.isEnabled(featureProperty));
-        System.setProperty(featureProperty, "true");
-        try {
-        } finally {
-            System.clearProperty(featureProperty);
-        }
-    }
-    
-    @Test
-    public void testCanSetDefaultWhichIsIgnoredIfSystemProperty() throws Exception {
-        String featureProperty = "brooklyn.experimental.feature.testCanSetDefaultWhichIsIgnoredIfSystemProperty";
-        System.setProperty(featureProperty, "false");
-        try {
-            BrooklynFeatureEnablement.setDefault(featureProperty, true);
-            assertFalse(BrooklynFeatureEnablement.isEnabled(featureProperty));
-        } finally {
-            System.clearProperty(featureProperty);
-        }
-    }
-    
-    @Test
-    public void testCanSetDefaultWhichIsIgnoredIfBrooklynProps() throws Exception {
-        String featureProperty = "brooklyn.experimental.feature.testCanSetDefaultWhichIsIgnoredIfBrooklynProps";
-        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
-        props.put(featureProperty, false);
-        BrooklynFeatureEnablement.init(props);
-        BrooklynFeatureEnablement.setDefault(featureProperty, true);
-        assertFalse(BrooklynFeatureEnablement.isEnabled(featureProperty));
-    }
-    
-    @Test
-    public void testPrefersSysPropOverBrooklynProps() throws Exception {
-        String featureProperty = "brooklyn.experimental.feature.testPrefersSysPropOverBrooklynProps";
-        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
-        props.put(featureProperty, false);
-        System.setProperty(featureProperty, "true");
-        try {
-            BrooklynFeatureEnablement.init(props);
-            BrooklynFeatureEnablement.setDefault(featureProperty, true);
-            assertTrue(BrooklynFeatureEnablement.isEnabled(featureProperty));
-        } finally {
-            System.clearProperty(featureProperty);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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
index 9d468a6..df0330a 100644
--- a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
+++ b/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
@@ -25,11 +25,11 @@ 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.internal.BrooklynFeatureEnablement;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.internal.ssh.SshAbstractTool.SshAction;
 import brooklyn.util.internal.ssh.sshj.SshjTool.ShellAction;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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
index de3f58e..f1e354c 100644
--- a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolIntegrationTest.java
+++ b/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolIntegrationTest.java
@@ -36,9 +36,9 @@ 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.internal.BrooklynFeatureEnablement;
 import brooklyn.test.Asserts;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.RuntimeTimeoutException;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/core/src/test/java/org/apache/brooklyn/core/internal/BrooklynFeatureEnablementTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/internal/BrooklynFeatureEnablementTest.java b/core/src/test/java/org/apache/brooklyn/core/internal/BrooklynFeatureEnablementTest.java
new file mode 100644
index 0000000..93ed8db
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/internal/BrooklynFeatureEnablementTest.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.internal;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+
+public class BrooklynFeatureEnablementTest {
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        BrooklynFeatureEnablement.clearCache();
+    }
+    
+    @Test
+    public void testDefaultIsNotEnabled() throws Exception {
+        assertFalse(BrooklynFeatureEnablement.isEnabled("feature.not.referenced.anywhere"));
+    }
+    
+    @Test
+    public void testCanSetPropertyEnablement() throws Exception {
+        String featureProperty = "brooklyn.experimental.feature.testCanSetPropertyEnablement";
+        boolean preTestVal = BrooklynFeatureEnablement.isEnabled(featureProperty);
+        try {
+            boolean oldVal = BrooklynFeatureEnablement.enable(featureProperty);
+            assertEquals(oldVal, preTestVal);
+            assertTrue(BrooklynFeatureEnablement.isEnabled(featureProperty));
+            
+            boolean oldVal2 = BrooklynFeatureEnablement.disable(featureProperty);
+            assertTrue(oldVal2);
+            assertFalse(BrooklynFeatureEnablement.isEnabled(featureProperty));
+        } finally {
+            BrooklynFeatureEnablement.setEnablement(featureProperty, preTestVal);
+        }
+    }
+    
+    @Test
+    public void testReadsEnablementFromProperties() throws Exception {
+        String featureProperty = "brooklyn.experimental.feature.testReadsEnablementFromProperties";
+        System.setProperty(featureProperty, "true");
+        try {
+            assertTrue(BrooklynFeatureEnablement.isEnabled(featureProperty));
+        } finally {
+            System.clearProperty(featureProperty);
+        }
+    }
+    
+    @Test
+    public void testCanSetDefaultWhichTakesEffectIfNoSystemProperty() throws Exception {
+        String featureProperty = "brooklyn.experimental.feature.testCanSetDefaultWhichTakesEffectIfNoSystemProperty";
+        BrooklynFeatureEnablement.setDefault(featureProperty, true);
+        assertTrue(BrooklynFeatureEnablement.isEnabled(featureProperty));
+        System.setProperty(featureProperty, "true");
+        try {
+        } finally {
+            System.clearProperty(featureProperty);
+        }
+    }
+    
+    @Test
+    public void testCanSetDefaultWhichIsIgnoredIfSystemProperty() throws Exception {
+        String featureProperty = "brooklyn.experimental.feature.testCanSetDefaultWhichIsIgnoredIfSystemProperty";
+        System.setProperty(featureProperty, "false");
+        try {
+            BrooklynFeatureEnablement.setDefault(featureProperty, true);
+            assertFalse(BrooklynFeatureEnablement.isEnabled(featureProperty));
+        } finally {
+            System.clearProperty(featureProperty);
+        }
+    }
+    
+    @Test
+    public void testCanSetDefaultWhichIsIgnoredIfBrooklynProps() throws Exception {
+        String featureProperty = "brooklyn.experimental.feature.testCanSetDefaultWhichIsIgnoredIfBrooklynProps";
+        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+        props.put(featureProperty, false);
+        BrooklynFeatureEnablement.init(props);
+        BrooklynFeatureEnablement.setDefault(featureProperty, true);
+        assertFalse(BrooklynFeatureEnablement.isEnabled(featureProperty));
+    }
+    
+    @Test
+    public void testPrefersSysPropOverBrooklynProps() throws Exception {
+        String featureProperty = "brooklyn.experimental.feature.testPrefersSysPropOverBrooklynProps";
+        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+        props.put(featureProperty, false);
+        System.setProperty(featureProperty, "true");
+        try {
+            BrooklynFeatureEnablement.init(props);
+            BrooklynFeatureEnablement.setDefault(featureProperty, true);
+            assertTrue(BrooklynFeatureEnablement.isEnabled(featureProperty));
+        } finally {
+            System.clearProperty(featureProperty);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerSplitBrainTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerSplitBrainTest.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerSplitBrainTest.java
index 7e34071..15caa29 100644
--- a/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerSplitBrainTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/HighAvailabilityManagerSplitBrainTest.java
@@ -34,6 +34,7 @@ import org.apache.brooklyn.api.management.ha.ManagementNodeState;
 import org.apache.brooklyn.api.management.ha.ManagementNodeSyncRecord;
 import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecord;
 import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister;
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl;
 import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore;
 import org.apache.brooklyn.core.management.ha.TestEntityFailingRebind.RebindException;
@@ -54,7 +55,6 @@ import brooklyn.entity.rebind.persister.InMemoryObjectStore;
 import brooklyn.entity.rebind.persister.ListeningObjectStore;
 import brooklyn.entity.rebind.persister.PersistMode;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-import brooklyn.internal.BrooklynFeatureEnablement;
 import brooklyn.test.Asserts;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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 f18b414..57ea76d 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
@@ -30,6 +30,7 @@ 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.api.management.ha.HighAvailabilityMode;
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.management.internal.LocalManagementContext;
 import org.apache.brooklyn.entity.proxy.nginx.NginxController;
 import org.apache.brooklyn.entity.webapp.tomcat.TomcatServer;
@@ -51,7 +52,6 @@ import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.group.DynamicCluster;
 import brooklyn.entity.rebind.RebindTestFixtureWithApp;
 import brooklyn.entity.rebind.RebindTestUtils;
-import brooklyn.internal.BrooklynFeatureEnablement;
 
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import org.apache.brooklyn.location.basic.SshMachineLocationReuseIntegrationTest.RecordingSshjTool;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
index 8d50dde..3081243 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
@@ -24,13 +24,13 @@ import static org.testng.Assert.assertFalse;
 import org.apache.brooklyn.api.policy.Enricher;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlRebindTest;
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
 import org.testng.annotations.Test;
 
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.basic.BasicEntity;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.StartableApplication;
-import brooklyn.internal.BrooklynFeatureEnablement;
 import brooklyn.test.policy.TestEnricher;
 import brooklyn.test.policy.TestPolicy;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9cf456dc/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 c9bf366..13ee15a 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
@@ -63,10 +63,10 @@ import brooklyn.BrooklynVersion;
 import brooklyn.config.BrooklynServerPaths;
 import brooklyn.config.BrooklynServiceAttributes;
 import brooklyn.config.ConfigKey;
-import brooklyn.internal.BrooklynInitialization;
 
 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.launcher.config.CustomResourceLocator;
 import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;