You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by dr...@apache.org on 2017/04/11 14:13:10 UTC

[3/8] brooklyn-server git commit: A group that sets a sequence of values as sensors on members

A group that sets a sequence of values as sensors on members


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

Branch: refs/heads/master
Commit: 2728745be0f1222d84b0230bb5c5576b54765bdb
Parents: f2bd653
Author: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Authored: Tue Jan 31 10:27:27 2017 +0000
Committer: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Committed: Tue Apr 11 14:34:26 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/group/SequenceGroup.java    | 104 ++++++++++++++++
 .../entity/group/SequenceGroupImpl.java         |  85 +++++++++++++
 .../entity/group/SequenceGroupTest.java         | 118 +++++++++++++++++++
 3 files changed, 307 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2728745b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroup.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroup.java b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroup.java
new file mode 100644
index 0000000..a6dee56
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroup.java
@@ -0,0 +1,104 @@
+/*
+ * 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.entity.group;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.annotation.Effector;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.effector.MethodEffector;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.entity.stock.SequenceEntity;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.apache.brooklyn.util.text.StringPredicates;
+
+import com.google.common.base.Predicates;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * A group that sets a sequence of values as sensors on its member entities
+ * <p>
+ * Usage:
+ * <pre>{@code
+ * - type: org.apache.brooklyn.entity.stock.SequenceGroup
+ *   id: entity-sequence
+ *   brooklyn.config:
+ *     entityFilter:
+ *       $brooklyn:object:
+ *         type: org.apache.brooklyn.core.entity.EntityPredicates
+ *         factoryMethod.name: "applicationIdEqualTo"
+ *         factoryMethod.args:
+ *           - $brooklyn:attributeWhenReady("application.id")
+ *     sequenceStart: 0
+ *     sequenceIncrement: 1
+ *     sequenceFormat: "entity%04x"
+ * }</pre>
+ */
+@ImplementedBy(SequenceGroupImpl.class)
+public interface SequenceGroup extends DynamicGroup {
+
+    @SetFromFlag("sequenceStart")
+    ConfigKey<Integer> SEQUENCE_START = ConfigKeys.builder(Integer.class)
+            .name("sequence.start")
+            .description("The starting point of the sequence")
+            .defaultValue(1)
+            .constraint(Predicates.<Integer>notNull())
+            .build();
+
+    @SetFromFlag("sequenceIncrement")
+    ConfigKey<Integer> SEQUENCE_INCREMENT =  ConfigKeys.builder(Integer.class)
+            .name("sequence.increment")
+            .description("The sequence increment for the next value")
+            .defaultValue(1)
+            .constraint(Predicates.<Integer>notNull())
+            .build();
+
+    @SetFromFlag("sequenceFormat")
+    ConfigKey<String> SEQUENCE_FORMAT = ConfigKeys.builder(String.class)
+            .name("sequence.format")
+            .description("A format used to generate a string representation of the sequence")
+            .defaultValue("%d")
+            .constraint(StringPredicates.containsRegex("%[-#+ 0,(]*[0-9]*[doxX]"))
+            .build();
+
+    AttributeSensor<Integer> SEQUENCE_NEXT = Sensors.builder(Integer.class, "sequence.next")
+            .description("The next value of the sequence")
+            .build();
+
+    AttributeSensor<Map<String, Integer>> SEQUENCE_CACHE = Sensors.builder(new TypeToken<Map<String, Integer>>() { }, "sequence.cache")
+            .description("The current cache of entity ids to sequence numbers")
+            .build();
+
+    MethodEffector<Void> RESET = new MethodEffector<Void>(SequenceEntity.class, "reset");
+
+    @Effector(description = "Reset the sequence to initial value")
+    Void reset();
+
+    AttributeSensor<Integer> SEQUENCE_VALUE = Sensors.builder(Integer.class, "sequence.value")
+            .description("The current value of the sequence")
+            .build();
+
+    AttributeSensor<String> SEQUENCE_STRING = Sensors.builder(String.class, "sequence.string")
+            .description("The current value of the sequence formatted as a string")
+            .build();
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2728745b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroupImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroupImpl.java b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroupImpl.java
new file mode 100644
index 0000000..f9fc04e
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroupImpl.java
@@ -0,0 +1,85 @@
+/*
+ * 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.entity.group;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Maps;
+
+public class SequenceGroupImpl extends DynamicGroupImpl implements SequenceGroup {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SequenceGroupImpl.class);
+
+    public SequenceGroupImpl() { }
+
+    @Override
+    public Void reset() {
+        synchronized (memberChangeMutex) {
+            sensors().set(SEQUENCE_CACHE, Maps.<String, Integer>newConcurrentMap());
+            Integer initial = config().get(SEQUENCE_START);
+            sensors().set(SEQUENCE_NEXT, initial);
+            return null;
+        }
+    }
+
+    @Override
+    public void rescanEntities() {
+        synchronized (memberChangeMutex) {
+            reset();
+            super.rescanEntities();
+        }
+    }
+
+    @Override
+    public boolean addMember(Entity member) {
+        synchronized (memberChangeMutex) {
+            boolean changed = super.addMember(member);
+            if (changed) {
+                Map<String, Integer> cache = sensors().get(SEQUENCE_CACHE);
+                if (!cache.containsKey(member.getId())) {
+                    Integer value = sequence(member);
+                    cache.put(member.getId(), value);
+                }
+            }
+            return changed;
+        }
+    }
+
+    private Integer sequence(Entity entity) {
+        String format = config().get(SEQUENCE_FORMAT);
+        Integer current = sensors().get(SEQUENCE_NEXT);
+        String string = String.format(format, current);
+
+        entity.sensors().set(SEQUENCE_VALUE, current);
+        entity.sensors().set(SEQUENCE_STRING,string);
+
+        Integer increment = config().get(SEQUENCE_INCREMENT);
+        Integer next = current + increment;
+        LOG.debug("Sequence for {} incremented to {}", this, next);
+
+        sensors().set(SEQUENCE_NEXT, next);
+
+        return current;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2728745b/core/src/test/java/org/apache/brooklyn/entity/group/SequenceGroupTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/entity/group/SequenceGroupTest.java b/core/src/test/java/org/apache/brooklyn/entity/group/SequenceGroupTest.java
new file mode 100644
index 0000000..afb5425
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/entity/group/SequenceGroupTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.entity.group;
+
+import static org.apache.brooklyn.test.Asserts.assertEqualsIgnoringOrder;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityAsserts;
+import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.test.Asserts;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
+    
+    private TestApplication app;
+    private SequenceGroup group;
+    private TestEntity e1;
+    private TestEntity e2;
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        group = app.createAndManageChild(EntitySpec.create(SequenceGroup.class));
+        e1 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        e2 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+    }
+
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+
+        super.tearDown();
+    }
+
+    @Test
+    public void testGroupWithMatchingFilterReturnsOnlyMatchingMembers() throws Exception {
+        group.setEntityFilter(EntityPredicates.idEqualTo(e1.getId()));
+
+        assertEqualsIgnoringOrder(group.getMembers(), ImmutableList.of(e1));
+        EntityAsserts.assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 1);
+        EntityAsserts.assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 2);
+    }
+
+    @Test
+    public void testGroupWithMatchingFilterReturnsEverythingThatMatches() throws Exception {
+        group.setEntityFilter(Predicates.alwaysTrue());
+
+        assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e1, e2, app, group));
+        EntityAsserts.assertAttributeEquals(app, SequenceGroup.SEQUENCE_VALUE, 1);
+        EntityAsserts.assertAttributeEquals(group, SequenceGroup.SEQUENCE_VALUE, 2);
+        EntityAsserts.assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 3);
+        EntityAsserts.assertAttributeEquals(e2, SequenceGroup.SEQUENCE_VALUE, 4);
+        EntityAsserts.assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 5);
+    }
+    
+    @Test
+    public void testGroupDetectsNewlyManagedMatchingMember() throws Exception {
+        group.setEntityFilter(EntityPredicates.displayNameEqualTo("myname"));
+        final Entity e3 = app.addChild(EntitySpec.create(TestEntity.class).displayName("myname"));
+        
+        Asserts.succeedsEventually(new Runnable() {
+            public void run() {
+                assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e3));
+                EntityAsserts.assertAttributeEquals(e3, SequenceGroup.SEQUENCE_VALUE, 1);
+                EntityAsserts.assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 2);
+            }});
+    }
+
+    @Test
+    public void testGroupUsesNewFilter() throws Exception {
+        group.setEntityFilter(EntityPredicates.hasInterfaceMatching(".*TestEntity"));
+
+        assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e1, e2));
+        EntityAsserts.assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 1);
+        EntityAsserts.assertAttributeEquals(e2, SequenceGroup.SEQUENCE_VALUE, 2);
+        EntityAsserts.assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 3);
+
+        final Entity e3 = app.addChild(EntitySpec.create(TestEntity.class));
+
+        Asserts.succeedsEventually(new Runnable() {
+            public void run() {
+                assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e1, e2, e3));
+                EntityAsserts.assertAttributeEquals(e3, SequenceGroup.SEQUENCE_VALUE, 3);
+                EntityAsserts.assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 4);
+            }});
+    }
+}