You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2017/04/21 22:55:49 UTC

[30/39] brooklyn-server git commit: Changes to move to catalogItemId + search path.

Changes to move to catalogItemId + search path.

Restore catalogItemId; change getCatalogItemIdHierarchy to getCatalogItemIdSearchPath.
Update "stackCatalogItemId" to work with catalogItemId + search path.
Use catalogItemIdAndSearchPath when merging wrapper parent to child.
Rebind changes for  catalogItemId + search path.
Update AbstractBrooklynObject[Spec] for catalogItemId + searchPath
Do catalogItemIdAndSearchPath only if non-null
Update "catalogItemIdHierarchy" -> "searchPath" in persistence


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

Branch: refs/heads/master
Commit: 81815f9424ba1987dbba5c3ee206deec4400babf
Parents: b61f972
Author: Geoff Macartney <ge...@cloudsoftcorp.com>
Authored: Fri Mar 10 17:10:33 2017 +0000
Committer: Geoff Macartney <ge...@cloudsoftcorp.com>
Committed: Thu Apr 20 11:20:36 2017 +0100

----------------------------------------------------------------------
 .../internal/AbstractBrooklynObjectSpec.java    |  90 ++++----
 .../mementos/BrooklynMementoManifest.java       |  10 +-
 .../api/mgmt/rebind/mementos/Memento.java       |   6 +-
 .../brooklyn/api/objs/BrooklynObject.java       |   9 +-
 .../catalog/CatalogOsgiYamlEntityTest.java      |   8 +-
 .../catalog/CatalogOsgiYamlTemplateTest.java    |   2 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java |   6 +-
 .../catalog/CatalogYamlLocationTest.java        |   4 +-
 .../brooklyn/catalog/CatalogYamlRebindTest.java |   6 +-
 .../catalog/CatalogYamlTemplateTest.java        |   4 +-
 .../core/catalog/internal/CatalogItemDo.java    |  12 +-
 .../internal/CatalogItemDtoAbstract.java        |   5 -
 .../core/catalog/internal/CatalogUtils.java     |  14 +-
 .../internal/JavaCatalogToSpecTransformer.java  |   4 +-
 .../access/PortForwardManagerClient.java        |   4 +-
 .../core/mgmt/EntityManagementUtils.java        |   6 +-
 .../internal/AbstractManagementContext.java     |  30 ++-
 .../BrooklynMementoPersisterToObjectStore.java  |  24 +-
 .../core/mgmt/persist/XmlMementoSerializer.java |   2 +-
 .../core/mgmt/rebind/RebindIteration.java       | 227 ++++++++++++-------
 .../core/mgmt/rebind/dto/AbstractMemento.java   |  48 +---
 .../rebind/dto/BrooklynMementoManifestImpl.java |   4 +-
 .../rebind/dto/EntityMementoManifestImpl.java   |  20 +-
 .../mgmt/rebind/dto/MementosGenerators.java     |   3 +-
 .../rebind/transformer/CompoundTransformer.java |  23 +-
 .../core/objs/AbstractBrooklynObject.java       |  38 ++--
 .../core/objs/AbstractEntityAdjunct.java        |   3 +-
 .../core/objs/BrooklynObjectInternal.java       |   8 +-
 .../core/objs/proxy/InternalEntityFactory.java  |  14 +-
 .../objs/proxy/InternalLocationFactory.java     |  16 +-
 .../core/objs/proxy/InternalPolicyFactory.java  |  30 +--
 .../brooklyn/util/core/ClassLoaderUtils.java    |   4 +-
 .../rebind/RebindConfigInheritanceTest.java     |   3 +-
 .../util/core/ClassLoaderUtilsTest.java         |   4 +-
 .../rest/transform/LocationTransformer.java     |   4 +-
 35 files changed, 374 insertions(+), 321 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java b/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
index 16b4175..10acfe4 100644
--- a/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
+++ b/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
@@ -68,7 +68,9 @@ public abstract class AbstractBrooklynObjectSpec<T, SpecT extends AbstractBrookl
 
     private final Class<? extends T> type;
     private String displayName;
-    private Deque<String> catalogItemIdStack;
+    private String catalogItemId;
+    private Deque<String> catalogItemIdSearchPath = new ArrayDeque<>();
+
     private Set<Object> tags = MutableSet.of();
     private List<SpecParameter<?>> parameters = ImmutableList.of();
 
@@ -108,13 +110,16 @@ public abstract class AbstractBrooklynObjectSpec<T, SpecT extends AbstractBrookl
      * (or TODO we add a separate field to record other catalog item IDs that could be applied for searching, see below)
      */
     public SpecT catalogItemId(String val) {
-        getCatalogItemIdStack().clear();
-        return stackCatalogItemId(val);
+        catalogItemId = val;
+        return self();
     }
 
-    protected SpecT catalogItemIdStack(Collection<String> catalogItemIdStack) {
-        this.catalogItemIdStack = null;
-        getCatalogItemIdStack().addAll(catalogItemIdStack);
+    public synchronized SpecT catalogItemIdAndSearchPath(String catalogItemId, Collection<String> searchPath) {
+        if (catalogItemId != null) {
+            catalogItemId(catalogItemId);
+            catalogItemIdSearchPath.clear();
+            catalogItemIdSearchPath.addAll(searchPath);
+        }
         return self();
     }
 
@@ -125,30 +130,37 @@ public abstract class AbstractBrooklynObjectSpec<T, SpecT extends AbstractBrookl
     @Beta
     public SpecT catalogItemIdIfNotNull(String val) {
         if (val!=null) {
-            stackCatalogItemId(val);
+            catalogItemId(val);
         }
         return self();
     }
 
-    private Deque<String> getCatalogItemIdStack() {
-        if (catalogItemIdStack == null) {
-            catalogItemIdStack = new ArrayDeque<>();
+    protected Object readResolve() {
+        if (catalogItemIdSearchPath == null) {
+            catalogItemIdSearchPath = new ArrayDeque<>();
         }
-        return catalogItemIdStack;
+        return this;
     }
 
     /**
      * Adds (stacks) the catalog item id of a wrapping specification.
      * Does nothing if the value is null.
+     * If the value is not null, and is not the same as the current
+     * catalogItemId, then the *current* catalogItemId will
+     * be moved to the start of the search path, and the value supplied
+     * as the parameter here will be set as the new catalogItemId.
      * <p>
-     * Used when we want to collect nested item ID's so that *all* can be searched.
+     * Used when we want to collect IDs of items that extend other items, so that *all* can be searched.
      * e.g. if R3 extends R2 which extends R1 any one of these might supply config keys
      * referencing resources or types in their local bundles.
      */
     @Beta
     public SpecT stackCatalogItemId(String val) {
-        if (null != val && (getCatalogItemIdStack().isEmpty() || !getCatalogItemIdStack().element().equals(val))) {
-            getCatalogItemIdStack().addFirst(val);
+        if (null != val) {
+            if (null != catalogItemId && !catalogItemId.equals(val)) {
+                catalogItemIdSearchPath.addFirst(catalogItemId);
+            }
+            catalogItemId(val);
         }
         return self();
     }
@@ -239,35 +251,26 @@ public abstract class AbstractBrooklynObjectSpec<T, SpecT extends AbstractBrookl
         return displayName;
     }
 
-    /**
-     * @deprecated since 0.11.0, use getOuterCatalogItemId or getInnerCatalogItemIds as appropriate
-     */
-    @Deprecated
     public final String getCatalogItemId() {
-        return getOuterCatalogItemId();
+        return catalogItemId;
     }
 
-    public final String getOuterCatalogItemId() {
-        if (getCatalogItemIdStack().size() != 0) {
-            return getCatalogItemIdStack().getFirst();
-        }
-        return null;
-    }
-
-
-        /**
-         * An immutable list of ids of this object's catalog item and its defining catalog items.
-         * Wrapping items are first in the list (i.e. wrapping items precede wrapped items),
-         * for example, if the catalog item is defined as
-         * <pre>
-         *     items:
-         *     - id: X
-         *       item: Y
-         * </pre>
-         * then the list will contain X, Y.
-         */
-    public final List<String> getCatalogItemIdHierarchy() {
-        return ImmutableList.copyOf(catalogItemIdStack);
+    /**
+     * An immutable list of ids of catalog items that define this item.
+     * Wrapping items are first in the list (i.e. wrapping items precede wrapped items),
+     * for example, for a spec of an item of type Z, where the catalog is:
+     * <pre>
+     *     items:
+     *     - id: X
+     *     - id: Y
+     *       item: X
+     *     - id: Z
+     *       item: Y
+     * </pre>
+     * then the spec will have getCatalogId() of Z and getCatalogItemIdSearchPath()  of Y, X.
+     */
+    public final List<String> getCatalogItemIdSearchPath() {
+        return ImmutableList.copyOf(catalogItemIdSearchPath);
     }
 
     public final Set<Object> getTags() {
@@ -317,7 +320,7 @@ public abstract class AbstractBrooklynObjectSpec<T, SpecT extends AbstractBrookl
             .configure(otherSpec.getConfig())
             .configure(otherSpec.getFlags())
             .tags(otherSpec.getTags())
-            .catalogItemIdStack(otherSpec.getCatalogItemIdHierarchy())
+            .catalogItemIdAndSearchPath(otherSpec.getCatalogItemId(), otherSpec.getCatalogItemIdSearchPath())
             .parameters(otherSpec.getParameters());
     }
 
@@ -327,7 +330,8 @@ public abstract class AbstractBrooklynObjectSpec<T, SpecT extends AbstractBrookl
         if (!obj.getClass().equals(getClass())) return false;
         AbstractBrooklynObjectSpec<?, ?> other = (AbstractBrooklynObjectSpec<?, ?>) obj;
         if (!Objects.equal(getDisplayName(), other.getDisplayName())) return false;
-        if (!Objects.equal(getCatalogItemIdHierarchy(), other.getCatalogItemIdHierarchy())) return false;
+        if (!Objects.equal(getCatalogItemId(), other.getCatalogItemId())) return false;
+        if (!Objects.equal(getCatalogItemIdSearchPath(), other.getCatalogItemIdSearchPath())) return false;
         if (!Objects.equal(getType(), other.getType())) return false;
         if (!Objects.equal(getTags(), other.getTags())) return false;
         if (!Objects.equal(getConfig(), other.getConfig())) return false;
@@ -338,7 +342,7 @@ public abstract class AbstractBrooklynObjectSpec<T, SpecT extends AbstractBrookl
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(getCatalogItemIdHierarchy(), getDisplayName(), getType(), getTags());
+        return Objects.hashCode(getCatalogItemIdSearchPath(), getDisplayName(), getType(), getTags());
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/BrooklynMementoManifest.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/BrooklynMementoManifest.java b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/BrooklynMementoManifest.java
index bcf246e..fa56d1d 100644
--- a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/BrooklynMementoManifest.java
+++ b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/BrooklynMementoManifest.java
@@ -36,14 +36,8 @@ public interface BrooklynMementoManifest extends Serializable {
         String getId();
         String getType();
         String getParent();
-
-        /**
-         * deprecated since 0.11.0, use {@link #getCatalogItemHierarchy()} instead
-         * @return
-         */
-        @Deprecated String getCatalogItemId();
-
-        List<String> getCatalogItemHierarchy();
+        String getCatalogItemId();
+        List<String> getCatalogItemIdSearchPath();
     }
 
     String getPlaneId();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/Memento.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/Memento.java b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/Memento.java
index 4ca219d..71fc587 100644
--- a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/Memento.java
+++ b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/Memento.java
@@ -48,15 +48,13 @@ public interface Memento extends Serializable {
 
     /**
      * The principal catalog item id.
-     * @deprecated since 0.11.0 - use {@link #getCatalogItemHierarchy()} instead
      */
-    @Deprecated
     String getCatalogItemId();
 
     /**
-     * Catalog Item Ids of all defining catalog items.
+     * Item Ids of all catalog items used to define this item.
      */
-    List<String> getCatalogItemHierarchy();
+    List<String> getCatalogItemIdSearchPath();
 
     String getDisplayName();
     

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/api/src/main/java/org/apache/brooklyn/api/objs/BrooklynObject.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/objs/BrooklynObject.java b/api/src/main/java/org/apache/brooklyn/api/objs/BrooklynObject.java
index ff49e1b..d72fe6a 100644
--- a/api/src/main/java/org/apache/brooklyn/api/objs/BrooklynObject.java
+++ b/api/src/main/java/org/apache/brooklyn/api/objs/BrooklynObject.java
@@ -60,16 +60,19 @@ public interface BrooklynObject extends Identifiable, Configurable {
     String getCatalogItemId();
 
     /**
-     * An immutable list of ids of this object's catalog item and its defining catalog items.
-     * e.g. if the catalog item is defined as
+     * An immutable list of ids of catalog items that define this item.
+     * e.g. if the catalog item is defined as a Z where
      * <pre>
      *     items:
      *     - id: X
+     *     - id: Y
+     *       item: X
+     *     - id: Z
      *       item: Y
      * </pre>
      * then the list will contain X, Y.
      */
-    List<String> getCatalogItemHierarchy();
+    List<String> getCatalogItemIdSearchPath();
     
     /** 
      * Tags are arbitrary objects which can be attached to an entity for subsequent reference.

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
index dbf9445..b0bf747 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
@@ -708,7 +708,7 @@ public class CatalogOsgiYamlEntityTest extends AbstractYamlTest {
         BrooklynTypeRegistry registry = mgmt().getTypeRegistry();
         RegisteredType item = registry.get(symbolicName, TEST_VERSION);
         AbstractBrooklynObjectSpec<?, ?> spec = registry.createSpec(item, null, null);
-        assertEquals(spec.getOuterCatalogItemId(), ver(symbolicName));
+        assertEquals(spec.getCatalogItemId(), ver(symbolicName));
 
         deleteCatalogEntity(symbolicName);
     }
@@ -813,9 +813,9 @@ public class CatalogOsgiYamlEntityTest extends AbstractYamlTest {
 
       Entity entity = app.getChildren().iterator().next();
       assertEquals(entity.getCatalogItemId(), ver(symbolicNameOuter));
-      assertEquals(entity.getCatalogItemHierarchy().size(), 2);
-      assertEquals(entity.getCatalogItemHierarchy().get(0), ver(symbolicNameOuter));
-      assertEquals(entity.getCatalogItemHierarchy().get(1), ver(symbolicNameInner));
+      assertEquals(entity.getCatalogItemIdSearchPath().size(), 1, "should have exactly one item in search path");
+      assertEquals(entity.getCatalogItemIdSearchPath().get(0), ver(symbolicNameInner),
+          "should have " + symbolicNameInner + " in search path");
 
       deleteCatalogEntity(symbolicNameInner);
       deleteCatalogEntity(symbolicNameOuter);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlTemplateTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlTemplateTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlTemplateTest.java
index 4ecfcf2..8b2a561 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlTemplateTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlTemplateTest.java
@@ -87,7 +87,7 @@ public class CatalogOsgiYamlTemplateTest extends AbstractYamlTest {
         
         EntitySpec<?> child = Iterables.getOnlyElement( spec.getChildren() );
         Assert.assertEquals(child.getType().getName(), SIMPLE_ENTITY_TYPE);
-        Assert.assertEquals(child.getOuterCatalogItemId(), "t1:"+TEST_VERSION);
+        Assert.assertEquals(child.getCatalogItemId(), "t1:"+TEST_VERSION);
     }
     
     private RegisteredType makeItem(String symbolicName, String templateType) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index c40f7f8..b3264f7 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -643,9 +643,9 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
 
         Entity entity = app.getChildren().iterator().next();
         assertEquals(entity.getCatalogItemId(), ver(symbolicNameOuter));
-        assertEquals(entity.getCatalogItemHierarchy().size(), 2);
-        assertEquals(entity.getCatalogItemHierarchy().get(0), ver(symbolicNameOuter));
-        assertEquals(entity.getCatalogItemHierarchy().get(1), ver(symbolicNameInner));
+        assertEquals(entity.getCatalogItemIdSearchPath().size(), 1, "should have exactly one item in search path");
+        assertEquals(entity.getCatalogItemIdSearchPath().get(0), ver(symbolicNameInner),
+            "should have " + symbolicNameInner + " in search path");
 
         deleteCatalogEntity(symbolicNameInner);
         deleteCatalogEntity(symbolicNameOuter);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
index dc017d3..01fb484 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
@@ -176,10 +176,10 @@ public class CatalogYamlLocationTest extends AbstractYamlTest {
         LocationSpec<? extends Location> spec1 = mgmt().getLocationRegistry().getLocationSpec(def1).get();
         LocationSpec<? extends Location> spec2 = mgmt().getLocationRegistry().getLocationSpec(def2).get();
         
-        assertEquals(spec1.getOuterCatalogItemId(), "loc1:0.1.2");
+        assertEquals(spec1.getCatalogItemId(), "loc1:0.1.2");
         assertEquals(spec1.getDisplayName(), "My Loc 1");
         assertContainsAll(spec1.getFlags(), ImmutableMap.of("mykey1", "myval1", "mykey1b", "myval1b"));
-        assertEquals(spec2.getOuterCatalogItemId(), "loc2:0.1.2");
+        assertEquals(spec2.getCatalogItemId(), "loc2:0.1.2");
         assertEquals(spec2.getDisplayName(), "My Loc 2");
         assertContainsAll(spec2.getFlags(), ImmutableMap.of("mykey1", "myvalOverridden", "mykey1b", "myval1b", "mykey2", "myval2"));
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
index 08616fc..874e301 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java
@@ -170,13 +170,13 @@ public class CatalogYamlRebindTest extends AbstractYamlRebindTest {
         testRebindWithCatalogAndAppUsingOptions(mode, osgiMode, RebindOptions.create());
     }
 
-    // Re-run the same tests as testRebindWithCatalogAndApp but with the XML updated to mimic state
-    // persisted before <catalogItemId> was replaced with <catalogItemHierarchy>.
+    // Re-run all the same tests as testRebindWithCatalogAndApp, but with the XML updated to mimic state
+    // persisted before <catalogItemIdSearchPath> was introduced.
     @Test(dataProvider = "dataProvider")
     public void testRebindWithCatalogAndAppRebindCatalogItemIds(RebindWithCatalogTestMode mode, OsgiMode osgiMode) throws Exception {
         final RebindOptions rebindOptions = RebindOptions.create();
         applyCompoundStateTransformer(rebindOptions, CompoundTransformer.builder()
-            .xmlReplaceItem("//catalogItemHierarchy", "<catalogItemId><xsl:value-of select=\"string\"/></catalogItemId>")
+            .xmlDeleteItem("//catalogItemIdSearchPath")
             .build());
         testRebindWithCatalogAndAppUsingOptions(mode, osgiMode, rebindOptions);
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
index f128abc..cb585e0 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
@@ -208,7 +208,7 @@ public class CatalogYamlTemplateTest extends AbstractYamlTest {
         
         EntitySpec<?> child = Iterables.getOnlyElement( spec.getChildren() );
         Assert.assertEquals(child.getType().getName(), TestEntity.class.getName());
-        Assert.assertEquals(child.getOuterCatalogItemId(), "t1:"+TEST_VERSION);
+        Assert.assertEquals(child.getCatalogItemId(), "t1:"+TEST_VERSION);
     }
     
     @Test
@@ -247,7 +247,7 @@ public class CatalogYamlTemplateTest extends AbstractYamlTest {
         Assert.assertEquals(spec.getChildren().size(), 0);
         Assert.assertEquals(spec.getType(), BasicApplication.class);
         Assert.assertEquals(ConfigBag.newInstance(spec.getConfig()).getStringKey("foo"), "boo");
-        Assert.assertEquals(spec.getOuterCatalogItemId(), "app1r:1");
+        Assert.assertEquals(spec.getCatalogItemId(), "app1r:1");
     }
     
     private RegisteredType addCatalogItem(String symbolicName, String templateType) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java
index 42cdb42..36e1192 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java
@@ -132,18 +132,18 @@ public class CatalogItemDo<T,SpecT> implements CatalogItem<T,SpecT>, BrooklynObj
     }
 
     @Override
-    public void setCatalogItemIdHierarchy(List<String> ids) {
-        itemDto.setCatalogItemIdHierarchy(ids);
+    public void setCatalogItemIdSearchPath(List<String> ids) {
+        itemDto.setCatalogItemIdSearchPath(ids);
     }
 
     @Override
-    public List<String> getCatalogItemHierarchy() {
-        return itemDto.getCatalogItemHierarchy();
+    public List<String> getCatalogItemIdSearchPath() {
+        return itemDto.getCatalogItemIdSearchPath();
     }
 
     @Override
-    public void nestCatalogItemId(String id) {
-        itemDto.nestCatalogItemId(id);
+    public void stackCatalogItemId(String id) {
+        itemDto.stackCatalogItemId(id);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
index 539b8b1..0bae32d 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
@@ -104,11 +104,6 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
     }
 
     @Override
-    public List<String> getCatalogItemHierarchy() {
-        return ImmutableList.of(getCatalogItemId());
-    }
-
-    @Override
     public String getJavaType() {
         if (javaType != null) return javaType;
         return type;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
index 4bb11fd..2f85db9 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
@@ -142,12 +142,12 @@ public class CatalogUtils {
     }
 
     public static BrooklynClassLoadingContext newClassLoadingContextForCatalogItems(
-        ManagementContext managementContext, List<String> catalogItemIds) {
+        ManagementContext managementContext, String catalogItemId, List<String> searchPath) {
 
-        BrooklynClassLoadingContextSequential seqLoader =
-            new BrooklynClassLoadingContextSequential(managementContext);
-        for (String catalogItemId : catalogItemIds) {
-            addCatalogItemContext(managementContext, seqLoader, catalogItemId);
+        BrooklynClassLoadingContextSequential seqLoader = new BrooklynClassLoadingContextSequential(managementContext);
+        addCatalogItemContext(managementContext, seqLoader, catalogItemId);
+        for (String searchId : searchPath) {
+            addCatalogItemContext(managementContext, seqLoader, searchId);
         }
         return seqLoader;
     }
@@ -195,7 +195,9 @@ public class CatalogUtils {
                 if (log.isDebugEnabled())
                     BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(entity),
                         "Catalog item addition: "+entity+" from "+entity.getCatalogItemId()+" applying its catalog item ID to "+itemBeingAdded);
-                ((BrooklynObjectInternal)itemBeingAdded).setCatalogItemIdHierarchy(entity.getCatalogItemHierarchy());
+                final BrooklynObjectInternal addInternal = (BrooklynObjectInternal) itemBeingAdded;
+                addInternal.setCatalogItemId(entity.getCatalogItemId());
+                addInternal.setCatalogItemIdSearchPath(entity.getCatalogItemIdSearchPath());
             } else {
                 if (!itemBeingAdded.getCatalogItemId().equals(entity.getCatalogItemId())) {
                     // not a problem, but something to watch out for

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/catalog/internal/JavaCatalogToSpecTransformer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/JavaCatalogToSpecTransformer.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/JavaCatalogToSpecTransformer.java
index bf698e8..f9c4131 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/JavaCatalogToSpecTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/JavaCatalogToSpecTransformer.java
@@ -26,7 +26,6 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
-import org.apache.brooklyn.api.mgmt.classloading.BrooklynClassLoadingContext;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.api.typereg.RegisteredType;
@@ -84,7 +83,8 @@ public class JavaCatalogToSpecTransformer implements PlanToSpecTransformer {
                 // but it doesn't hurt (and if we re-instate a class+bundle approach for RegisteredType 
                 // we will want to do this)
                 final BrooklynClassLoadingContextSequential ctx = new BrooklynClassLoadingContextSequential(mgmt);
-                ctx.add(CatalogUtils.newClassLoadingContextForCatalogItems(mgmt, item.getCatalogItemHierarchy()));
+                ctx.add(CatalogUtils.newClassLoadingContextForCatalogItems(mgmt, item.getCatalogItemId(),
+                    item.getCatalogItemIdSearchPath()));
                 type = ctx.loadClass(javaType);
             } catch (Exception e) {
                 Exceptions.propagateIfFatal(e);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/location/access/PortForwardManagerClient.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/access/PortForwardManagerClient.java b/core/src/main/java/org/apache/brooklyn/core/location/access/PortForwardManagerClient.java
index d5781cd..92091fc 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/access/PortForwardManagerClient.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/access/PortForwardManagerClient.java
@@ -388,8 +388,8 @@ public class PortForwardManagerClient implements PortForwardManager {
     }
 
     @Override
-    public List<String> getCatalogItemHierarchy() {
-        return getDelegate().getCatalogItemHierarchy();
+    public List<String> getCatalogItemIdSearchPath() {
+        return getDelegate().getCatalogItemIdSearchPath();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
index d222151..8fb6345 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
@@ -259,12 +259,14 @@ public class EntityManagementUtils {
             wrappedChild.parametersAdd(wrapperParent.getParameters());
         }
 
-        wrappedChild.stackCatalogItemId(wrapperParent.getOuterCatalogItemId());
+        wrappedChild.catalogItemIdAndSearchPath(wrapperParent.getCatalogItemId(), wrapperParent.getCatalogItemIdSearchPath());
 
         // NB: this clobber's child config wherever they conflict; might prefer to deeply merge maps etc
         // (or maybe even prevent the merge in these cases; 
         // not sure there is a compelling reason to have config on a pure-wrapper parent)
-        Map<ConfigKey<?>, Object> configWithoutWrapperMarker = Maps.filterKeys(wrapperParent.getConfig(), Predicates.not(Predicates.<ConfigKey<?>>equalTo(EntityManagementUtils.WRAPPER_APP_MARKER)));
+        Map<ConfigKey<?>, Object> configWithoutWrapperMarker =
+            Maps.filterKeys(wrapperParent.getConfig(),
+                Predicates.not(Predicates.<ConfigKey<?>>equalTo(EntityManagementUtils.WRAPPER_APP_MARKER)));
         wrappedChild.configure(configWithoutWrapperMarker);
         wrappedChild.configure(wrapperParent.getFlags());
         

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
index 16bb3de..6d81626 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
@@ -51,6 +51,7 @@ import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityManager;
 import org.apache.brooklyn.api.mgmt.rebind.RebindManager;
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry;
+import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.config.StringConfigMap;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.catalog.internal.CatalogInitialization;
@@ -130,16 +131,25 @@ public abstract class AbstractManagementContext implements ManagementContextInte
             public BrooklynClassLoadingContext apply(@Nullable Object input) {
                 if (input instanceof EntityInternal) {
                     EntityInternal internal = (EntityInternal)input;
-                    final List<String> catalogItemHierarchy = internal.getCatalogItemHierarchy();
-                    if (catalogItemHierarchy.size() > 0) {
-                        final ManagementContext managementContext = internal.getManagementContext();
-                        BrooklynClassLoadingContextSequential seqLoader =
-                            new BrooklynClassLoadingContextSequential(managementContext);
-                        seqLoader.add(newClassLoadingContextForCatalogItems(managementContext, catalogItemHierarchy));
-                        JavaBrooklynClassLoadingContext entityLoader =
-                            JavaBrooklynClassLoadingContext.create(input.getClass().getClassLoader());
-                        seqLoader.add(entityLoader);
-                        return seqLoader;
+                    String inputCatalogItemId = internal.getCatalogItemId();
+                    if(inputCatalogItemId != null) {
+                        RegisteredType item = internal.getManagementContext().getTypeRegistry().get(internal.getCatalogItemId());
+
+                        if (item != null) {
+                            final List<String> searchPath = internal.getCatalogItemIdSearchPath();
+                            final ManagementContext managementContext = internal.getManagementContext();
+                            BrooklynClassLoadingContextSequential seqLoader =
+                                new BrooklynClassLoadingContextSequential(managementContext);
+                            seqLoader.add(newClassLoadingContextForCatalogItems(managementContext, inputCatalogItemId, searchPath));
+                            JavaBrooklynClassLoadingContext entityLoader =
+                                JavaBrooklynClassLoadingContext.create(input.getClass().getClassLoader());
+                            seqLoader.add(entityLoader);
+                            return seqLoader;
+                        } else {
+                            log.error("Can't find catalog item " + internal.getCatalogItemId() +
+                                    " used for instantiating entity " + internal +
+                                    ". Falling back to application classpath.");
+                        }
                     }
                     return apply(internal.getManagementSupport());
                 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/BrooklynMementoPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/BrooklynMementoPersisterToObjectStore.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/BrooklynMementoPersisterToObjectStore.java
index a716151..5aace6d 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/BrooklynMementoPersisterToObjectStore.java
@@ -37,8 +37,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 import javax.annotation.Nullable;
 import javax.xml.xpath.XPathConstants;
 
-import com.google.common.collect.ImmutableList;
-
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.mgmt.rebind.PersistenceExceptionHandler;
 import org.apache.brooklyn.api.mgmt.rebind.RebindExceptionHandler;
@@ -195,8 +193,8 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
             return null;
         } else {
             final BrooklynClassLoadingContextSequential ctx = new BrooklynClassLoadingContextSequential(managementContext);
-            ctx.add(
-                CatalogUtils.newClassLoadingContextForCatalogItems(managementContext, item.getCatalogItemHierarchy()));
+            ctx.add(CatalogUtils.newClassLoadingContextForCatalogItems(managementContext,
+                    item.getCatalogItemId(), item.getCatalogItemIdSearchPath()));
             return ClassLoaderFromBrooklynClassLoadingContext.of(ctx);
         }
     }
@@ -360,20 +358,6 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
         }
     }
 
-    // We must be able to cope with XML serialized with either a single "catalogItemId"
-    // or a list "catalogItemHierarchy" of catalog item ids. Only one should be encountered
-    // but in any case prefer the list of ids.
-    private ImmutableList<String> getCatalogItemIds(XPathHelper x) {
-        final MutableList<String> list = MutableList.of();
-        final List<String> catalogItemHierarchy = x.getStringList("catalogItemHierarchy");
-        final String catalogItemId = Strings.emptyToNull(x.get("catalogItemId"));
-        if (!catalogItemHierarchy.isEmpty()) {
-            list.addAll(catalogItemHierarchy);
-        } else if (catalogItemId != null) {
-            list.add(catalogItemId);
-        }
-        return ImmutableList.copyOf(list);
-    }
 
     @Override
     public BrooklynMementoManifest loadMementoManifest(BrooklynMementoRawData mementoData,
@@ -391,7 +375,9 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
                 XPathHelper x = new XPathHelper(contents, "/"+type.toCamelCase()+"/");
                 switch (type) {
                     case ENTITY:
-                        builder.entity(x.get("id"), x.get("type"), Strings.emptyToNull(x.get("parent")), getCatalogItemIds(x));
+                        builder.entity(x.get("id"), x.get("type"), Strings.emptyToNull(x.get("parent")),
+                            Strings.emptyToNull(x.get("catalogItemId")),
+                            x.getStringList("searchPath"));
                         break;
                     case LOCATION:
                     case POLICY:

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/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 12b2c3a..16beb4e 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
@@ -455,7 +455,7 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
         public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
             if (source == null) return;
             AbstractBrooklynObjectSpec<?, ?> spec = (AbstractBrooklynObjectSpec<?, ?>) source;
-            String catalogItemId = spec.getOuterCatalogItemId();
+            String catalogItemId = spec.getCatalogItemId();
             if (Strings.isNonBlank(catalogItemId)) {
                 // write this field first, so we can peek at it when we read
                 writer.startNode("catalogItemId");

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
index 80c5eb2..a12bd7e 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
@@ -19,6 +19,8 @@
 package org.apache.brooklyn.core.mgmt.rebind;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.brooklyn.core.BrooklynFeatureEnablement.FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND;
+import static org.apache.brooklyn.core.BrooklynFeatureEnablement.FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND;
 import static org.apache.brooklyn.core.catalog.internal.CatalogUtils.newClassLoadingContextForCatalogItems;
 
 import java.io.IOException;
@@ -32,7 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
+
 import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Application;
@@ -771,7 +773,7 @@ public abstract class RebindIteration {
         if (!isEmpty) {
             BrooklynLogging.log(LOG, shouldLogRebinding() ? LoggingLevel.INFO : LoggingLevel.DEBUG, 
                 "Rebind complete " + "("+mode+(readOnlyRebindCount.get()>=0 ? ", iteration "+readOnlyRebindCount : "")+")" +
-                    " in {}: {} app{}, {} entit{}, {} location{}, {} polic{}, {} enricher{}, {} feed{}, {} catalog item{}", new Object[]{
+                    " in {}: {} app{}, {} entit{}, {} location{}, {} polic{}, {} enricher{}, {} feed{}, {} catalog item{}",
                 Time.makeTimeStringRounded(timer), applications.size(), Strings.s(applications),
                 rebindContext.getEntities().size(), Strings.ies(rebindContext.getEntities()),
                 rebindContext.getLocations().size(), Strings.s(rebindContext.getLocations()),
@@ -779,7 +781,7 @@ public abstract class RebindIteration {
                 rebindContext.getEnrichers().size(), Strings.s(rebindContext.getEnrichers()),
                 rebindContext.getFeeds().size(), Strings.s(rebindContext.getFeeds()),
                 rebindContext.getCatalogItems().size(), Strings.s(rebindContext.getCatalogItems())
-            });
+            );
         }
 
         // Return the top-level applications
@@ -797,30 +799,51 @@ public abstract class RebindIteration {
             rebindMetrics.noteError(messages);
         }
     }
-    
-    protected List<String> findCatalogItemIds(ClassLoader cl, Map<String, EntityMementoManifest> entityIdToManifest, EntityMementoManifest entityManifest) {
-        if (!entityManifest.getCatalogItemHierarchy().isEmpty()) {
-            return entityManifest.getCatalogItemHierarchy();
+
+    protected class CatalogItemIdAndSearchPath {
+        private String catalogItemId;
+        private List<String> searchPath;
+
+        public CatalogItemIdAndSearchPath(String catalogItemId, List<String> searchPath) {
+            this.catalogItemId = catalogItemId;
+            this.searchPath = searchPath;
+        }
+        public String getCatalogItemId() { return catalogItemId; }
+        public List<String> getSearchPath() { return searchPath; }
+    }
+
+    protected CatalogItemIdAndSearchPath findCatalogItemIds(ClassLoader cl, Map<String,
+        EntityMementoManifest> entityIdToManifest, EntityMementoManifest entityManifest) {
+
+        if (entityManifest.getCatalogItemId() != null) {
+            return new CatalogItemIdAndSearchPath(entityManifest.getCatalogItemId(),
+                entityManifest.getCatalogItemIdSearchPath());
         }
 
-        if (BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND)) {
+        if (BrooklynFeatureEnablement.isEnabled(FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND)) {
+            String typeId = null;
+            List<String> searchPath = MutableList.of();
             //First check if any of the parent entities has a catalogItemId set.
             EntityMementoManifest ptr = entityManifest;
             while (ptr != null) {
-                if (ptr.getCatalogItemHierarchy() != null) {
-                    List<String> ids = MutableList.of();
-                    for (String id : ptr.getCatalogItemHierarchy()) {
-                        RegisteredType type = managementContext.getTypeRegistry().get(id);
+                final String pId = ptr.getCatalogItemId();
+                if (pId != null) {
+                    RegisteredType type = managementContext.getTypeRegistry().get(pId);
+                    if (type != null) {
+                        typeId = type.getId();
+                    }
+                    for (String id : ptr.getCatalogItemIdSearchPath()) {
+                        type = managementContext.getTypeRegistry().get(id);
                         if (type != null) {
-                            ids.add(type.getId());
+                            searchPath.add(type.getId());
                         } else {
-                            //Couldn't find a catalog item with this id, but return it anyway and
+                            //Couldn't find a catalog item with this id, but add it anyway and
                             //let the caller deal with the error.
                             //TODO under what circumstances is this permitted?
-                            ids.add(id);
+                            searchPath.add(id);
                         }
                     }
-                    return ids;
+                    return new CatalogItemIdAndSearchPath(typeId, searchPath);
                 }
                 if (ptr.getParent() != null) {
                     ptr = entityIdToManifest.get(ptr.getParent());
@@ -839,7 +862,7 @@ public abstract class RebindIteration {
                 RegisteredType t = types.get(ptr.getType(), BrooklynCatalog.DEFAULT_VERSION);
                 if (t != null) {
                     LOG.debug("Inferred catalog item ID "+t.getId()+" for "+entityManifest+" from ancestor "+ptr);
-                    return ImmutableList.of(t.getId());
+                    return new CatalogItemIdAndSearchPath(t.getId(), ImmutableList.<String>of());
                 }
                 if (ptr.getParent() != null) {
                     ptr = entityIdToManifest.get(ptr.getParent());
@@ -851,7 +874,7 @@ public abstract class RebindIteration {
             //As a last resort go through all catalog items trying to load the type and use the first that succeeds.
             //But first check if can be loaded from the default classpath
             if (JavaBrooklynClassLoadingContext.create(managementContext).tryLoadClass(entityManifest.getType()).isPresent()) {
-                return ImmutableList.of();
+                return new CatalogItemIdAndSearchPath(null, ImmutableList.<String>of());
             }
 
             // TODO get to the point when we can deprecate this behaviour!:
@@ -859,21 +882,24 @@ public abstract class RebindIteration {
                 BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(managementContext, item);
                 boolean canLoadClass = loader.tryLoadClass(entityManifest.getType()).isPresent();
                 if (canLoadClass) {
-                    LOG.warn("Missing catalog item for "+entityManifest.getId()+" ("+entityManifest.getType()+"), inferring as "+item.getId()+" because that is able to load the item");
-                    return ImmutableList.of(item.getId());
+                    LOG.warn("Missing catalog item for " + entityManifest.getId() + " (" + entityManifest.getType()
+                        + "), inferring as " + item.getId() + " because that is able to load the item");
+                    return new CatalogItemIdAndSearchPath(item.getId(), ImmutableList.<String>of());
                 }
             }
         }
-        return ImmutableList.of();
+        return new CatalogItemIdAndSearchPath(null, ImmutableList.<String>of());
     }
 
     protected static class LoadedClass<T extends BrooklynObject> {
         protected final Class<? extends T> clazz;
-        protected final List<String> catalogItemIds;
-        
-        protected LoadedClass(Class<? extends T> clazz, List<String> catalogItemIds) {
+        protected final String catalogItemId;
+        protected final List<String> searchPath;
+
+        protected LoadedClass(Class<? extends T> clazz, String catalogItemId, List<String> searchPath) {
             this.clazz = clazz;
-            this.catalogItemIds = catalogItemIds;
+            this.catalogItemId = catalogItemId;
+            this.searchPath = searchPath;
         }
     }
 
@@ -891,10 +917,12 @@ public abstract class RebindIteration {
 
         protected Entity newEntity(EntityMementoManifest entityManifest) {
             String entityId = entityManifest.getId();
-            List<String> catalogItemIds = findCatalogItemIds(classLoader, mementoManifest.getEntityIdToManifest(), entityManifest);
+            CatalogItemIdAndSearchPath idPath =
+                findCatalogItemIds(classLoader, mementoManifest.getEntityIdToManifest(), entityManifest);
             String entityType = entityManifest.getType();
 
-            LoadedClass<? extends Entity> loaded = load(Entity.class, entityType, catalogItemIds, entityId);
+            LoadedClass<? extends Entity> loaded =
+                load(Entity.class, entityType, idPath.getCatalogItemId(), idPath.getSearchPath(), entityId);
             Class<? extends Entity> entityClazz = loaded.clazz;
 
             Entity entity;
@@ -906,7 +934,8 @@ public abstract class RebindIteration {
                 entity = entityFactory.constructEntity(entityClazz, Reflections.getAllInterfaces(entityClazz), entityId);
 
             } else {
-                LOG.warn("Deprecated rebind of entity without no-arg constructor; this may not be supported in future versions: id="+entityId+"; type="+entityType);
+                LOG.warn("Deprecated rebind of entity without no-arg constructor; " +
+                    "this may not be supported in future versions: id=" + entityId+"; type=" + entityType);
 
                 // There are several possibilities for the constructor; find one that works.
                 // Prefer passing in the flags because required for Application to set the management context
@@ -916,9 +945,11 @@ public abstract class RebindIteration {
                 flags.put("id", entityId);
                 if (AbstractApplication.class.isAssignableFrom(entityClazz)) flags.put("mgmt", managementContext);
 
-                // TODO document the multiple sources of flags, and the reason for setting the mgmt context *and* supplying it as the flag
+                // TODO document the multiple sources of flags, and the reason for setting the mgmt context *and*
+                // supplying it as the flag
                 // (NB: merge reported conflict as the two things were added separately)
-                entity = invokeConstructor(null, entityClazz, new Object[] {flags}, new Object[] {flags, null}, new Object[] {null}, new Object[0]);
+                entity = invokeConstructor(null, entityClazz,
+                    new Object[] {flags}, new Object[] {flags, null}, new Object[] {null}, new Object[0]);
 
                 // In case the constructor didn't take the Map arg, then also set it here.
                 // e.g. for top-level app instances such as WebClusterDatabaseExampleApp will (often?) not have
@@ -933,90 +964,110 @@ public abstract class RebindIteration {
                 managementContext.prePreManage(entity);
             }
 
-            setCatalogItemIds(entity, loaded.catalogItemIds);
+            setCatalogItemIds(entity, loaded.catalogItemId, loaded.searchPath);
 
             return entity;
         }
 
-        protected void setCatalogItemIds(BrooklynObject object, List<String> idHierarchy) {
-            ((BrooklynObjectInternal)object).setCatalogItemIdHierarchy(idHierarchy);
+        protected void setCatalogItemIds(BrooklynObject object, String catalogItemId, List<String> searchPath) {
+            final BrooklynObjectInternal internal = (BrooklynObjectInternal) object;
+            internal.setCatalogItemId(catalogItemId);
+            internal.setCatalogItemIdSearchPath(searchPath);
         }
 
 
         protected <T extends BrooklynObject> LoadedClass<? extends T> load(Class<T> bType, Memento memento) {
-            return load(bType, memento.getType(), memento.getCatalogItemHierarchy(), memento.getId());
+            return load(bType, memento.getType(), memento.getCatalogItemId(), memento.getCatalogItemIdSearchPath(),
+                memento.getId());
         }
         
         @SuppressWarnings("unchecked")
-        protected <T extends BrooklynObject> LoadedClass<? extends T> load(Class<T> bType, String jType, List<String> catalogItemIds, String contextSuchAsId) {
+        protected <T extends BrooklynObject> LoadedClass<? extends T> load(Class<T> bType, String jType,
+                String catalogItemId, List<String> searchPath, String contextSuchAsId) {
             checkNotNull(jType, "Type of %s (%s) must not be null", contextSuchAsId, bType.getSimpleName());
 
-            List<String> idsFromReboundCatalog = MutableList.of();
-            if (catalogItemIds != null && !catalogItemIds.isEmpty()) {
-                findCatalogIdsInReboundCatalog(bType, catalogItemIds, contextSuchAsId, idsFromReboundCatalog);
-                if (idsFromReboundCatalog.size() != catalogItemIds.size()) {
-                    LOG.warn("Unable to load all catalog items "+ Iterables.toString(catalogItemIds)
-                        +" for "+contextSuchAsId + " (" + bType.getSimpleName()+"); attempting load nevertheless");
+            List<String> reboundSearchPath = MutableList.of();
+            if (searchPath != null && !searchPath.isEmpty()) {
+                for (String searchItemId : searchPath) {
+                    CatalogItem<?, ?> searchItem = findCatalogItemInReboundCatalog(bType, searchItemId, contextSuchAsId);
+                    if (searchItem != null) {
+                        reboundSearchPath.add(searchItem.getCatalogItemId());
+                    } else {
+                        LOG.warn("Unable to load catalog item "+ searchItemId
+                            +" for "+contextSuchAsId + " (" + bType.getSimpleName()+"); attempting load nevertheless");
+                    }
                 }
-                try {
-                    BrooklynClassLoadingContextSequential loader = new BrooklynClassLoadingContextSequential(managementContext);
-                    loader.add(newClassLoadingContextForCatalogItems(managementContext, idsFromReboundCatalog));
-                    return new LoadedClass<T>(loader.loadClass(jType, bType), idsFromReboundCatalog);
-                } catch (Exception e) {
-                    Exceptions.propagateIfFatal(e);
-                    LOG.warn("Unable to load "+jType+" using loader; will try reflections");
+            }
+
+            if (catalogItemId != null) {
+                CatalogItem<?, ?> catalogItem = findCatalogItemInReboundCatalog(bType, catalogItemId, contextSuchAsId);
+                if (catalogItem != null) {
+                    String transformedCatalogItemId = catalogItem.getCatalogItemId();
+                    try {
+                        BrooklynClassLoadingContextSequential loader =
+                            new BrooklynClassLoadingContextSequential(managementContext);
+                        loader.add(newClassLoadingContextForCatalogItems(managementContext, transformedCatalogItemId,
+                            reboundSearchPath));
+                        return new LoadedClass<T>(loader.loadClass(jType, bType), transformedCatalogItemId, reboundSearchPath);
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        LOG.warn("Unable to load "+jType+" using loader; will try reflections");
+                    }
+                } else {
+                    LOG.warn("Unable to load catalog item "+catalogItemId+" for " + contextSuchAsId +
+                      " ("+bType.getSimpleName()+"); will try reflection");
                 }
             }
-            
+
             try {
-                return new LoadedClass<T>((Class<T>)loadClass(jType), catalogItemIds);
+                return new LoadedClass<T>((Class<T>)loadClass(jType), catalogItemId, reboundSearchPath);
             } catch (Exception e) {
                 Exceptions.propagateIfFatal(e);
                 LOG.warn("Unable to load "+jType+" using reflections; will try standard context");
             }
 
-            if (catalogItemIds != null && !catalogItemIds.isEmpty()) {
-                throw new IllegalStateException("Unable to load catalog items " + Iterables.toString(catalogItemIds)
-                    + " for "+contextSuchAsId+", or load class from classpath");
-            } else if (BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND)) {
+            if (catalogItemId != null) {
+                throw new IllegalStateException("Unable to load catalog item " + catalogItemId + " for " +
+                    contextSuchAsId + ", or load class from classpath");
+            } else if (BrooklynFeatureEnablement.isEnabled(FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND)) {
                 //Try loading from whichever catalog bundle succeeds.
                 BrooklynCatalog catalog = managementContext.getCatalog();
                 for (CatalogItem<?, ?> item : catalog.getCatalogItems()) {
                     BrooklynClassLoadingContext catalogLoader = CatalogUtils.newClassLoadingContext(managementContext, item);
                     Maybe<Class<?>> catalogClass = catalogLoader.tryLoadClass(jType);
                     if (catalogClass.isPresent()) {
-                        return new LoadedClass<T>((Class<? extends T>) catalogClass.get(), catalogItemIds);
+                        return new LoadedClass<T>((Class<? extends T>) catalogClass.get(), catalogItemId, reboundSearchPath);
                     }
                 }
-                throw new IllegalStateException("No catalogItemId specified for "+contextSuchAsId+" and can't load class (" + jType + ") from either classpath or catalog items");
+                throw new IllegalStateException("No catalogItemId specified for " + contextSuchAsId +
+                    " and can't load class (" + jType + ") from either classpath or catalog items");
             } else {
-                throw new IllegalStateException("No catalogItemId specified for "+contextSuchAsId+" and can't load class (" + jType + ") from classpath");
+                throw new IllegalStateException("No catalogItemId specified for " + contextSuchAsId +
+                    " and can't load class (" + jType + ") from classpath");
             }
         }
 
-        private <T extends BrooklynObject> void findCatalogIdsInReboundCatalog(Class<T> bType, List<String> catalogItemIds, String contextSuchAsId, List<String> idsToUse) {
-            for (String catalogItemId : catalogItemIds) {
-                CatalogItem<?, ?> catalogItem = rebindContext.lookup().lookupCatalogItem(catalogItemId);
-                if (catalogItem == null) {
-                    if (BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND)) {
-                        // See https://issues.apache.org/jira/browse/BROOKLYN-149
-                        // This is a dangling reference to the catalog item (which will have been logged by lookupCatalogItem).
-                        // Try loading as any version.
-                        if (CatalogUtils.looksLikeVersionedId(catalogItemId)) {
-                            String symbolicName = CatalogUtils.getSymbolicNameFromVersionedId(catalogItemId);
-                            catalogItem = rebindContext.lookup().lookupCatalogItem(symbolicName);
-
-                            if (catalogItem != null) {
-                                LOG.warn("Unable to load catalog item "+ catalogItemIds +" for "+contextSuchAsId
-                                        +" ("+bType.getSimpleName()+"); will auto-upgrade to "+catalogItem.getCatalogItemId());
-                                idsToUse.add(catalogItem.getCatalogItemId());
-                            }
+        private <T extends BrooklynObject> CatalogItem<?, ?> findCatalogItemInReboundCatalog(Class<T> bType,
+                String catalogItemId, String contextSuchAsId) {
+            CatalogItem<?, ?> catalogItem = rebindContext.lookup().lookupCatalogItem(catalogItemId);
+            if (catalogItem == null) {
+                if (BrooklynFeatureEnablement.isEnabled(FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND)) {
+                    // See https://issues.apache.org/jira/browse/BROOKLYN-149
+                    // This is a dangling reference to the catalog item (which will have been logged by lookupCatalogItem).
+                    // Try loading as any version.
+                    if (CatalogUtils.looksLikeVersionedId(catalogItemId)) {
+                        String symbolicName = CatalogUtils.getSymbolicNameFromVersionedId(catalogItemId);
+                        catalogItem = rebindContext.lookup().lookupCatalogItem(symbolicName);
+
+                        if (catalogItem != null) {
+                            LOG.warn("Unable to load catalog item " + catalogItemId + " for " + contextSuchAsId
+                                + " (" + bType.getSimpleName() + "); will auto-upgrade to "
+                                + catalogItem.getCatalogItemId() + ":" + catalogItem.getVersion());
                         }
                     }
-                } else {
-                    idsToUse.add(catalogItemId);
                 }
             }
+            return catalogItem;
         }
 
         protected Class<?> loadClass(String jType) throws ClassNotFoundException {
@@ -1056,7 +1107,8 @@ public abstract class RebindIteration {
 
                 return location;
             } else {
-                LOG.warn("Deprecated rebind of location without no-arg constructor; this may not be supported in future versions: id="+locationId+"; type="+locationType);
+                LOG.warn("Deprecated rebind of location without no-arg constructor; " +
+                    "this may not be supported in future versions: id=" + locationId + "; type="+locationType);
 
                 // There are several possibilities for the constructor; find one that works.
                 // Prefer passing in the flags because required for Application to set the management context
@@ -1073,7 +1125,8 @@ public abstract class RebindIteration {
          */
         protected Policy newPolicy(PolicyMemento memento) {
             String id = memento.getId();
-            LoadedClass<? extends Policy> loaded = load(Policy.class, memento.getType(), memento.getCatalogItemHierarchy(), id);
+            LoadedClass<? extends Policy> loaded = load(Policy.class, memento.getType(), memento.getCatalogItemId(),
+                memento.getCatalogItemIdSearchPath(), id);
             Class<? extends Policy> policyClazz = loaded.clazz;
 
             Policy policy;
@@ -1084,7 +1137,8 @@ public abstract class RebindIteration {
                 ((AbstractPolicy)policy).setManagementContext(managementContext);
 
             } else {
-                LOG.warn("Deprecated rebind of policy without no-arg constructor; this may not be supported in future versions: id="+id+"; type="+policyClazz);
+                LOG.warn("Deprecated rebind of policy without no-arg constructor; " +
+                    "this may not be supported in future versions: id=" + id + "; type="+policyClazz);
 
                 // There are several possibilities for the constructor; find one that works.
                 // Prefer passing in the flags because required for Application to set the management context
@@ -1098,7 +1152,7 @@ public abstract class RebindIteration {
                 policy = invokeConstructor(null, policyClazz, new Object[] {flags});
             }
             
-            setCatalogItemIds(policy, memento.getCatalogItemHierarchy());
+            setCatalogItemIds(policy, memento.getCatalogItemId(), memento.getCatalogItemIdSearchPath());
             return policy;
         }
 
@@ -1118,7 +1172,8 @@ public abstract class RebindIteration {
                 ((AbstractEnricher)enricher).setManagementContext(managementContext);
 
             } else {
-                LOG.warn("Deprecated rebind of enricher without no-arg constructor; this may not be supported in future versions: id="+id+"; type="+enricherClazz);
+                LOG.warn("Deprecated rebind of enricher without no-arg constructor; " +
+                    "this may not be supported in future versions: id=" + id + "; type="+enricherClazz);
 
                 // There are several possibilities for the constructor; find one that works.
                 // Prefer passing in the flags because required for Application to set the management context
@@ -1132,7 +1187,7 @@ public abstract class RebindIteration {
                 enricher = invokeConstructor(reflections, enricherClazz, new Object[] {flags});
             }
             
-            setCatalogItemIds(enricher, memento.getCatalogItemHierarchy());
+            setCatalogItemIds(enricher, memento.getCatalogItemId(), memento.getCatalogItemIdSearchPath());
             return enricher;
         }
 
@@ -1152,10 +1207,11 @@ public abstract class RebindIteration {
                 ((AbstractFeed)feed).setManagementContext(managementContext);
 
             } else {
-                throw new IllegalStateException("rebind of feed without no-arg constructor unsupported: id="+id+"; type="+feedClazz);
+                throw new IllegalStateException("rebind of feed without no-arg constructor unsupported: id=" + id +
+                    "; type="+feedClazz);
             }
             
-            setCatalogItemIds(feed, memento.getCatalogItemHierarchy());
+            setCatalogItemIds(feed,  memento.getCatalogItemId(), memento.getCatalogItemIdSearchPath());
             return feed;
         }
 
@@ -1189,7 +1245,8 @@ public abstract class RebindIteration {
                     args.append(Arrays.asList(possibleArgs[i]));
                 }
             }
-            throw new IllegalStateException("Cannot instantiate instance of type "+clazz+"; expected constructor signature not found ("+args+")");
+            throw new IllegalStateException("Cannot instantiate instance of type " + clazz +
+                "; expected constructor signature not found ("+args+")");
         }
     }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/AbstractMemento.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/AbstractMemento.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/AbstractMemento.java
index 876ab24..6e2a0f2 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/AbstractMemento.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/AbstractMemento.java
@@ -24,7 +24,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import com.google.common.collect.Iterables;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.Memento;
 import org.apache.brooklyn.core.BrooklynVersion;
 import org.apache.brooklyn.core.config.Sanitizer;
@@ -47,9 +46,8 @@ public abstract class AbstractMemento implements Memento, Serializable {
         protected String type;
         protected Class<?> typeClass;
         protected String displayName;
-        // catalogItemId is retained to support rebind of previously persisted state (prior to catalogItemHierarchy)
         protected String catalogItemId;
-        protected List<String> catalogItemHierarchy;
+        protected List<String> searchPath;
         protected Map<String, Object> customFields = Maps.newLinkedHashMap();
         protected List<Object> tags = Lists.newArrayList();
         protected Map<String,Set<String>> relations = Maps.newLinkedHashMap();
@@ -68,7 +66,9 @@ public abstract class AbstractMemento implements Memento, Serializable {
             type = other.getType();
             typeClass = other.getTypeClass();
             displayName = other.getDisplayName();
-            setCatalogItemIdHierarchy(other.getCatalogItemHierarchy(), other.getCatalogItemId());
+            catalogItemId = other.getCatalogItemId();
+            searchPath = isEmpty(other.getCatalogItemIdSearchPath()) ?
+                MutableList.<String>of() : MutableList.copyOf(other.getCatalogItemIdSearchPath());
             customFields.putAll(other.getCustomFields());
             tags.addAll(other.getTags());
             relations.putAll(other.getRelations());
@@ -76,16 +76,6 @@ public abstract class AbstractMemento implements Memento, Serializable {
             return self();
         }
 
-        private void setCatalogItemIdHierarchy(List<String> otherItemIdHierarchy, String otherItemId) {
-            if (isEmpty(otherItemIdHierarchy) && otherItemId == null) {
-                catalogItemHierarchy = MutableList.of();
-            } else if (isEmpty(otherItemIdHierarchy) && otherItemId != null) {
-                catalogItemHierarchy = MutableList.of(otherItemId);
-            } else {
-                catalogItemHierarchy = MutableList.copyOf(otherItemIdHierarchy);
-            }
-        }
-
         private boolean isEmpty(List<String> ids) {
             return ids == null || ids.isEmpty();
         }
@@ -103,14 +93,8 @@ public abstract class AbstractMemento implements Memento, Serializable {
     private String type;
     private String id;
     private String displayName;
-
-    @Deprecated
-    /**
-     @deprecated since 0.11.0; retained to support rebind of previously persisted state (prior to catalogItemHierarchy)
-      */
     protected String catalogItemId;
-
-    private List<String> catalogItemHierarchy;
+    private List<String> searchPath;
     private List<Object> tags;
     private Map<String,Set<String>> relations;
     
@@ -130,7 +114,8 @@ public abstract class AbstractMemento implements Memento, Serializable {
         type = builder.type;
         typeClass = builder.typeClass;
         displayName = builder.displayName;
-        catalogItemHierarchy = builder.catalogItemHierarchy;
+        catalogItemId = builder.catalogItemId;
+        searchPath = builder.searchPath;
         setCustomFields(builder.customFields);
         tags = toPersistedList(builder.tags);
         relations = toPersistedMap(builder.relations);
@@ -141,17 +126,6 @@ public abstract class AbstractMemento implements Memento, Serializable {
     // but the method declared here simplifies how it is connected in via builder etc
     protected abstract void setCustomFields(Map<String, Object> fields);
 
-    // deals with value created by deserialization of state persisted with <catalogItemId>
-    private void normalizeCatalogItemIds() {
-        if (catalogItemHierarchy == null) {
-            catalogItemHierarchy = MutableList.of();
-        }
-        if (catalogItemHierarchy.isEmpty() && catalogItemId != null) {
-            catalogItemHierarchy = MutableList.of(catalogItemId);
-            catalogItemId = null;
-        }
-    }
-
     @Override
     public void injectTypeClass(Class<?> clazz) {
         this.typeClass = clazz;
@@ -184,14 +158,12 @@ public abstract class AbstractMemento implements Memento, Serializable {
 
     @Override
     public String getCatalogItemId() {
-        normalizeCatalogItemIds();
-        return Iterables.getFirst(getCatalogItemHierarchy(), null);
+        return catalogItemId;
     }
 
     @Override
-    public List<String> getCatalogItemHierarchy() {
-        normalizeCatalogItemIds();
-        return catalogItemHierarchy;
+    public List<String> getCatalogItemIdSearchPath() {
+        return searchPath;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BrooklynMementoManifestImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BrooklynMementoManifestImpl.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BrooklynMementoManifestImpl.java
index a4a7ecc..9e7d5da 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BrooklynMementoManifestImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BrooklynMementoManifestImpl.java
@@ -56,8 +56,8 @@ public class BrooklynMementoManifestImpl implements BrooklynMementoManifest, Ser
         public Builder brooklynVersion(String val) {
             brooklynVersion = val; return this;
         }
-        public Builder entity(String id, String type, String parent, List<String> catalogItemIds) {
-            entityIdToManifest.put(id, new EntityMementoManifestImpl(id, type, parent, catalogItemIds));
+        public Builder entity(String id, String type, String parent, String catalogItemId, List<String> searchPath) {
+            entityIdToManifest.put(id, new EntityMementoManifestImpl(id, type, parent, catalogItemId, searchPath));
             return this;
         }
         public Builder location(String id, String type) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/EntityMementoManifestImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/EntityMementoManifestImpl.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/EntityMementoManifestImpl.java
index 28c2a7e..439db61 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/EntityMementoManifestImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/EntityMementoManifestImpl.java
@@ -19,7 +19,7 @@
 package org.apache.brooklyn.core.mgmt.rebind.dto;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
+
 import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoManifest.EntityMementoManifest;
 
 import java.util.List;
@@ -28,13 +28,15 @@ public class EntityMementoManifestImpl implements EntityMementoManifest {
     private String id;
     private String type;
     private String parentId;
-    private List<String> catalogItemIds;
+    private String catalogItemId;
+    private List<String> searchPath;
 
-    public EntityMementoManifestImpl(String id, String type, String parentId, List<String> catalogItemIds) {
+    public EntityMementoManifestImpl(String id, String type, String parentId, String catalogItemId, List<String> searchPath) {
         this.id = id;
         this.type = type;
         this.parentId = parentId;
-        this.catalogItemIds = ImmutableList.copyOf(catalogItemIds);
+        this.catalogItemId = catalogItemId;
+        this.searchPath = ImmutableList.copyOf(searchPath);
     }
 
     @Override
@@ -52,18 +54,14 @@ public class EntityMementoManifestImpl implements EntityMementoManifest {
         return parentId;
     }
 
-    /**
-     * @deprecated since 0.11.0, use {@link #getCatalogItemHierarchy()} instead
-     */
-    @Deprecated
     @Override
     public String getCatalogItemId() {
-        return Iterables.getFirst(catalogItemIds, null);
+        return catalogItemId;
     }
 
     @Override
-    public List<String> getCatalogItemHierarchy() {
-        return catalogItemIds;
+    public List<String> getCatalogItemIdSearchPath() {
+        return searchPath;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
index 06f567c..c998de7 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
@@ -451,7 +451,8 @@ public class MementosGenerators {
 
         builder.id = instance.getId();
         builder.displayName = instance.getDisplayName();
-        builder.catalogItemHierarchy = instance.getCatalogItemHierarchy();
+        builder.catalogItemId = instance.getCatalogItemId();
+        builder.searchPath = instance.getCatalogItemIdSearchPath();
         builder.type = (typePrefix.isPresent() ? typePrefix.get() : "") + instance.getClass().getName();
         builder.typeClass = instance.getClass();
         if (instance instanceof EntityAdjunct) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/transformer/CompoundTransformer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/transformer/CompoundTransformer.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/transformer/CompoundTransformer.java
index ece0cdf..a793183 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/transformer/CompoundTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/transformer/CompoundTransformer.java
@@ -102,7 +102,8 @@ public class CompoundTransformer {
             return xsltTransformer(xslt);
         }
 
-        /** Discards and replaces the item at the given XPath.
+        /**
+         * Discards and replaces the item at the given XPath.
          * <p>
          * For example to replace all occurrences 
          * of text "foo" inside a tag "Tag1", you can use <code>TagName/text()[.='foo']</code>;
@@ -119,7 +120,15 @@ public class CompoundTransformer {
                     + newValue
                 + "</xsl:template>");
         }
-        
+
+        /**
+         * Discards the item at the given XPath.
+         */
+        public Builder xmlDeleteItem(String xpathToMatch) {
+            return xsltTransformerRecursiveCopyWithExtraRules(
+                "<xsl:template match=\"" + xpathToMatch + "\"></xsl:template>");
+        }
+
         /** 
          * Replaces a tag, but while continuing to recurse.
          */
@@ -182,12 +191,12 @@ public class CompoundTransformer {
          * old text matches oldSymbolicName and optionally oldVersion
          * to have newSymbolicName and newVersion.
          * <p>
-         * Also changes contents of elements within a list of 'catalogItemHierarchy' e.g.
+         * Also changes contents of elements within a list of 'catalogItemIdSearchPath' e.g.
          * <pre>
-         *     &lt;catalogItemHierarchy>
+         *     &lt;catalogItemIdSearchPath>
          *        &lt;string>one&lt;/string>
          *        &lt;string>two&lt;/string>
-         *     &lt;/catalogItemHierarchy>
+         *     &lt;/catalogItemIdSearchPath>
          * </pre>
          * </p>
          * This provides a programmatic way to change the catalogItemID. */
@@ -196,13 +205,13 @@ public class CompoundTransformer {
             if (oldVersion==null)
                 return changeCatalogItemId(oldSymbolicName, newSymbolicName, newVersion);
             // warnings use underscore notation because that's what CompoundTransformerLoader uses
-            return xmlReplaceItem("*[self::catalogItemId|parent::catalogItemHierarchy]/text()[.='"+
+            return xmlReplaceItem("*[self::catalogItemId|parent::catalogItemIdSearchPath]/text()[.='"+
                 Preconditions.checkNotNull(oldSymbolicName, "old_symbolic_name")+":"+Preconditions.checkNotNull(oldVersion, "old_version")+"']", 
                 Preconditions.checkNotNull(newSymbolicName, "new_symbolic_name")+":"+Preconditions.checkNotNull(newVersion, "new_version"));
         }
         /** As {@link #changeCatalogItemId(String, String, String, String)} matching any old version. */
         public Builder changeCatalogItemId(String oldSymbolicName, String newSymbolicName, String newVersion) {
-            return xmlReplaceItem("*[self::catalogItemId|parent::catalogItemHierarchy]/text()[starts-with(.,'"+Preconditions.checkNotNull(oldSymbolicName, "old_symbolic_name")+":')]",
+            return xmlReplaceItem("*[self::catalogItemId|parent::catalogItemIdSearchPath]/text()[starts-with(.,'"+Preconditions.checkNotNull(oldSymbolicName, "old_symbolic_name")+":')]",
                 Preconditions.checkNotNull(newSymbolicName, "new_symbolic_name")+":"+Preconditions.checkNotNull(newVersion, "new_version"));
         }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/objs/AbstractBrooklynObject.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractBrooklynObject.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractBrooklynObject.java
index 9ba131c..cadeab8 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractBrooklynObject.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractBrooklynObject.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import java.util.Set;
 
 import com.google.common.collect.ImmutableList;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -62,7 +63,8 @@ public abstract class AbstractBrooklynObject implements BrooklynObjectInternal {
     @SetFromFlag("id")
     private String id = Identifiers.makeRandomLowercaseId(10);
 
-    private Deque<String> catalogItemIdStack = new ArrayDeque<>();
+    private String catalogItemId;
+    private Deque<String> searchPath = new ArrayDeque<>();
 
     /** callers (only in TagSupport) should synchronize on this for all access */
     @SetFromFlag("tags")
@@ -100,6 +102,13 @@ public abstract class AbstractBrooklynObject implements BrooklynObjectInternal {
         // rely on sub-class to call configure(properties), because otherwise its fields will not have been initialised
     }
 
+    protected Object readResolve() {
+        if (searchPath == null) {
+            searchPath = new ArrayDeque<>();
+        }
+        return this;
+    }
+
     /**
      * See {@link #configure(Map)}
      *
@@ -202,33 +211,32 @@ public abstract class AbstractBrooklynObject implements BrooklynObjectInternal {
 
     @Override
     public void setCatalogItemId(String id) {
-        catalogItemIdStack.clear();
-        nestCatalogItemId(id);
+        catalogItemId = id;
     }
 
     @Override
-    public void setCatalogItemIdHierarchy(List<String> ids) {
-        catalogItemIdStack.clear();
-        catalogItemIdStack.addAll(ids);
+    public void setCatalogItemIdSearchPath(List<String> ids) {
+        searchPath.clear();
+        searchPath.addAll(ids);
     }
 
-        @Override
-    public void nestCatalogItemId(String id) {
+    @Override
+    public void stackCatalogItemId(String id) {
         if (null != id) {
-            catalogItemIdStack.addFirst(id);
+            if (null != catalogItemId && !catalogItemId.equals(id)) {
+                searchPath.addFirst(catalogItemId);
+            }
+            setCatalogItemId(id);
         }
     }
 
-    public List<String> getCatalogItemHierarchy() {
-        return ImmutableList.copyOf(catalogItemIdStack);
+    public List<String> getCatalogItemIdSearchPath() {
+        return ImmutableList.copyOf(searchPath);
     }
 
     @Override
     public String getCatalogItemId() {
-        if (catalogItemIdStack.size() != 0) {
-            return catalogItemIdStack.getFirst();
-        }
-        return null;
+        return catalogItemId;
     }
 
     protected void onTagsChanged() {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
index 260c210..ea02877 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
@@ -379,7 +379,8 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
         this.entity = entity;
         this.execution = ((EntityInternal) entity).getExecutionContext();
         if (entity!=null && getCatalogItemId() == null) {
-            setCatalogItemIdHierarchy(entity.getCatalogItemHierarchy());
+            setCatalogItemId(entity.getCatalogItemId());
+            setCatalogItemIdSearchPath(entity.getCatalogItemIdSearchPath());
         }
     }
     

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/81815f94/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
index 4c05ed6..6cec5ab 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
@@ -38,9 +38,13 @@ import com.google.common.annotations.Beta;
 public interface BrooklynObjectInternal extends BrooklynObject, Rebindable {
     
     void setCatalogItemId(String id);
-    void setCatalogItemIdHierarchy(List<String> id);
+    void setCatalogItemIdSearchPath(List<String> id);
 
-    void nestCatalogItemId(String id);
+    /**
+     * Moves the current catalog item id onto the start of the search path,
+     * then sets the catalog item id to the supplied value.
+     */
+    void stackCatalogItemId(String id);
     
     // subclasses typically apply stronger typing
     @Override