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 2021/05/28 15:15:12 UTC

[brooklyn-server] branch master updated (dd07beb -> b18dd9c)

This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git.


    from dd07beb  Added specList for types to be displayed int catalog
     new e812e0f  make spec hierarchy tags more strongly typed, with logic in clearer places, and conversion to strongly-typed representations done just-in-time if needed
     new 4f8c8fe  tidy spec history summaries, show when based on another type, or when it's the root blueprint
     new 0adb476  add depth_in_ancestor tags to all entity specs and entities
     new 0d68529  Merge branch 'master' into depth-tags
     new 6688770  call it `spec_hierarchy` and let it take a list when supplied via catalog
     new 8abcb0c  use single-key map style for spec_hierarchy in live specs and entities, as well as catalog
     new b18dd9c  This closes #1177

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../spi/creation/CampTypePlanTransformer.java      |   6 +-
 .../catalog/CatalogOsgiYamlTemplateTest.java       |  36 ++-
 .../CatalogYamlEntityOsgiTypeRegistryTest.java     |  22 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java    |  11 +-
 .../brooklyn/catalog/CatalogYamlTemplateTest.java  |   4 +-
 .../brooklyn/core/entity/AbstractEntity.java       |   2 +-
 .../apache/brooklyn/core/mgmt/BrooklynTags.java    | 295 +++++++++++++++------
 .../core/mgmt/internal/LocalEntityManager.java     |   2 +-
 .../core/objs/proxy/InternalEntityFactory.java     |   2 +-
 .../core/typereg/AbstractTypePlanTransformer.java  |  68 +++--
 .../typereg/JavaClassNameTypePlanTransformer.java  |   3 +-
 .../brooklyn/core/typereg/RegisteredTypes.java     |  18 +-
 .../internal/StaticTypePlanTransformer.java        |   5 +-
 .../core/location/LocationManagementTest.java      |   2 +-
 .../typereg/ExampleXmlTypePlanTransformer.java     |   5 +-
 .../apache/brooklyn/karaf/commands/EntityInfo.java |   2 +-
 .../brooklyn/rest/resources/EntityResource.java    |   8 +-
 .../brooklyn/rest/transform/TypeTransformer.java   |  15 +-
 .../SoftwareProcessLocationUnmanageTest.java       |   4 +-
 19 files changed, 344 insertions(+), 166 deletions(-)

[brooklyn-server] 05/07: call it `spec_hierarchy` and let it take a list when supplied via catalog

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 66887707c8f0d28e1d42c6c8617a0308365c8899
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri May 28 15:02:02 2021 +0100

    call it `spec_hierarchy` and let it take a list when supplied via catalog
---
 .../CatalogYamlEntityOsgiTypeRegistryTest.java     |  4 ++--
 .../brooklyn/catalog/CatalogYamlEntityTest.java    |  4 ++--
 .../apache/brooklyn/core/mgmt/BrooklynTags.java    | 26 +++++++++++++++-------
 .../core/typereg/AbstractTypePlanTransformer.java  |  8 +++----
 4 files changed, 26 insertions(+), 16 deletions(-)

diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java
index 101978c..42677df 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java
@@ -223,8 +223,8 @@ public class CatalogYamlEntityOsgiTypeRegistryTest extends CatalogYamlEntityTest
                 "  id: " + symbolicName,
                 "  version: " + TEST_VERSION,
                 "  tags:",
-                "  - "+ BrooklynTags.YAML_SPEC_HIERARCHY +": ",
-                "           format: " + CampTypePlanTransformer.FORMAT,
+                "  - "+ BrooklynTags.SPEC_HIERARCHY +": ",
+                "         - format: " + CampTypePlanTransformer.FORMAT,
                 "           summary:  Plan for " + symbolicName,
                 "           contents:  | " ,
                 "               line 1" ,
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 f99eb5c..e499639 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
@@ -248,8 +248,8 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
                 "    item:",
                 "      type: " + TestEntity.class.getName(),
                 "    tags:" ,
-                "    - yaml_spec_hierarchy:" ,
-                "        format: camp-test",
+                "    - "+BrooklynTags.SPEC_HIERARCHY+":",
+                "      - format: camp-test",
                 "        summary:  Test Type." ,
                 "        contents: |" ,
                 "            type: " + referencedSymbolicName,
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
index 3eefe5d..37f87e4 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
@@ -43,9 +43,13 @@ public class BrooklynTags {
 
     private static final Logger LOG = LoggerFactory.getLogger(BrooklynTags.class);
 
+    // could deprecate this in favour of spec_hierarchy
     public static final String YAML_SPEC_KIND = "yaml_spec";
-    public static final String YAML_SPEC_HIERARCHY = "yaml_spec_hierarchy"; // TODO rename spec_hierarchy, have spec_source for catalog which gets removed
+
+    public static final String SPEC_HIERARCHY = "spec_hierarchy";
+
     public static final String DEPTH_IN_ANCESTOR = "depth_in_ancestor";
+
     public static final String NOTES_KIND = "notes";
     public static final String OWNER_ENTITY_ID = "owner_entity_id";
     public static final String ICON_URL = "icon_url";
@@ -181,7 +185,7 @@ public class BrooklynTags {
     public static class SpecHierarchyTag implements Serializable, HasKind {
         private static final long serialVersionUID = 3805124696862755492L;
 
-        public static final String KIND = YAML_SPEC_HIERARCHY;
+        public static final String KIND = SPEC_HIERARCHY;
 
         public static class SpecSummary implements Serializable {
             @JsonProperty
@@ -257,7 +261,7 @@ public class BrooklynTags {
             }
 
             public SpecHierarchyTag buildSpecHierarchyTag() {
-                return new SpecHierarchyTag(BrooklynTags.YAML_SPEC_HIERARCHY, MutableList.of(buildSpecSummary()));
+                return new SpecHierarchyTag(SPEC_HIERARCHY, MutableList.of(buildSpecSummary()));
             }
         }
 
@@ -306,14 +310,20 @@ public class BrooklynTags {
         }
 
         public void push(SpecSummary newFirstSpecTag) {
-            // usually the list has a single element here, if
             specList.add(0, newFirstSpecTag);
         }
+        public void push(List<SpecSummary> newFirstSpecs) {
+            if (newFirstSpecs==null || newFirstSpecs.isEmpty()) return;
+            if (newFirstSpecs.size()==1) {
+                push(newFirstSpecs.iterator().next());
+            } else {
+                List<SpecSummary> l = MutableList.copyOf(newFirstSpecs);
+                Collections.reverse(l);
+                l.forEach(this::push);
+            }
+        }
         public void push(SpecHierarchyTag newFirstSpecs) {
-            // usually the list has a single element here, if
-            List<SpecSummary> l = MutableList.copyOf(newFirstSpecs.getSpecList());
-            Collections.reverse(l);
-            l.forEach(this::push);
+            push(newFirstSpecs.getSpecList());
         }
 
         public SpecSummary pop() {
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
index e97ac0f..081957d 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
@@ -18,6 +18,7 @@
  */
 package org.apache.brooklyn.core.typereg;
 
+import com.google.common.reflect.TypeToken;
 import java.util.List;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -193,11 +194,10 @@ public abstract class AbstractTypePlanTransformer implements BrooklynTypePlanTra
             spec.tag(specTag);
         }
 
-        // TODO rename key as spec_sources
-        SpecSummary source = BrooklynTags.findSingleKeyMapValue(BrooklynTags.YAML_SPEC_HIERARCHY, SpecSummary.class, type.getTags());
-        if (source != null) {
+        List<SpecSummary> sources = BrooklynTags.findSingleKeyMapValue(BrooklynTags.SPEC_HIERARCHY, new TypeToken<List<SpecSummary>>() {}, type.getTags());
+        if (sources != null) {
             specTag.modifyHeadSummary(s -> "Converted for catalog to "+s);
-            specTag.push(source);
+            specTag.push(sources);
         }
 
         if (spec instanceof EntitySpec) {

[brooklyn-server] 07/07: This closes #1177

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit b18dd9cbced79065078fcaf4e7158531adf13d4e
Merge: dd07beb 8abcb0c
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri May 28 16:14:27 2021 +0100

    This closes #1177

 .../spi/creation/CampTypePlanTransformer.java      |   6 +-
 .../catalog/CatalogOsgiYamlTemplateTest.java       |  36 ++-
 .../CatalogYamlEntityOsgiTypeRegistryTest.java     |  22 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java    |  11 +-
 .../brooklyn/catalog/CatalogYamlTemplateTest.java  |   4 +-
 .../brooklyn/core/entity/AbstractEntity.java       |   2 +-
 .../apache/brooklyn/core/mgmt/BrooklynTags.java    | 295 +++++++++++++++------
 .../core/mgmt/internal/LocalEntityManager.java     |   2 +-
 .../core/objs/proxy/InternalEntityFactory.java     |   2 +-
 .../core/typereg/AbstractTypePlanTransformer.java  |  68 +++--
 .../typereg/JavaClassNameTypePlanTransformer.java  |   3 +-
 .../brooklyn/core/typereg/RegisteredTypes.java     |  18 +-
 .../internal/StaticTypePlanTransformer.java        |   5 +-
 .../core/location/LocationManagementTest.java      |   2 +-
 .../typereg/ExampleXmlTypePlanTransformer.java     |   5 +-
 .../apache/brooklyn/karaf/commands/EntityInfo.java |   2 +-
 .../brooklyn/rest/resources/EntityResource.java    |   8 +-
 .../brooklyn/rest/transform/TypeTransformer.java   |  15 +-
 .../SoftwareProcessLocationUnmanageTest.java       |   4 +-
 19 files changed, 344 insertions(+), 166 deletions(-)

[brooklyn-server] 03/07: add depth_in_ancestor tags to all entity specs and entities

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 0adb476a47929379f15e4b00b542a47cbc7a1fda
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri May 28 14:17:30 2021 +0100

    add depth_in_ancestor tags to all entity specs and entities
---
 .../catalog/CatalogOsgiYamlTemplateTest.java       |  11 +-
 .../apache/brooklyn/core/mgmt/BrooklynTags.java    | 155 ++++++++++++---------
 .../core/typereg/AbstractTypePlanTransformer.java  |  20 ++-
 3 files changed, 119 insertions(+), 67 deletions(-)

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 a641166..e3b05ac 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
@@ -80,7 +80,9 @@ public class CatalogOsgiYamlTemplateTest extends AbstractYamlTest {
         EntitySpec<? extends Application> spec = EntityManagementUtils.createEntitySpecForApplication(mgmt(), 
             "services: [ { type: t1 } ]\n" +
             "location: localhost");
-        
+
+        // parent
+
         List<NamedStringTag> yamls = BrooklynTags.findAllNamedStringTags(BrooklynTags.YAML_SPEC_KIND, spec.getTags());
         Assert.assertEquals(yamls.size(), 1, "Expected 1 yaml tag; instead had: "+yamls);
         String yaml = Iterables.getOnlyElement(yamls).getContents();
@@ -90,18 +92,25 @@ public class CatalogOsgiYamlTemplateTest extends AbstractYamlTest {
         Assert.assertNotNull(yamlsH);
         Assert.assertEquals(yamlsH.getSpecList().size(), 1, "Expected 1 yaml tag in hierarchy; instead had: "+yamlsH);
 
+        Assert.assertNull(BrooklynTags.getDepthInAncestorTag(spec.getTags()));
+
+        // and child
+
         EntitySpec<?> child = Iterables.getOnlyElement( spec.getChildren() );
         Assert.assertEquals(child.getType().getName(), SIMPLE_ENTITY_TYPE);
         Assert.assertEquals(child.getCatalogItemId(), "t1:"+TEST_VERSION);
 
         List<NamedStringTag> yamls2 = BrooklynTags.findAllNamedStringTags(BrooklynTags.YAML_SPEC_KIND, child.getTags());
         Assert.assertEquals(yamls2.size(), 1, "Expected 1 yaml tag; instead had: "+yamls);
+
         SpecHierarchyTag yamlsH2 = BrooklynTags.findSpecHierarchyTag( child.getTags() );
         Assert.assertNotNull(yamlsH2);
         Assert.assertEquals(yamlsH2.getSpecList().size(), 1, "Expected 1 yaml tag in hierarchy; instead had: "+yamlsH2);
         Asserts.assertStringContainsIgnoreCase(yamlsH2.getSpecList().iterator().next().contents.toString(),
                 "# this sample comment should be included",
                 "SimpleEntity");
+
+        Assert.assertEquals(BrooklynTags.getDepthInAncestorTag(spec.getTags()), (Integer) 1);
     }
     
     private RegisteredType makeItem(String symbolicName, String templateType) {
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
index 78a460c..c8a76d9 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
@@ -18,12 +18,14 @@
  */
 package org.apache.brooklyn.core.mgmt;
 
+import com.google.common.reflect.TypeToken;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
 import com.google.common.base.MoreObjects;
+import java.util.Set;
 import org.apache.brooklyn.core.resolve.jackson.BeanWithTypeUtils;
 import org.apache.brooklyn.util.collections.MutableList;
 
@@ -33,6 +35,7 @@ import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.collect.Lists;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -45,6 +48,7 @@ public class BrooklynTags {
 
     public static final String YAML_SPEC_KIND = "yaml_spec";
     public static final String YAML_SPEC_HIERARCHY = "yaml_spec_hierarchy";
+    public static final String DEPTH_IN_ANCESTOR = "depth_in_ancestor";
     public static final String NOTES_KIND = "notes";
     public static final String OWNER_ENTITY_ID = "owner_entity_id";
     public static final String ICON_URL = "icon_url";
@@ -53,10 +57,94 @@ public class BrooklynTags {
      * and does not have to resolve */
     public static final Object CATALOG_TEMPLATE = "catalog_template";
 
+    /** find a tag which is a map of size one whose single key matches the key here, and if found return the value
+     * coerced to the indicated type */
+    public static <T> T findSingleKeyMapValue(String key, TypeToken<T> type, Set<Object> tags) {
+        if (tags==null) return null;
+        for (Object tag: tags) {
+            if (tag instanceof Map && ((Map)tag).size()==1 && Objects.equal(key, ((Map)tag).keySet().iterator().next())) {
+                Object value = ((Map)tag).get(key);
+                return TypeCoercions.coerce(value, type);
+            }
+        }
+        return null;
+    }
+    /** convenience for {@link #findSingleKeyMapValue(String, TypeToken, Set)} */
+    public static <T> T findSingleKeyMapValue(String key, Class<T> type, Set<Object> tags) {
+        return findSingleKeyMapValue(key, TypeToken.of(type), tags);
+    }
+
+    public static NamedStringTag findFirstNamedStringTag(String kind, Iterable<Object> tags) {
+        return findFirstOfKind(kind, NamedStringTag.class, tags);
+    }
+
+    public static List<NamedStringTag> findAllNamedStringTags(String kind, Iterable<Object> tags) {
+        return findAllOfKind(kind, NamedStringTag.class, tags);
+    }
+
+    /** @deprecated since 1.1 use {@link #findFirstNamedStringTag(String, Iterable)} */
+    @Deprecated
+    public static NamedStringTag findFirst(String kind, Iterable<Object> tags) {
+        return findFirstNamedStringTag(kind, tags);
+    }
+
+    /** @deprecated since 1.1 use {@link #findAllNamedStringTags(String, Iterable)} */
+    @Deprecated
+    public static List<NamedStringTag> findAll(String kind, Iterable<Object> tags) {
+        return findAllNamedStringTags(kind, tags);
+    }
+
     public interface HasKind {
         public String getKind();
     }
 
+    public static <T extends HasKind> T findFirstOfKind(String kind, Class<T> type, Iterable<Object> tags) {
+        for (Object o: tags) {
+            if (type.isInstance(o)) {
+                if (kind.equals(((T) o).getKind())) {
+                    return (T) o;
+                }
+            } else if (o instanceof Map) {
+                Object k2 = ((Map) o).get("kind");
+                if (kind.equals(k2)) {
+                    try {
+                        return BeanWithTypeUtils.newMapper(null, false, null, true).convertValue(o, type);
+
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        LOG.warn("Tag '"+o+"' declares kind '"+k2+"' but does not convert to "+type+" (ignoring): "+e);
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    public static <T extends HasKind> List<T> findAllOfKind(String kind, Class<T> type, Iterable<Object> tags) {
+        List<T> result = MutableList.of();
+
+        for (Object o: tags) {
+            if (type.isInstance(o)) {
+                if (kind.equals(((T) o).getKind())) {
+                    result.add( (T)o );
+                }
+            } else if (o instanceof Map) {
+                Object k2 = ((Map) o).get("kind");
+                if (kind.equals(k2)) {
+                    try {
+                        result.add( BeanWithTypeUtils.newMapper(null, false, null, true).convertValue(o, type) );
+
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        LOG.warn("Tag '"+o+"' declares kind '"+k2+"' but does not convert to "+type+" (ignoring): "+e);
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
     public static class NamedStringTag implements Serializable, HasKind {
         private static final long serialVersionUID = 7932098757009051348L;
         @JsonProperty
@@ -282,6 +370,7 @@ public class BrooklynTags {
         }
     }
 
+
     public static NamedStringTag newYamlSpecTag(String contents) {
         return new NamedStringTag(YAML_SPEC_KIND, contents);
     }
@@ -306,75 +395,13 @@ public class BrooklynTags {
         return new TraitsTag(interfaces);
     }
 
-    public static <T extends HasKind> T findFirstOfKind(String kind, Class<T> type, Iterable<Object> tags) {
-        for (Object o: tags) {
-            if (type.isInstance(o)) {
-                if (kind.equals(((T) o).getKind())) {
-                    return (T) o;
-                }
-            } else if (o instanceof Map) {
-                Object k2 = ((Map) o).get("kind");
-                if (kind.equals(k2)) {
-                    try {
-                        return BeanWithTypeUtils.newMapper(null, false, null, true).convertValue(o, type);
-
-                    } catch (Exception e) {
-                        Exceptions.propagateIfFatal(e);
-                        LOG.warn("Tag '"+o+"' declares kind '"+k2+"' but does not convert to "+type+" (ignoring): "+e);
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-    public static <T extends HasKind> List<T> findAllOfKind(String kind, Class<T> type, Iterable<Object> tags) {
-        List<T> result = MutableList.of();
-
-        for (Object o: tags) {
-            if (type.isInstance(o)) {
-                if (kind.equals(((T) o).getKind())) {
-                    result.add( (T)o );
-                }
-            } else if (o instanceof Map) {
-                Object k2 = ((Map) o).get("kind");
-                if (kind.equals(k2)) {
-                    try {
-                        result.add( BeanWithTypeUtils.newMapper(null, false, null, true).convertValue(o, type) );
-
-                    } catch (Exception e) {
-                        Exceptions.propagateIfFatal(e);
-                        LOG.warn("Tag '"+o+"' declares kind '"+k2+"' but does not convert to "+type+" (ignoring): "+e);
-                    }
-                }
-            }
-        }
-
-        return result;
-    }
 
     public static SpecHierarchyTag findSpecHierarchyTag(Iterable<Object> tags) {
         return findFirstOfKind(SpecHierarchyTag.KIND, SpecHierarchyTag.class, tags);
     }
 
-    public static NamedStringTag findFirstNamedStringTag(String kind, Iterable<Object> tags) {
-        return findFirstOfKind(kind, NamedStringTag.class, tags);
-    }
-
-    public static List<NamedStringTag> findAllNamedStringTags(String kind, Iterable<Object> tags) {
-        return findAllOfKind(kind, NamedStringTag.class, tags);
-    }
-
-    /** @deprecated since 1.1 use {@link #findFirstNamedStringTag(String, Iterable)} */
-    @Deprecated
-    public static NamedStringTag findFirst(String kind, Iterable<Object> tags) {
-        return findFirstNamedStringTag(kind, tags);
-    }
-
-    /** @deprecated since 1.1 use {@link #findAllNamedStringTags(String, Iterable)} */
-    @Deprecated
-    public static List<NamedStringTag> findAll(String kind, Iterable<Object> tags) {
-        return findAllNamedStringTags(kind, tags);
+    public static Integer getDepthInAncestorTag(Set<Object> tags) {
+        return BrooklynTags.findSingleKeyMapValue(BrooklynTags.DEPTH_IN_ANCESTOR, Integer.class, tags);
     }
 
 }
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
index 14bba3d..ad27456 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
@@ -18,10 +18,11 @@
  */
 package org.apache.brooklyn.core.typereg;
 
+import java.util.List;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import javax.annotation.Nullable;
-import org.apache.brooklyn.api.catalog.BrooklynCatalog;
+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.typereg.RegisteredType;
@@ -30,6 +31,7 @@ import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag;
 import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag.SpecSummary;
+import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.javalang.JavaClassNames;
@@ -196,7 +198,21 @@ public abstract class AbstractTypePlanTransformer implements BrooklynTypePlanTra
             spec.tag(specTag);
         }
 
+        if (spec instanceof EntitySpec) {
+            addDepthTagsWhereMissing( ((EntitySpec<?>)spec).getChildren(), 1 );
+        }
+
         return spec;
     }
-    
+
+    protected void addDepthTagsWhereMissing(List<EntitySpec<?>> children, int depth) {
+        children.forEach(c -> {
+            Integer existingDepth = BrooklynTags.getDepthInAncestorTag(c.getTags());
+            if (existingDepth==null) {
+                c.tag(MutableMap.of(BrooklynTags.DEPTH_IN_ANCESTOR, depth));
+                addDepthTagsWhereMissing(c.getChildren(), depth+1);
+            }
+        });
+    }
+
 }

[brooklyn-server] 01/07: make spec hierarchy tags more strongly typed, with logic in clearer places, and conversion to strongly-typed representations done just-in-time if needed

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit e812e0f7ee0c86a6ff835fb300b28030fd15cece
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri May 28 11:43:55 2021 +0100

    make spec hierarchy tags more strongly typed, with logic in clearer places,
    and conversion to strongly-typed representations done just-in-time if needed
---
 .../catalog/CatalogOsgiYamlTemplateTest.java       |  18 +-
 .../CatalogYamlEntityOsgiTypeRegistryTest.java     |  10 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java    |   4 +-
 .../brooklyn/catalog/CatalogYamlTemplateTest.java  |   4 +-
 .../brooklyn/core/entity/AbstractEntity.java       |   2 +-
 .../apache/brooklyn/core/mgmt/BrooklynTags.java    | 215 +++++++++++++++------
 .../core/mgmt/internal/LocalEntityManager.java     |   2 +-
 .../core/objs/proxy/InternalEntityFactory.java     |   2 +-
 .../core/typereg/AbstractTypePlanTransformer.java  |  17 +-
 .../brooklyn/core/typereg/RegisteredTypes.java     |  18 +-
 .../core/location/LocationManagementTest.java      |   2 +-
 .../apache/brooklyn/karaf/commands/EntityInfo.java |   2 +-
 .../brooklyn/rest/resources/EntityResource.java    |   7 +-
 .../SoftwareProcessLocationUnmanageTest.java       |   4 +-
 14 files changed, 207 insertions(+), 100 deletions(-)

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 ba96c85..a641166 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
@@ -18,6 +18,7 @@
  */
 package org.apache.brooklyn.camp.brooklyn.catalog;
 
+import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag;
 import static org.testng.Assert.assertEquals;
 
 import java.util.List;
@@ -80,14 +81,27 @@ public class CatalogOsgiYamlTemplateTest extends AbstractYamlTest {
             "services: [ { type: t1 } ]\n" +
             "location: localhost");
         
-        List<NamedStringTag> yamls = BrooklynTags.findAll(BrooklynTags.YAML_SPEC_KIND, spec.getTags());
+        List<NamedStringTag> yamls = BrooklynTags.findAllNamedStringTags(BrooklynTags.YAML_SPEC_KIND, spec.getTags());
         Assert.assertEquals(yamls.size(), 1, "Expected 1 yaml tag; instead had: "+yamls);
         String yaml = Iterables.getOnlyElement(yamls).getContents();
         Asserts.assertStringContains(yaml, "services:", "t1", "localhost");
-        
+
+        SpecHierarchyTag yamlsH = BrooklynTags.findSpecHierarchyTag(spec.getTags());
+        Assert.assertNotNull(yamlsH);
+        Assert.assertEquals(yamlsH.getSpecList().size(), 1, "Expected 1 yaml tag in hierarchy; instead had: "+yamlsH);
+
         EntitySpec<?> child = Iterables.getOnlyElement( spec.getChildren() );
         Assert.assertEquals(child.getType().getName(), SIMPLE_ENTITY_TYPE);
         Assert.assertEquals(child.getCatalogItemId(), "t1:"+TEST_VERSION);
+
+        List<NamedStringTag> yamls2 = BrooklynTags.findAllNamedStringTags(BrooklynTags.YAML_SPEC_KIND, child.getTags());
+        Assert.assertEquals(yamls2.size(), 1, "Expected 1 yaml tag; instead had: "+yamls);
+        SpecHierarchyTag yamlsH2 = BrooklynTags.findSpecHierarchyTag( child.getTags() );
+        Assert.assertNotNull(yamlsH2);
+        Assert.assertEquals(yamlsH2.getSpecList().size(), 1, "Expected 1 yaml tag in hierarchy; instead had: "+yamlsH2);
+        Asserts.assertStringContainsIgnoreCase(yamlsH2.getSpecList().iterator().next().contents.toString(),
+                "# this sample comment should be included",
+                "SimpleEntity");
     }
     
     private RegisteredType makeItem(String symbolicName, String templateType) {
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java
index a0c193e..101978c 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.collections.CollectionFunctionals;
 import org.apache.brooklyn.util.osgi.VersionedName;
 import org.apache.brooklyn.util.yaml.Yamls;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Predicates;
@@ -233,11 +234,12 @@ public class CatalogYamlEntityOsgiTypeRegistryTest extends CatalogYamlEntityTest
 
         RegisteredType item = mgmt().getTypeRegistry().get(symbolicName, TEST_VERSION);
 
-        assertTrue(item.getTags().stream().anyMatch(tag -> tag instanceof BrooklynTags.SpecTag));
-        BrooklynTags.SpecTag specTag = (BrooklynTags.SpecTag) item.getTags().stream().filter(x -> x instanceof BrooklynTags.SpecTag).findFirst().orElse(null);
+        BrooklynTags.SpecHierarchyTag specTag = BrooklynTags.findSpecHierarchyTag(item.getTags());
+        Assert.assertNotNull(specTag);
         assertEquals(specTag.getSpecList().size(), 1);
-        Asserts.assertEquals(((Map<String,String>)specTag.getSpecList().get(0)).get("format"), CampTypePlanTransformer.FORMAT);
-        Asserts.assertEquals(((Map<String,String>)specTag.getSpecList().get(0)).get("summary"), "Plan for " + symbolicName);
+
+        Asserts.assertEquals(specTag.getSpecList().get(0).format, CampTypePlanTransformer.FORMAT);
+        Asserts.assertEquals(specTag.getSpecList().get(0).summary, "Plan for " + symbolicName);
         deleteCatalogRegisteredType(symbolicName);
     }
 
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 96e91f6..e45bae4 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
@@ -265,8 +265,8 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
         assertEquals(entity.getEntityType().getName(), TestEntity.class.getName());
 
         // tests that the plan tag was set
-        assertTrue(entity.tags().getTags().stream().anyMatch(tag -> tag instanceof BrooklynTags.SpecTag));
-        BrooklynTags.SpecTag specTag = (BrooklynTags.SpecTag) entity.tags().getTags().stream().filter(tag -> tag instanceof BrooklynTags.SpecTag).findAny().orElse(null);
+        BrooklynTags.SpecHierarchyTag specTag = BrooklynTags.findSpecHierarchyTag(entity.tags().getTags());
+        Assert.assertNotNull(specTag);
         assertEquals(specTag.getSpecList().size(), 2);
 
         deleteCatalogRegisteredType(referencedSymbolicName);
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 ebb399e..797ec69 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
@@ -202,7 +202,7 @@ public class CatalogYamlTemplateTest extends AbstractYamlTest {
                         "services:",
                         "- type: t1"));
         
-        List<NamedStringTag> yamls = BrooklynTags.findAll(BrooklynTags.YAML_SPEC_KIND, spec.getTags());
+        List<NamedStringTag> yamls = BrooklynTags.findAllNamedStringTags(BrooklynTags.YAML_SPEC_KIND, spec.getTags());
         Assert.assertEquals(yamls.size(), 1, "Expected 1 yaml tag; instead had: "+yamls);
         String yaml = Iterables.getOnlyElement(yamls).getContents();
         Asserts.assertStringContains(yaml, "services:", "t1", "localhost");
@@ -240,7 +240,7 @@ public class CatalogYamlTemplateTest extends AbstractYamlTest {
             "services: [ { type: app1r } ]\n" +
             "location: localhost");
         
-        List<NamedStringTag> yamls = BrooklynTags.findAll(BrooklynTags.YAML_SPEC_KIND, spec.getTags());
+        List<NamedStringTag> yamls = BrooklynTags.findAllNamedStringTags(BrooklynTags.YAML_SPEC_KIND, spec.getTags());
         Assert.assertTrue(yamls.size() >= 1, "Expected at least 1 yaml tag; instead had: "+yamls);
         String yaml = yamls.iterator().next().getContents();
         Asserts.assertStringContains(yaml, "services:", "type: app1r", "localhost");
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index dac5a4e..515ed81 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -836,7 +836,7 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
         }
 
         for (Location loc : newLocations) {
-            NamedStringTag ownerEntityTag = BrooklynTags.findFirst(BrooklynTags.OWNER_ENTITY_ID, loc.tags().getTags());
+            NamedStringTag ownerEntityTag = BrooklynTags.findFirstNamedStringTag(BrooklynTags.OWNER_ENTITY_ID, loc.tags().getTags());
             if (ownerEntityTag != null) {
                 if (!getId().equals(ownerEntityTag.getContents())) {
                     // A location is "owned" if it was created as part of the EntitySpec of an entity (by Brooklyn).
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
index 11264ed..38de08a 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 
 import com.google.common.base.MoreObjects;
+import org.apache.brooklyn.core.resolve.jackson.BeanWithTypeUtils;
 import org.apache.brooklyn.util.collections.MutableList;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -32,12 +33,16 @@ import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.collect.Lists;
-import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /** @since 0.7.0 some strongly typed tags for reference; note these may migrate elsewhere! */
 @Beta
 public class BrooklynTags {
 
+    private static final Logger LOG = LoggerFactory.getLogger(BrooklynTags.class);
+
     public static final String YAML_SPEC_KIND = "yaml_spec";
     public static final String YAML_SPEC_HIERARCHY = "yaml_spec_hierarchy";
     public static final String NOTES_KIND = "notes";
@@ -48,7 +53,11 @@ public class BrooklynTags {
      * and does not have to resolve */
     public static final Object CATALOG_TEMPLATE = "catalog_template";
 
-    public static class NamedStringTag implements Serializable {
+    public interface HasKind {
+        public String getKind();
+    }
+
+    public static class NamedStringTag implements Serializable, HasKind {
         private static final long serialVersionUID = 7932098757009051348L;
         @JsonProperty
         final String kind;
@@ -84,16 +93,92 @@ public class BrooklynTags {
         }
     }
 
-    public static class SpecTag implements Serializable {
+    public static class SpecHierarchyTag implements Serializable, HasKind {
         private static final long serialVersionUID = 3805124696862755492L;
 
+        public static final String KIND = YAML_SPEC_HIERARCHY;
+
+        public static class SpecSummary implements Serializable {
+            @JsonProperty
+            public final String summary;
+            @JsonProperty
+            public final String format;
+            @JsonProperty
+            public final Object contents;
+
+            public SpecSummary(String summary, String format, Object contents) {
+                this.summary = summary;
+                this.format = format;
+                this.contents = contents;
+            }
+
+            @Override
+            public boolean equals(Object o) {
+                if (this == o) return true;
+                if (o == null || getClass() != o.getClass()) return false;
+                SpecSummary that = (SpecSummary) o;
+                return java.util.Objects.equals(summary, that.summary) && java.util.Objects.equals(format, that.format) && java.util.Objects.equals(contents, that.contents);
+            }
+
+            @Override
+            public int hashCode() {
+                return java.util.Objects.hash(summary, format, contents);
+            }
+
+            @Override
+            public String toString() {
+                return "SpecSummary{" +
+                        "summary='" + summary + '\'' +
+                        ", format='" + format + '\'' +
+                        ", contents=" + contents +
+                        '}';
+            }
+        }
+
+        public static Builder builder() { return new Builder(); }
+
+        public static class Builder {
+            private String format;
+            private String summary;
+            private Object contents;
+
+            private Builder() {}
+
+            public Builder summary(final String summary) {
+                this.summary = summary;
+                return this;
+            }
+
+            public Builder format(final String format) {
+                this.format = format;
+                return this;
+            }
+
+            public Builder contents(final Object contents) {
+                this.contents = contents;
+                return this;
+            }
+
+
+            public SpecSummary buildSpecSummary() {
+                return new SpecSummary(summary, format, contents);
+            }
+
+            public SpecHierarchyTag buildSpecHierarchyTag() {
+                return new SpecHierarchyTag(BrooklynTags.YAML_SPEC_HIERARCHY, MutableList.of(buildSpecSummary()));
+            }
+        }
+
         @JsonProperty
-        final String kind;
+        String kind;
 
         @JsonProperty
-        final List<Object> specList;
+        List<SpecSummary> specList;
+
+        // for JSON
+        private SpecHierarchyTag() {}
 
-        public SpecTag(@JsonProperty("kind")String kind, @JsonProperty("specList")List<Object> specList) {
+        private SpecHierarchyTag(String kind, List<SpecSummary> specList) {
             this.kind = kind;
             this.specList = specList;
         }
@@ -111,7 +196,7 @@ public class BrooklynTags {
         public boolean equals(Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
-            SpecTag specTag = (SpecTag) o;
+            SpecHierarchyTag specTag = (SpecHierarchyTag) o;
             return Objects.equal(kind, specTag.kind) && Objects.equal(specList, specTag.specList) ;
         }
 
@@ -124,45 +209,17 @@ public class BrooklynTags {
             return kind;
         }
 
-        public List<Object> getSpecList() {
+        public List<SpecSummary> getSpecList() {
             return specList;
         }
 
-        public void push(SpecTag currentSpecTag) {
+        public void push(SpecSummary newFirstSpecTag) {
             // usually the list has a single element here, if
-            currentSpecTag.getSpecList().forEach(e -> specList.add(0, e));
+            specList.add(0, newFirstSpecTag);
         }
-    }
-
-    public static class HierarchySpecTagBuilder {
-        private String format;
-        private String summary;
-        private Object contents;
-
-        public HierarchySpecTagBuilder format(final String format) {
-            this.format = format;
-            return this;
-        }
-
-        public HierarchySpecTagBuilder summary(final String summary) {
-            this.summary = summary;
-            return this;
-        }
-
-        public HierarchySpecTagBuilder contents(final Object contents) {
-            this.contents = contents;
-            return this;
-        }
-
-
-        public SpecTag build() {
-            return new SpecTag(BrooklynTags.YAML_SPEC_HIERARCHY, MutableList.of(
-                    MutableMap.of(
-                            "format", format,
-                            "summary", summary,
-                            "contents", contents
-                    )
-            ));
+        public void push(SpecHierarchyTag newFirstSpecs) {
+            // usually the list has a single element here, if
+            newFirstSpecs.getSpecList().forEach(this::push);
         }
     }
 
@@ -237,29 +294,75 @@ public class BrooklynTags {
         return new TraitsTag(interfaces);
     }
 
-    public static NamedStringTag findFirst(String kind, Iterable<Object> tags) {
-        for (Object object: tags) {
-            if (object instanceof NamedStringTag && kind.equals(((NamedStringTag)object).kind))
-                return (NamedStringTag) object;
+    public static <T extends HasKind> T findFirstOfKind(String kind, Class<T> type, Iterable<Object> tags) {
+        for (Object o: tags) {
+            if (type.isInstance(o)) {
+                if (kind.equals(((T) o).getKind())) {
+                    return (T) o;
+                }
+            } else if (o instanceof Map) {
+                Object k2 = ((Map) o).get("kind");
+                if (kind.equals(k2)) {
+                    try {
+                        return BeanWithTypeUtils.newMapper(null, false, null, true).convertValue(o, type);
+
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        LOG.warn("Tag '"+o+"' declares kind '"+k2+"' but does not convert to "+type+" (ignoring): "+e);
+                    }
+                }
+            }
         }
         return null;
     }
 
-    public  static SpecTag findHierarchySpecTag(String kind, Iterable<Object> tags) {
-        for (Object object: tags) {
-            if (object instanceof SpecTag && kind.equals((((SpecTag) object).kind)))
-                return (SpecTag) object;
+    public static <T extends HasKind> List<T> findAllOfKind(String kind, Class<T> type, Iterable<Object> tags) {
+        List<T> result = MutableList.of();
+
+        for (Object o: tags) {
+            if (type.isInstance(o)) {
+                if (kind.equals(((T) o).getKind())) {
+                    result.add( (T)o );
+                }
+            } else if (o instanceof Map) {
+                Object k2 = ((Map) o).get("kind");
+                if (kind.equals(k2)) {
+                    try {
+                        result.add( BeanWithTypeUtils.newMapper(null, false, null, true).convertValue(o, type) );
+
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        LOG.warn("Tag '"+o+"' declares kind '"+k2+"' but does not convert to "+type+" (ignoring): "+e);
+                    }
+                }
+            }
         }
-        return null;
+
+        return result;
+    }
+
+    public static SpecHierarchyTag findSpecHierarchyTag(Iterable<Object> tags) {
+        return findFirstOfKind(SpecHierarchyTag.KIND, SpecHierarchyTag.class, tags);
+    }
+
+    public static NamedStringTag findFirstNamedStringTag(String kind, Iterable<Object> tags) {
+        return findFirstOfKind(kind, NamedStringTag.class, tags);
     }
 
+    public static List<NamedStringTag> findAllNamedStringTags(String kind, Iterable<Object> tags) {
+        return findAllOfKind(kind, NamedStringTag.class, tags);
+    }
+
+    /** @deprecated since 1.1 use {@link #findFirstNamedStringTag(String, Iterable)} */
+    @Deprecated
+    public static NamedStringTag findFirst(String kind, Iterable<Object> tags) {
+        return findFirstNamedStringTag(kind, tags);
+    }
+
+    /** @deprecated since 1.1 use {@link #findAllNamedStringTags(String, Iterable)} */
+    @Deprecated
     public static List<NamedStringTag> findAll(String kind, Iterable<Object> tags) {
-        List<NamedStringTag> result = MutableList.of();
-        for (Object object: tags) {
-            if (object instanceof NamedStringTag && kind.equals(((NamedStringTag)object).kind))
-                result.add( (NamedStringTag) object );
-        }
-        return result;
+        return findAllNamedStringTags(kind, tags);
     }
 
 }
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
index bd8ea5b..98551fd 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
@@ -880,7 +880,7 @@ public class LocalEntityManager implements EntityManagerInternal {
 
     private void unmanageOwnedLocations(Entity e) {
         for (Location loc : e.getLocations()) {
-            NamedStringTag ownerEntityTag = BrooklynTags.findFirst(BrooklynTags.OWNER_ENTITY_ID, loc.tags().getTags());
+            NamedStringTag ownerEntityTag = BrooklynTags.findFirstNamedStringTag(BrooklynTags.OWNER_ENTITY_ID, loc.tags().getTags());
             if (ownerEntityTag != null) {
                 if (e.getId().equals(ownerEntityTag.getContents())) {
                     managementContext.getLocationManager().unmanage(loc);
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
index 8f2f24d..cf8821b 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
@@ -210,7 +210,7 @@ public class InternalEntityFactory extends InternalFactory {
             
             loadUnitializedEntity(entity, spec, options);
             
-            List<NamedStringTag> upgradedFrom = BrooklynTags.findAll(BrooklynTags.UPGRADED_FROM, spec.getTags());
+            List<NamedStringTag> upgradedFrom = BrooklynTags.findAllNamedStringTags(BrooklynTags.UPGRADED_FROM, spec.getTags());
             if (!upgradedFrom.isEmpty()) {
                 log.warn("Entity "+entity.getId()+" created with upgraded type "+entity.getCatalogItemId()+" "+upgradedFrom+" (in "+entity.getApplicationId()+", under "+entity.getParent()+")");
             }
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
index acff741..9d4333a 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
@@ -26,6 +26,7 @@ import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
+import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.javalang.JavaClassNames;
@@ -166,19 +167,19 @@ public abstract class AbstractTypePlanTransformer implements BrooklynTypePlanTra
                 ? format + " plan for " + (Strings.isNonBlank(type.getSymbolicName())? type.getSymbolicName() : type.getDisplayName())
                 : summary;
 
-        BrooklynTags.SpecTag currentSpecTag = new BrooklynTags.HierarchySpecTagBuilder()
+        BrooklynTags.SpecHierarchyTag.Builder currentSpecTagBuilder = BrooklynTags.SpecHierarchyTag.builder()
                 .format(format)
                 .summary(specSummary)
-                .contents(type.getPlan().getPlanData())
-                .build();
+                .contents(type.getPlan().getPlanData());
 
-        Object specTagObj =  spec.getTag(tag -> tag instanceof BrooklynTags.SpecTag);
-        if(specTagObj != null) {
-            BrooklynTags.SpecTag specTag = (BrooklynTags.SpecTag) specTagObj;
-            specTag.push(currentSpecTag);
+        SpecHierarchyTag specTag = BrooklynTags.findSpecHierarchyTag(spec.getTags());
+        if (specTag != null) {
+            specTag.push(currentSpecTagBuilder.buildSpecSummary());
         } else {
-            spec.tag(currentSpecTag);
+            specTag = currentSpecTagBuilder.buildSpecHierarchyTag();
+            spec.tag(specTag);
         }
+
         return spec;
     }
     
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
index 838879e..80d36ac 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
@@ -48,6 +48,7 @@ import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.mgmt.BrooklynTags.NamedStringTag;
+import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import org.apache.brooklyn.core.typereg.JavaClassNameTypePlanTransformer.JavaClassNameTypeImplementationPlan;
 import org.apache.brooklyn.util.collections.Jsonya;
@@ -332,20 +333,7 @@ public class RegisteredTypes {
     @Beta
     public static RegisteredType addTag(RegisteredType type, Object tag) {
         if (tag!=null) {
-            if (tag instanceof Map &&( (Map<String,Object>) tag).containsKey(BrooklynTags.YAML_SPEC_HIERARCHY)) {
-                Map<String,Object> mapTag = (Map<String,Object>) tag;
-                if(mapTag.containsKey(BrooklynTags.YAML_SPEC_HIERARCHY)) {
-                    Map<String,String> hierarchySpecTag = (Map<String,String>) mapTag.get(BrooklynTags.YAML_SPEC_HIERARCHY);
-                    BrooklynTags.SpecTag currentSpecTag = new BrooklynTags.HierarchySpecTagBuilder()
-                            .format(hierarchySpecTag.get("format"))
-                            .summary(StringUtils.isNotBlank(hierarchySpecTag.get("summary"))? hierarchySpecTag.get("summary") : "Plan for " + type.getSymbolicName())
-                            .contents(StringUtils.isNotBlank(hierarchySpecTag.get("contents"))? hierarchySpecTag.get("contents") : "n/a")
-                            .build();
-                    ((BasicRegisteredType)type).tags.add( currentSpecTag );
-                }
-            } else {
-                ((BasicRegisteredType) type).tags.add(tag);
-            }
+            ((BasicRegisteredType) type).tags.add(tag);
         }
         return type;
     }
@@ -659,7 +647,7 @@ public class RegisteredTypes {
     public static String getIconUrl(BrooklynObject object) {
         if (object==null) return null;
         
-        NamedStringTag fromTag = BrooklynTags.findFirst(BrooklynTags.ICON_URL, object.tags().getTags());
+        NamedStringTag fromTag = BrooklynTags.findFirstNamedStringTag(BrooklynTags.ICON_URL, object.tags().getTags());
         if (fromTag!=null) return fromTag.getContents();
         
         ManagementContext mgmt = ((BrooklynObjectInternal)object).getManagementContext();
diff --git a/core/src/test/java/org/apache/brooklyn/core/location/LocationManagementTest.java b/core/src/test/java/org/apache/brooklyn/core/location/LocationManagementTest.java
index da85a9e..97ab0b3 100644
--- a/core/src/test/java/org/apache/brooklyn/core/location/LocationManagementTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/location/LocationManagementTest.java
@@ -140,7 +140,7 @@ public class LocationManagementTest extends BrooklynAppUnitTestSupport {
         app.start(ImmutableList.<Location>of());
         Location appLocation = Iterables.getOnlyElement(app.getLocations());
 
-        NamedStringTag ownerEntityTag = BrooklynTags.findFirst(BrooklynTags.OWNER_ENTITY_ID, appLocation.tags().getTags());
+        NamedStringTag ownerEntityTag = BrooklynTags.findFirstNamedStringTag(BrooklynTags.OWNER_ENTITY_ID, appLocation.tags().getTags());
         Assert.assertNotNull(ownerEntityTag);
         Assert.assertEquals(ownerEntityTag.getContents(), app.getId());
 
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/EntityInfo.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/EntityInfo.java
index efade8d..cbb0640 100644
--- a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/EntityInfo.java
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/EntityInfo.java
@@ -120,7 +120,7 @@ public class EntityInfo implements Action {
         }
 
         if (displayBlueprint || displayAll) {
-            final Optional<String> bluePrint = Optional.ofNullable(BrooklynTags.findFirst(BrooklynTags.YAML_SPEC_KIND, entity.get().tags().getTags()))
+            final Optional<String> bluePrint = Optional.ofNullable(BrooklynTags.findFirstNamedStringTag(BrooklynTags.YAML_SPEC_KIND, entity.get().tags().getTags()))
                     .map(BrooklynTags.NamedStringTag::getContents);
             if (bluePrint.isPresent()) {
                 printHeader("Blueprint Information");
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
index 7bd5797..934e578 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
@@ -55,7 +55,6 @@ import org.apache.brooklyn.rest.transform.TaskTransformer;
 import org.apache.brooklyn.rest.util.EntityRelationUtils;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 import org.apache.brooklyn.util.collections.MutableList;
-import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.ResourceUtils;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
@@ -307,7 +306,7 @@ public class EntityResource extends AbstractBrooklynRestResource implements Enti
     @Override
     public String getSpec(String applicationToken, String entityToken) {
         Entity entity = brooklyn().getEntity(applicationToken, entityToken);
-        NamedStringTag spec = BrooklynTags.findFirst(BrooklynTags.YAML_SPEC_KIND, entity.tags().getTags());
+        NamedStringTag spec = BrooklynTags.findFirstNamedStringTag(BrooklynTags.YAML_SPEC_KIND, entity.tags().getTags());
         if (spec == null)
             return null;
         return (String) WebResourceUtils.getValueForDisplay(spec.getContents(), false, true);
@@ -316,7 +315,7 @@ public class EntityResource extends AbstractBrooklynRestResource implements Enti
     @Override
     public List<Object>  getSpecList(String applicationId, String entityId) {
         Entity entity = brooklyn().getEntity(applicationId, entityId);
-        BrooklynTags.SpecTag specTag =  BrooklynTags.findHierarchySpecTag(BrooklynTags.YAML_SPEC_HIERARCHY, entity.tags().getTags());
-        return (List<Object>) resolving( specTag.getSpecList()).preferJson(true).resolve();
+        BrooklynTags.SpecHierarchyTag specTag =  BrooklynTags.findSpecHierarchyTag(entity.tags().getTags());
+        return (List<Object>) resolving(specTag.getSpecList()).preferJson(true).resolve();
     }
 }
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/SoftwareProcessLocationUnmanageTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/SoftwareProcessLocationUnmanageTest.java
index 45f6c67..ac6ce87 100644
--- a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/SoftwareProcessLocationUnmanageTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/SoftwareProcessLocationUnmanageTest.java
@@ -106,13 +106,13 @@ public class SoftwareProcessLocationUnmanageTest extends BrooklynAppUnitTestSupp
     }
 
     private void assertOwned(BasicApplication app, Location loc) {
-        NamedStringTag ownerEntityTag = BrooklynTags.findFirst(BrooklynTags.OWNER_ENTITY_ID, loc.tags().getTags());
+        NamedStringTag ownerEntityTag = BrooklynTags.findFirstNamedStringTag(BrooklynTags.OWNER_ENTITY_ID, loc.tags().getTags());
         Assert.assertNotNull(ownerEntityTag);
         Assert.assertEquals(ownerEntityTag.getContents(), app.getId());
     }
 
     private void assertNotOwned(Location loc) {
-        NamedStringTag ownerEntityTag = BrooklynTags.findFirst(BrooklynTags.OWNER_ENTITY_ID, loc.tags().getTags());
+        NamedStringTag ownerEntityTag = BrooklynTags.findFirstNamedStringTag(BrooklynTags.OWNER_ENTITY_ID, loc.tags().getTags());
         Assert.assertNull(ownerEntityTag);
     }
 

[brooklyn-server] 04/07: Merge branch 'master' into depth-tags

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 0d6852920bb7fdc31d3160a08d4a63042ec05589
Merge: 0adb476 dd07beb
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri May 28 14:48:17 2021 +0100

    Merge branch 'master' into depth-tags
    
    Resolve the catalog yaml spec tags

 .../brooklyn/catalog/CatalogYamlEntityTest.java    |  8 ++++++-
 .../apache/brooklyn/core/mgmt/BrooklynTags.java    | 22 +++++++++++++------
 .../core/typereg/AbstractTypePlanTransformer.java  | 18 +++++++++-------
 .../brooklyn/rest/transform/TypeTransformer.java   | 25 ++++++++++++++++++----
 4 files changed, 54 insertions(+), 19 deletions(-)

diff --cc camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index e45bae4,6658654..f99eb5c
--- 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
@@@ -265,9 -271,9 +271,9 @@@ public class CatalogYamlEntityTest exte
          assertEquals(entity.getEntityType().getName(), TestEntity.class.getName());
  
          // tests that the plan tag was set
 -        assertTrue(entity.tags().getTags().stream().anyMatch(tag -> tag instanceof BrooklynTags.SpecTag));
 -        BrooklynTags.SpecTag specTag = (BrooklynTags.SpecTag) entity.tags().getTags().stream().filter(tag -> tag instanceof BrooklynTags.SpecTag).findAny().orElse(null);
 +        BrooklynTags.SpecHierarchyTag specTag = BrooklynTags.findSpecHierarchyTag(entity.tags().getTags());
 +        Assert.assertNotNull(specTag);
-         assertEquals(specTag.getSpecList().size(), 2);
+         assertEquals(specTag.getSpecList().size(), 3);
  
          deleteCatalogRegisteredType(referencedSymbolicName);
          deleteCatalogRegisteredType(referrerSymbolicName);
diff --cc core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
index c8a76d9,d5ce619..3eefe5d
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
@@@ -18,15 -18,13 +18,12 @@@
   */
  package org.apache.brooklyn.core.mgmt;
  
 +import com.google.common.reflect.TypeToken;
  import java.io.Serializable;
--import java.util.ArrayList;
--import java.util.List;
 -import java.util.ListIterator;
--import java.util.Map;
++import java.util.*;
  
  import com.google.common.base.MoreObjects;
- import java.util.Set;
 +import org.apache.brooklyn.core.resolve.jackson.BeanWithTypeUtils;
  import org.apache.brooklyn.util.collections.MutableList;
  
  import com.fasterxml.jackson.annotation.JsonIgnore;
@@@ -44,11 -39,8 +41,11 @@@ import org.slf4j.LoggerFactory
  @Beta
  public class BrooklynTags {
  
 +    private static final Logger LOG = LoggerFactory.getLogger(BrooklynTags.class);
 +
      public static final String YAML_SPEC_KIND = "yaml_spec";
--    public static final String YAML_SPEC_HIERARCHY = "yaml_spec_hierarchy";
++    public static final String YAML_SPEC_HIERARCHY = "yaml_spec_hierarchy"; // TODO rename spec_hierarchy, have spec_source for catalog which gets removed
 +    public static final String DEPTH_IN_ANCESTOR = "depth_in_ancestor";
      public static final String NOTES_KIND = "notes";
      public static final String OWNER_ENTITY_ID = "owner_entity_id";
      public static final String ICON_URL = "icon_url";
@@@ -181,98 -85,16 +178,99 @@@
          }
      }
  
 -    public static class SpecTag implements Serializable {
 +    public static class SpecHierarchyTag implements Serializable, HasKind {
          private static final long serialVersionUID = 3805124696862755492L;
  
 +        public static final String KIND = YAML_SPEC_HIERARCHY;
 +
 +        public static class SpecSummary implements Serializable {
 +            @JsonProperty
 +            public final String summary;
 +            @JsonProperty
 +            public final String format;
 +            @JsonProperty
 +            public final Object contents;
 +
++            private SpecSummary() { this(null, null, null); }; //for JSON
 +            public SpecSummary(String summary, String format, Object contents) {
 +                this.summary = summary;
 +                this.format = format;
 +                this.contents = contents;
 +            }
 +
 +            @Override
 +            public boolean equals(Object o) {
 +                if (this == o) return true;
 +                if (o == null || getClass() != o.getClass()) return false;
 +                SpecSummary that = (SpecSummary) o;
 +                return java.util.Objects.equals(summary, that.summary) && java.util.Objects.equals(format, that.format) && java.util.Objects.equals(contents, that.contents);
 +            }
 +
 +            @Override
 +            public int hashCode() {
 +                return java.util.Objects.hash(summary, format, contents);
 +            }
 +
 +            @Override
 +            public String toString() {
 +                return "SpecSummary{" +
 +                        "summary='" + summary + '\'' +
 +                        ", format='" + format + '\'' +
 +                        ", contents=" + contents +
 +                        '}';
 +            }
 +        }
 +
 +        public static Builder builder() { return new Builder(); }
 +        public static Builder builder(SpecSummary base) { return new Builder(base); }
 +
 +        public static class Builder {
 +            private String summary;
 +            private String format;
 +            private Object contents;
 +
 +            private Builder() {}
 +            private Builder(SpecSummary base) {
 +                summary = base.summary;
 +                format = base.format;
 +                contents = base.contents;
 +            }
 +
 +            public Builder summary(final String summary) {
 +                this.summary = summary;
 +                return this;
 +            }
 +
 +            public Builder format(final String format) {
 +                this.format = format;
 +                return this;
 +            }
 +
 +            public Builder contents(final Object contents) {
 +                this.contents = contents;
 +                return this;
 +            }
 +
 +
 +            public SpecSummary buildSpecSummary() {
 +                return new SpecSummary(summary, format, contents);
 +            }
 +
 +            public SpecHierarchyTag buildSpecHierarchyTag() {
 +                return new SpecHierarchyTag(BrooklynTags.YAML_SPEC_HIERARCHY, MutableList.of(buildSpecSummary()));
 +            }
 +        }
 +
          @JsonProperty
 -        final String kind;
 +        String kind;
  
          @JsonProperty
 -        final List<Object> specList;
 +        List<SpecSummary> specList;
 +
 +        // for JSON
 +        private SpecHierarchyTag() {}
  
 -        public SpecTag(@JsonProperty("kind")String kind, @JsonProperty("specList")List<Object> specList) {
 +        private SpecHierarchyTag(String kind, List<SpecSummary> specList) {
              this.kind = kind;
              this.specList = specList;
          }
@@@ -307,20 -129,45 +305,32 @@@
              return specList;
          }
  
 -        public void push(SpecTag currentSpecTag) {
 -            List<Object> specs = currentSpecTag.getSpecList();
 -            ListIterator<Object> li = specs.listIterator(specs.size());
 -            while (li.hasPrevious()) {
 -                specList.add(0, li.previous());
 -            }
 -        }
 -    }
 -
 -    public static class HierarchySpecTagBuilder {
 -        private String format;
 -        private String summary;
 -        private Object contents;
 -
 -        public HierarchySpecTagBuilder format(final String format) {
 -            this.format = format;
 -            return this;
 +        public void push(SpecSummary newFirstSpecTag) {
 +            // usually the list has a single element here, if
 +            specList.add(0, newFirstSpecTag);
          }
 -
 -        public HierarchySpecTagBuilder summary(final String summary) {
 -            this.summary = summary;
 -            return this;
 +        public void push(SpecHierarchyTag newFirstSpecs) {
 +            // usually the list has a single element here, if
-             newFirstSpecs.getSpecList().forEach(this::push);
++            List<SpecSummary> l = MutableList.copyOf(newFirstSpecs.getSpecList());
++            Collections.reverse(l);
++            l.forEach(this::push);
          }
  
 -        public HierarchySpecTagBuilder contents(final Object contents) {
 -            this.contents = contents;
 -            return this;
 +        public SpecSummary pop() {
 +            if (getSpecList().isEmpty()) return null;
 +            return getSpecList().remove(0);
          }
  
 -
 -        public SpecTag build() {
 -            return new SpecTag(BrooklynTags.YAML_SPEC_HIERARCHY, MutableList.of(
 -                    MutableMap.of(
 -                            "format", format,
 -                            "summary", summary,
 -                            "contents", contents
 -                    )
 -            ));
++        public boolean modifyHeadSummary(java.util.function.Function<String, String> previousSummaryModification) {
++            if (!getSpecList().isEmpty() && previousSummaryModification!=null) {
++                SpecSummary oldHead = pop();
++                SpecSummary newPrevHead = SpecHierarchyTag.builder(oldHead).summary(
++                        previousSummaryModification.apply(oldHead.summary)).buildSpecSummary();
++                push(newPrevHead);
++                return true;
++            }
++            return false;
+         }
      }
  
      public static class ListTag<T> {
diff --cc core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
index ad27456,393614d..e97ac0f
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
@@@ -167,52 -161,30 +167,54 @@@ public abstract class AbstractTypePlanT
  
      protected abstract Object createBean(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception;
  
 -    protected AbstractBrooklynObjectSpec<?,?> decorateWithHierarchySpecTag(AbstractBrooklynObjectSpec<?, ?> spec, RegisteredType type, final String format, final String summary) {
 -        final String specSummary = Strings.isBlank(summary)
 -                ? format + " plan for " + (Strings.isNonBlank(type.getSymbolicName())? type.getSymbolicName() : type.getDisplayName())
 -                : summary;
 -
 -        BrooklynTags.SpecTag currentSpecTag = new BrooklynTags.HierarchySpecTagBuilder()
 +    protected AbstractBrooklynObjectSpec<?,?> decorateWithHierarchySpecTag(AbstractBrooklynObjectSpec<?, ?> spec, RegisteredType type,
 +                                                                           final String format, @Nullable final String summary,
 +                                                                           @Nullable Function<String,String> previousSummaryModification) {
 +        final String specSummary = Strings.isNonBlank(summary)
 +                ? summary
 +                : format + " plan" +
 +                    (Strings.isNonBlank(type.getSymbolicName())
-                             ? "for type "+type.getSymbolicName()
++                            ? " for type "+type.getSymbolicName()
 +                            : Strings.isNonBlank(type.getDisplayName())
-                                 ? "for "+type.getDisplayName()
++                                ? " for "+type.getDisplayName()
 +                                : "");
 +
 +        BrooklynTags.SpecHierarchyTag.Builder currentSpecTagBuilder = BrooklynTags.SpecHierarchyTag.builder()
                  .format(format)
                  .summary(specSummary)
 -                .contents(type.getPlan().getPlanData())
 -                .build();
 +                .contents(type.getPlan().getPlanData());
  
 -        Object rtSpecTag =  type.getTags().stream().filter(tag -> tag instanceof BrooklynTags.SpecTag).findAny().orElse(null);
 -        if(rtSpecTag != null) {
 -            currentSpecTag.push((BrooklynTags.SpecTag)rtSpecTag);
 +        SpecHierarchyTag specTag = BrooklynTags.findSpecHierarchyTag(spec.getTags());
 +        if (specTag != null) {
-             if (!specTag.getSpecList().isEmpty() && previousSummaryModification!=null) {
-                 SpecSummary oldHead = specTag.pop();
-                 SpecSummary newPrevHead = SpecHierarchyTag.builder(oldHead).summary(
-                         previousSummaryModification.apply(oldHead.summary)).buildSpecSummary();
-                 specTag.push(newPrevHead);
-             }
++            specTag.modifyHeadSummary(previousSummaryModification);
 +            specTag.push(currentSpecTagBuilder.buildSpecSummary());
 +        } else {
 +            specTag = currentSpecTagBuilder.buildSpecHierarchyTag();
 +            spec.tag(specTag);
          }
  
 -        Object specTagObj =  spec.getTag(tag -> tag instanceof BrooklynTags.SpecTag);
 -        if(specTagObj != null) {
 -            BrooklynTags.SpecTag specTag = (BrooklynTags.SpecTag) specTagObj;
 -            specTag.push(currentSpecTag);
 -        } else {
 -            spec.tag(currentSpecTag);
++        // TODO rename key as spec_sources
++        SpecSummary source = BrooklynTags.findSingleKeyMapValue(BrooklynTags.YAML_SPEC_HIERARCHY, SpecSummary.class, type.getTags());
++        if (source != null) {
++            specTag.modifyHeadSummary(s -> "Converted for catalog to "+s);
++            specTag.push(source);
+         }
++
 +        if (spec instanceof EntitySpec) {
 +            addDepthTagsWhereMissing( ((EntitySpec<?>)spec).getChildren(), 1 );
 +        }
 +
          return spec;
      }
 -    
 +
 +    protected void addDepthTagsWhereMissing(List<EntitySpec<?>> children, int depth) {
 +        children.forEach(c -> {
 +            Integer existingDepth = BrooklynTags.getDepthInAncestorTag(c.getTags());
 +            if (existingDepth==null) {
 +                c.tag(MutableMap.of(BrooklynTags.DEPTH_IN_ANCESTOR, depth));
 +                addDepthTagsWhereMissing(c.getChildren(), depth+1);
 +            }
 +        });
 +    }
 +
  }
diff --cc rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
index 2eb5c35,741c887..84f38b5
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
@@@ -18,6 -18,6 +18,7 @@@
   */
  package org.apache.brooklyn.rest.transform;
  
++import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag;
  import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceUriBuilder;
  
  import java.net.URI;
@@@ -98,6 -98,19 +99,22 @@@ public class TypeTransformer 
                  result.setExtraField("iconUrlSource", item.getIconUrl());
              }
          }
+ 
+         // adding tag type spec hierarchy using hierarchy tag
 -        BrooklynTags.SpecTag currentSpecTag = new BrooklynTags.HierarchySpecTagBuilder()
++        SpecHierarchyTag currentSpecTag = SpecHierarchyTag.builder()
+                 .format(StringUtils.isBlank(item.getPlan().getPlanFormat()) ? CampTypePlanTransformer.FORMAT : item.getPlan().getPlanFormat())
+                 // the default type implementation is camp in this location, but hierarchy tag provides the original implementation, so it takes precedence.
+                 .summary((StringUtils.isBlank(item.getPlan().getPlanFormat()) ? CampTypePlanTransformer.FORMAT : item.getPlan().getPlanFormat()) + " implementation")
+                 .contents(item.getPlan().getPlanData())
 -                .build();
 -        BrooklynTags.SpecTag specTag =  BrooklynTags.findHierarchySpecTag(BrooklynTags.YAML_SPEC_HIERARCHY, item.getTags());
++                .buildSpecHierarchyTag();
++
++        SpecHierarchyTag specTag = BrooklynTags.findSpecHierarchyTag(item.getTags());
+         if(specTag!= null){
++            currentSpecTag.modifyHeadSummary(s -> "Converted to "+s);
+             currentSpecTag.push(specTag);
+         }
++
+         result.setExtraField("specList", currentSpecTag.getSpecList());
          
          if (detail) {
              if (RegisteredTypes.isSubtypeOf(item, Entity.class)) {

[brooklyn-server] 06/07: use single-key map style for spec_hierarchy in live specs and entities, as well as catalog

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 8abcb0cf21da297f059c2b22441a2ace707e0bc1
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri May 28 15:15:41 2021 +0100

    use single-key map style for spec_hierarchy in live specs and entities, as well as catalog
---
 .../spi/creation/CampTypePlanTransformer.java      |   4 +-
 .../catalog/CatalogOsgiYamlTemplateTest.java       |  21 +--
 .../CatalogYamlEntityOsgiTypeRegistryTest.java     |  14 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java    |   5 +-
 .../apache/brooklyn/core/mgmt/BrooklynTags.java    | 197 +++++++++------------
 .../core/typereg/AbstractTypePlanTransformer.java  |  36 ++--
 .../typereg/JavaClassNameTypePlanTransformer.java  |   3 +-
 .../brooklyn/core/typereg/RegisteredTypes.java     |   2 -
 .../internal/StaticTypePlanTransformer.java        |   5 +-
 .../typereg/ExampleXmlTypePlanTransformer.java     |   3 +-
 .../brooklyn/rest/resources/EntityResource.java    |   5 +-
 .../brooklyn/rest/transform/TypeTransformer.java   |  17 +-
 12 files changed, 142 insertions(+), 170 deletions(-)

diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
index 5558429..c6d0cb3 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
@@ -20,7 +20,6 @@ package org.apache.brooklyn.camp.brooklyn.spi.creation;
 
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.function.BiFunction;
 
 import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
@@ -28,7 +27,6 @@ import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry.RegisteredTypeKind;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.api.typereg.RegisteredType.TypeImplementationPlan;
 import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
-import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.typereg.*;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
@@ -106,7 +104,7 @@ public class CampTypePlanTransformer extends AbstractTypePlanTransformer {
     @Override
     protected AbstractBrooklynObjectSpec<?, ?> createSpec(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
         try {
-            return decorateWithHierarchySpecTag(new CampResolver(mgmt, type, context).createSpec(), type, "Brooklyn CAMP", null,
+            return decorateWithCommonTags(new CampResolver(mgmt, type, context).createSpec(), type, null, null,
                     prevHeadSpecSummary -> "Based on "+prevHeadSpecSummary);
 
         } catch (Exception e) {
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 e3b05ac..7436870 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
@@ -18,11 +18,8 @@
  */
 package org.apache.brooklyn.camp.brooklyn.catalog;
 
-import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag;
-import static org.testng.Assert.assertEquals;
-
+import com.google.common.collect.Iterables;
 import java.util.List;
-
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
@@ -30,6 +27,7 @@ import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.mgmt.BrooklynTags.NamedStringTag;
+import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecSummary;
 import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
 import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest;
 import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
@@ -38,10 +36,9 @@ import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.test.support.TestResourceUnavailableException;
 import org.apache.brooklyn.util.osgi.OsgiTestResources;
 import org.testng.Assert;
+import static org.testng.Assert.assertEquals;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 
 public class CatalogOsgiYamlTemplateTest extends AbstractYamlTest {
     
@@ -88,9 +85,9 @@ public class CatalogOsgiYamlTemplateTest extends AbstractYamlTest {
         String yaml = Iterables.getOnlyElement(yamls).getContents();
         Asserts.assertStringContains(yaml, "services:", "t1", "localhost");
 
-        SpecHierarchyTag yamlsH = BrooklynTags.findSpecHierarchyTag(spec.getTags());
+        List<SpecSummary> yamlsH = BrooklynTags.findSpecHierarchyTag(spec.getTags());
         Assert.assertNotNull(yamlsH);
-        Assert.assertEquals(yamlsH.getSpecList().size(), 1, "Expected 1 yaml tag in hierarchy; instead had: "+yamlsH);
+        Assert.assertEquals(yamlsH.size(), 1, "Expected 1 yaml tag in hierarchy; instead had: "+yamlsH);
 
         Assert.assertNull(BrooklynTags.getDepthInAncestorTag(spec.getTags()));
 
@@ -103,14 +100,14 @@ public class CatalogOsgiYamlTemplateTest extends AbstractYamlTest {
         List<NamedStringTag> yamls2 = BrooklynTags.findAllNamedStringTags(BrooklynTags.YAML_SPEC_KIND, child.getTags());
         Assert.assertEquals(yamls2.size(), 1, "Expected 1 yaml tag; instead had: "+yamls);
 
-        SpecHierarchyTag yamlsH2 = BrooklynTags.findSpecHierarchyTag( child.getTags() );
+        List<SpecSummary> yamlsH2 = BrooklynTags.findSpecHierarchyTag(child.getTags());
         Assert.assertNotNull(yamlsH2);
-        Assert.assertEquals(yamlsH2.getSpecList().size(), 1, "Expected 1 yaml tag in hierarchy; instead had: "+yamlsH2);
-        Asserts.assertStringContainsIgnoreCase(yamlsH2.getSpecList().iterator().next().contents.toString(),
+        Assert.assertEquals(yamlsH2.size(), 1, "Expected 1 yaml tag in hierarchy; instead had: "+yamlsH2);
+        Asserts.assertStringContainsIgnoreCase(yamlsH2.iterator().next().contents.toString(),
                 "# this sample comment should be included",
                 "SimpleEntity");
 
-        Assert.assertEquals(BrooklynTags.getDepthInAncestorTag(spec.getTags()), (Integer) 1);
+        Assert.assertEquals(BrooklynTags.getDepthInAncestorTag(child.getTags()), (Integer) 1);
     }
     
     private RegisteredType makeItem(String symbolicName, String templateType) {
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java
index 42677df..87b3b78 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java
@@ -18,11 +18,13 @@
  */
 package org.apache.brooklyn.camp.brooklyn.catalog;
 
+import java.util.List;
 import java.util.Map;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.CampTypePlanTransformer;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
+import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecSummary;
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
 import org.apache.brooklyn.entity.stock.BasicEntity;
@@ -227,19 +229,19 @@ public class CatalogYamlEntityOsgiTypeRegistryTest extends CatalogYamlEntityTest
                 "         - format: " + CampTypePlanTransformer.FORMAT,
                 "           summary:  Plan for " + symbolicName,
                 "           contents:  | " ,
-                "               line 1" ,
-                "               line 2" ,
+                "               line 1",
+                "               line 2",
                 "  itemType: entity",
                 "  item: " + BasicEntity.class.getName());
 
         RegisteredType item = mgmt().getTypeRegistry().get(symbolicName, TEST_VERSION);
 
-        BrooklynTags.SpecHierarchyTag specTag = BrooklynTags.findSpecHierarchyTag(item.getTags());
+        List<SpecSummary> specTag = BrooklynTags.findSpecHierarchyTag(item.getTags());
         Assert.assertNotNull(specTag);
-        assertEquals(specTag.getSpecList().size(), 1);
+        assertEquals(specTag.size(), 1);
 
-        Asserts.assertEquals(specTag.getSpecList().get(0).format, CampTypePlanTransformer.FORMAT);
-        Asserts.assertEquals(specTag.getSpecList().get(0).summary, "Plan for " + symbolicName);
+        Asserts.assertEquals(specTag.get(0).format, CampTypePlanTransformer.FORMAT);
+        Asserts.assertEquals(specTag.get(0).summary, "Plan for " + symbolicName);
         deleteCatalogRegisteredType(symbolicName);
     }
 
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 e499639..a33329d 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
@@ -26,6 +26,7 @@ import java.util.Set;
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.core.entity.Dumper;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
+import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecSummary;
 import org.apache.brooklyn.util.collections.MutableSet;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -271,9 +272,9 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
         assertEquals(entity.getEntityType().getName(), TestEntity.class.getName());
 
         // tests that the plan tag was set
-        BrooklynTags.SpecHierarchyTag specTag = BrooklynTags.findSpecHierarchyTag(entity.tags().getTags());
+        List<SpecSummary> specTag = BrooklynTags.findSpecHierarchyTag(entity.tags().getTags());
         Assert.assertNotNull(specTag);
-        assertEquals(specTag.getSpecList().size(), 3);
+        assertEquals(specTag.size(), 3);
 
         deleteCatalogRegisteredType(referencedSymbolicName);
         deleteCatalogRegisteredType(referrerSymbolicName);
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
index 37f87e4..1b43c0a 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
@@ -22,7 +22,9 @@ import com.google.common.reflect.TypeToken;
 import java.io.Serializable;
 import java.util.*;
 
-import com.google.common.base.MoreObjects;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
 import org.apache.brooklyn.core.resolve.jackson.BeanWithTypeUtils;
 import org.apache.brooklyn.util.collections.MutableList;
 
@@ -32,6 +34,7 @@ import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.collect.Lists;
+import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.slf4j.Logger;
@@ -46,8 +49,11 @@ public class BrooklynTags {
     // could deprecate this in favour of spec_hierarchy
     public static final String YAML_SPEC_KIND = "yaml_spec";
 
+    /** used as a single-key map key whose value is a list of {@link SpecSummary} maps, first one being the original source, then going in to conversions/definitions */
     public static final String SPEC_HIERARCHY = "spec_hierarchy";
 
+    /** used as a single-key map key whose value is a number 1 or more, indicating how many ancestors up need to be traversed
+     * to find where a particular entity was defined */
     public static final String DEPTH_IN_ANCESTOR = "depth_in_ancestor";
 
     public static final String NOTES_KIND = "notes";
@@ -60,21 +66,44 @@ public class BrooklynTags {
 
     /** find a tag which is a map of size one whose single key matches the key here, and if found return the value
      * coerced to the indicated type */
-    public static <T> T findSingleKeyMapValue(String key, TypeToken<T> type, Set<Object> tags) {
+    public static <T> T findSingleKeyMapValue(String key, TypeToken<T> type, Iterable<Object> tags) {
         if (tags==null) return null;
         for (Object tag: tags) {
-            if (tag instanceof Map && ((Map)tag).size()==1 && Objects.equal(key, ((Map)tag).keySet().iterator().next())) {
-                Object value = ((Map)tag).get(key);
+            if (isTagSingleKeyMap(tag, key)) {
+                java.lang.Object value = ((Map)tag).get(key);
                 return TypeCoercions.coerce(value, type);
             }
         }
         return null;
     }
-    /** convenience for {@link #findSingleKeyMapValue(String, TypeToken, Set)} */
-    public static <T> T findSingleKeyMapValue(String key, Class<T> type, Set<Object> tags) {
+
+    private static <Object> boolean isTagSingleKeyMap(Object tag, String key) {
+        return tag instanceof Map && ((Map) tag).size() == 1 && Objects.equal(key, ((Map) tag).keySet().iterator().next());
+    }
+
+    /** convenience for {@link #findSingleKeyMapValue(String, TypeToken, Iterable)} */
+    public static <T> T findSingleKeyMapValue(String key, Class<T> type, Iterable<Object> tags) {
         return findSingleKeyMapValue(key, TypeToken.of(type), tags);
     }
 
+    public static <T> void upsertSingleKeyMapValueTag(AbstractBrooklynObjectSpec<?, ?> spec, String key, T value) {
+        MutableList<Object> tags = MutableList.copyOf(spec.getTags());
+        AtomicInteger count = new AtomicInteger();
+        List<Object> newTags = tags.stream().map(t -> {
+            if (isTagSingleKeyMap(t, key)) {
+                count.incrementAndGet();
+                return MutableMap.of(key, value);
+            } else {
+                return t;
+            }
+        }).collect(Collectors.toList());
+        if (count.get()>0) {
+            spec.tagsReplace(newTags);
+        } else {
+            spec.tag(MutableMap.of(key, value));
+        }
+    }
+
     public static NamedStringTag findFirstNamedStringTag(String kind, Iterable<Object> tags) {
         return findFirstOfKind(kind, NamedStringTag.class, tags);
     }
@@ -182,47 +211,41 @@ public class BrooklynTags {
         }
     }
 
-    public static class SpecHierarchyTag implements Serializable, HasKind {
-        private static final long serialVersionUID = 3805124696862755492L;
-
-        public static final String KIND = SPEC_HIERARCHY;
-
-        public static class SpecSummary implements Serializable {
-            @JsonProperty
-            public final String summary;
-            @JsonProperty
-            public final String format;
-            @JsonProperty
-            public final Object contents;
+    public static class SpecSummary implements Serializable {
+        @JsonProperty
+        public final String summary;
+        @JsonProperty
+        public final String format;
+        @JsonProperty
+        public final Object contents;
 
-            private SpecSummary() { this(null, null, null); }; //for JSON
-            public SpecSummary(String summary, String format, Object contents) {
-                this.summary = summary;
-                this.format = format;
-                this.contents = contents;
-            }
+        private SpecSummary() { this(null, null, null); }; //for JSON
+        public SpecSummary(String summary, String format, Object contents) {
+            this.summary = summary;
+            this.format = format;
+            this.contents = contents;
+        }
 
-            @Override
-            public boolean equals(Object o) {
-                if (this == o) return true;
-                if (o == null || getClass() != o.getClass()) return false;
-                SpecSummary that = (SpecSummary) o;
-                return java.util.Objects.equals(summary, that.summary) && java.util.Objects.equals(format, that.format) && java.util.Objects.equals(contents, that.contents);
-            }
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            SpecSummary that = (SpecSummary) o;
+            return java.util.Objects.equals(summary, that.summary) && java.util.Objects.equals(format, that.format) && java.util.Objects.equals(contents, that.contents);
+        }
 
-            @Override
-            public int hashCode() {
-                return java.util.Objects.hash(summary, format, contents);
-            }
+        @Override
+        public int hashCode() {
+            return java.util.Objects.hash(summary, format, contents);
+        }
 
-            @Override
-            public String toString() {
-                return "SpecSummary{" +
-                        "summary='" + summary + '\'' +
-                        ", format='" + format + '\'' +
-                        ", contents=" + contents +
-                        '}';
-            }
+        @Override
+        public String toString() {
+            return "SpecSummary{" +
+                    "summary='" + summary + '\'' +
+                    ", format='" + format + '\'' +
+                    ", contents=" + contents +
+                    '}';
         }
 
         public static Builder builder() { return new Builder(); }
@@ -233,7 +256,9 @@ public class BrooklynTags {
             private String format;
             private Object contents;
 
-            private Builder() {}
+            private Builder() {
+            }
+
             private Builder(SpecSummary base) {
                 summary = base.summary;
                 format = base.format;
@@ -255,88 +280,36 @@ public class BrooklynTags {
                 return this;
             }
 
-
-            public SpecSummary buildSpecSummary() {
+            public SpecSummary build() {
                 return new SpecSummary(summary, format, contents);
             }
-
-            public SpecHierarchyTag buildSpecHierarchyTag() {
-                return new SpecHierarchyTag(SPEC_HIERARCHY, MutableList.of(buildSpecSummary()));
-            }
         }
 
-        @JsonProperty
-        String kind;
-
-        @JsonProperty
-        List<SpecSummary> specList;
-
-        // for JSON
-        private SpecHierarchyTag() {}
-
-        private SpecHierarchyTag(String kind, List<SpecSummary> specList) {
-            this.kind = kind;
-            this.specList = specList;
-        }
-
-        @Override
-        public String toString() {
-            return MoreObjects.toStringHelper(this)
-                    .omitNullValues()
-                    .add("kind", kind)
-                    .add("specList", specList)
-                    .toString();
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            SpecHierarchyTag specTag = (SpecHierarchyTag) o;
-            return Objects.equal(kind, specTag.kind) && Objects.equal(specList, specTag.specList) ;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hashCode(kind, specList);
-        }
-
-        public String getKind() {
-            return kind;
-        }
-
-        public List<SpecSummary> getSpecList() {
-            return specList;
-        }
-
-        public void push(SpecSummary newFirstSpecTag) {
+        public static void pushToList(List<SpecSummary> specList, SpecSummary newFirstSpecTag) {
             specList.add(0, newFirstSpecTag);
         }
-        public void push(List<SpecSummary> newFirstSpecs) {
+        public static void pushToList(List<SpecSummary> specList, List<SpecSummary> newFirstSpecs) {
             if (newFirstSpecs==null || newFirstSpecs.isEmpty()) return;
             if (newFirstSpecs.size()==1) {
-                push(newFirstSpecs.iterator().next());
+                pushToList(specList, newFirstSpecs.iterator().next());
             } else {
                 List<SpecSummary> l = MutableList.copyOf(newFirstSpecs);
                 Collections.reverse(l);
-                l.forEach(this::push);
+                l.forEach(li -> pushToList(specList, li));
             }
         }
-        public void push(SpecHierarchyTag newFirstSpecs) {
-            push(newFirstSpecs.getSpecList());
-        }
 
-        public SpecSummary pop() {
-            if (getSpecList().isEmpty()) return null;
-            return getSpecList().remove(0);
+        public static SpecSummary popFromList(List<SpecSummary> specList) {
+            if (specList.isEmpty()) return null;
+            return specList.remove(0);
         }
 
-        public boolean modifyHeadSummary(java.util.function.Function<String, String> previousSummaryModification) {
-            if (!getSpecList().isEmpty() && previousSummaryModification!=null) {
-                SpecSummary oldHead = pop();
-                SpecSummary newPrevHead = SpecHierarchyTag.builder(oldHead).summary(
-                        previousSummaryModification.apply(oldHead.summary)).buildSpecSummary();
-                push(newPrevHead);
+        public static boolean modifyHeadSummary(List<SpecSummary> specList, java.util.function.Function<String, String> previousSummaryModification) {
+            if (!specList.isEmpty() && previousSummaryModification!=null) {
+                SpecSummary oldHead = popFromList(specList);
+                SpecSummary newPrevHead = SpecSummary.builder(oldHead).summary(
+                        previousSummaryModification.apply(oldHead.summary)).build();
+                pushToList(specList, newPrevHead);
                 return true;
             }
             return false;
@@ -416,8 +389,8 @@ public class BrooklynTags {
     }
 
 
-    public static SpecHierarchyTag findSpecHierarchyTag(Iterable<Object> tags) {
-        return findFirstOfKind(SpecHierarchyTag.KIND, SpecHierarchyTag.class, tags);
+    public static List<SpecSummary> findSpecHierarchyTag(Iterable<Object> tags) {
+        return findSingleKeyMapValue(SPEC_HIERARCHY, new TypeToken<List<SpecSummary>>() {}, tags);
     }
 
     public static Integer getDepthInAncestorTag(Set<Object> tags) {
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
index 081957d..97832b7 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
@@ -30,8 +30,8 @@ import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
-import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag;
-import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag.SpecSummary;
+import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecSummary;
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
@@ -168,10 +168,11 @@ public abstract class AbstractTypePlanTransformer implements BrooklynTypePlanTra
 
     protected abstract Object createBean(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception;
 
-    protected AbstractBrooklynObjectSpec<?,?> decorateWithHierarchySpecTag(AbstractBrooklynObjectSpec<?, ?> spec, RegisteredType type,
-                                                                           final String format, @Nullable final String summary,
-                                                                           @Nullable Function<String,String> previousSummaryModification) {
-        final String specSummary = Strings.isNonBlank(summary)
+    protected AbstractBrooklynObjectSpec<?,?> decorateWithCommonTags(AbstractBrooklynObjectSpec<?, ?> spec, RegisteredType type,
+                                                                     @Nullable String format, @Nullable String summary,
+                                                                     @Nullable Function<String,String> previousSummaryModification) {
+        if (Strings.isBlank(format)) format = getFormatCode();
+        final String specSummaryText = Strings.isNonBlank(summary)
                 ? summary
                 : format + " plan" +
                     (Strings.isNonBlank(type.getSymbolicName())
@@ -180,25 +181,26 @@ public abstract class AbstractTypePlanTransformer implements BrooklynTypePlanTra
                                 ? " for "+type.getDisplayName()
                                 : "");
 
-        BrooklynTags.SpecHierarchyTag.Builder currentSpecTagBuilder = BrooklynTags.SpecHierarchyTag.builder()
+        SpecSummary specSummary = SpecSummary.builder()
                 .format(format)
-                .summary(specSummary)
-                .contents(type.getPlan().getPlanData());
+                .summary(specSummaryText)
+                .contents(type.getPlan().getPlanData())
+                .build();
 
-        SpecHierarchyTag specTag = BrooklynTags.findSpecHierarchyTag(spec.getTags());
+        List<SpecSummary> specTag = BrooklynTags.findSpecHierarchyTag(spec.getTags());
         if (specTag != null) {
-            specTag.modifyHeadSummary(previousSummaryModification);
-            specTag.push(currentSpecTagBuilder.buildSpecSummary());
+            SpecSummary.modifyHeadSummary(specTag, previousSummaryModification);
+            SpecSummary.pushToList(specTag, specSummary);
         } else {
-            specTag = currentSpecTagBuilder.buildSpecHierarchyTag();
-            spec.tag(specTag);
+            specTag = MutableList.of(specSummary);
         }
 
-        List<SpecSummary> sources = BrooklynTags.findSingleKeyMapValue(BrooklynTags.SPEC_HIERARCHY, new TypeToken<List<SpecSummary>>() {}, type.getTags());
+        List<SpecSummary> sources = BrooklynTags.findSpecHierarchyTag(type.getTags());
         if (sources != null) {
-            specTag.modifyHeadSummary(s -> "Converted for catalog to "+s);
-            specTag.push(sources);
+            SpecSummary.modifyHeadSummary(specTag, s -> "Converted for catalog to "+s);
+            SpecSummary.pushToList(specTag, sources);
         }
+        BrooklynTags.upsertSingleKeyMapValueTag(spec, BrooklynTags.SPEC_HIERARCHY, specTag);
 
         if (spec instanceof EntitySpec) {
             addDepthTagsWhereMissing( ((EntitySpec<?>)spec).getChildren(), 1 );
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/JavaClassNameTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/JavaClassNameTypePlanTransformer.java
index b5e2394..1e9fdcc 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/JavaClassNameTypePlanTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/JavaClassNameTypePlanTransformer.java
@@ -19,7 +19,6 @@
 package org.apache.brooklyn.core.typereg;
 
 import java.lang.reflect.Constructor;
-import java.util.List;
 
 import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
 import org.apache.brooklyn.api.objs.BrooklynObject;
@@ -65,7 +64,7 @@ public class JavaClassNameTypePlanTransformer extends AbstractTypePlanTransforme
     @SuppressWarnings({ "unchecked" })
     @Override
     protected AbstractBrooklynObjectSpec<?,?> createSpec(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
-        return decorateWithHierarchySpecTag(RegisteredTypes.newSpecInstance(mgmt, (Class<? extends BrooklynObject>) getType(type, context)), type, FORMAT, null, null);
+        return decorateWithCommonTags(RegisteredTypes.newSpecInstance(mgmt, (Class<? extends BrooklynObject>) getType(type, context)), type, null, null, null);
     }
 
     @Override
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
index 80d36ac..bd496fd 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
@@ -48,7 +48,6 @@ import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.mgmt.BrooklynTags.NamedStringTag;
-import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import org.apache.brooklyn.core.typereg.JavaClassNameTypePlanTransformer.JavaClassNameTypeImplementationPlan;
 import org.apache.brooklyn.util.collections.Jsonya;
@@ -60,7 +59,6 @@ import org.apache.brooklyn.util.text.NaturalOrderComparator;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.text.VersionComparator;
 import org.apache.brooklyn.util.yaml.Yamls;
-import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/StaticTypePlanTransformer.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/StaticTypePlanTransformer.java
index 413bacd..a0e8388 100644
--- a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/StaticTypePlanTransformer.java
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/StaticTypePlanTransformer.java
@@ -18,7 +18,6 @@
  */
 package org.apache.brooklyn.core.catalog.internal;
 
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -93,9 +92,9 @@ public class StaticTypePlanTransformer extends AbstractTypePlanTransformer {
     @Override
     protected AbstractBrooklynObjectSpec<?, ?> createSpec(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
         if (REGISTERED_SPECS.containsKey(type.getSymbolicName()))
-            return decorateWithHierarchySpecTag(get(type.getSymbolicName()), type, FORMAT, null, null);
+            return decorateWithCommonTags(get(type.getSymbolicName()), type, null, null, null);
         if (type.getPlan().getPlanData()!=null && REGISTERED_SPECS.containsKey(type.getPlan().getPlanData()))
-            return decorateWithHierarchySpecTag(get((String)type.getPlan().getPlanData()), type, FORMAT, null, null);
+            return decorateWithCommonTags(get((String)type.getPlan().getPlanData()), type, null, null, null);
         return null;
     }
 
diff --git a/core/src/test/java/org/apache/brooklyn/core/typereg/ExampleXmlTypePlanTransformer.java b/core/src/test/java/org/apache/brooklyn/core/typereg/ExampleXmlTypePlanTransformer.java
index ab8a6cd..320ac00 100644
--- a/core/src/test/java/org/apache/brooklyn/core/typereg/ExampleXmlTypePlanTransformer.java
+++ b/core/src/test/java/org/apache/brooklyn/core/typereg/ExampleXmlTypePlanTransformer.java
@@ -19,7 +19,6 @@
 package org.apache.brooklyn.core.typereg;
 
 import java.io.StringReader;
-import java.util.List;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -68,7 +67,7 @@ public class ExampleXmlTypePlanTransformer extends AbstractTypePlanTransformer {
 
     @Override
     protected AbstractBrooklynObjectSpec<?, ?> createSpec(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
-        return decorateWithHierarchySpecTag(toEntitySpec(parseXml((String)type.getPlan().getPlanData()),
+        return decorateWithCommonTags(toEntitySpec(parseXml((String)type.getPlan().getPlanData()),
             isApplicationExpected(type, context) ? 0 : 1), type, "example-xml", null, null);
     }
 
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
index 934e578..f21d6e9 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.rest.resources;
 import static javax.ws.rs.core.Response.created;
 import static javax.ws.rs.core.Response.status;
 import static javax.ws.rs.core.Response.Status.ACCEPTED;
+import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecSummary;
 import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceAbsoluteUriBuilder;
 
 import java.net.URI;
@@ -315,7 +316,7 @@ public class EntityResource extends AbstractBrooklynRestResource implements Enti
     @Override
     public List<Object>  getSpecList(String applicationId, String entityId) {
         Entity entity = brooklyn().getEntity(applicationId, entityId);
-        BrooklynTags.SpecHierarchyTag specTag =  BrooklynTags.findSpecHierarchyTag(entity.tags().getTags());
-        return (List<Object>) resolving(specTag.getSpecList()).preferJson(true).resolve();
+        List<SpecSummary> specTag = BrooklynTags.findSpecHierarchyTag(entity.tags().getTags());
+        return (List<Object>) resolving(specTag).preferJson(true).resolve();
     }
 }
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
index 84f38b5..683d9d2 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
@@ -18,7 +18,6 @@
  */
 package org.apache.brooklyn.rest.transform;
 
-import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag;
 import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceUriBuilder;
 
 import java.net.URI;
@@ -46,6 +45,7 @@ import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.CampTypePlanTransformer;
 import org.apache.brooklyn.core.entity.EntityDynamicType;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
+import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecSummary;
 import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult;
 import org.apache.brooklyn.core.objs.BrooklynTypes;
 import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
@@ -61,6 +61,7 @@ import org.apache.brooklyn.rest.domain.SummaryComparators;
 import org.apache.brooklyn.rest.domain.TypeDetail;
 import org.apache.brooklyn.rest.domain.TypeSummary;
 import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
@@ -101,20 +102,22 @@ public class TypeTransformer {
         }
 
         // adding tag type spec hierarchy using hierarchy tag
-        SpecHierarchyTag currentSpecTag = SpecHierarchyTag.builder()
+        SpecSummary currentSpec = SpecSummary.builder()
                 .format(StringUtils.isBlank(item.getPlan().getPlanFormat()) ? CampTypePlanTransformer.FORMAT : item.getPlan().getPlanFormat())
                 // the default type implementation is camp in this location, but hierarchy tag provides the original implementation, so it takes precedence.
                 .summary((StringUtils.isBlank(item.getPlan().getPlanFormat()) ? CampTypePlanTransformer.FORMAT : item.getPlan().getPlanFormat()) + " implementation")
                 .contents(item.getPlan().getPlanData())
-                .buildSpecHierarchyTag();
+                .build();
 
-        SpecHierarchyTag specTag = BrooklynTags.findSpecHierarchyTag(item.getTags());
+        List<SpecSummary> specTag = BrooklynTags.findSpecHierarchyTag(item.getTags());
         if(specTag!= null){
-            currentSpecTag.modifyHeadSummary(s -> "Converted to "+s);
-            currentSpecTag.push(specTag);
+            SpecSummary.modifyHeadSummary(specTag, s -> "Converted to "+s);
+            SpecSummary.pushToList(specTag, currentSpec);
+        } else {
+            specTag = MutableList.of(currentSpec);
         }
 
-        result.setExtraField("specList", currentSpecTag.getSpecList());
+        result.setExtraField("specList", specTag);
         
         if (detail) {
             if (RegisteredTypes.isSubtypeOf(item, Entity.class)) {

[brooklyn-server] 02/07: tidy spec history summaries, show when based on another type, or when it's the root blueprint

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 4f8c8fe9c945701d591c3035d6c7435e5929d339
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri May 28 13:13:51 2021 +0100

    tidy spec history summaries, show when based on another type, or when it's the root blueprint
---
 .../spi/creation/CampTypePlanTransformer.java      |  4 +++-
 .../apache/brooklyn/core/mgmt/BrooklynTags.java    | 14 ++++++++++++-
 .../core/typereg/AbstractTypePlanTransformer.java  | 24 ++++++++++++++++++----
 .../typereg/JavaClassNameTypePlanTransformer.java  |  2 +-
 .../internal/StaticTypePlanTransformer.java        |  4 ++--
 .../typereg/ExampleXmlTypePlanTransformer.java     |  2 +-
 6 files changed, 40 insertions(+), 10 deletions(-)

diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
index b76cd19..5558429 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampTypePlanTransformer.java
@@ -106,7 +106,9 @@ public class CampTypePlanTransformer extends AbstractTypePlanTransformer {
     @Override
     protected AbstractBrooklynObjectSpec<?, ?> createSpec(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
         try {
-            return decorateWithHierarchySpecTag(new CampResolver(mgmt, type, context).createSpec(), type, FORMAT, null);
+            return decorateWithHierarchySpecTag(new CampResolver(mgmt, type, context).createSpec(), type, "Brooklyn CAMP", null,
+                    prevHeadSpecSummary -> "Based on "+prevHeadSpecSummary);
+
         } catch (Exception e) {
             Exceptions.propagateIfFatal(e);
             String message = null;
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
index 38de08a..78a460c 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
@@ -136,13 +136,19 @@ public class BrooklynTags {
         }
 
         public static Builder builder() { return new Builder(); }
+        public static Builder builder(SpecSummary base) { return new Builder(base); }
 
         public static class Builder {
-            private String format;
             private String summary;
+            private String format;
             private Object contents;
 
             private Builder() {}
+            private Builder(SpecSummary base) {
+                summary = base.summary;
+                format = base.format;
+                contents = base.contents;
+            }
 
             public Builder summary(final String summary) {
                 this.summary = summary;
@@ -221,6 +227,12 @@ public class BrooklynTags {
             // usually the list has a single element here, if
             newFirstSpecs.getSpecList().forEach(this::push);
         }
+
+        public SpecSummary pop() {
+            if (getSpecList().isEmpty()) return null;
+            return getSpecList().remove(0);
+        }
+
     }
 
     public static class ListTag<T> {
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
index 9d4333a..14bba3d 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
@@ -18,7 +18,9 @@
  */
 package org.apache.brooklyn.core.typereg;
 
+import java.util.function.Function;
 import java.util.function.Supplier;
+import javax.annotation.Nullable;
 import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
@@ -27,6 +29,7 @@ import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag;
+import org.apache.brooklyn.core.mgmt.BrooklynTags.SpecHierarchyTag.SpecSummary;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.javalang.JavaClassNames;
@@ -162,10 +165,17 @@ public abstract class AbstractTypePlanTransformer implements BrooklynTypePlanTra
 
     protected abstract Object createBean(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception;
 
-    protected AbstractBrooklynObjectSpec<?,?> decorateWithHierarchySpecTag(AbstractBrooklynObjectSpec<?, ?> spec, RegisteredType type, final String format, final String summary) {
-        final String specSummary = Strings.isBlank(summary)
-                ? format + " plan for " + (Strings.isNonBlank(type.getSymbolicName())? type.getSymbolicName() : type.getDisplayName())
-                : summary;
+    protected AbstractBrooklynObjectSpec<?,?> decorateWithHierarchySpecTag(AbstractBrooklynObjectSpec<?, ?> spec, RegisteredType type,
+                                                                           final String format, @Nullable final String summary,
+                                                                           @Nullable Function<String,String> previousSummaryModification) {
+        final String specSummary = Strings.isNonBlank(summary)
+                ? summary
+                : format + " plan" +
+                    (Strings.isNonBlank(type.getSymbolicName())
+                            ? "for type "+type.getSymbolicName()
+                            : Strings.isNonBlank(type.getDisplayName())
+                                ? "for "+type.getDisplayName()
+                                : "");
 
         BrooklynTags.SpecHierarchyTag.Builder currentSpecTagBuilder = BrooklynTags.SpecHierarchyTag.builder()
                 .format(format)
@@ -174,6 +184,12 @@ public abstract class AbstractTypePlanTransformer implements BrooklynTypePlanTra
 
         SpecHierarchyTag specTag = BrooklynTags.findSpecHierarchyTag(spec.getTags());
         if (specTag != null) {
+            if (!specTag.getSpecList().isEmpty() && previousSummaryModification!=null) {
+                SpecSummary oldHead = specTag.pop();
+                SpecSummary newPrevHead = SpecHierarchyTag.builder(oldHead).summary(
+                        previousSummaryModification.apply(oldHead.summary)).buildSpecSummary();
+                specTag.push(newPrevHead);
+            }
             specTag.push(currentSpecTagBuilder.buildSpecSummary());
         } else {
             specTag = currentSpecTagBuilder.buildSpecHierarchyTag();
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/JavaClassNameTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/JavaClassNameTypePlanTransformer.java
index 6bfe0a7..b5e2394 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/JavaClassNameTypePlanTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/JavaClassNameTypePlanTransformer.java
@@ -65,7 +65,7 @@ public class JavaClassNameTypePlanTransformer extends AbstractTypePlanTransforme
     @SuppressWarnings({ "unchecked" })
     @Override
     protected AbstractBrooklynObjectSpec<?,?> createSpec(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
-        return  decorateWithHierarchySpecTag(RegisteredTypes.newSpecInstance(mgmt, (Class<? extends BrooklynObject>) getType(type, context)), type, FORMAT, null);
+        return decorateWithHierarchySpecTag(RegisteredTypes.newSpecInstance(mgmt, (Class<? extends BrooklynObject>) getType(type, context)), type, FORMAT, null, null);
     }
 
     @Override
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/StaticTypePlanTransformer.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/StaticTypePlanTransformer.java
index 2524564..413bacd 100644
--- a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/StaticTypePlanTransformer.java
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/StaticTypePlanTransformer.java
@@ -93,9 +93,9 @@ public class StaticTypePlanTransformer extends AbstractTypePlanTransformer {
     @Override
     protected AbstractBrooklynObjectSpec<?, ?> createSpec(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
         if (REGISTERED_SPECS.containsKey(type.getSymbolicName()))
-            return decorateWithHierarchySpecTag(get(type.getSymbolicName()), type, FORMAT, null );
+            return decorateWithHierarchySpecTag(get(type.getSymbolicName()), type, FORMAT, null, null);
         if (type.getPlan().getPlanData()!=null && REGISTERED_SPECS.containsKey(type.getPlan().getPlanData()))
-            return decorateWithHierarchySpecTag(get((String)type.getPlan().getPlanData()), type, FORMAT, null);
+            return decorateWithHierarchySpecTag(get((String)type.getPlan().getPlanData()), type, FORMAT, null, null);
         return null;
     }
 
diff --git a/core/src/test/java/org/apache/brooklyn/core/typereg/ExampleXmlTypePlanTransformer.java b/core/src/test/java/org/apache/brooklyn/core/typereg/ExampleXmlTypePlanTransformer.java
index be3bd54..ab8a6cd 100644
--- a/core/src/test/java/org/apache/brooklyn/core/typereg/ExampleXmlTypePlanTransformer.java
+++ b/core/src/test/java/org/apache/brooklyn/core/typereg/ExampleXmlTypePlanTransformer.java
@@ -69,7 +69,7 @@ public class ExampleXmlTypePlanTransformer extends AbstractTypePlanTransformer {
     @Override
     protected AbstractBrooklynObjectSpec<?, ?> createSpec(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
         return decorateWithHierarchySpecTag(toEntitySpec(parseXml((String)type.getPlan().getPlanData()),
-            isApplicationExpected(type, context) ? 0 : 1), type, "example-xml", null);
+            isApplicationExpected(type, context) ? 0 : 1), type, "example-xml", null, null);
     }
 
     private static boolean isApplicationExpected(RegisteredType type, RegisteredTypeLoadingContext context) {