Repository: brooklyn-server Updated Branches: refs/heads/master 4956e6c48 -> b60863556
BROOKLYN-460: Brooklyn Camp syntax for adding tags to an entity spec services: - type: BasicApplication brooklyn.tags: [tag1, tag2] brooklyn.tags adds NamedStringTag to an entity spec. Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/08d7bf98 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/08d7bf98 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/08d7bf98 Branch: refs/heads/master Commit: 08d7bf9856f9d2f3c7e405dec76d5c7578842aeb Parents: 022be36 Author: Valentin Aitken <[email protected]> Authored: Mon Mar 27 13:14:40 2017 +0300 Committer: Valentin Aitken <[email protected]> Committed: Thu Apr 6 07:11:50 2017 +0300 ---------------------------------------------------------------------- .../camp/brooklyn/BrooklynCampReservedKeys.java | 1 + .../BrooklynComponentTemplateResolver.java | 1 + .../BrooklynEntityDecorationResolver.java | 56 +++++++- .../spi/creation/BrooklynEntityMatcher.java | 1 + .../camp/brooklyn/BrooklynTagsRebindTest.java | 59 ++++++++ .../camp/brooklyn/spi/dsl/TagsYamlTest.java | 135 +++++++++++++++++++ .../apache/brooklyn/core/mgmt/BrooklynTags.java | 18 ++- 7 files changed, 268 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/08d7bf98/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampReservedKeys.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampReservedKeys.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampReservedKeys.java index 52f52e9..3332d0c 100644 --- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampReservedKeys.java +++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampReservedKeys.java @@ -27,4 +27,5 @@ public interface BrooklynCampReservedKeys { public static final String BROOKLYN_INITIALIZERS = "brooklyn.initializers"; public static final String BROOKLYN_PARAMETERS = "brooklyn.parameters"; public static final String BROOKLYN_CATALOG = "brooklyn.catalog"; + public static final String BROOKLYN_TAGS = "brooklyn.tags"; } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/08d7bf98/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java index adeb7a7..34f67c3 100644 --- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java +++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java @@ -257,6 +257,7 @@ public class BrooklynComponentTemplateResolver { new BrooklynEntityDecorationResolver.EnricherSpecResolver(yamlLoader).decorate(spec, attrs, encounteredRegisteredTypeIds); new BrooklynEntityDecorationResolver.InitializerResolver(yamlLoader).decorate(spec, attrs, encounteredRegisteredTypeIds); new BrooklynEntityDecorationResolver.SpecParameterResolver(yamlLoader).decorate(spec, attrs, encounteredRegisteredTypeIds); + new BrooklynEntityDecorationResolver.TagsResolver(yamlLoader).decorate(spec, attrs, encounteredRegisteredTypeIds); configureEntityConfig(spec, encounteredRegisteredTypeIds); } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/08d7bf98/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java index 071ec1b..ca47f7e 100644 --- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java +++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java @@ -18,14 +18,18 @@ */ package org.apache.brooklyn.camp.brooklyn.spi.creation; +import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.base.Function; +import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.sun.org.apache.xpath.internal.operations.Bool; import org.apache.brooklyn.api.entity.EntityInitializer; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.api.mgmt.ManagementContext; @@ -37,13 +41,21 @@ import org.apache.brooklyn.api.sensor.EnricherSpec; import org.apache.brooklyn.api.typereg.RegisteredType; import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys; import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator.InstantiatorFromKey; +import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslAccessible; +import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon; +import org.apache.brooklyn.core.mgmt.BrooklynTags; import org.apache.brooklyn.core.objs.BasicSpecParameter; import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts; import org.apache.brooklyn.core.typereg.RegisteredTypes; import org.apache.brooklyn.util.collections.MutableList; import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.core.task.DeferredSupplier; import org.apache.brooklyn.util.guava.Maybe; +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkArgument; + /** * Pattern for resolving "decorations" on service specs / entity specs, such as policies, enrichers, etc. * @@ -227,7 +239,49 @@ public abstract class BrooklynEntityDecorationResolver<DT> { @Override protected void addDecorationFromJsonMap(Map<?, ?> decorationJson, List<SpecParameter<?>> decorations) { - throw new IllegalStateException("Not called"); + throw new UnsupportedOperationException("SpecParameterResolver.addDecorationFromJsonMap should never be called."); + } + } + + public static class TagsResolver extends BrooklynEntityDecorationResolver<Iterable<Object>> { + protected TagsResolver(BrooklynYamlTypeInstantiator.Factory instantiator) { + super(instantiator); + } + + @Override + public void decorate(EntitySpec<?> entitySpec, ConfigBag attrs, Set<String> encounteredRegisteredTypeIds) { + Iterable<Object> decorationAttributeJsonValue = getDecorationAttributeJsonValue(attrs); + if (decorationAttributeJsonValue != null) { + entitySpec.tagsAdd(decorationAttributeJsonValue); + } + } + + @Override + protected String getDecorationKind() { + return "Brooklyn Tags"; + } + + @Override + protected Iterable<Object> getDecorationAttributeJsonValue(ConfigBag attrs) { + Object brooklynTags = attrs.getStringKey(BrooklynCampReservedKeys.BROOKLYN_TAGS); + if (brooklynTags == null) { + return null; + } else if (!(brooklynTags instanceof List)) { + throw new IllegalArgumentException(BrooklynCampReservedKeys.BROOKLYN_TAGS + " should be a List of String elements. You supplied " + brooklynTags); + } else { + checkArgument(Iterables.all((List) brooklynTags, new Predicate() { + @Override + public boolean apply(Object input) { + return !(input instanceof DeferredSupplier); + } + }), BrooklynCampReservedKeys.BROOKLYN_TAGS + " should not contain DeferredSupplier. A DeferredSupplier is made when using $brooklyn:attributeWhenReady. You supplied " + brooklynTags); + return (List)brooklynTags; + } + } + + @Override + protected void addDecorationFromJsonMap(Map<?, ?> decorationJson, List<Iterable<Object>> decorations) { + throw new UnsupportedOperationException("TagsResolver.addDecorationFromJsonMap should never be called."); } } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/08d7bf98/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java index b0356c3..091e760 100644 --- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java +++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java @@ -129,6 +129,7 @@ public class BrooklynEntityMatcher implements PdpMatcher { addCustomListAttributeIfNonNull(builder, attrs, BrooklynCampReservedKeys.BROOKLYN_CHILDREN); addCustomListAttributeIfNonNull(builder, attrs, BrooklynCampReservedKeys.BROOKLYN_PARAMETERS); addCustomMapAttributeIfNonNull(builder, attrs, BrooklynCampReservedKeys.BROOKLYN_CATALOG); + addCustomListAttributeIfNonNull(builder, attrs, BrooklynCampReservedKeys.BROOKLYN_TAGS); brooklynFlags.putAll(attrs); if (!brooklynFlags.isEmpty()) { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/08d7bf98/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/BrooklynTagsRebindTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/BrooklynTagsRebindTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/BrooklynTagsRebindTest.java new file mode 100644 index 0000000..9381308 --- /dev/null +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/BrooklynTagsRebindTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.camp.brooklyn; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.entity.stock.BasicApplication; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class BrooklynTagsRebindTest extends AbstractYamlRebindTest { + @Test + public void testRebindTags() throws Exception { + final Entity entity = createAndStartApplication("services:", + "- type: " + BasicApplication.class.getName(), + " brooklyn.tags:", + " - 1", + " - hi world", + " - $brooklyn:object:", + " type: " + TagObject.class.getName()); + Assert.assertTrue(entity.tags().getTags().contains(1)); + Assert.assertTrue(entity.tags().getTags().contains("hi world")); + Assert.assertTrue(Iterables.any(entity.tags().getTags(), new Predicate<Object>() { + @Override + public boolean apply(Object input) { + return input instanceof TagObject; + } + })); + rebind(); + final Entity newEntity = mgmt().getEntityManager().getEntity(entity.getId()); + Assert.assertTrue(newEntity.tags().getTags().contains(1)); + Assert.assertTrue(newEntity.tags().getTags().contains("hi world")); + Assert.assertTrue(Iterables.any(newEntity.tags().getTags(), new Predicate<Object>() { + @Override + public boolean apply(Object input) { + return input instanceof TagObject; + } + })); + } + + public static class TagObject {} +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/08d7bf98/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/TagsYamlTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/TagsYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/TagsYamlTest.java new file mode 100644 index 0000000..b384a8b --- /dev/null +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/TagsYamlTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2016 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.brooklyn.camp.brooklyn.spi.dsl; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest; +import org.apache.brooklyn.entity.stock.BasicApplication; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.exceptions.CompoundRuntimeException; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.testng.annotations.Test; + +import javax.annotation.Nullable; + +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +public class TagsYamlTest extends AbstractYamlTest { + @Test + public void testBrooklynCampSingleTag() throws Exception { + final Entity app = createAndStartApplication( + "services:", + "- type: " + BasicApplication.class.getName(), + " brooklyn.tags:", + " - hi"); + assertTrue(app.tags().getTags().contains("hi")); + } + + @Test + public void testBrooklynCampMultipleTags() throws Exception { + final Entity app = createAndStartApplication( + "services:", + "- type: " + BasicApplication.class.getName(), + " brooklyn.tags:", + " - tag1", + " - \"2\"", + " - \"- 3\""); + assertTrue(app.tags().getTags().contains("tag1")); + assertTrue(app.tags().getTags().contains("2")); + assertTrue(app.tags().getTags().contains("- 3")); + } + + @Test + public void testBrooklynCampTagsFailNonList() throws Exception { + try { + final Entity app = createAndStartApplication( + "services:", + "- type: " + BasicApplication.class.getName(), + " brooklyn.tags:", + " tag1: true", + " tag2: 2"); + fail("Should throw IllegalArgumentException exception."); + } catch (CompoundRuntimeException e) { + Asserts.assertStringContainsAtLeastOne(Exceptions.getFirstInteresting(e).getMessage(),"brooklyn.tags must be a list, is: "); + } + } + + @Test + public void testBrooklynCampKnowsIntegerTags() throws Exception { + final Entity app = createAndStartApplication( + "services:", + "- type: " + BasicApplication.class.getName(), + " brooklyn.tags:", + " - tag1", + " - 3"); + assertTrue(app.tags().getTags().contains(3)); + assertTrue(app.tags().getTags().contains("tag1")); + } + + @Test + public void testBrooklynCampObjectTags() throws Exception { + final Entity app = createAndStartApplication( + "services:", + "- type: " + BasicApplication.class.getName(), + " brooklyn.tags:", + " - tag1", + " - $brooklyn:object:", + " type: " + TagsTestObject.class.getName()); + assertTrue(Iterables.any(app.tags().getTags(), new Predicate<Object>() { + @Override + public boolean apply(@Nullable Object input) { + return input instanceof TagsTestObject; + } + })); + assertTrue(app.tags().getTags().contains("tag1")); + } + + @Test + public void testBrooklynCampFailDslTags() throws Exception { + try { + final Entity app = createAndStartApplication( + "services:", + "- type: " + BasicApplication.class.getName(), + " brooklyn.tags:", + " - tag1", + " - $brooklyn:object:", + " type: "+TagsTestObject.class.getName(), + " constructor.args:", + " - $brooklyn:attributeWhenReady(\"host.name\")"); + fail("Should throw IllegalArgumentException exception."); + } catch (CompoundRuntimeException e) { + Asserts.assertStringContainsAtLeastOne(Exceptions.getFirstInteresting(e).getMessage(),"brooklyn.tags should not contain DeferredSupplier. A DeferredSupplier is made when using $brooklyn:attributeWhenReady"); + } + } + + @Test + public void testTagWithDslValue() throws Exception { + Entity app = createAndStartApplication( + "services:", + "- type: " + BasicApplication.class.getName(), + " brooklyn.tags:", + " - $brooklyn:literal(\"myval\")"); + assertTrue(app.tags().getTags().contains("myval")); + } + + public static class TagsTestObject { + public TagsTestObject() {} + public TagsTestObject(Object arg1) {} + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/08d7bf98/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java ---------------------------------------------------------------------- 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 20b42ac..7f8b0c8 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 @@ -38,7 +38,7 @@ public class BrooklynTags { public static final String YAML_SPEC_KIND = "yaml_spec"; public static final String NOTES_KIND = "notes"; public static final String OWNER_ENTITY_ID = "owner_entity_id"; - + public static class NamedStringTag implements Serializable { private static final long serialVersionUID = 7932098757009051348L; @JsonProperty @@ -59,6 +59,20 @@ public class BrooklynTags { public String getContents() { return contents; } + + @Override + public boolean equals(Object other) { + if (!(other instanceof NamedStringTag)) { + return false; + } + NamedStringTag o = (NamedStringTag) other; + return Objects.equal(kind, o.kind) && Objects.equal(contents, o.contents); + } + + @Override + public int hashCode() { + return Objects.hashCode(kind, contents); + } } public static class ListTag<T> { @@ -123,7 +137,7 @@ public class BrooklynTags { public static TraitsTag newTraitsTag(List<Class<?>> interfaces) { 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))
