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 <[email protected]>
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);
     }
 

Reply via email to