You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by gr...@apache.org on 2016/03/22 19:06:43 UTC

[01/10] brooklyn-server git commit: Test the Clocker pattern (of LocationOwner and DynamicLocation)

Repository: brooklyn-server
Updated Branches:
  refs/heads/master 1e5f3b608 -> b8211ed17


Test the Clocker pattern (of LocationOwner and DynamicLocation)


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

Branch: refs/heads/master
Commit: 292b4cfb8d5b0bbff0f38eba5fc84a1fc9fe4eee
Parents: d059088
Author: Aled Sage <al...@gmail.com>
Authored: Fri Mar 11 19:37:13 2016 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Fri Mar 18 22:18:21 2016 +0000

----------------------------------------------------------------------
 ...ClockerDynamicLocationPatternRebindTest.java | 113 +++++++++
 .../ClockerDynamicLocationPatternTest.java      |  96 ++++++++
 .../dynamic/clocker/StubAttributes.java         |  34 +++
 .../location/dynamic/clocker/StubContainer.java |  33 +++
 .../dynamic/clocker/StubContainerImpl.java      | 123 ++++++++++
 .../dynamic/clocker/StubContainerLocation.java  |  41 ++++
 .../core/location/dynamic/clocker/StubHost.java |  38 +++
 .../location/dynamic/clocker/StubHostImpl.java  | 127 ++++++++++
 .../dynamic/clocker/StubHostLocation.java       | 109 ++++++++
 .../dynamic/clocker/StubInfrastructure.java     |  92 +++++++
 .../dynamic/clocker/StubInfrastructureImpl.java | 246 +++++++++++++++++++
 .../clocker/StubInfrastructureLocation.java     |  83 +++++++
 .../location/dynamic/clocker/StubResolver.java  | 196 +++++++++++++++
 .../location/dynamic/clocker/StubUtils.java     |  95 +++++++
 14 files changed, 1426 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternRebindTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternRebindTest.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternRebindTest.java
new file mode 100644
index 0000000..361ee95
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternRebindTest.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.location.dynamic.clocker;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.location.BasicLocationRegistry;
+import org.apache.brooklyn.core.location.Locations;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.mgmt.rebind.RebindTestFixtureWithApp;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+public class ClockerDynamicLocationPatternRebindTest extends RebindTestFixtureWithApp {
+
+    @Override
+    protected LocalManagementContext createOrigManagementContext() {
+        LocalManagementContext result = super.createOrigManagementContext();
+        StubResolver stubResolver = new StubResolver();
+        ((BasicLocationRegistry)result.getLocationRegistry()).registerResolver(stubResolver);
+        return result;
+    }
+    
+    @Override
+    protected LocalManagementContext createNewManagementContext(File mementoDir) {
+        LocalManagementContext result = super.createNewManagementContext(mementoDir);
+        StubResolver stubResolver = new StubResolver();
+        ((BasicLocationRegistry)result.getLocationRegistry()).registerResolver(stubResolver);
+        return result;
+    }
+    
+    // To make this fail (with Clocker code as at 2016-03-11) requires several apps - there's a bug
+    // in rebind that only happens when there are several entities, so the order that they rebind
+    // is more interleaved.
+    @Test
+    public void testRebind() throws Exception {
+        final int NUM_INFRAS = 10;
+        final int HOST_CLUSTER_SIZE = 1;
+        Location loc = mgmt().getLocationRegistry().resolve("localhost");
+        
+        // Maps from the infrastructure locSpec to the (potentially many) host locSpecs
+        Map<String, List<String>> locSpecs = Maps.newLinkedHashMap();
+        
+        for (int i = 0; i < NUM_INFRAS; i++) {
+            StubInfrastructure infra = mgmt().getEntityManager().createEntity(EntitySpec.create(StubInfrastructure.class)
+                    .configure(StubInfrastructure.LOCATION_NAME, "myname"+i));
+            infra.start(ImmutableList.of(loc));
+            infra.getStubHostCluster().resize(HOST_CLUSTER_SIZE);
+            assertEquals(infra.getStubHostCluster().getMembers().size(), HOST_CLUSTER_SIZE);
+            
+            String infraLocSpec = infra.sensors().get(StubInfrastructure.LOCATION_SPEC);
+            List<String> hostLocSpecs = Lists.newArrayList();
+            for (Entity host : infra.getStubHostCluster().getMembers()) {
+                hostLocSpecs.add(host.sensors().get(StubInfrastructure.LOCATION_SPEC));
+            }
+    
+            locSpecs.put(infraLocSpec, hostLocSpecs);
+        }
+        assertEquals(locSpecs.size(), NUM_INFRAS); // in case the infrastructures all used the same loc name!
+
+        rebind();
+
+        for (Map.Entry<String, List<String>> entry : locSpecs.entrySet()) {
+            String infraLocSpec = entry.getKey();
+            List<String> hostLocSpecs = entry.getValue();
+            
+            StubInfrastructureLocation newInfraLoc = (StubInfrastructureLocation) mgmt().getLocationRegistry().resolve(infraLocSpec);
+            assertNotNull(newInfraLoc);
+            for (String hostLocSpec: hostLocSpecs) {
+                StubHostLocation newHostLoc = (StubHostLocation) mgmt().getLocationRegistry().resolve(hostLocSpec);
+                assertNotNull(newHostLoc);
+            }
+    
+            // Confirm that it still functions
+            StubContainerLocation containerLoc = (StubContainerLocation) newInfraLoc.obtain(ImmutableMap.of());
+            StubContainer container = containerLoc.getOwner();
+            
+            newInfraLoc.release(containerLoc);
+            assertFalse(Entities.isManaged(container));
+            assertFalse(Locations.isManaged(containerLoc));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternTest.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternTest.java
new file mode 100644
index 0000000..618c552
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternTest.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.location.dynamic.clocker;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.location.BasicLocationRegistry;
+import org.apache.brooklyn.core.location.Locations;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+public class ClockerDynamicLocationPatternTest extends BrooklynAppUnitTestSupport {
+
+    private LocalhostMachineProvisioningLocation loc;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        super.setUp();
+        
+        StubResolver stubResolver = new StubResolver();
+        ((BasicLocationRegistry)mgmt.getLocationRegistry()).registerResolver(stubResolver);
+        
+        loc = app.newLocalhostProvisioningLocation();
+    }
+    
+    @Test
+    public void testCreateAndReleaseDirectly() throws Exception {
+        StubInfrastructure infra = mgmt.getEntityManager().createEntity(EntitySpec.create(StubInfrastructure.class));
+        infra.start(ImmutableList.of(loc));
+        
+        StubInfrastructureLocation loc = infra.getDynamicLocation();
+        MachineLocation machine = loc.obtain(ImmutableMap.of());
+        
+        StubHost host = (StubHost) Iterables.getOnlyElement(infra.getStubHostCluster().getMembers());
+        StubHostLocation hostLoc = host.getDynamicLocation();
+        
+        StubContainer container = (StubContainer) Iterables.getOnlyElement(host.getDockerContainerCluster().getMembers());
+        StubContainerLocation containerLoc = container.getDynamicLocation();
+        assertEquals(containerLoc, machine);
+        assertEquals(Iterables.getOnlyElement(hostLoc.getChildren()), machine);
+        
+        loc.release(machine);
+        assertFalse(Entities.isManaged(container));
+        assertFalse(Locations.isManaged(containerLoc));
+    }
+    
+    @Test
+    public void testThroughLocationRegistry() throws Exception {
+        StubInfrastructure infra = mgmt.getEntityManager().createEntity(EntitySpec.create(StubInfrastructure.class));
+        infra.start(ImmutableList.of(loc));
+        
+        String infraLocSpec = infra.sensors().get(StubInfrastructure.LOCATION_SPEC);
+        StubInfrastructureLocation infraLoc = (StubInfrastructureLocation) mgmt.getLocationRegistry().resolve(infraLocSpec);
+
+        MachineLocation machine = infraLoc.obtain(ImmutableMap.of());
+        
+        StubHost host = (StubHost) Iterables.getOnlyElement(infra.getStubHostCluster().getMembers());
+        String hostLocSpec = host.sensors().get(StubInfrastructure.LOCATION_SPEC);
+        StubHostLocation hostLoc = (StubHostLocation) mgmt.getLocationRegistry().resolve(hostLocSpec);
+
+        StubContainer container = (StubContainer) Iterables.getOnlyElement(host.getDockerContainerCluster().getMembers());
+        StubContainerLocation containerLoc = container.getDynamicLocation();
+        assertEquals(containerLoc, machine);
+        assertEquals(Iterables.getOnlyElement(hostLoc.getChildren()), machine);
+        
+        infraLoc.release(machine);
+        assertFalse(Entities.isManaged(container));
+        assertFalse(Locations.isManaged(containerLoc));
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubAttributes.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubAttributes.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubAttributes.java
new file mode 100644
index 0000000..e356801
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubAttributes.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.location.dynamic.clocker;
+
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
+
+public class StubAttributes {
+    public static final AttributeSensorAndConfigKey<StubInfrastructure, StubInfrastructure> DOCKER_INFRASTRUCTURE = ConfigKeys.newSensorAndConfigKey(
+            StubInfrastructure.class,
+            "docker.infrastructure", 
+            "The Docker infrastructure");
+    
+    public static final AttributeSensorAndConfigKey<StubHost, StubHost> DOCKER_HOST = ConfigKeys.newSensorAndConfigKey(
+            StubHost.class,
+            "docker.host", 
+            "The Docker Host");
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainer.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainer.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainer.java
new file mode 100644
index 0000000..504159d
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainer.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.location.dynamic.clocker;
+
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.core.location.dynamic.LocationOwner;
+import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
+import org.apache.brooklyn.entity.stock.BasicStartable;
+
+@ImplementedBy(StubContainerImpl.class)
+public interface StubContainer extends BasicStartable, LocationOwner<StubContainerLocation, StubContainer> {
+    AttributeSensorAndConfigKey<StubInfrastructure, StubInfrastructure> DOCKER_INFRASTRUCTURE = StubAttributes.DOCKER_INFRASTRUCTURE;
+    AttributeSensorAndConfigKey<StubHost, StubHost> DOCKER_HOST = StubAttributes.DOCKER_HOST;
+    
+    StubInfrastructure getInfrastructure();
+    StubHost getDockerHost();
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerImpl.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerImpl.java
new file mode 100644
index 0000000..45cf9d0
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerImpl.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.location.dynamic.clocker;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.mgmt.LocationManager;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
+import org.apache.brooklyn.core.feed.ConfigToAttributes;
+import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
+import org.apache.brooklyn.entity.stock.BasicStartableImpl;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StubContainerImpl extends BasicStartableImpl implements StubContainer {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StubContainerImpl.class);
+
+    @Override
+    public void init() {
+        super.init();
+
+        ConfigToAttributes.apply(this);
+    }
+
+    @Override
+    public StubInfrastructure getInfrastructure() {
+        return config().get(DOCKER_INFRASTRUCTURE);
+    }
+
+    @Override
+    public StubHost getDockerHost() {
+        return (StubHost) config().get(DOCKER_HOST);
+    }
+
+    @Override
+    public StubContainerLocation getDynamicLocation() {
+        return (StubContainerLocation) sensors().get(DYNAMIC_LOCATION);
+    }
+
+    @Override
+    public void start(Collection<? extends Location> locations) {
+        ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
+
+        Map<String, ?> flags = MutableMap.copyOf(config().get(LOCATION_FLAGS));
+        createLocation(flags);
+
+        super.start(locations);
+
+        ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING);
+    }
+
+    @Override
+    public StubContainerLocation createLocation(Map<String, ?> flags) {
+        StubHost dockerHost = getDockerHost();
+        StubHostLocation host = dockerHost.getDynamicLocation();
+
+        SshMachineLocation containerMachine = getManagementContext().getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+                .configure("address", "1.2.3.4"));
+
+        // Create our wrapper location around the container
+        LocationSpec<StubContainerLocation> spec = LocationSpec.create(StubContainerLocation.class)
+                .parent(host)
+                .configure(flags)
+                .configure(DynamicLocation.OWNER, this)
+                .configure("machine", containerMachine)
+                .configure(containerMachine.config().getBag().getAllConfig());
+        StubContainerLocation location = getManagementContext().getLocationManager().createLocation(spec);
+
+        sensors().set(DYNAMIC_LOCATION, location);
+        sensors().set(LOCATION_NAME, location.getId());
+        
+        return location;
+    }
+
+    @Override
+    public boolean isLocationAvailable() {
+        return getDynamicLocation() != null;
+    }
+
+    @Override
+    public void deleteLocation() {
+        StubContainerLocation location = getDynamicLocation();
+
+        if (location != null) {
+            try {
+                location.close();
+            } catch (IOException ioe) {
+                LOG.debug("Error closing container location", ioe);
+            }
+            LocationManager mgr = getManagementContext().getLocationManager();
+            if (mgr.isManaged(location)) {
+                mgr.unmanage(location);
+            }
+        }
+
+        sensors().set(DYNAMIC_LOCATION, null);
+        sensors().set(LOCATION_NAME, null);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerLocation.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerLocation.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerLocation.java
new file mode 100644
index 0000000..f4b0345
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerLocation.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.location.dynamic.clocker;
+
+import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+
+public class StubContainerLocation extends SshMachineLocation implements DynamicLocation<StubContainer, StubContainerLocation> {
+
+    @SetFromFlag("machine")
+    private SshMachineLocation machine;
+
+    @SetFromFlag("owner")
+    private StubContainer owner;
+
+    @Override
+    public StubContainer getOwner() {
+        return owner;
+    }
+
+    public SshMachineLocation getMachine() {
+        return machine;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHost.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHost.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHost.java
new file mode 100644
index 0000000..e5b4005
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHost.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.location.dynamic.clocker;
+
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.location.dynamic.LocationOwner;
+import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.machine.MachineEntity;
+
+@ImplementedBy(StubHostImpl.class)
+public interface StubHost extends MachineEntity, LocationOwner<StubHostLocation, StubHost> {
+    AttributeSensorAndConfigKey<StubInfrastructure, StubInfrastructure> DOCKER_INFRASTRUCTURE = StubAttributes.DOCKER_INFRASTRUCTURE;
+    
+    AttributeSensor<DynamicCluster> DOCKER_CONTAINER_CLUSTER = Sensors.newSensor(DynamicCluster.class,
+            "docker.container.cluster", "The cluster of Docker containers");
+
+    StubInfrastructure getInfrastructure();
+    DynamicCluster getDockerContainerCluster();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java
new file mode 100644
index 0000000..4c2ad36
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.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.location.dynamic.clocker;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.core.feed.ConfigToAttributes;
+import org.apache.brooklyn.core.location.BasicLocationDefinition;
+import org.apache.brooklyn.core.location.Locations;
+import org.apache.brooklyn.core.location.Machines;
+import org.apache.brooklyn.entity.group.Cluster;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.machine.MachineEntityImpl;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StubHostImpl extends MachineEntityImpl implements StubHost {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(StubHostImpl.class);
+
+    @Override
+    public void init() {
+        super.init();
+
+        ConfigToAttributes.apply(this);
+
+        EntitySpec<?> dockerContainerSpec = EntitySpec.create(StubContainer.class)
+                .configure(StubContainer.DOCKER_HOST, this)
+                .configure(StubContainer.DOCKER_INFRASTRUCTURE, getInfrastructure());
+
+        DynamicCluster containers = addChild(EntitySpec.create(DynamicCluster.class)
+                .configure(Cluster.INITIAL_SIZE, 0)
+                .configure(DynamicCluster.QUARANTINE_FAILED_ENTITIES, false)
+                .configure(DynamicCluster.MEMBER_SPEC, dockerContainerSpec)
+                .configure(DynamicCluster.RUNNING_QUORUM_CHECK, QuorumChecks.atLeastOneUnlessEmpty())
+                .configure(DynamicCluster.UP_QUORUM_CHECK, QuorumChecks.atLeastOneUnlessEmpty())
+                .displayName("Docker Containers"));
+        sensors().set(DOCKER_CONTAINER_CLUSTER, containers);
+    }
+    
+    @Override
+    public StubInfrastructure getInfrastructure() {
+        return config().get(DOCKER_INFRASTRUCTURE);
+    }
+
+    @Override
+    public DynamicCluster getDockerContainerCluster() {
+        return sensors().get(DOCKER_CONTAINER_CLUSTER);
+    }
+
+    @Override
+    public StubHostLocation getDynamicLocation() {
+        return (StubHostLocation) sensors().get(DYNAMIC_LOCATION);
+    }
+
+    @Override
+    public void preStart() {
+        super.preStart();
+        ConfigToAttributes.apply(this);
+        
+        Maybe<SshMachineLocation> found = Machines.findUniqueMachineLocation(getLocations(), SshMachineLocation.class);
+
+        Map<String, ?> flags = MutableMap.<String, Object>builder()
+                .putAll(config().get(LOCATION_FLAGS))
+                .put("machine", found.get())
+                .build();
+
+        createLocation(flags);
+        sensors().get(DOCKER_CONTAINER_CLUSTER).sensors().set(SERVICE_UP, Boolean.TRUE);
+    }
+
+    @Override
+    public StubHostLocation createLocation(Map<String, ?> flags) {
+        StubInfrastructure infrastructure = getInfrastructure();
+        StubInfrastructureLocation docker = infrastructure.getDynamicLocation();
+        String locationName = docker.getId() + "-" + getId();
+
+        String locationSpec = String.format(StubResolver.DOCKER_HOST_MACHINE_SPEC, infrastructure.getId(), getId()) + String.format(":(name=\"%s\")", locationName);
+        sensors().set(LOCATION_SPEC, locationSpec);
+
+        LocationDefinition definition = new BasicLocationDefinition(locationName, locationSpec, flags);
+        Location location = getManagementContext().getLocationRegistry().resolve(definition);
+        sensors().set(DYNAMIC_LOCATION, location);
+        sensors().set(LOCATION_NAME, location.getId());
+
+        LOG.info("New Docker host location {} created", location);
+        return (StubHostLocation) location;
+    }
+
+    @Override
+    public boolean isLocationAvailable() {
+        return sensors().get(DYNAMIC_LOCATION) != null;
+    }
+
+    @Override
+    public void deleteLocation() {
+        StubHostLocation loc = (StubHostLocation) sensors().get(DYNAMIC_LOCATION);
+        if (loc != null) {
+            Locations.unmanage(loc);
+        }
+        sensors().set(DYNAMIC_LOCATION, null);
+        sensors().set(LOCATION_NAME, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostLocation.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostLocation.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostLocation.java
new file mode 100644
index 0000000..a861220
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostLocation.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.location.dynamic.clocker;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.location.LocationConfigKeys;
+import org.apache.brooklyn.core.location.Locations;
+import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class StubHostLocation extends AbstractLocation implements MachineProvisioningLocation<StubContainerLocation>, DynamicLocation<StubHost, StubHostLocation> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StubHostLocation.class);
+
+    @SetFromFlag("machine")
+    private SshMachineLocation machine;
+
+    @SetFromFlag("owner")
+    private StubHost dockerHost;
+
+    @Override
+    public StubHost getOwner() {
+        return (StubHost) getConfig(OWNER);
+    }
+
+    @Override
+    public StubContainerLocation obtain(Map<?, ?> flags) throws NoMachinesAvailableException {
+        Entity callerContext = (Entity) flags.get(LocationConfigKeys.CALLER_CONTEXT.getName());
+        if (callerContext == null) callerContext = getOwner();
+        
+        DynamicCluster cluster = dockerHost.getDockerContainerCluster();
+        Entity added = cluster.addNode(machine, flags);
+        if (added == null) {
+            throw new NoMachinesAvailableException(String.format("Failed to create container at %s", dockerHost));
+        } else {
+            Entities.invokeEffector((EntityLocal) callerContext, added, Startable.START,  MutableMap.of("locations", ImmutableList.of(machine))).getUnchecked();
+        }
+        StubContainer container = (StubContainer) added;
+
+        return container.getDynamicLocation();
+    }
+
+    @Override
+    public void release(StubContainerLocation machine) {
+        DynamicCluster cluster = dockerHost.getDockerContainerCluster();
+        StubContainer container = machine.getOwner();
+        if (cluster.removeMember(container)) {
+            LOG.info("Docker Host {}: member {} released", dockerHost, machine);
+        } else {
+            LOG.warn("Docker Host {}: member {} not found for release", dockerHost, machine);
+        }
+
+        // Now close and unmange the container
+        try {
+            container.stop();
+            machine.close();
+        } catch (Exception e) {
+            LOG.warn("Error stopping container: " + container, e);
+            Exceptions.propagateIfFatal(e);
+        } finally {
+            Entities.unmanage(container);
+            Locations.unmanage(machine);
+        }
+    }
+
+    @Override
+    public MachineProvisioningLocation<StubContainerLocation> newSubLocation(Map<?, ?> newFlags) {
+        return this;
+    }
+
+    @Override
+    public Map<String, Object> getProvisioningFlags(Collection<String> tags) {
+        return ImmutableMap.<String, Object>of();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructure.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructure.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructure.java
new file mode 100644
index 0000000..3170cf2
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructure.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.location.dynamic.clocker;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.dynamic.LocationOwner;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.group.DynamicGroup;
+import org.apache.brooklyn.entity.group.DynamicMultiGroup;
+
+/**
+ * These entities and locations mirror the structure used in Clocker v1 (prepend everything 
+ * with "Docker" instead of "Stub"). The purpose of repeating it here is to ensure that the 
+ * functionality Clocker is relying on will get regularly tested and kept working - or if it 
+ * does change, then Clocker can be updated as appropriate.
+ * 
+ * The Clocker hierarchy has the following structure:
+ * 
+ * <pre>
+ * Infrastructure -----------> InfrastructureLocation
+ *       |                               |
+ *      Host      ----------->      HostLocation
+ *       |                               |
+ *    Container   ----------->   ContainerLocation
+ * <pre>
+ * 
+ * The Infrastructure, Host and Container are all entities that implement {@link LocationOwner}. 
+ * Whenever one of these is started, it creates the corresponding Location object (shown on the
+ * right).
+ * 
+ * The Infrastructure has a cluster of Host instances. A host has a cluster of Container 
+ * instances.
+ * 
+ * When the {@code Infrastructure} is initially provisioned, it registers the newly create 
+ * location in the {@link ManagementContext#getLocationRegistry()}. This makes it a named 
+ * location, which can be used by other apps.
+ * 
+ * When {@code InfrastructureLocation.obtain()) is called, it will create a container somewhere in 
+ * the cluster. The following chain of events happens:
+ * <ol>
+ *   <li>From the {@code Infrastructure} (i.e. the location's "owner"), get the set of 
+ *       {@code Host}s and thus the set of {@code HostLocation}s.
+ *   <li>Choose a {@code HostLocation}
+ *       (if there's not one, consider scaling up the {@code Infrastructure}'s cluster, if permitted)
+ *   <li>Delegate to {@code DockerHostLocation.obtain()}, and return the result.
+ *   <li>The {@code DockerHostLocation} first does some fiddly Docker stuff around images
+ *       (not represented in this stub structure).
+ *   <li>Get the {@code dockerHost.getDockerContainerCluster()}, and use that to create a new 
+ *       {@code Container} entity.
+ *   <li>Invoke {@code start} effector on the {@code Container} entity
+ *   <li>The {code Container.start} calls {@code Container.createLocation}, which instantiates
+ *       the actual Docker container (by using the jclouds provider).
+ *   <li>Return the {@code container.getDynamicLocation()} (i.e. the {@code ContainerLocation}).
+ * <ul>
+ */
+@ImplementedBy(StubInfrastructureImpl.class)
+public interface StubInfrastructure extends Application, Startable, LocationOwner<StubInfrastructureLocation, StubInfrastructure> {
+    ConfigKey<String> LOCATION_NAME = ConfigKeys.newConfigKeyWithDefault(LocationOwner.LOCATION_NAME.getConfigKey(), "my-stub-cloud");
+    
+    AttributeSensor<DynamicCluster> DOCKER_HOST_CLUSTER = Sensors.newSensor(DynamicCluster.class, "docker.hosts", "Docker host cluster");
+    AttributeSensor<DynamicGroup> DOCKER_CONTAINER_FABRIC = Sensors.newSensor(DynamicGroup.class, "docker.fabric", "Docker container fabric");
+    AttributeSensor<DynamicMultiGroup> DOCKER_APPLICATIONS = Sensors.newSensor(DynamicMultiGroup.class, "docker.buckets", "Docker applications");
+    
+    List<Entity> getStubHostList();
+    DynamicCluster getStubHostCluster();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java
new file mode 100644
index 0000000..0d807fa
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.location.dynamic.clocker;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.api.mgmt.LocationManager;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.entity.AbstractApplication;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.feed.ConfigToAttributes;
+import org.apache.brooklyn.core.location.BasicLocationDefinition;
+import org.apache.brooklyn.core.location.dynamic.LocationOwner;
+import org.apache.brooklyn.entity.group.BasicGroup;
+import org.apache.brooklyn.entity.group.Cluster;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.group.DynamicGroup;
+import org.apache.brooklyn.entity.group.DynamicMultiGroup;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks;
+import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class StubInfrastructureImpl extends AbstractApplication implements StubInfrastructure {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StubInfrastructureImpl.class);
+
+    @Override
+    public void init() {
+        super.init();
+        
+        ConfigToAttributes.apply(this);
+
+        EntitySpec<?> dockerHostSpec = EntitySpec.create(StubHost.class)
+                .configure(StubHost.DOCKER_INFRASTRUCTURE, this)
+                .configure(SoftwareProcess.CHILDREN_STARTABLE_MODE, SoftwareProcess.ChildStartableMode.BACKGROUND_LATE);
+
+        DynamicCluster hosts = addChild(EntitySpec.create(DynamicCluster.class)
+                .configure(Cluster.INITIAL_SIZE, 1)
+                .configure(DynamicCluster.QUARANTINE_FAILED_ENTITIES, true)
+                .configure(DynamicCluster.MEMBER_SPEC, dockerHostSpec)
+                .configure(DynamicCluster.RUNNING_QUORUM_CHECK, QuorumChecks.atLeastOneUnlessEmpty())
+                .configure(DynamicCluster.UP_QUORUM_CHECK, QuorumChecks.atLeastOneUnlessEmpty())
+                .displayName("Docker Hosts"));
+
+        DynamicGroup fabric = addChild(EntitySpec.create(DynamicGroup.class)
+                .configure(DynamicGroup.ENTITY_FILTER, Predicates.and(Predicates.instanceOf(StubContainer.class), EntityPredicates.attributeEqualTo(StubContainer.DOCKER_INFRASTRUCTURE, this)))
+                .configure(DynamicGroup.MEMBER_DELEGATE_CHILDREN, true)
+                .displayName("All Docker Containers"));
+
+        DynamicMultiGroup buckets = addChild(EntitySpec.create(DynamicMultiGroup.class)
+                .configure(DynamicMultiGroup.ENTITY_FILTER, StubUtils.sameInfrastructure(this))
+                .configure(DynamicMultiGroup.RESCAN_INTERVAL, 15L)
+                .configure(DynamicMultiGroup.BUCKET_FUNCTION, new Function<Entity, String>() {
+                    @Override
+                    public String apply(@Nullable Entity input) {
+                        return input.getApplication().getDisplayName() + ":" + input.getApplicationId();
+                    }
+                })
+                .configure(DynamicMultiGroup.BUCKET_SPEC, EntitySpec.create(BasicGroup.class)
+                        .configure(BasicGroup.MEMBER_DELEGATE_CHILDREN, true))
+                .displayName("Docker Applications"));
+
+        sensors().set(DOCKER_HOST_CLUSTER, hosts);
+        sensors().set(DOCKER_CONTAINER_FABRIC, fabric);
+        sensors().set(DOCKER_APPLICATIONS, buckets);
+    }
+    
+    @Override
+    public void rebind() {
+        super.rebind();
+
+        // Reload our location definition on rebind
+        ManagementContext.PropertiesReloadListener listener = sensors().get(Attributes.PROPERTIES_RELOAD_LISTENER);
+        if (listener != null) {
+            listener.reloaded();
+        }
+    }
+
+    @Override
+    public StubInfrastructureLocation getDynamicLocation() {
+        return (StubInfrastructureLocation) sensors().get(DYNAMIC_LOCATION);
+    }
+
+    @Override
+    public void doStart(Collection<? extends Location> locations) {
+        // TODO support multiple locations
+        sensors().set(SERVICE_UP, Boolean.FALSE);
+
+        Location provisioner = Iterables.getOnlyElement(locations);
+        LOG.info("Creating new DockerLocation wrapping {}", provisioner);
+
+        Map<String, ?> flags = MutableMap.<String, Object>builder()
+                .putAll(config().get(LOCATION_FLAGS))
+                .put("provisioner", provisioner)
+                .build();
+        createLocation(flags);
+
+        super.doStart(locations);
+
+        sensors().set(SERVICE_UP, Boolean.TRUE);
+    }
+
+    /**
+     * De-register our {@link DockerLocation} and its children.
+     */
+    @Override
+    public void stop() {
+        sensors().set(SERVICE_UP, Boolean.FALSE);
+
+        // Find all applications and stop, blocking for up to five minutes until ended
+        try {
+            Iterable<Entity> entities = Iterables.filter(getManagementContext().getEntityManager().getEntities(), StubUtils.sameInfrastructure(this));
+            Set<Application> applications = ImmutableSet.copyOf(Iterables.transform(entities, new Function<Entity, Application>() {
+                @Override
+                public Application apply(Entity input) {
+                    return input.getApplication();
+                }
+            }));
+            LOG.debug("Stopping applications: {}", Iterables.toString(applications));
+            Entities.invokeEffectorList(this, applications, Startable.STOP).get(Duration.THIRTY_SECONDS);
+        } catch (Exception e) {
+            LOG.warn("Error stopping applications", e);
+        }
+
+        // Stop all Docker hosts in parallel
+        try {
+            DynamicCluster hosts = sensors().get(DOCKER_HOST_CLUSTER);
+            LOG.debug("Stopping hosts: {}", Iterables.toString(hosts.getMembers()));
+            Entities.invokeEffectorList(this, hosts.getMembers(), Startable.STOP).get(Duration.THIRTY_SECONDS);
+        } catch (Exception e) {
+            LOG.warn("Error stopping hosts", e);
+        }
+
+        // Stop anything else left over
+        super.stop();
+
+        deleteLocation();
+    }
+
+    @Override
+    public StubInfrastructureLocation createLocation(Map<String, ?> flags) {
+        String locationName = config().get(LOCATION_NAME);
+
+        LocationDefinition check = getManagementContext().getLocationRegistry().getDefinedLocationByName(locationName);
+        if (check != null) {
+            throw new IllegalStateException("Location " + locationName + " is already defined: " + check);
+        }
+
+        String locationSpec = String.format(StubResolver.DOCKER_INFRASTRUCTURE_SPEC, getId()) + String.format(":(name=\"%s\")", locationName);
+        sensors().set(LOCATION_SPEC, locationSpec);
+        LocationDefinition definition = new BasicLocationDefinition(locationName, locationSpec, flags);
+        Location location = getManagementContext().getLocationRegistry().resolve(definition);
+        getManagementContext().getLocationRegistry().updateDefinedLocation(definition);
+
+        ManagementContext.PropertiesReloadListener listener = StubUtils.reloadLocationListener(getManagementContext(), definition);
+        getManagementContext().addPropertiesReloadListener(listener);
+        sensors().set(Attributes.PROPERTIES_RELOAD_LISTENER, listener);
+
+        sensors().set(LocationOwner.LOCATION_DEFINITION, definition);
+        sensors().set(LocationOwner.DYNAMIC_LOCATION, location);
+        sensors().set(LocationOwner.LOCATION_NAME, location.getId());
+        
+        return (StubInfrastructureLocation) location;
+    }
+
+    @Override
+    public boolean isLocationAvailable() {
+        return getDynamicLocation() != null;
+    }
+
+    @Override
+    public void deleteLocation() {
+        StubInfrastructureLocation location = getDynamicLocation();
+
+        if (location != null) {
+            LocationManager mgr = getManagementContext().getLocationManager();
+            if (mgr.isManaged(location)) {
+                mgr.unmanage(location);
+            }
+            final LocationDefinition definition = sensors().get(LocationOwner.LOCATION_DEFINITION);
+            if (definition != null) {
+                getManagementContext().getLocationRegistry().removeDefinedLocation(definition.getId());
+            }
+        }
+        ManagementContext.PropertiesReloadListener listener = sensors().get(Attributes.PROPERTIES_RELOAD_LISTENER);
+        if (listener != null) {
+            getManagementContext().removePropertiesReloadListener(listener);
+        }
+
+        sensors().set(LocationOwner.LOCATION_DEFINITION, null);
+        sensors().set(LocationOwner.DYNAMIC_LOCATION, null);
+        sensors().set(LocationOwner.LOCATION_NAME, null);
+    }
+
+    @Override
+    public List<Entity> getStubHostList() {
+        if (getStubHostCluster() == null) {
+            return ImmutableList.of();
+        } else {
+            return ImmutableList.copyOf(getStubHostCluster().getMembers());
+        }
+    }
+
+    @Override
+    public DynamicCluster getStubHostCluster() {
+        return sensors().get(DOCKER_HOST_CLUSTER);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureLocation.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureLocation.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureLocation.java
new file mode 100644
index 0000000..05587f1
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureLocation.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.location.dynamic.clocker;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+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.location.AbstractLocation;
+import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+
+public class StubInfrastructureLocation extends AbstractLocation implements MachineProvisioningLocation<MachineLocation>, DynamicLocation<StubInfrastructure, StubInfrastructureLocation> {
+
+    @SuppressWarnings("unused")
+    private static final Logger LOG = LoggerFactory.getLogger(StubInfrastructureLocation.class);
+
+    @SetFromFlag("owner")
+    private StubInfrastructure infrastructure;
+
+    @SetFromFlag("machines")
+    private final SetMultimap<StubHostLocation, String> containers = Multimaps.synchronizedSetMultimap(HashMultimap.<StubHostLocation, String>create());
+
+    @Override
+    public StubInfrastructure getOwner() {
+        return infrastructure;
+    }
+
+    @Override
+    public MachineLocation obtain(Map<?, ?> flags) throws NoMachinesAvailableException {
+        StubHost host = (StubHost) Iterables.get(infrastructure.getStubHostList(), 0);
+        StubHostLocation hostLocation = host.getDynamicLocation();
+        StubContainerLocation containerLocation = hostLocation.obtain(flags);
+        containers.put(hostLocation, containerLocation.getId());
+        return containerLocation;
+    }
+
+    @Override
+    public void release(MachineLocation machine) {
+        Set<StubHostLocation> set = Multimaps.filterValues(containers, Predicates.equalTo(machine.getId())).keySet();
+        StubHostLocation hostLocation = Iterables.getOnlyElement(set);
+        hostLocation.release((StubContainerLocation)machine);
+        containers.remove(hostLocation, machine);
+    }
+
+    @Override
+    public MachineProvisioningLocation<MachineLocation> newSubLocation(Map<?, ?> newFlags) {
+        return this;
+    }
+
+    @Override
+    public Map<String, Object> getProvisioningFlags(Collection<String> tags) {
+        return ImmutableMap.of();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolver.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolver.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolver.java
new file mode 100644
index 0000000..99fd2b1
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolver.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.location.dynamic.clocker;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationRegistry;
+import org.apache.brooklyn.api.location.LocationResolver.EnableableLocationResolver;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.location.BasicLocationRegistry;
+import org.apache.brooklyn.core.location.LocationPropertiesFromBrooklynProperties;
+import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
+import org.apache.brooklyn.core.location.internal.LocationInternal;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.text.KeyValueParser;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
+/**
+ * Examples of valid specs:
+ *   <ul>
+ *     <li>docker:infrastructureId
+ *     <li>docker:infrastructureId:(name=docker-infrastructure)
+ *     <li>docker:infrastructureId:dockerHostId
+ *     <li>docker:infrastructureId:dockerHostId:(name=dockerHost-brooklyn-1234,user=docker)
+ *   </ul>
+ */
+public class StubResolver implements EnableableLocationResolver {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StubResolver.class);
+
+    public static final String DOCKER = "docker";
+    public static final Pattern PATTERN = Pattern.compile("("+DOCKER+"|"+DOCKER.toUpperCase()+")" + ":([a-zA-Z0-9]+)" +
+            "(:([a-zA-Z0-9]+))?" + "(:\\((.*)\\))?$");
+    public static final Set<String> ACCEPTABLE_ARGS = ImmutableSet.of("name", "displayName");
+
+    public static final String DOCKER_INFRASTRUCTURE_SPEC = "docker:%s";
+    public static final String DOCKER_HOST_MACHINE_SPEC = "docker:%s:%s";
+
+    private ManagementContext managementContext;
+
+    @Override
+    public void init(ManagementContext managementContext) {
+        this.managementContext = checkNotNull(managementContext, "managementContext");
+    }
+
+    @Override
+    public String getPrefix() {
+        return DOCKER;
+    }
+
+    @Override
+    public Location newLocationFromString(Map locationFlags, String spec, LocationRegistry registry) {
+        return newLocationFromString(spec, registry, registry.getProperties(), locationFlags);
+    }
+
+    protected Location newLocationFromString(String spec, LocationRegistry registry, Map properties, Map locationFlags) {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Resolving location '" + spec + "' with flags " + Joiner.on(",").withKeyValueSeparator("=").join(locationFlags));
+        }
+        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
+
+        Matcher matcher = PATTERN.matcher(spec);
+        if (!matcher.matches()) {
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; must specify something like docker:entityId or docker:entityId:(name=abc)");
+        }
+
+        String argsPart = matcher.group(6);
+        Map<String, String> argsMap = (argsPart != null) ? KeyValueParser.parseMap(argsPart) : Collections.<String,String>emptyMap();
+        String displayNamePart = argsMap.get("displayName");
+        String namePart = argsMap.get("name");
+
+        if (!ACCEPTABLE_ARGS.containsAll(argsMap.keySet())) {
+            Set<String> illegalArgs = Sets.difference(argsMap.keySet(), ACCEPTABLE_ARGS);
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; illegal args "+illegalArgs+"; acceptable args are "+ACCEPTABLE_ARGS);
+        }
+        if (argsMap.containsKey("displayName") && Strings.isEmpty(displayNamePart)) {
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; if displayName supplied then value must be non-empty");
+        }
+        if (argsMap.containsKey("name") && Strings.isEmpty(namePart)) {
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; if name supplied then value must be non-empty");
+        }
+
+        Map<String, Object> filteredProperties = new LocationPropertiesFromBrooklynProperties().getLocationProperties(DOCKER, namedLocation, properties);
+        MutableMap<String, Object> flags = MutableMap.<String, Object>builder().putAll(filteredProperties).putAll(locationFlags).build();
+
+        String infrastructureId = matcher.group(2);
+        if (Strings.isBlank(infrastructureId)) {
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; infrastructure entity id must be non-empty");
+        }
+        String dockerHostId = matcher.group(4);
+
+        // Build the display name
+        StringBuilder name = new StringBuilder();
+        if (displayNamePart != null) {
+            name.append(displayNamePart);
+        } else {
+            name.append("Docker ");
+            if (dockerHostId == null) {
+                name.append("Infrastructure ").append(infrastructureId);
+            } else {
+                name.append("Host ").append(dockerHostId);
+            }
+        }
+        final String displayName =  name.toString();
+
+        // Build the location name
+        name = new StringBuilder();
+        if (namePart != null) {
+            name.append(namePart);
+        } else {
+            name.append("docker-");
+            name.append(infrastructureId);
+            if (dockerHostId != null) {
+                name.append("-").append(dockerHostId);
+            }
+        }
+        final String locationName =  name.toString();
+        StubInfrastructure infrastructure = (StubInfrastructure) managementContext.getEntityManager().getEntity(infrastructureId);
+        Iterable<Location> managedLocations = managementContext.getLocationManager().getLocations();
+
+        if (dockerHostId == null) {
+            for (Location location : managedLocations) {
+                if (location instanceof StubInfrastructureLocation) {
+                    if (((StubInfrastructureLocation) location).getOwner().getId().equals(infrastructureId)) {
+                        return location;
+                    }
+                }
+            }
+            LocationSpec<StubInfrastructureLocation> locationSpec = LocationSpec.create(StubInfrastructureLocation.class)
+                    .configure(flags)
+                    .configure(DynamicLocation.OWNER, infrastructure)
+                    .configure(LocationInternal.NAMED_SPEC_NAME, locationName)
+                    .displayName(displayName);
+            return managementContext.getLocationManager().createLocation(locationSpec);
+        } else {
+            StubHost dockerHost = (StubHost) managementContext.getEntityManager().getEntity(dockerHostId);
+            for (Location location : managedLocations) {
+                if (location instanceof StubHostLocation) {
+                    if (((StubHostLocation) location).getOwner().getId().equals(dockerHostId)) {
+                        return location;
+                    }
+                }
+            }
+
+            LocationSpec<StubHostLocation> locationSpec = LocationSpec.create(StubHostLocation.class)
+                    .parent(infrastructure.getDynamicLocation())
+                    .configure(flags)
+                    .configure(DynamicLocation.OWNER, dockerHost)
+                    .configure(LocationInternal.NAMED_SPEC_NAME, locationName)
+                    .displayName(displayName);
+            return managementContext.getLocationManager().createLocation(locationSpec);
+        }
+    }
+
+    @Override
+    public boolean accepts(String spec, LocationRegistry registry) {
+        return BasicLocationRegistry.isResolverPrefixForSpec(this, spec, true);
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+//        return Iterables.tryFind(managementContext.getEntityManager().getEntities(), Predicates.instanceOf(StubInfrastructure.class)).isPresent();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/292b4cfb/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubUtils.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubUtils.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubUtils.java
new file mode 100644
index 0000000..eaea4fc
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubUtils.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.location.dynamic.clocker;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+
+public class StubUtils {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StubUtils.class);
+
+    /** Do not instantiate. */
+    private StubUtils() { }
+
+    /*
+     * Configuration and constants.
+     */
+
+    public static final Predicate<Entity> sameInfrastructure(Entity entity) {
+        Preconditions.checkNotNull(entity, "entity");
+        return new SameInfrastructurePredicate(entity.getId());
+    }
+
+    public static class SameInfrastructurePredicate implements Predicate<Entity> {
+
+        private final String id;
+
+        public SameInfrastructurePredicate(String id) {
+            this.id = Preconditions.checkNotNull(id, "id");
+        }
+
+        @Override
+        public boolean apply(@Nullable Entity input) {
+            // Check if entity is deployed to a DockerContainerLocation
+            Optional<Location> lookup = Iterables.tryFind(input.getLocations(), Predicates.instanceOf(StubContainerLocation.class));
+            if (lookup.isPresent()) {
+                StubContainerLocation container = (StubContainerLocation) lookup.get();
+                // Only containers that are part of this infrastructure
+                return id.equals(container.getOwner().getDockerHost().getInfrastructure().getId());
+            } else {
+                return false;
+            }
+        }
+    };
+
+    public static final ManagementContext.PropertiesReloadListener reloadLocationListener(ManagementContext context, LocationDefinition definition) {
+        return new ReloadDockerLocation(context, definition);
+    }
+
+    public static class ReloadDockerLocation implements ManagementContext.PropertiesReloadListener {
+
+        private final ManagementContext context;
+        private final LocationDefinition definition;
+
+        public ReloadDockerLocation(ManagementContext context, LocationDefinition definition) {
+            this.context = Preconditions.checkNotNull(context, "context");
+            this.definition = Preconditions.checkNotNull(definition, "definition");
+        }
+
+        @Override
+        public void reloaded() {
+            Location resolved = context.getLocationRegistry().resolve(definition);
+            context.getLocationRegistry().updateDefinedLocation(definition);
+            context.getLocationManager().manage(resolved);
+        }
+    };
+}


[06/10] brooklyn-server git commit: Fix log.warn when calling Location.unmanage

Posted by gr...@apache.org.
Fix log.warn when calling Location.unmanage

Previously, if a location being unmanaged had a parent, we’d try to
unmanaged it twice - the second time would give a log.warn

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

Branch: refs/heads/master
Commit: 3118f427b4bc8e95ce4c19a54f97325957687d61
Parents: 3df2b8b
Author: Aled Sage <al...@gmail.com>
Authored: Tue Mar 22 12:45:03 2016 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Tue Mar 22 12:45:03 2016 +0000

----------------------------------------------------------------------
 .../java/org/apache/brooklyn/core/location/AbstractLocation.java | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3118f427/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
index dc48862..d4e565e 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
@@ -706,7 +706,9 @@ public abstract class AbstractLocation extends AbstractBrooklynObject implements
             }
             child.setParent(null);
             
-            if (isManaged()) {
+            if (isManaged() && Locations.isManaged(child)) {
+                // This is called as part of child's LocalLocationManager.unmanage; don't try to 
+                // unmanage it yet again as then would get a log.warn!
                 getManagementContext().getLocationManager().unmanage(child);
             }
         }


[09/10] brooklyn-server git commit: Clocker-testing: fix where we delete the location

Posted by gr...@apache.org.
Clocker-testing: fix where we delete the location

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

Branch: refs/heads/master
Commit: b59fda75be280dee68e44115d9ca2112e8404658
Parents: b5dae5b
Author: Aled Sage <al...@gmail.com>
Authored: Tue Mar 22 12:48:01 2016 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Tue Mar 22 12:48:01 2016 +0000

----------------------------------------------------------------------
 .../location/dynamic/clocker/StubContainerImpl.java |  8 ++++++++
 .../core/location/dynamic/clocker/StubHostImpl.java | 16 ++++++++++++++++
 .../dynamic/clocker/StubInfrastructureImpl.java     |  6 ++++--
 3 files changed, 28 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b59fda75/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerImpl.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerImpl.java
index 45cf9d0..2ef9480 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerImpl.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerImpl.java
@@ -27,11 +27,13 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.mgmt.LocationManager;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
+import org.apache.brooklyn.core.entity.trait.StartableMethods;
 import org.apache.brooklyn.core.feed.ConfigToAttributes;
 import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
 import org.apache.brooklyn.entity.stock.BasicStartableImpl;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
 import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -74,6 +76,12 @@ public class StubContainerImpl extends BasicStartableImpl implements StubContain
     }
 
     @Override
+    public void stop() {
+        super.stop();
+        deleteLocation();
+    }
+
+    @Override
     public StubContainerLocation createLocation(Map<String, ?> flags) {
         StubHost dockerHost = getDockerHost();
         StubHostLocation host = dockerHost.getDynamicLocation();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b59fda75/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java
index 3139d93..314ebda 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java
@@ -19,10 +19,15 @@
 package org.apache.brooklyn.core.location.dynamic.clocker;
 
 import java.util.Map;
+import java.util.Set;
 
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.location.LocationDefinition;
 import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.feed.ConfigToAttributes;
 import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.core.location.Machines;
@@ -31,9 +36,14 @@ import org.apache.brooklyn.entity.group.DynamicCluster;
 import org.apache.brooklyn.entity.software.base.EmptySoftwareProcessImpl;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
 import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks;
+import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
 public class StubHostImpl extends EmptySoftwareProcessImpl implements StubHost {
     
     private static final Logger LOG = LoggerFactory.getLogger(StubHostImpl.class);
@@ -83,6 +93,12 @@ public class StubHostImpl extends EmptySoftwareProcessImpl implements StubHost {
     }
 
     @Override
+    public void postStop() {
+        super.postStop();
+        deleteLocation();
+    }
+
+    @Override
     public StubHostLocation createLocation(Map<String, ?> flags) {
         StubInfrastructure infrastructure = getInfrastructure();
         StubInfrastructureLocation docker = infrastructure.getDynamicLocation();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b59fda75/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java
index 0f578bc..05367c4 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java
@@ -34,6 +34,7 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.core.entity.AbstractApplication;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.feed.ConfigToAttributes;
 import org.apache.brooklyn.core.location.Locations;
@@ -132,6 +133,7 @@ public class StubInfrastructureImpl extends AbstractApplication implements StubI
      */
     @Override
     public void stop() {
+        setExpectedStateAndRecordLifecycleEvent(Lifecycle.STOPPING);
         sensors().set(SERVICE_UP, Boolean.FALSE);
 
         // Find all applications and stop, blocking for up to five minutes until ended
@@ -158,10 +160,10 @@ public class StubInfrastructureImpl extends AbstractApplication implements StubI
             LOG.warn("Error stopping hosts", e);
         }
 
+        deleteLocation();
+
         // Stop anything else left over
         super.stop();
-
-        deleteLocation();
     }
 
     @Override


[02/10] brooklyn-server git commit: Clocker pattern: change how LocationRegistry used

Posted by gr...@apache.org.
Clocker pattern: change how LocationRegistry used

Adds DynamicLocation.register() and .deregister()


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

Branch: refs/heads/master
Commit: 453183f1d89336ea793aab2edbe05c3b4070104b
Parents: 292b4cf
Author: Aled Sage <al...@gmail.com>
Authored: Fri Mar 11 20:50:45 2016 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Sat Mar 19 21:35:41 2016 +0000

----------------------------------------------------------------------
 .../core/location/dynamic/DynamicLocation.java  |  12 ++
 .../core/location/dynamic/LocationOwner.java    |   4 -
 ...ClockerDynamicLocationPatternRebindTest.java |  35 ++++-
 .../ClockerDynamicLocationPatternTest.java      |  22 ++-
 .../dynamic/clocker/StubContainerLocation.java  |  13 +-
 .../core/location/dynamic/clocker/StubHost.java |   4 +-
 .../location/dynamic/clocker/StubHostImpl.java  |  38 +++--
 .../dynamic/clocker/StubHostLocation.java       |  70 ++++++++-
 .../dynamic/clocker/StubInfrastructure.java     |   7 +-
 .../dynamic/clocker/StubInfrastructureImpl.java |  66 +++-----
 .../clocker/StubInfrastructureLocation.java     |  62 +++++++-
 .../location/dynamic/clocker/StubResolver.java  | 157 +++++++------------
 .../dynamic/clocker/StubResolverTest.java       |  82 ++++++++++
 13 files changed, 384 insertions(+), 188 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/core/src/main/java/org/apache/brooklyn/core/location/dynamic/DynamicLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/dynamic/DynamicLocation.java b/core/src/main/java/org/apache/brooklyn/core/location/dynamic/DynamicLocation.java
index b04ebac..d961cdf 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/dynamic/DynamicLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/dynamic/DynamicLocation.java
@@ -20,6 +20,8 @@ package org.apache.brooklyn.core.location.dynamic;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
@@ -47,4 +49,14 @@ public interface DynamicLocation<E extends Entity & LocationOwner<L, E>, L exten
 
     E getOwner();
 
+    /**
+     * An opportunity to register this location (e.g. with the {@link LocationRegistry} or the 
+     * catalog, so that it will be persisted).
+     */
+    LocationDefinition register();
+    
+    /**
+     * The complement of {@link #register()}, to deregister this location.
+     */
+    void deregister();
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/core/src/main/java/org/apache/brooklyn/core/location/dynamic/LocationOwner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/dynamic/LocationOwner.java b/core/src/main/java/org/apache/brooklyn/core/location/dynamic/LocationOwner.java
index 718b97e..2c5a66c 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/dynamic/LocationOwner.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/dynamic/LocationOwner.java
@@ -22,7 +22,6 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.location.LocationDefinition;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
@@ -71,9 +70,6 @@ public interface LocationOwner<L extends Location & DynamicLocation<E, L>, E ext
     AttributeSensor<Boolean> DYNAMIC_LOCATION_STATUS = Sensors.newBooleanSensor(
             "entity.dynamicLocation.status", "The status of the location owned by this entity");
 
-    AttributeSensor<LocationDefinition> LOCATION_DEFINITION = Sensors.newSensor(
-        LocationDefinition.class, "entity.dynamicLocation.definition", "The location definition for the location owned by this entity");
-
     L getDynamicLocation();
 
     L createLocation(Map<String, ?> flags);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternRebindTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternRebindTest.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternRebindTest.java
index 361ee95..6ccd86c 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternRebindTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternRebindTest.java
@@ -29,6 +29,7 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.location.BasicLocationRegistry;
 import org.apache.brooklyn.core.location.Locations;
@@ -41,6 +42,9 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
+/**
+ * See explanation of what we're testing in {@link StubInfrastructure}.
+ */
 public class ClockerDynamicLocationPatternRebindTest extends RebindTestFixtureWithApp {
 
     @Override
@@ -59,9 +63,9 @@ public class ClockerDynamicLocationPatternRebindTest extends RebindTestFixtureWi
         return result;
     }
     
-    // To make this fail (with Clocker code as at 2016-03-11) requires several apps - there's a bug
-    // in rebind that only happens when there are several entities, so the order that they rebind
-    // is more interleaved.
+    // To make this fail previously (with Clocker code as at 2016-03-11) required several apps - 
+    // there was a bug in rebind that only happened when there were several entities, so the order 
+    // that they did the rebind was more interleaved.
     @Test
     public void testRebind() throws Exception {
         final int NUM_INFRAS = 10;
@@ -70,26 +74,45 @@ public class ClockerDynamicLocationPatternRebindTest extends RebindTestFixtureWi
         
         // Maps from the infrastructure locSpec to the (potentially many) host locSpecs
         Map<String, List<String>> locSpecs = Maps.newLinkedHashMap();
+        Map<String, List<String>> locNames = Maps.newLinkedHashMap();
         
         for (int i = 0; i < NUM_INFRAS; i++) {
+            String infraLocName = "myname"+i;
             StubInfrastructure infra = mgmt().getEntityManager().createEntity(EntitySpec.create(StubInfrastructure.class)
-                    .configure(StubInfrastructure.LOCATION_NAME, "myname"+i));
+                    .configure(StubInfrastructure.LOCATION_NAME, infraLocName)
+                    .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true));
             infra.start(ImmutableList.of(loc));
             infra.getStubHostCluster().resize(HOST_CLUSTER_SIZE);
             assertEquals(infra.getStubHostCluster().getMembers().size(), HOST_CLUSTER_SIZE);
             
             String infraLocSpec = infra.sensors().get(StubInfrastructure.LOCATION_SPEC);
             List<String> hostLocSpecs = Lists.newArrayList();
+            List<String> hostLocNames = Lists.newArrayList();
             for (Entity host : infra.getStubHostCluster().getMembers()) {
-                hostLocSpecs.add(host.sensors().get(StubInfrastructure.LOCATION_SPEC));
+                hostLocSpecs.add(host.sensors().get(StubHost.LOCATION_SPEC));
+                hostLocNames.add(host.sensors().get(StubHost.LOCATION_NAME));
             }
     
             locSpecs.put(infraLocSpec, hostLocSpecs);
+            locNames.put(infraLocName, hostLocNames);
         }
         assertEquals(locSpecs.size(), NUM_INFRAS); // in case the infrastructures all used the same loc name!
+        assertEquals(locNames.size(), NUM_INFRAS); // in case the infrastructures all used the same loc name!
 
         rebind();
-
+        
+        for (Map.Entry<String, List<String>> entry : locNames.entrySet()) {
+            String infraLocName = entry.getKey();
+            List<String> hostLocName = entry.getValue();
+            
+            StubInfrastructureLocation newInfraLoc = (StubInfrastructureLocation) mgmt().getLocationRegistry().resolve(infraLocName);
+            assertNotNull(newInfraLoc);
+            for (String hostLocSpec: hostLocName) {
+                StubHostLocation newHostLoc = (StubHostLocation) mgmt().getLocationRegistry().resolve(hostLocSpec);
+                assertNotNull(newHostLoc);
+            }
+        }
+        
         for (Map.Entry<String, List<String>> entry : locSpecs.entrySet()) {
             String infraLocSpec = entry.getKey();
             List<String> hostLocSpecs = entry.getValue();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternTest.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternTest.java
index 618c552..b67b11c 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/ClockerDynamicLocationPatternTest.java
@@ -20,13 +20,16 @@ package org.apache.brooklyn.core.location.dynamic.clocker;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertSame;
 
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.location.BasicLocationRegistry;
 import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestApplication;
 import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -35,6 +38,9 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 
+/**
+ * See explanation of what we're testing in {@link StubInfrastructure}.
+ */
 public class ClockerDynamicLocationPatternTest extends BrooklynAppUnitTestSupport {
 
     private LocalhostMachineProvisioningLocation loc;
@@ -51,7 +57,8 @@ public class ClockerDynamicLocationPatternTest extends BrooklynAppUnitTestSuppor
     
     @Test
     public void testCreateAndReleaseDirectly() throws Exception {
-        StubInfrastructure infra = mgmt.getEntityManager().createEntity(EntitySpec.create(StubInfrastructure.class));
+        StubInfrastructure infra = mgmt.getEntityManager().createEntity(EntitySpec.create(StubInfrastructure.class)
+                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution()));
         infra.start(ImmutableList.of(loc));
         
         StubInfrastructureLocation loc = infra.getDynamicLocation();
@@ -72,17 +79,24 @@ public class ClockerDynamicLocationPatternTest extends BrooklynAppUnitTestSuppor
     
     @Test
     public void testThroughLocationRegistry() throws Exception {
-        StubInfrastructure infra = mgmt.getEntityManager().createEntity(EntitySpec.create(StubInfrastructure.class));
+        StubInfrastructure infra = mgmt.getEntityManager().createEntity(EntitySpec.create(StubInfrastructure.class)
+                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution()));
         infra.start(ImmutableList.of(loc));
         
         String infraLocSpec = infra.sensors().get(StubInfrastructure.LOCATION_SPEC);
+        String infraLocName = infra.sensors().get(StubInfrastructure.LOCATION_NAME);
         StubInfrastructureLocation infraLoc = (StubInfrastructureLocation) mgmt.getLocationRegistry().resolve(infraLocSpec);
-
+        StubInfrastructureLocation infraLoc2 = (StubInfrastructureLocation) mgmt.getLocationRegistry().resolve(infraLocName);
+        assertSame(infraLoc, infraLoc2);
+        
         MachineLocation machine = infraLoc.obtain(ImmutableMap.of());
         
         StubHost host = (StubHost) Iterables.getOnlyElement(infra.getStubHostCluster().getMembers());
-        String hostLocSpec = host.sensors().get(StubInfrastructure.LOCATION_SPEC);
+        String hostLocSpec = host.sensors().get(StubHost.LOCATION_SPEC);
+        String hostLocName = host.sensors().get(StubHost.LOCATION_NAME);
         StubHostLocation hostLoc = (StubHostLocation) mgmt.getLocationRegistry().resolve(hostLocSpec);
+        StubHostLocation hostLoc2 = (StubHostLocation) mgmt.getLocationRegistry().resolve(hostLocName);
+        assertSame(hostLoc, hostLoc2);
 
         StubContainer container = (StubContainer) Iterables.getOnlyElement(host.getDockerContainerCluster().getMembers());
         StubContainerLocation containerLoc = container.getDynamicLocation();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerLocation.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerLocation.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerLocation.java
index f4b0345..570cd73 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerLocation.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubContainerLocation.java
@@ -18,6 +18,7 @@
  */
 package org.apache.brooklyn.core.location.dynamic.clocker;
 
+import org.apache.brooklyn.api.location.LocationDefinition;
 import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
@@ -38,4 +39,14 @@ public class StubContainerLocation extends SshMachineLocation implements Dynamic
     public SshMachineLocation getMachine() {
         return machine;
     }
-}
\ No newline at end of file
+
+    @Override
+    public LocationDefinition register() {
+        throw new UnsupportedOperationException("Container location type definition cannot be persisted");
+    }
+
+    @Override
+    public void deregister() {
+        // no-op
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHost.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHost.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHost.java
index e5b4005..6b2d319 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHost.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHost.java
@@ -24,10 +24,10 @@ import org.apache.brooklyn.core.location.dynamic.LocationOwner;
 import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.entity.group.DynamicCluster;
-import org.apache.brooklyn.entity.machine.MachineEntity;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
 
 @ImplementedBy(StubHostImpl.class)
-public interface StubHost extends MachineEntity, LocationOwner<StubHostLocation, StubHost> {
+public interface StubHost extends EmptySoftwareProcess, LocationOwner<StubHostLocation, StubHost> {
     AttributeSensorAndConfigKey<StubInfrastructure, StubInfrastructure> DOCKER_INFRASTRUCTURE = StubAttributes.DOCKER_INFRASTRUCTURE;
     
     AttributeSensor<DynamicCluster> DOCKER_CONTAINER_CLUSTER = Sensors.newSensor(DynamicCluster.class,

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java
index 4c2ad36..3139d93 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostImpl.java
@@ -21,23 +21,20 @@ package org.apache.brooklyn.core.location.dynamic.clocker;
 import java.util.Map;
 
 import org.apache.brooklyn.api.entity.EntitySpec;
-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.core.feed.ConfigToAttributes;
-import org.apache.brooklyn.core.location.BasicLocationDefinition;
 import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.core.location.Machines;
 import org.apache.brooklyn.entity.group.Cluster;
 import org.apache.brooklyn.entity.group.DynamicCluster;
-import org.apache.brooklyn.entity.machine.MachineEntityImpl;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcessImpl;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
-import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks;
-import org.apache.brooklyn.util.guava.Maybe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class StubHostImpl extends MachineEntityImpl implements StubHost {
+public class StubHostImpl extends EmptySoftwareProcessImpl implements StubHost {
     
     private static final Logger LOG = LoggerFactory.getLogger(StubHostImpl.class);
 
@@ -81,14 +78,7 @@ public class StubHostImpl extends MachineEntityImpl implements StubHost {
         super.preStart();
         ConfigToAttributes.apply(this);
         
-        Maybe<SshMachineLocation> found = Machines.findUniqueMachineLocation(getLocations(), SshMachineLocation.class);
-
-        Map<String, ?> flags = MutableMap.<String, Object>builder()
-                .putAll(config().get(LOCATION_FLAGS))
-                .put("machine", found.get())
-                .build();
-
-        createLocation(flags);
+        createLocation(config().get(LOCATION_FLAGS));
         sensors().get(DOCKER_CONTAINER_CLUSTER).sensors().set(SERVICE_UP, Boolean.TRUE);
     }
 
@@ -96,18 +86,25 @@ public class StubHostImpl extends MachineEntityImpl implements StubHost {
     public StubHostLocation createLocation(Map<String, ?> flags) {
         StubInfrastructure infrastructure = getInfrastructure();
         StubInfrastructureLocation docker = infrastructure.getDynamicLocation();
+        SshMachineLocation machine = Machines.findUniqueMachineLocation(getLocations(), SshMachineLocation.class).get();
         String locationName = docker.getId() + "-" + getId();
 
-        String locationSpec = String.format(StubResolver.DOCKER_HOST_MACHINE_SPEC, infrastructure.getId(), getId()) + String.format(":(name=\"%s\")", locationName);
-        sensors().set(LOCATION_SPEC, locationSpec);
+        StubHostLocation location = getManagementContext().getLocationManager().createLocation(LocationSpec.create(StubHostLocation.class)
+                .parent(infrastructure.getDynamicLocation())
+                .displayName("Docker Host("+getId()+")")
+                .configure(flags)
+                .configure("owner", getProxy())
+                .configure("machine", machine)
+                .configure("locationName", locationName));
+        
+        LocationDefinition definition = location.register();
 
-        LocationDefinition definition = new BasicLocationDefinition(locationName, locationSpec, flags);
-        Location location = getManagementContext().getLocationRegistry().resolve(definition);
+        sensors().set(LOCATION_SPEC, definition.getSpec());
+        sensors().set(LOCATION_NAME, locationName);
         sensors().set(DYNAMIC_LOCATION, location);
-        sensors().set(LOCATION_NAME, location.getId());
 
         LOG.info("New Docker host location {} created", location);
-        return (StubHostLocation) location;
+        return location;
     }
 
     @Override
@@ -119,6 +116,7 @@ public class StubHostImpl extends MachineEntityImpl implements StubHost {
     public void deleteLocation() {
         StubHostLocation loc = (StubHostLocation) sensors().get(DYNAMIC_LOCATION);
         if (loc != null) {
+            loc.deregister();
             Locations.unmanage(loc);
         }
         sensors().set(DYNAMIC_LOCATION, null);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostLocation.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostLocation.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostLocation.java
index a861220..9c76848 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostLocation.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubHostLocation.java
@@ -18,16 +18,22 @@
  */
 package org.apache.brooklyn.core.location.dynamic.clocker;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.util.Collection;
 import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.location.LocationDefinition;
 import org.apache.brooklyn.api.location.MachineProvisioningLocation;
 import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.location.BasicLocationDefinition;
 import org.apache.brooklyn.core.location.LocationConfigKeys;
 import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
@@ -46,12 +52,68 @@ public class StubHostLocation extends AbstractLocation implements MachineProvisi
 
     private static final Logger LOG = LoggerFactory.getLogger(StubHostLocation.class);
 
-    @SetFromFlag("machine")
-    private SshMachineLocation machine;
+    public static final ConfigKey<String> LOCATION_NAME = ConfigKeys.newStringConfigKey("locationName");
+
+    public static final ConfigKey<SshMachineLocation> MACHINE = ConfigKeys.newConfigKey(
+            SshMachineLocation.class, 
+            "machine");
+
+    @SetFromFlag("locationRegistrationId")
+    private String locationRegistrationId;
 
-    @SetFromFlag("owner")
-    private StubHost dockerHost;
+    private transient StubHost dockerHost;
+    private transient SshMachineLocation machine;
+    
+    @Override
+    public void init() {
+        super.init();
+        dockerHost = (StubHost) checkNotNull(getConfig(OWNER), "owner");
+        machine = (SshMachineLocation) checkNotNull(getConfig(MACHINE), "machine");
+    }
+    
+    @Override
+    public void rebind() {
+        super.rebind();
 
+        dockerHost = (StubHost) getConfig(OWNER);
+        machine = (SshMachineLocation) getConfig(MACHINE);
+        
+        if (getConfig(LOCATION_NAME) != null) {
+            register();
+        }
+    }
+
+    @Override
+    public LocationDefinition register() {
+        String locationName = checkNotNull(getConfig(LOCATION_NAME), "config %s", LOCATION_NAME.getName());
+
+        LocationDefinition check = getManagementContext().getLocationRegistry().getDefinedLocationByName(locationName);
+        if (check != null) {
+            throw new IllegalStateException("Location " + locationName + " is already defined: " + check);
+        }
+
+        String hostLocId = getId();
+        String infraLocId = (getParent() != null) ? getParent().getId() : "";
+        String locationSpec = String.format(StubResolver.DOCKER_HOST_MACHINE_SPEC, infraLocId, hostLocId) + String.format(":(name=\"%s\")", locationName);
+
+        LocationDefinition definition = new BasicLocationDefinition(locationName, locationSpec, ImmutableMap.<String, Object>of());
+        getManagementContext().getLocationRegistry().updateDefinedLocation(definition);
+        
+        locationRegistrationId = definition.getId();
+        requestPersist();
+        
+        return definition;
+    }
+    
+    @Override
+    public void deregister() {
+        if (locationRegistrationId != null) {
+            getManagementContext().getLocationRegistry().removeDefinedLocation(locationRegistrationId);
+            locationRegistrationId = null;
+            requestPersist();
+        }
+    }
+    
     @Override
     public StubHost getOwner() {
         return (StubHost) getConfig(OWNER);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructure.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructure.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructure.java
index 3170cf2..f6a9afd 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructure.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructure.java
@@ -29,6 +29,8 @@ import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.location.dynamic.LocationOwner;
+import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
+import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.entity.group.DynamicCluster;
 import org.apache.brooklyn.entity.group.DynamicGroup;
@@ -81,8 +83,9 @@ import org.apache.brooklyn.entity.group.DynamicMultiGroup;
  */
 @ImplementedBy(StubInfrastructureImpl.class)
 public interface StubInfrastructure extends Application, Startable, LocationOwner<StubInfrastructureLocation, StubInfrastructure> {
-    ConfigKey<String> LOCATION_NAME = ConfigKeys.newConfigKeyWithDefault(LocationOwner.LOCATION_NAME.getConfigKey(), "my-stub-cloud");
-    
+    AttributeSensorAndConfigKey<String, String> LOCATION_NAME = ConfigKeys.newSensorAndConfigKeyWithDefault(LocationOwner.LOCATION_NAME, "my-stub-cloud");
+    ConfigKey<Integer> DOCKER_HOST_CLUSTER_MIN_SIZE = ConfigKeys.newConfigKeyWithPrefix("docker.host.", DynamicCluster.INITIAL_SIZE);
+
     AttributeSensor<DynamicCluster> DOCKER_HOST_CLUSTER = Sensors.newSensor(DynamicCluster.class, "docker.hosts", "Docker host cluster");
     AttributeSensor<DynamicGroup> DOCKER_CONTAINER_FABRIC = Sensors.newSensor(DynamicGroup.class, "docker.fabric", "Docker container fabric");
     AttributeSensor<DynamicMultiGroup> DOCKER_APPLICATIONS = Sensors.newSensor(DynamicMultiGroup.class, "docker.buckets", "Docker applications");

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java
index 0d807fa..0f578bc 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureImpl.java
@@ -30,15 +30,13 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationDefinition;
-import org.apache.brooklyn.api.mgmt.LocationManager;
-import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.core.entity.AbstractApplication;
-import org.apache.brooklyn.core.entity.Attributes;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityPredicates;
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.feed.ConfigToAttributes;
-import org.apache.brooklyn.core.location.BasicLocationDefinition;
+import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.core.location.dynamic.LocationOwner;
 import org.apache.brooklyn.entity.group.BasicGroup;
 import org.apache.brooklyn.entity.group.Cluster;
@@ -48,11 +46,13 @@ import org.apache.brooklyn.entity.group.DynamicMultiGroup;
 import org.apache.brooklyn.entity.software.base.SoftwareProcess;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks;
+import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
+import com.google.common.base.Joiner;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -73,7 +73,7 @@ public class StubInfrastructureImpl extends AbstractApplication implements StubI
                 .configure(SoftwareProcess.CHILDREN_STARTABLE_MODE, SoftwareProcess.ChildStartableMode.BACKGROUND_LATE);
 
         DynamicCluster hosts = addChild(EntitySpec.create(DynamicCluster.class)
-                .configure(Cluster.INITIAL_SIZE, 1)
+                .configure(Cluster.INITIAL_SIZE, config().get(DOCKER_HOST_CLUSTER_MIN_SIZE))
                 .configure(DynamicCluster.QUARANTINE_FAILED_ENTITIES, true)
                 .configure(DynamicCluster.MEMBER_SPEC, dockerHostSpec)
                 .configure(DynamicCluster.RUNNING_QUORUM_CHECK, QuorumChecks.atLeastOneUnlessEmpty())
@@ -104,17 +104,6 @@ public class StubInfrastructureImpl extends AbstractApplication implements StubI
     }
     
     @Override
-    public void rebind() {
-        super.rebind();
-
-        // Reload our location definition on rebind
-        ManagementContext.PropertiesReloadListener listener = sensors().get(Attributes.PROPERTIES_RELOAD_LISTENER);
-        if (listener != null) {
-            listener.reloaded();
-        }
-    }
-
-    @Override
     public StubInfrastructureLocation getDynamicLocation() {
         return (StubInfrastructureLocation) sensors().get(DYNAMIC_LOCATION);
     }
@@ -178,27 +167,25 @@ public class StubInfrastructureImpl extends AbstractApplication implements StubI
     @Override
     public StubInfrastructureLocation createLocation(Map<String, ?> flags) {
         String locationName = config().get(LOCATION_NAME);
-
-        LocationDefinition check = getManagementContext().getLocationRegistry().getDefinedLocationByName(locationName);
-        if (check != null) {
-            throw new IllegalStateException("Location " + locationName + " is already defined: " + check);
+        if (Strings.isBlank(locationName)) {
+            String prefix = config().get(LOCATION_NAME_PREFIX);
+            String suffix = config().get(LOCATION_NAME_SUFFIX);
+            locationName = Joiner.on("-").skipNulls().join(prefix, getId(), suffix);
         }
 
-        String locationSpec = String.format(StubResolver.DOCKER_INFRASTRUCTURE_SPEC, getId()) + String.format(":(name=\"%s\")", locationName);
-        sensors().set(LOCATION_SPEC, locationSpec);
-        LocationDefinition definition = new BasicLocationDefinition(locationName, locationSpec, flags);
-        Location location = getManagementContext().getLocationRegistry().resolve(definition);
-        getManagementContext().getLocationRegistry().updateDefinedLocation(definition);
-
-        ManagementContext.PropertiesReloadListener listener = StubUtils.reloadLocationListener(getManagementContext(), definition);
-        getManagementContext().addPropertiesReloadListener(listener);
-        sensors().set(Attributes.PROPERTIES_RELOAD_LISTENER, listener);
+        StubInfrastructureLocation location = getManagementContext().getLocationManager().createLocation(LocationSpec.create(StubInfrastructureLocation.class)
+                .displayName("Docker Infrastructure("+getId()+")")
+                .configure(flags)
+                .configure("owner", getProxy())
+                .configure("locationName", locationName));
+        
+        LocationDefinition definition = location.register();
 
-        sensors().set(LocationOwner.LOCATION_DEFINITION, definition);
+        sensors().set(LocationOwner.LOCATION_SPEC, definition.getSpec());
         sensors().set(LocationOwner.DYNAMIC_LOCATION, location);
-        sensors().set(LocationOwner.LOCATION_NAME, location.getId());
+        sensors().set(LocationOwner.LOCATION_NAME, locationName);
         
-        return (StubInfrastructureLocation) location;
+        return location;
     }
 
     @Override
@@ -211,21 +198,10 @@ public class StubInfrastructureImpl extends AbstractApplication implements StubI
         StubInfrastructureLocation location = getDynamicLocation();
 
         if (location != null) {
-            LocationManager mgr = getManagementContext().getLocationManager();
-            if (mgr.isManaged(location)) {
-                mgr.unmanage(location);
-            }
-            final LocationDefinition definition = sensors().get(LocationOwner.LOCATION_DEFINITION);
-            if (definition != null) {
-                getManagementContext().getLocationRegistry().removeDefinedLocation(definition.getId());
-            }
-        }
-        ManagementContext.PropertiesReloadListener listener = sensors().get(Attributes.PROPERTIES_RELOAD_LISTENER);
-        if (listener != null) {
-            getManagementContext().removePropertiesReloadListener(listener);
+            location.deregister();
+            Locations.unmanage(location);
         }
 
-        sensors().set(LocationOwner.LOCATION_DEFINITION, null);
         sensors().set(LocationOwner.DYNAMIC_LOCATION, null);
         sensors().set(LocationOwner.LOCATION_NAME, null);
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureLocation.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureLocation.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureLocation.java
index 05587f1..5d5695c 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureLocation.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubInfrastructureLocation.java
@@ -18,14 +18,20 @@
  */
 package org.apache.brooklyn.core.location.dynamic.clocker;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.brooklyn.api.location.LocationDefinition;
 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.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.location.BasicLocationDefinition;
 import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
 import org.slf4j.Logger;
@@ -43,15 +49,65 @@ public class StubInfrastructureLocation extends AbstractLocation implements Mach
     @SuppressWarnings("unused")
     private static final Logger LOG = LoggerFactory.getLogger(StubInfrastructureLocation.class);
 
-    @SetFromFlag("owner")
-    private StubInfrastructure infrastructure;
+    public static final ConfigKey<String> LOCATION_NAME = ConfigKeys.newStringConfigKey("locationName");
+
+    @SetFromFlag("locationRegistrationId")
+    private String locationRegistrationId;
 
     @SetFromFlag("machines")
     private final SetMultimap<StubHostLocation, String> containers = Multimaps.synchronizedSetMultimap(HashMultimap.<StubHostLocation, String>create());
 
+    private transient StubInfrastructure infrastructure;
+
+    @Override
+    public void init() {
+        super.init();
+        infrastructure = (StubInfrastructure) checkNotNull(getConfig(OWNER), "owner");
+    }
+    
+    @Override
+    public void rebind() {
+        super.rebind();
+
+        infrastructure = (StubInfrastructure) getConfig(OWNER);
+        
+        if (getConfig(LOCATION_NAME) != null) {
+            register();
+        }
+    }
+
+    @Override
+    public LocationDefinition register() {
+        String locationName = checkNotNull(getConfig(LOCATION_NAME), "config %s", LOCATION_NAME.getName());
+
+        LocationDefinition check = getManagementContext().getLocationRegistry().getDefinedLocationByName(locationName);
+        if (check != null) {
+            throw new IllegalStateException("Location " + locationName + " is already defined: " + check);
+        }
+
+        String locationSpec = String.format(StubResolver.DOCKER_INFRASTRUCTURE_SPEC, getId()) + String.format(":(name=\"%s\")", locationName);
+
+        LocationDefinition definition = new BasicLocationDefinition(locationName, locationSpec, ImmutableMap.<String, Object>of());
+        getManagementContext().getLocationRegistry().updateDefinedLocation(definition);
+        
+        locationRegistrationId = definition.getId();
+        requestPersist();
+        
+        return definition;
+    }
+    
+    @Override
+    public void deregister() {
+        if (locationRegistrationId != null) {
+            getManagementContext().getLocationRegistry().removeDefinedLocation(locationRegistrationId);
+            locationRegistrationId = null;
+            requestPersist();
+        }
+    }
+    
     @Override
     public StubInfrastructure getOwner() {
-        return infrastructure;
+        return (StubInfrastructure) getConfig(OWNER);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolver.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolver.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolver.java
index 99fd2b1..8aaf4a8 100644
--- a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolver.java
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolver.java
@@ -26,21 +26,23 @@ import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationRegistry;
-import org.apache.brooklyn.api.location.LocationResolver.EnableableLocationResolver;
+import org.apache.brooklyn.api.location.LocationResolver;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.location.BasicLocationRegistry;
-import org.apache.brooklyn.core.location.LocationPropertiesFromBrooklynProperties;
-import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
-import org.apache.brooklyn.core.location.internal.LocationInternal;
-import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.core.objs.proxy.SpecialBrooklynObjectConstructor;
+import org.apache.brooklyn.core.objs.proxy.SpecialBrooklynObjectConstructor.Config;
 import org.apache.brooklyn.util.text.KeyValueParser;
 import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
@@ -54,17 +56,17 @@ import com.google.common.collect.Sets;
  *     <li>docker:infrastructureId:dockerHostId:(name=dockerHost-brooklyn-1234,user=docker)
  *   </ul>
  */
-public class StubResolver implements EnableableLocationResolver {
+public class StubResolver implements LocationResolver {
 
     private static final Logger LOG = LoggerFactory.getLogger(StubResolver.class);
 
-    public static final String DOCKER = "docker";
+    public static final String DOCKER = "stub";
     public static final Pattern PATTERN = Pattern.compile("("+DOCKER+"|"+DOCKER.toUpperCase()+")" + ":([a-zA-Z0-9]+)" +
             "(:([a-zA-Z0-9]+))?" + "(:\\((.*)\\))?$");
-    public static final Set<String> ACCEPTABLE_ARGS = ImmutableSet.of("name", "displayName");
+    public static final String DOCKER_INFRASTRUCTURE_SPEC = "stub:%s";
+    public static final String DOCKER_HOST_MACHINE_SPEC = "stub:%s:%s";
 
-    public static final String DOCKER_INFRASTRUCTURE_SPEC = "docker:%s";
-    public static final String DOCKER_HOST_MACHINE_SPEC = "docker:%s:%s";
+    private static final Set<String> ACCEPTABLE_ARGS = ImmutableSet.of("name");
 
     private ManagementContext managementContext;
 
@@ -79,118 +81,79 @@ public class StubResolver implements EnableableLocationResolver {
     }
 
     @Override
-    public Location newLocationFromString(Map locationFlags, String spec, LocationRegistry registry) {
-        return newLocationFromString(spec, registry, registry.getProperties(), locationFlags);
+    public boolean accepts(String spec, LocationRegistry registry) {
+        return BasicLocationRegistry.isResolverPrefixForSpec(this, spec, true);
     }
 
-    protected Location newLocationFromString(String spec, LocationRegistry registry, Map properties, Map locationFlags) {
+    @Override
+    public boolean isEnabled() {
+        // TODO Should we base enablement on whether the required location/entity exists?
+        return true;
+    }
+
+    @Override
+    public LocationSpec<? extends Location> newLocationSpecFromString(String spec, Map<?,?> locationFlags, LocationRegistry registry) {
+        Map<?,?> properties = registry.getProperties();
+
         if (LOG.isDebugEnabled()) {
             LOG.debug("Resolving location '" + spec + "' with flags " + Joiner.on(",").withKeyValueSeparator("=").join(locationFlags));
         }
-        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
 
         Matcher matcher = PATTERN.matcher(spec);
         if (!matcher.matches()) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; must specify something like docker:entityId or docker:entityId:(name=abc)");
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; must specify something like stub:entityId or stub:entityId:(name=abc)");
         }
 
+        String infrastructureLocId = matcher.group(2);
+        if (Strings.isBlank(infrastructureLocId)) {
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; infrastructure location id must be non-empty");
+        }
+        String hostLocId = matcher.group(4);
+
+        // TODO Could validate that the namePart matches the existing loc
         String argsPart = matcher.group(6);
         Map<String, String> argsMap = (argsPart != null) ? KeyValueParser.parseMap(argsPart) : Collections.<String,String>emptyMap();
-        String displayNamePart = argsMap.get("displayName");
+        @SuppressWarnings("unused")
         String namePart = argsMap.get("name");
 
         if (!ACCEPTABLE_ARGS.containsAll(argsMap.keySet())) {
             Set<String> illegalArgs = Sets.difference(argsMap.keySet(), ACCEPTABLE_ARGS);
             throw new IllegalArgumentException("Invalid location '"+spec+"'; illegal args "+illegalArgs+"; acceptable args are "+ACCEPTABLE_ARGS);
         }
-        if (argsMap.containsKey("displayName") && Strings.isEmpty(displayNamePart)) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; if displayName supplied then value must be non-empty");
-        }
-        if (argsMap.containsKey("name") && Strings.isEmpty(namePart)) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; if name supplied then value must be non-empty");
-        }
 
-        Map<String, Object> filteredProperties = new LocationPropertiesFromBrooklynProperties().getLocationProperties(DOCKER, namedLocation, properties);
-        MutableMap<String, Object> flags = MutableMap.<String, Object>builder().putAll(filteredProperties).putAll(locationFlags).build();
-
-        String infrastructureId = matcher.group(2);
-        if (Strings.isBlank(infrastructureId)) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; infrastructure entity id must be non-empty");
+        Location infrastructureLoc = managementContext.getLocationManager().getLocation(infrastructureLocId);
+        if (infrastructureLoc == null) {
+            throw new IllegalArgumentException("Unknown Clocker infrastructure location id "+infrastructureLocId+", spec "+spec);
+        } else if (!(infrastructureLoc instanceof StubInfrastructureLocation)) {
+            throw new IllegalArgumentException("Invalid location id for Clocker infrastructure, spec "+spec+"; instead matches "+infrastructureLoc);
         }
-        String dockerHostId = matcher.group(4);
 
-        // Build the display name
-        StringBuilder name = new StringBuilder();
-        if (displayNamePart != null) {
-            name.append(displayNamePart);
-        } else {
-            name.append("Docker ");
-            if (dockerHostId == null) {
-                name.append("Infrastructure ").append(infrastructureId);
-            } else {
-                name.append("Host ").append(dockerHostId);
+        if (hostLocId != null) {
+            Location hostLoc = managementContext.getLocationManager().getLocation(hostLocId);
+            if (hostLoc == null) {
+                throw new IllegalArgumentException("Unknown Clocker host location id "+hostLocId+", spec "+spec);
+            } else if (!(hostLoc instanceof StubHostLocation)) {
+                throw new IllegalArgumentException("Invalid location id for Clocker host, spec "+spec+"; instead matches "+hostLoc);
             }
-        }
-        final String displayName =  name.toString();
-
-        // Build the location name
-        name = new StringBuilder();
-        if (namePart != null) {
-            name.append(namePart);
+            
+            return LocationSpec.create(StubHostLocation.class)
+                    .configure(StubLocationConstructor.LOCATION, hostLoc)
+                    .configure(Config.SPECIAL_CONSTRUCTOR, StubLocationConstructor.class);
         } else {
-            name.append("docker-");
-            name.append(infrastructureId);
-            if (dockerHostId != null) {
-                name.append("-").append(dockerHostId);
-            }
-        }
-        final String locationName =  name.toString();
-        StubInfrastructure infrastructure = (StubInfrastructure) managementContext.getEntityManager().getEntity(infrastructureId);
-        Iterable<Location> managedLocations = managementContext.getLocationManager().getLocations();
-
-        if (dockerHostId == null) {
-            for (Location location : managedLocations) {
-                if (location instanceof StubInfrastructureLocation) {
-                    if (((StubInfrastructureLocation) location).getOwner().getId().equals(infrastructureId)) {
-                        return location;
-                    }
-                }
-            }
-            LocationSpec<StubInfrastructureLocation> locationSpec = LocationSpec.create(StubInfrastructureLocation.class)
-                    .configure(flags)
-                    .configure(DynamicLocation.OWNER, infrastructure)
-                    .configure(LocationInternal.NAMED_SPEC_NAME, locationName)
-                    .displayName(displayName);
-            return managementContext.getLocationManager().createLocation(locationSpec);
-        } else {
-            StubHost dockerHost = (StubHost) managementContext.getEntityManager().getEntity(dockerHostId);
-            for (Location location : managedLocations) {
-                if (location instanceof StubHostLocation) {
-                    if (((StubHostLocation) location).getOwner().getId().equals(dockerHostId)) {
-                        return location;
-                    }
-                }
-            }
-
-            LocationSpec<StubHostLocation> locationSpec = LocationSpec.create(StubHostLocation.class)
-                    .parent(infrastructure.getDynamicLocation())
-                    .configure(flags)
-                    .configure(DynamicLocation.OWNER, dockerHost)
-                    .configure(LocationInternal.NAMED_SPEC_NAME, locationName)
-                    .displayName(displayName);
-            return managementContext.getLocationManager().createLocation(locationSpec);
+            return LocationSpec.create(StubInfrastructureLocation.class)
+                    .configure(StubLocationConstructor.LOCATION, infrastructureLoc)
+                    .configure(Config.SPECIAL_CONSTRUCTOR, StubLocationConstructor.class);
         }
     }
 
-    @Override
-    public boolean accepts(String spec, LocationRegistry registry) {
-        return BasicLocationRegistry.isResolverPrefixForSpec(this, spec, true);
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return true;
-//        return Iterables.tryFind(managementContext.getEntityManager().getEntities(), Predicates.instanceOf(StubInfrastructure.class)).isPresent();
+    @Beta
+    public static class StubLocationConstructor implements SpecialBrooklynObjectConstructor {
+        public static ConfigKey<Location> LOCATION = ConfigKeys.newConfigKey(Location.class, "stubresolver.location");
+        
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> T create(ManagementContext mgmt, Class<T> type, AbstractBrooklynObjectSpec<?, ?> spec) {
+            return (T) checkNotNull(spec.getConfig().get(LOCATION), LOCATION.getName());
+        }
     }
-
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/453183f1/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolverTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolverTest.java b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolverTest.java
new file mode 100644
index 0000000..6ffb387
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/core/location/dynamic/clocker/StubResolverTest.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.location.dynamic.clocker;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
+import org.apache.brooklyn.core.location.BasicLocationRegistry;
+import org.apache.brooklyn.core.location.dynamic.LocationOwner;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class StubResolverTest extends BrooklynAppUnitTestSupport {
+
+    private StubInfrastructure infrastructure;
+    private Location localhostLoc;
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        
+        StubResolver stubResolver = new StubResolver();
+        ((BasicLocationRegistry)mgmt.getLocationRegistry()).registerResolver(stubResolver);
+
+        infrastructure = app.createAndManageChild(EntitySpec.create(StubInfrastructure.class)
+                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution())
+                .configure(StubInfrastructure.DOCKER_HOST_CLUSTER_MIN_SIZE, 1));
+        localhostLoc = mgmt.getLocationManager()
+                .createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
+        app.start(ImmutableList.of(localhostLoc));
+    }
+
+    @Test
+    public void testResolveInfrastructure() {
+        String spec = "stub:" + infrastructure.getDynamicLocation().getId();
+        StubInfrastructureLocation loc = (StubInfrastructureLocation) mgmt.getLocationRegistry().resolve(spec);
+        assertEquals(loc, infrastructure.getDynamicLocation());
+        
+        String spec2 = infrastructure.sensors().get(LocationOwner.LOCATION_SPEC);
+        StubInfrastructureLocation loc2 = (StubInfrastructureLocation) mgmt.getLocationRegistry().resolve(spec2);
+        assertEquals(loc2, infrastructure.getDynamicLocation());
+    }
+
+    @Test
+    public void testResolveHost() {
+        StubHost host = (StubHost) Iterables.getOnlyElement(infrastructure.getStubHostCluster().getMembers());
+        
+        String spec = "stub:" + infrastructure.getDynamicLocation().getId() + ":" + host.getDynamicLocation().getId();
+        StubHostLocation loc = (StubHostLocation) mgmt.getLocationRegistry().resolve(spec);
+        assertEquals(loc, host.getDynamicLocation());
+        
+        
+        String spec2 = host.sensors().get(LocationOwner.LOCATION_SPEC);
+        StubHostLocation loc2 = (StubHostLocation) mgmt.getLocationRegistry().resolve(spec2);
+        assertEquals(loc2, host.getDynamicLocation());
+    }
+}


[03/10] brooklyn-server git commit: ServerPool: creates/registers single ServerPoolLocation

Posted by gr...@apache.org.
ServerPool: creates/registers single ServerPoolLocation


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

Branch: refs/heads/master
Commit: 9f1a120812895a604f42df29f8e16ec4d4a1ed30
Parents: 453183f
Author: Aled Sage <al...@gmail.com>
Authored: Thu Mar 17 12:59:24 2016 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Sat Mar 19 22:03:55 2016 +0000

----------------------------------------------------------------------
 .../entity/machine/pool/ServerPool.java         |  5 +-
 .../entity/machine/pool/ServerPoolImpl.java     | 53 +++++++++-------
 .../entity/machine/pool/ServerPoolLocation.java | 53 ++++++++++++++--
 .../pool/ServerPoolLocationResolver.java        | 64 +++++++++----------
 .../machine/pool/AbstractServerPoolTest.java    | 23 ++++---
 .../pool/ServerPoolLocationResolverTest.java    | 65 ++++++--------------
 .../entity/machine/pool/ServerPoolTest.java     |  8 +--
 7 files changed, 148 insertions(+), 123 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f1a1208/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPool.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPool.java b/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPool.java
index 668a89e..4e42778 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPool.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPool.java
@@ -21,8 +21,6 @@ package org.apache.brooklyn.entity.machine.pool;
 import java.util.Collection;
 import java.util.Map;
 
-import com.google.common.annotations.Beta;
-
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
@@ -41,6 +39,8 @@ import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.entity.group.DynamicCluster;
 import org.apache.brooklyn.entity.machine.MachineEntity;
 
+import com.google.common.annotations.Beta;
+
 /**
  * A preallocated server pool is an entity that other applications can deploy to.
  * Behaving as a cluster, the machines it creates for its members are reused.
@@ -70,6 +70,7 @@ import org.apache.brooklyn.entity.machine.MachineEntity;
 @ImplementedBy(ServerPoolImpl.class)
 @Beta
 public interface ServerPool extends DynamicCluster, LocationOwner<ServerPoolLocation, ServerPool> {
+    ConfigKey<String> LOCATION_NAME_PREFIX = ConfigKeys.newConfigKeyWithDefault(LocationOwner.LOCATION_NAME_PREFIX, "server-pool");
 
     ConfigKey<Integer> INITIAL_SIZE = ConfigKeys.newConfigKeyWithDefault(DynamicCluster.INITIAL_SIZE, 2);
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f1a1208/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolImpl.java b/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolImpl.java
index 513ee29..e7ad6db 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolImpl.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolImpl.java
@@ -25,6 +25,8 @@ import java.util.Map;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationDefinition;
+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.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.mgmt.LocationManager;
@@ -38,19 +40,19 @@ import org.apache.brooklyn.core.entity.Attributes;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.apache.brooklyn.core.entity.trait.Startable;
-import org.apache.brooklyn.core.location.BasicLocationDefinition;
 import org.apache.brooklyn.core.location.Machines;
 import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
 import org.apache.brooklyn.core.location.internal.LocationInternal;
 import org.apache.brooklyn.core.mgmt.internal.LocalLocationManager;
+import org.apache.brooklyn.core.location.dynamic.LocationOwner;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.entity.group.AbstractMembershipTrackingPolicy;
 import org.apache.brooklyn.entity.group.DynamicClusterImpl;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.task.DynamicTasks;
 import org.apache.brooklyn.util.guava.Maybe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
@@ -64,6 +66,19 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.reflect.TypeToken;
 
+/**
+ * The normal usage pattern is to instantiate this entity to create your cluster, which will
+ * automatically create a {@link ServerPoolLocation}. The location can be looked up from the 
+ * {@link LocationRegistry} (e.g. {@code mgmt.getLocationRegistry().resolve("my-pool-name")}),
+ * which uses the {@link ServerPoolLocationResolver}.
+ * 
+ * Each location delegates to the associated {@link ServerPool} to claim machines (which are
+ * provisioned as required), and to release machines back to the pool.
+ * 
+ * This differs from Clocker's use of {@link DynamicLocation} in that the ServerPool has <em>multiple</em>
+ * locations. This makes the use of {@link LocationOwner} a little confusing 
+ * (e.g. {@link LocationOwner#DYNAMIC_LOCATION} is not set).
+ */
 public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
 
     private static final Logger LOG = LoggerFactory.getLogger(ServerPoolImpl.class);
@@ -96,9 +111,6 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
     public static final AttributeSensor<Map<MachineLocation, Entity>> MACHINE_ENTITY = Sensors.newSensor(new TypeToken<Map<MachineLocation, Entity>>() {},
             "pool.machineEntityMap", "A mapping of machine locations and their entities");
 
-    public static final AttributeSensor<LocationDefinition> DYNAMIC_LOCATION_DEFINITION = Sensors.newSensor(LocationDefinition.class,
-            "pool.locationDefinition", "The location definition used to create the pool's dynamic location");
-
     public static final ConfigKey<Boolean> REMOVABLE = ConfigKeys.newBooleanConfigKey(
             "pool.member.removable", "Whether a pool member is removable from the cluster. Used to denote additional " +
                     "existing machines that were manually added to the pool", true);
@@ -171,16 +183,18 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
             locationName = Joiner.on("-").skipNulls().join(prefix, getId(), suffix);
         }
 
-        String locationSpec = String.format(ServerPoolLocationResolver.POOL_SPEC, getId()) + String.format(":(name=\"%s\")", locationName);
-        LocationDefinition definition = new BasicLocationDefinition(locationName, locationSpec, flags);
-        getManagementContext().getLocationRegistry().updateDefinedLocation(definition);
-        Location location = getManagementContext().getLocationManager().createLocation( getManagementContext().getLocationRegistry().getLocationSpec(definition).get() );
-        LOG.info("Resolved and registered dynamic location {}: {}", locationName, location);
+        ServerPoolLocation location = getManagementContext().getLocationManager().createLocation(LocationSpec.create(ServerPoolLocation.class)
+                .displayName("Server Pool(" + getId() + ")")
+                .configure(flags)
+                .configure("owner", getProxy())
+                .configure("locationName", locationName));
+        
+        LocationDefinition definition = location.register();
+        LOG.info("Resolved and registered dynamic location {} for server pool {}: {}", new Object[] {locationName, this, location});
 
-        sensors().set(LOCATION_SPEC, locationSpec);
+        sensors().set(LOCATION_SPEC, definition.getSpec());
+        sensors().set(LOCATION_NAME, locationName);
         sensors().set(DYNAMIC_LOCATION, location);
-        sensors().set(LOCATION_NAME, location.getId());
-        sensors().set(DYNAMIC_LOCATION_DEFINITION, definition);
 
         return (ServerPoolLocation) location;
     }
@@ -189,20 +203,15 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
     public void deleteLocation() {
         LocationManager mgr = getManagementContext().getLocationManager();
         ServerPoolLocation location = getDynamicLocation();
-        if (mgr.isManaged(location)) {
+        if (location != null && mgr.isManaged(location)) {
             LOG.debug("{} deleting and unmanaging location {}", this, location);
+            location.deregister();
             mgr.unmanage(location);
         }
-        // definition will only be null if deleteLocation has already been called, e.g. by two calls to stop().
-        LocationDefinition definition = getAttribute(DYNAMIC_LOCATION_DEFINITION);
-        if (definition != null) {
-            LOG.debug("{} unregistering dynamic location {}", this, definition);
-            getManagementContext().getLocationRegistry().removeDefinedLocation(definition.getId());
-        }
+        
         sensors().set(LOCATION_SPEC, null);
         sensors().set(DYNAMIC_LOCATION, null);
         sensors().set(LOCATION_NAME, null);
-        sensors().set(DYNAMIC_LOCATION_DEFINITION, null);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f1a1208/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocation.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocation.java b/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocation.java
index bc1e2dd..3bb985a 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocation.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocation.java
@@ -23,19 +23,21 @@ import static com.google.common.base.Preconditions.checkNotNull;
 import java.util.Collection;
 import java.util.Map;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.Maps;
-
+import org.apache.brooklyn.api.location.LocationDefinition;
 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.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.location.BasicLocationDefinition;
 import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 
 public class ServerPoolLocation extends AbstractLocation implements MachineProvisioningLocation<MachineLocation>,
         DynamicLocation<ServerPool, ServerPoolLocation> {
@@ -46,6 +48,12 @@ public class ServerPoolLocation extends AbstractLocation implements MachineProvi
     public static final ConfigKey<ServerPool> OWNER = ConfigKeys.newConfigKey(
             ServerPool.class, "pool.location.owner");
 
+    @SetFromFlag("locationName")
+    public static final ConfigKey<String> LOCATION_NAME = ConfigKeys.newStringConfigKey("pool.location.name");
+
+    @SetFromFlag("locationRegistrationId")
+    private String locationRegistrationId;
+    
     @Override
     public void init() {
         LOG.debug("Initialising. Owner is: {}", checkNotNull(getConfig(OWNER), OWNER.getName()));
@@ -53,6 +61,41 @@ public class ServerPoolLocation extends AbstractLocation implements MachineProvi
     }
 
     @Override
+    public void rebind() {
+        super.rebind();
+
+        if (config().get(LOCATION_NAME) != null) {
+            register();
+        }
+    }
+
+    @Override
+    public LocationDefinition register() {
+        String locationName = checkNotNull(config().get(LOCATION_NAME), "config %s", LOCATION_NAME.getName());
+        LocationDefinition check = getManagementContext().getLocationRegistry().getDefinedLocationByName(locationName);
+        if (check != null) {
+            throw new IllegalStateException("Location " + locationName + " is already defined: " + check);
+        }
+
+        String locationSpec = String.format(ServerPoolLocationResolver.POOL_SPEC, getId()) + String.format(":(name=\"%s\")", locationName);
+
+        LocationDefinition definition = new BasicLocationDefinition(locationName, locationSpec, ImmutableMap.<String, Object>of());
+        getManagementContext().getLocationRegistry().updateDefinedLocation(definition);
+        
+        locationRegistrationId = definition.getId();
+        requestPersist();
+        
+        return definition;
+    }
+    
+    @Override
+    public void deregister() {
+        if (locationRegistrationId != null) {
+            getManagementContext().getLocationRegistry().removeDefinedLocation(locationRegistrationId);
+        }
+    }
+    
+    @Override
     public ServerPool getOwner() {
         return getConfig(OWNER);
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f1a1208/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolver.java b/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolver.java
index 54f232b..6cc9102 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolver.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolver.java
@@ -26,22 +26,24 @@ import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
+import org.apache.brooklyn.api.location.Location;
 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.mgmt.ManagementContext;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.location.BasicLocationRegistry;
 import org.apache.brooklyn.core.location.LocationConfigUtils;
-import org.apache.brooklyn.core.location.LocationPropertiesFromBrooklynProperties;
-import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
-import org.apache.brooklyn.core.location.internal.LocationInternal;
-import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.core.objs.proxy.SpecialBrooklynObjectConstructor;
+import org.apache.brooklyn.core.objs.proxy.SpecialBrooklynObjectConstructor.Config;
 import org.apache.brooklyn.util.text.KeyValueParser;
 import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
@@ -55,7 +57,7 @@ public class ServerPoolLocationResolver implements LocationResolver {
             ":([a-zA-Z0-9]+)" + // pool Id
             "(:\\((.*)\\))?$"); // arguments, e.g. displayName
 
-    private static final Set<String> ACCEPTABLE_ARGS = ImmutableSet.of("name", "displayName");
+    private static final Set<String> ACCEPTABLE_ARGS = ImmutableSet.of("name");
 
     private ManagementContext managementContext;
 
@@ -85,7 +87,6 @@ public class ServerPoolLocationResolver implements LocationResolver {
         if (LOG.isDebugEnabled()) {
             LOG.debug("Resolving location '" + spec + "' with flags " + Joiner.on(",").withKeyValueSeparator("=").join(locationFlags));
         }
-        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
 
         Matcher matcher = PATTERN.matcher(spec);
         if (!matcher.matches()) {
@@ -94,43 +95,42 @@ public class ServerPoolLocationResolver implements LocationResolver {
             throw new IllegalArgumentException(m);
         }
 
+        // TODO Could validate that the namePart matches the existing poolLoc
         String argsPart = matcher.group(4);
         Map<String, String> argsMap = (argsPart != null) ? KeyValueParser.parseMap(argsPart) : Collections.<String,String>emptyMap();
-        String displayNamePart = argsMap.get("displayName");
+        @SuppressWarnings("unused")
         String namePart = argsMap.get("name");
 
         if (!ACCEPTABLE_ARGS.containsAll(argsMap.keySet())) {
             Set<String> illegalArgs = Sets.difference(argsMap.keySet(), ACCEPTABLE_ARGS);
             throw new IllegalArgumentException("Invalid location '"+spec+"'; illegal args "+illegalArgs+"; acceptable args are "+ACCEPTABLE_ARGS);
         }
-        if (argsMap.containsKey("displayName") && Strings.isEmpty(displayNamePart)) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; if displayName supplied then value must be non-empty");
-        }
-        if (argsMap.containsKey("name") && Strings.isEmpty(namePart)) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; if name supplied then value must be non-empty");
-        }
 
-        Map<String, Object> filteredProperties = new LocationPropertiesFromBrooklynProperties()
-                .getLocationProperties(PREFIX, namedLocation, registry.getProperties());
-        MutableMap<String, Object> flags = MutableMap.<String, Object>builder()
-                .putAll(filteredProperties)
-                .putAll(locationFlags)
-                .build();
-
-        String poolId = matcher.group(2);
-        if (Strings.isBlank(poolId)) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; pool's entity id must be non-empty");
+        String poolLocId = matcher.group(2);
+        if (Strings.isBlank(poolLocId)) {
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; pool's location id must be non-empty");
         }
 
-        final String displayName = displayNamePart != null ? displayNamePart : "Server Pool " + poolId;
-        final String locationName = namePart != null ? namePart : "serverpool-" + poolId;
+        Location poolLoc = managementContext.getLocationManager().getLocation(poolLocId);
+        if (poolLoc == null) {
+            throw new IllegalArgumentException("Unknown ServerPool location id "+poolLocId+", spec "+spec);
+        } else if (!(poolLoc instanceof ServerPoolLocation)) {
+            throw new IllegalArgumentException("Invalid location id for ServerPool, spec "+spec+"; instead matches "+poolLoc);
+        }
 
-        Entity pool = managementContext.getEntityManager().getEntity(poolId);
         return LocationSpec.create(ServerPoolLocation.class)
-                .configure(flags)
-                .configure(DynamicLocation.OWNER, pool)
-                .configure(LocationInternal.NAMED_SPEC_NAME, locationName)
-                .displayName(displayName);
+                .configure(LocationConstructor.LOCATION, poolLoc)
+                .configure(Config.SPECIAL_CONSTRUCTOR, LocationConstructor.class);
+    }
+    
+    @Beta
+    public static class LocationConstructor implements SpecialBrooklynObjectConstructor {
+        public static ConfigKey<Location> LOCATION = ConfigKeys.newConfigKey(Location.class, "serverpoolresolver.location");
+        
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> T create(ManagementContext mgmt, Class<T> type, AbstractBrooklynObjectSpec<?, ?> spec) {
+            return (T) checkNotNull(spec.getConfig().get(LOCATION), LOCATION.getName());
+        }
     }
-
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f1a1208/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/AbstractServerPoolTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/AbstractServerPoolTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/AbstractServerPoolTest.java
index 4a13d0d..0da5a2e 100644
--- a/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/AbstractServerPoolTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/AbstractServerPoolTest.java
@@ -30,12 +30,11 @@ import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.core.entity.Attributes;
 import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
 import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
+import org.apache.brooklyn.core.entity.EntityAsserts;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.core.test.entity.TestApplication;
 import org.apache.brooklyn.entity.machine.pool.ServerPool;
 import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
-import org.apache.brooklyn.test.EntityTestUtils;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -62,15 +61,14 @@ public abstract class AbstractServerPoolTest {
         createdApps.clear();
         mgmt = createManagementContext();
         location = createLocation();
-        EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class)
-                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution());
-        poolApp = ApplicationBuilder.newManagedApp(appSpec, mgmt);
+        poolApp = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution()));
 
         pool = poolApp.createAndManageChild(EntitySpec.create(ServerPool.class)
                 .configure(ServerPool.INITIAL_SIZE, getInitialPoolSize())
                 .configure(ServerPool.MEMBER_SPEC, EntitySpec.create(EmptySoftwareProcess.class)));
         poolApp.start(ImmutableList.of(location));
-        EntityTestUtils.assertAttributeEqualsEventually(pool, Attributes.SERVICE_UP, true);
+        EntityAsserts.assertAttributeEqualsEventually(pool, Attributes.SERVICE_UP, true);
         assertAvailableCountEventuallyEquals(getInitialPoolSize());
     }
 
@@ -120,7 +118,7 @@ public abstract class AbstractServerPoolTest {
     }
 
     protected void assertAvailableCountEventuallyEquals(ServerPool pool, int count) {
-        EntityTestUtils.assertAttributeEqualsEventually(pool, ServerPool.AVAILABLE_COUNT, count);
+        EntityAsserts.assertAttributeEqualsEventually(pool, ServerPool.AVAILABLE_COUNT, count);
     }
 
     protected void assertClaimedCountEventuallyEquals(int count) {
@@ -128,16 +126,17 @@ public abstract class AbstractServerPoolTest {
     }
 
     protected void assertClaimedCountEventuallyEquals(ServerPool pool, Integer count) {
-        EntityTestUtils.assertAttributeEqualsEventually(pool, ServerPool.CLAIMED_COUNT, count);
+        EntityAsserts.assertAttributeEqualsEventually(pool, ServerPool.CLAIMED_COUNT, count);
     }
 
     protected TestApplication createAppWithChildren(int numChildren) {
         if (numChildren < 0) fail("Invalid number of children for app: " + numChildren);
-        EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class)
-                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution());
-        TestApplication app = ApplicationBuilder.newManagedApp(appSpec, mgmt);
+
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution()));
+
         while (numChildren-- > 0) {
-            app.createAndManageChild(EntitySpec.create(EmptySoftwareProcess.class));
+            app.addChild(EntitySpec.create(EmptySoftwareProcess.class));
         }
         createdApps.add(app);
         return app;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f1a1208/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolverTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolverTest.java
index cd71497..482ba42 100644
--- a/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolverTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolLocationResolverTest.java
@@ -20,71 +20,44 @@ package org.apache.brooklyn.entity.machine.pool;
 
 import static org.testng.Assert.assertEquals;
 
-import java.util.Map;
-
-import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
-import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
-import org.apache.brooklyn.core.internal.BrooklynProperties;
-import org.apache.brooklyn.core.location.dynamic.DynamicLocation;
-import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
-import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
-import org.apache.brooklyn.core.test.entity.TestApplication;
-import org.apache.brooklyn.entity.machine.pool.ServerPool;
-import org.apache.brooklyn.entity.machine.pool.ServerPoolLocation;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
+import org.apache.brooklyn.core.location.dynamic.LocationOwner;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
 import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
-import org.apache.brooklyn.util.collections.MutableMap;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
 
-import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
-
-public class ServerPoolLocationResolverTest {
+public class ServerPoolLocationResolverTest extends BrooklynAppUnitTestSupport {
 
-    private LocalManagementContext managementContext;
-    private Entity locationOwner;
+    private ServerPool serverPool;
 
     @BeforeMethod(alwaysRun=true)
+    @Override
     public void setUp() throws Exception {
-        managementContext = new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty());
-        TestApplication t = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext);
-        locationOwner = t.createAndManageChild(EntitySpec.create(ServerPool.class)
+        super.setUp();
+        serverPool = app.createAndManageChild(EntitySpec.create(ServerPool.class)
+                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution())
                 .configure(ServerPool.INITIAL_SIZE, 0)
                 .configure(ServerPool.MEMBER_SPEC, EntitySpec.create(EmptySoftwareProcess.class)));
-        Location poolLocation = managementContext.getLocationManager()
+        Location localhostLoc = mgmt.getLocationManager()
                 .createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
-        t.start(ImmutableList.of(poolLocation));
-    }
-
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() throws Exception {
-        if (managementContext != null) Entities.destroyAll(managementContext);
+        app.start(ImmutableList.of(localhostLoc));
     }
 
     @Test
     public void testResolve() {
-        ServerPoolLocation location = resolve("pool:" + locationOwner.getId());
-        assertEquals(location.getOwner().getId(), locationOwner.getId());
+        String spec = "pool:" + serverPool.getDynamicLocation().getId();
+        ServerPoolLocation loc = (ServerPoolLocation) mgmt.getLocationRegistry().getLocationManaged(spec);
+        assertEquals(loc, serverPool.getDynamicLocation());
+        
+        String spec2 = serverPool.sensors().get(LocationOwner.LOCATION_SPEC);
+        ServerPoolLocation loc2 = (ServerPoolLocation) mgmt.getLocationRegistry().getLocationManaged(spec2);
+        assertEquals(loc2, serverPool.getDynamicLocation());
     }
-
-    @Test
-    public void testSetsDisplayName() {
-        ServerPoolLocation location = resolve("pool:" + locationOwner.getId() + ":(displayName=xyz)");
-        assertEquals(location.getDisplayName(), "xyz");
-    }
-
-    private ServerPoolLocation resolve(String val) {
-        Map<String, Object> flags = MutableMap.<String, Object>of(DynamicLocation.OWNER.getName(), locationOwner);
-        Location l = managementContext.getLocationRegistry().getLocationManaged(val, flags);
-        Assert.assertNotNull(l);
-        return (ServerPoolLocation) l;
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f1a1208/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolTest.java
index 4a24d62..3dacf7f 100644
--- a/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/pool/ServerPoolTest.java
@@ -28,10 +28,10 @@ import java.util.Iterator;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.EntityAsserts;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.apache.brooklyn.core.test.entity.TestApplication;
 import org.apache.brooklyn.entity.machine.pool.ServerPoolImpl;
-import org.apache.brooklyn.test.EntityTestUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.Test;
@@ -59,7 +59,7 @@ public class ServerPoolTest extends AbstractServerPoolTest {
     public void testFailureWhenNotEnoughServersAvailable() {
         TestApplication app = createAppWithChildren(getInitialPoolSize() + 1);
         assertNoMachinesAvailableForApp(app);
-        EntityTestUtils.assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE);
+        EntityAsserts.assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE);
     }
 
     @Test
@@ -68,7 +68,7 @@ public class ServerPoolTest extends AbstractServerPoolTest {
         TestApplication app2 = createAppWithChildren(1);
 
         app.start(ImmutableList.of(pool.getDynamicLocation()));
-        EntityTestUtils.assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, true);
+        EntityAsserts.assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, true);
         assertAvailableCountEventuallyEquals(0);
         assertNoMachinesAvailableForApp(app2);
 
@@ -77,7 +77,7 @@ public class ServerPoolTest extends AbstractServerPoolTest {
         assertAvailableCountEventuallyEquals(getInitialPoolSize());
 
         app2.start(ImmutableList.of(pool.getDynamicLocation()));
-        EntityTestUtils.assertAttributeEqualsEventually(app2, Attributes.SERVICE_UP, true);
+        EntityAsserts.assertAttributeEqualsEventually(app2, Attributes.SERVICE_UP, true);
         
         assertAvailableCountEventuallyEquals(getInitialPoolSize() - 1);
         assertClaimedCountEventuallyEquals(1);


[08/10] brooklyn-server git commit: Fix XML serialiser for SpecConverter

Posted by gr...@apache.org.
Fix XML serialiser for SpecConverter

It was not thread-safe. The XmlMementoSerializer uses a single
instance of SpecConverter. If multiple threads tried to deserialise
different objects at the same time, they’d overwrite the “instance”
value and cause serious problems deserialising.

Now use thread-local storage for the “instance” cache.

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

Branch: refs/heads/master
Commit: b5dae5b7ad5b4d0919e00da1f02b5a54a1e4d3e1
Parents: 89b3a66
Author: Aled Sage <al...@gmail.com>
Authored: Tue Mar 22 12:47:36 2016 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Tue Mar 22 12:47:36 2016 +0000

----------------------------------------------------------------------
 .../core/mgmt/persist/XmlMementoSerializer.java       | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b5dae5b7/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
index 588f8fd..0df2340 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
@@ -393,6 +393,12 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
         }
     }
 
+    // Would prefer this as a field of SpecConverter, but can't do that because the class is not static.
+    // Must use thread-local storage because a single instance of the SpecConverter is used by this 
+    // XmlMementoSerializer. In BrooklynMementoPersisterToObjectStore.visitMemento, it uses a thread-pool
+    // for concurrently deserializing multiple objects.
+    private static final ThreadLocal<Object> SpecConverterLocalInstance = new ThreadLocal<Object>();
+
     /** When reading/writing specs, it checks whether there is a catalog item id set and uses it to load */
     public class SpecConverter extends ReflectionConverter {
         SpecConverter() {
@@ -454,25 +460,25 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
                 result.catalogItemId(catalogItemId);
                 return result;
             } finally {
-                instance = null;
+                SpecConverterLocalInstance.remove();
                 if (customLoaderSet) {
                     popXstreamCustomClassLoader();
                 }
             }
         }
 
-        Object instance;
-        
         @Override
         protected Object instantiateNewInstance(HierarchicalStreamReader reader, UnmarshallingContext context) {
             // the super calls getAttribute which requires that we have not yet done moveDown,
             // so we do this earlier and cache it for when we call super.unmarshal
+            Object instance = SpecConverterLocalInstance.get();
             if (instance==null)
                 throw new IllegalStateException("Instance should be created and cached");
             return instance;
         }
         protected void instantiateNewInstanceSettingCache(HierarchicalStreamReader reader, UnmarshallingContext context) {
-            instance = super.instantiateNewInstance(reader, context);
+            Object instance = super.instantiateNewInstance(reader, context);
+            SpecConverterLocalInstance.set(instance);
         }
     }
     


[05/10] brooklyn-server git commit: BROOKLYN-242: fix race in BasicTest.get()

Posted by gr...@apache.org.
BROOKLYN-242: fix race in BasicTest.get()

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

Branch: refs/heads/master
Commit: 3df2b8b4c131eedd889a319a8ba1f03cfe32e547
Parents: 0b9682b
Author: Aled Sage <al...@gmail.com>
Authored: Tue Mar 22 12:44:11 2016 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Tue Mar 22 12:44:11 2016 +0000

----------------------------------------------------------------------
 .../brooklyn/util/core/task/BasicTask.java      | 11 +++++++-
 .../core/effector/EffectorBasicTest.java        | 29 ++++++++++++++++++++
 2 files changed, 39 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3df2b8b4/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java b/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java
index efd3001..410ba12 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java
@@ -451,7 +451,16 @@ public class BasicTask<T> implements TaskInternal<T> {
         }
         Long remaining = end==null ? null : end -  System.currentTimeMillis();
         if (isDone()) {
-            return internalFuture.get(1, TimeUnit.MILLISECONDS);
+            // Don't just call internalFuture.get(1ms) - see comment in isDone() about setting of endTimeUtc,
+            // and see BROOKLYN-242.
+            if (internalFuture == null) {
+                assert cancelled: "task="+this+"; endTimeUtc="+endTimeUtc+"; cancelled="+cancelled+"; isDone=true; null internal future";
+                throw new CancellationException();
+            } else if (remaining == null) {
+                return internalFuture.get();
+            } else {
+                return internalFuture.get(Math.max(remaining, 1000), TimeUnit.MILLISECONDS);
+            }
         } else if (remaining == null) {
             return internalFuture.get();
         } else if (remaining > 0) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3df2b8b4/core/src/test/java/org/apache/brooklyn/core/effector/EffectorBasicTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/EffectorBasicTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/EffectorBasicTest.java
index 05b3a10..8af533f 100644
--- a/core/src/test/java/org/apache/brooklyn/core/effector/EffectorBasicTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/effector/EffectorBasicTest.java
@@ -18,6 +18,8 @@
  */
 package org.apache.brooklyn.core.effector;
 
+import static org.testng.Assert.assertEquals;
+
 import java.util.List;
 import java.util.concurrent.Callable;
 
@@ -25,6 +27,7 @@ import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.mgmt.HasTaskChildren;
 import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.StartableApplication;
 import org.apache.brooklyn.core.entity.trait.FailingEntity;
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.location.SimulatedLocation;
@@ -36,11 +39,13 @@ import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.time.Duration;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 
 public class EffectorBasicTest extends BrooklynAppUnitTestSupport {
 
@@ -92,6 +97,30 @@ public class EffectorBasicTest extends BrooklynAppUnitTestSupport {
         Assert.assertTrue(starting.getTags().contains(ManagementContextInternal.EFFECTOR_TAG));
     }
 
+    @Test
+    public void testInvokeEffectorListWithEmpty() throws Exception{
+        Entities.invokeEffectorList(app, ImmutableList.<StartableApplication>of(), Startable.STOP).get(Duration.THIRTY_SECONDS); 
+    }
+
+    @Test
+    public void testInvokeEffectorList() throws Exception{
+        List<TestEntity> entities = Lists.newArrayList();
+        for (int i = 0; i < 10; i++) {
+            entities.add(app.addChild(EntitySpec.create(TestEntity.class)));
+        }
+        Entities.invokeEffectorList(app, entities, Startable.STOP).get(Duration.THIRTY_SECONDS);
+        for (TestEntity entity : entities) {
+            assertEquals(entity.getCallHistory(), ImmutableList.of("stop"));
+        }
+    }
+
+    @Test(expectedExceptions=IllegalStateException.class, expectedExceptionsMessageRegExp=".*no longer managed.*")
+    public void testInvokeEffectorListWithEmptyUsingUnmanagedContext() throws Exception{
+        TestEntity entity = app.addChild(EntitySpec.create(TestEntity.class));
+        Entities.unmanage(entity);
+        Entities.invokeEffectorList(entity, ImmutableList.<StartableApplication>of(), Startable.STOP).get(Duration.THIRTY_SECONDS);
+    }
+
     // check various failure situations
     
     private FailingEntity createFailingEntity() {


[10/10] brooklyn-server git commit: This closes #59

Posted by gr...@apache.org.
This closes #59

* github/pr/59:
  Clocker-testing: fix where we delete the location
  Fix XML serialiser for SpecConverter
  LocationFactor’s “special constructor” for pre-existing loc
  Fix log.warn when calling Location.unmanage
  BROOKLYN-242: fix race in BasicTest.get()
  NotUpDiagnostics: don’t collect if starting && !up
  ServerPool: creates/registers single ServerPoolLocation
  Clocker pattern: change how LocationRegistry used
  Test the Clocker pattern (of LocationOwner and DynamicLocation)


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

Branch: refs/heads/master
Commit: b8211ed17c354dba49ad33d7bf6ee5db675ff6a9
Parents: 1e5f3b6 b59fda7
Author: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Authored: Tue Mar 22 17:17:25 2016 +0000
Committer: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Committed: Tue Mar 22 17:17:25 2016 +0000

----------------------------------------------------------------------
 .../core/location/AbstractLocation.java         |   4 +-
 .../core/location/dynamic/DynamicLocation.java  |  12 +
 .../core/location/dynamic/LocationOwner.java    |   4 -
 .../core/mgmt/persist/XmlMementoSerializer.java |  14 +-
 .../objs/proxy/InternalLocationFactory.java     |  13 ++
 .../brooklyn/util/core/task/BasicTask.java      |  11 +-
 .../core/effector/EffectorBasicTest.java        |  29 +++
 .../entity/machine/pool/ServerPool.java         |   5 +-
 .../entity/machine/pool/ServerPoolImpl.java     |  53 +++--
 .../entity/machine/pool/ServerPoolLocation.java |  53 ++++-
 .../pool/ServerPoolLocationResolver.java        |  64 +++---
 ...ClockerDynamicLocationPatternRebindTest.java | 136 +++++++++++
 .../ClockerDynamicLocationPatternTest.java      | 110 +++++++++
 .../dynamic/clocker/StubAttributes.java         |  34 +++
 .../location/dynamic/clocker/StubContainer.java |  33 +++
 .../dynamic/clocker/StubContainerImpl.java      | 131 +++++++++++
 .../dynamic/clocker/StubContainerLocation.java  |  52 +++++
 .../core/location/dynamic/clocker/StubHost.java |  38 ++++
 .../location/dynamic/clocker/StubHostImpl.java  | 141 ++++++++++++
 .../dynamic/clocker/StubHostLocation.java       | 171 ++++++++++++++
 .../dynamic/clocker/StubInfrastructure.java     |  95 ++++++++
 .../dynamic/clocker/StubInfrastructureImpl.java | 224 +++++++++++++++++++
 .../clocker/StubInfrastructureLocation.java     | 139 ++++++++++++
 .../location/dynamic/clocker/StubResolver.java  | 159 +++++++++++++
 .../dynamic/clocker/StubResolverTest.java       |  82 +++++++
 .../location/dynamic/clocker/StubUtils.java     |  95 ++++++++
 .../machine/pool/AbstractServerPoolTest.java    |  23 +-
 .../pool/ServerPoolLocationResolverTest.java    |  65 ++----
 .../entity/machine/pool/ServerPoolTest.java     |   8 +-
 29 files changed, 1865 insertions(+), 133 deletions(-)
----------------------------------------------------------------------



[07/10] brooklyn-server git commit: LocationFactor’s “special constructor” for pre-existing loc

Posted by gr...@apache.org.
LocationFactor’s “special constructor” for pre-existing loc

If location already existed, then don’t try to manage it and don’t
reset its config. Previously we got a log.warn about an attempt to
manage the location when it was already managed.

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

Branch: refs/heads/master
Commit: 89b3a663902fb6ecdfe6666c641ce75ffca514e0
Parents: 3118f42
Author: Aled Sage <al...@gmail.com>
Authored: Tue Mar 22 12:46:02 2016 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Tue Mar 22 12:46:02 2016 +0000

----------------------------------------------------------------------
 .../core/objs/proxy/InternalLocationFactory.java       | 13 +++++++++++++
 1 file changed, 13 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/89b3a663/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalLocationFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalLocationFactory.java b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalLocationFactory.java
index baf0e5b..4ba0658 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalLocationFactory.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalLocationFactory.java
@@ -27,12 +27,15 @@ import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.core.location.internal.LocationInternal;
 import org.apache.brooklyn.core.mgmt.internal.LocalLocationManager;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.collect.ImmutableMap;
 
@@ -46,6 +49,8 @@ import com.google.common.collect.ImmutableMap;
  */
 public class InternalLocationFactory extends InternalFactory {
 
+    private static final Logger LOG = LoggerFactory.getLogger(InternalLocationFactory.class);
+
     /**
      * Returns true if this is a "new-style" location (i.e. where not expected to call the constructor to instantiate it).
      * 
@@ -96,9 +101,17 @@ public class InternalLocationFactory extends InternalFactory {
             
             T loc = construct(clazz, spec, null);
 
+            if (Locations.isManaged(loc)) {
+                // Construct can return an existing instance, if using SpecialBrooklynObjectConstructor.Config.SPECIAL_CONSTRUCTOR.
+                // In which case, don't reconfigure it (don't change its parent, etc).
+                LOG.debug("Location-factory returning pre-existing location; skipping initialization of {}", loc);
+                return loc;
+            }
+
             if (spec.getId() != null) {
                 FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", spec.getId()), loc);
             }
+            
             managementContext.prePreManage(loc);
 
             if (spec.getDisplayName()!=null)


[04/10] brooklyn-server git commit: NotUpDiagnostics: don’t collect if starting && !up

Posted by gr...@apache.org.
NotUpDiagnostics: don’t collect if starting && !up

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

Branch: refs/heads/master
Commit: 0b9682b8830df7715bb8b579cfc40a1734f03038
Parents: 9f1a120
Author: Aled Sage <al...@gmail.com>
Authored: Fri Mar 18 23:09:04 2016 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Sat Mar 19 22:03:56 2016 +0000

----------------------------------------------------------------------
 .../apache/brooklyn/entity/software/base/SoftwareProcessImpl.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/0b9682b8/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
index ace4ebe..4bf6543 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
@@ -172,7 +172,7 @@ public abstract class SoftwareProcessImpl extends AbstractEntity implements Soft
             Lifecycle state = entity.getAttribute(SERVICE_STATE_ACTUAL);
             if (up == null || up) {
                 entity.sensors().set(ServiceStateLogic.SERVICE_NOT_UP_DIAGNOSTICS, ImmutableMap.<String, Object>of());
-            } else if (state == null || state == Lifecycle.CREATED) {
+            } else if (state == null || state == Lifecycle.CREATED || state == Lifecycle.STARTING) {
                 // not yet started; do nothing
             } else if (state == Lifecycle.STOPPING || state == Lifecycle.STOPPED || state == Lifecycle.DESTROYED) {
                 // stopping/stopped, so expect not to be up; get rid of the diagnostics.