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:08 UTC

[1/8] brooklyn-server git commit: An entity that emits a sequence of integers

Repository: brooklyn-server
Updated Branches:
  refs/heads/master a23d42ffb -> d9a8c1df5


An entity that emits a sequence of integers


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

Branch: refs/heads/master
Commit: f2bd6538d32dddcab6820059f5910f09bda79770
Parents: a23d42f
Author: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Authored: Tue Jan 31 09:32:29 2017 +0000
Committer: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Committed: Tue Apr 11 14:34:25 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/stock/SequenceEntity.java   | 112 ++++++++++++
 .../entity/stock/SequenceEntityImpl.java        |  99 ++++++++++
 .../entity/stock/SequenceEntityTest.java        | 181 +++++++++++++++++++
 3 files changed, 392 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/f2bd6538/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
new file mode 100644
index 0000000..5135f3c
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
@@ -0,0 +1,112 @@
+/*
+ * 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.stock;
+
+import org.apache.brooklyn.api.entity.Entity;
+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.entity.trait.Startable;
+import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.apache.brooklyn.util.text.StringPredicates;
+
+import com.google.common.base.Predicates;
+
+/**
+ * An entity that supplies a sequence of values through an effector.
+ * <p>
+ * Usage:
+ * <pre>{@code
+ * - type: org.apache.brooklyn.entity.stock.SequenceEntity
+ *   id: global-sequence
+ *   brooklyn.config:
+ *     sequence.start: 0
+ *     sequence.increment: 1
+ *     sequence.format: "global-%03d"
+ *     sequence.name: "global"
+ * }</pre>
+ */
+@ImplementedBy(SequenceEntityImpl.class)
+public interface SequenceEntity extends Entity, Startable {
+
+    @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();
+
+    @SetFromFlag("sequenceName")
+    AttributeSensorAndConfigKey<String, String> SEQUENCE_NAME = ConfigKeys.newStringSensorAndConfigKey("sequence.name", "The name of the sequence", "sequence");
+
+    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();
+
+    MethodEffector<Void> RESET = new MethodEffector<Void>(SequenceEntity.class, "reset");
+    MethodEffector<Void> INCREMENT = new MethodEffector<Void>(SequenceEntity.class, "increment");
+    MethodEffector<Integer> CURRENT_VALUE = new MethodEffector<Integer>(SequenceEntity.class, "currentValue");
+    MethodEffector<String> CURRENT_STRING = new MethodEffector<String>(SequenceEntity.class, "currentString");
+    MethodEffector<Integer> NEXT_VALUE = new MethodEffector<Integer>(SequenceEntity.class, "nextValue");
+    MethodEffector<String> NEXT_STRING = new MethodEffector<String>(SequenceEntity.class, "nextString");
+
+    @Effector(description = "Reset the sequence to initial value")
+    Void reset();
+
+    @Effector(description = "Update the value of the sequence by the configured increment")
+    Void increment();
+
+    @Effector(description = "Return the current numeric value of the sequence")
+    Integer currentValue();
+
+    @Effector(description = "Return the current string representation of the sequence")
+    String currentString();
+
+    @Effector(description = "Update and return the next numeric value of the sequence")
+    Integer nextValue();
+
+    @Effector(description = "Update and return the next string representation of the sequence")
+    String nextString();
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/f2bd6538/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
new file mode 100644
index 0000000..338b323
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
@@ -0,0 +1,99 @@
+/*
+ * 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.stock;
+
+import java.util.Collection;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.entity.AbstractEntity;
+import org.apache.brooklyn.core.feed.ConfigToAttributes;
+
+public class SequenceEntityImpl extends AbstractEntity implements SequenceEntity {
+
+    public SequenceEntityImpl() { }
+
+    public void init() {
+        super.init();
+
+        ConfigToAttributes.apply(this, SEQUENCE_NAME);
+    }
+
+    @Override
+    public void start(Collection<? extends Location> locations) {
+        addLocations(locations);
+        reset();
+        sensors().set(SERVICE_UP, Boolean.TRUE);
+    }
+
+    @Override
+    public void stop() {
+        sensors().set(SERVICE_UP, Boolean.FALSE);
+    }
+
+    @Override
+    public void restart() {
+        stop();
+        start(getLocations());
+    }
+
+    protected void sequence(Integer value) {
+        String format = config().get(SEQUENCE_FORMAT);
+
+        sensors().set(SEQUENCE_VALUE, value);
+        sensors().set(SEQUENCE_STRING, String.format(format, value));
+    }
+
+    @Override
+    public Integer currentValue() {
+        return sensors().get(SEQUENCE_VALUE);
+    }
+
+    @Override
+    public String currentString() {
+        return sensors().get(SEQUENCE_STRING);
+    }
+
+    @Override
+    public synchronized Integer nextValue() {
+        increment();
+        return currentValue();
+    }
+
+    @Override
+    public synchronized String nextString() {
+        increment();
+        return currentString();
+    }
+
+    @Override
+    public synchronized Void increment() {
+        Integer increment = config().get(SEQUENCE_INCREMENT);
+        Integer current = currentValue();
+        sequence(current + increment);
+        return null;
+    }
+
+    @Override
+    public synchronized Void reset() {
+        Integer start = config().get(SEQUENCE_START);
+        sequence(start);
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/f2bd6538/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java b/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
new file mode 100644
index 0000000..2b20f8d
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.stock;
+
+import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEquals;
+import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEqualsEventually;
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.entity.EntityAsserts;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.SimulatedLocation;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class SequenceEntityTest extends BrooklynAppUnitTestSupport {
+
+    private SimulatedLocation loc1;
+    private SequenceEntity sequence;
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        loc1 = mgmt.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
+    }
+
+    @Test
+    public void testSequenceInitial() throws Exception {
+        sequence = app.addChild(EntitySpec.create(SequenceEntity.class)
+                .configure(SequenceEntity.SEQUENCE_START, 0));
+        app.start(ImmutableList.of(loc1));
+
+        assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
+
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 0);
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "0");
+    }
+
+    @Test
+    public void testSequenceInitialConfig() throws Exception {
+        sequence = app.addChild(EntitySpec.create(SequenceEntity.class)
+                .configure(SequenceEntity.SEQUENCE_NAME, "forty-two")
+                .configure(SequenceEntity.SEQUENCE_START, 42)
+                .configure(SequenceEntity.SEQUENCE_FORMAT, "id-%02x"));
+        app.start(ImmutableList.of(loc1));
+
+        assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
+
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 42);
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "id-2a");
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_NAME, "forty-two");
+    }
+
+    @Test
+    public void testSequenceIncrementEffector() throws Exception {
+        sequence = app.addChild(EntitySpec.create(SequenceEntity.class));
+        app.start(ImmutableList.of(loc1));
+
+        EntityAsserts.assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
+
+        assertEquals(sequence.currentValue(), Integer.valueOf(1));
+        assertEquals(sequence.currentString(), "1");
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 1);
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "1");
+
+        sequence.increment();
+
+        assertEquals(sequence.currentValue(), Integer.valueOf(2));
+        assertEquals(sequence.currentString(), "2");
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 2);
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "2");
+
+        sequence.invoke(SequenceEntity.INCREMENT, ImmutableMap.<String, Object>of()).getUnchecked();
+
+        assertEquals(sequence.currentValue(), Integer.valueOf(3));
+        assertEquals(sequence.currentString(), "3");
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 3);
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "3");
+    }
+
+    @Test
+    public void testSequenceIncrementEffectorConfig() throws Exception {
+        sequence = app.addChild(EntitySpec.create(SequenceEntity.class)
+                .configure(SequenceEntity.SEQUENCE_START, 0)
+                .configure(SequenceEntity.SEQUENCE_INCREMENT, 2)
+                .configure(SequenceEntity.SEQUENCE_FORMAT, "%03d"));
+        app.start(ImmutableList.of(loc1));
+
+        EntityAsserts.assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
+
+        assertEquals(sequence.currentValue(), Integer.valueOf(0));
+        assertEquals(sequence.currentString(), "000");
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 0);
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "000");
+
+        sequence.increment();
+
+        assertEquals(sequence.currentValue(), Integer.valueOf(2));
+        assertEquals(sequence.currentString(), "002");
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 2);
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "002");
+
+        sequence.invoke(SequenceEntity.INCREMENT, ImmutableMap.<String, Object>of()).getUnchecked();
+
+        assertEquals(sequence.currentValue(), Integer.valueOf(4));
+        assertEquals(sequence.currentString(), "004");
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 4);
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "004");
+    }
+
+    @Test
+    public void testSequenceNextEffectors() throws Exception {
+        sequence = app.addChild(EntitySpec.create(SequenceEntity.class));
+        app.start(ImmutableList.of(loc1));
+
+        assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
+
+        assertEquals(sequence.currentValue(), Integer.valueOf(1));
+        assertEquals(sequence.currentString(), "1");
+
+        Integer nextValue = sequence.invoke(SequenceEntity.NEXT_VALUE, ImmutableMap.<String, Object>of()).getUnchecked();
+        assertEquals(nextValue, Integer.valueOf(2));
+
+        String nextString = sequence.invoke(SequenceEntity.NEXT_STRING, ImmutableMap.<String, Object>of()).getUnchecked();
+        assertEquals(nextString, "3");
+
+        assertEquals(sequence.currentValue(), Integer.valueOf(3));
+        assertEquals(sequence.currentString(), "3");
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 3);
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "3");
+    }
+
+    @Test
+    public void testSequenceReset() throws Exception {
+        sequence = app.addChild(EntitySpec.create(SequenceEntity.class));
+        app.start(ImmutableList.of(loc1));
+
+        assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
+
+        assertEquals(sequence.currentValue(), Integer.valueOf(1));
+
+        sequence.increment();
+        sequence.increment();
+        sequence.increment();
+
+        assertEquals(sequence.currentValue(), Integer.valueOf(4));
+        assertEquals(sequence.currentString(), "4");
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 4);
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "4");
+
+        sequence.invoke(SequenceEntity.RESET, ImmutableMap.<String, Object>of()).getUnchecked();
+
+        assertEquals(sequence.currentValue(), Integer.valueOf(1));
+        assertEquals(sequence.currentString(), "1");
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 1);
+        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "1");
+    }
+
+}


[8/8] brooklyn-server git commit: This closes #546

Posted by dr...@apache.org.
This closes #546


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

Branch: refs/heads/master
Commit: d9a8c1df522e4e3bdc92db28466a415481207be4
Parents: a23d42f 62989ac
Author: Duncan Godwin <dr...@googlemail.com>
Authored: Tue Apr 11 15:12:52 2017 +0100
Committer: Duncan Godwin <dr...@googlemail.com>
Committed: Tue Apr 11 15:12:52 2017 +0100

----------------------------------------------------------------------
 .../entity/lifecycle/ServiceStateLogic.java     | 150 +++++++++-----
 .../brooklyn/entity/group/SequenceGroup.java    | 140 +++++++++++++
 .../entity/group/SequenceGroupImpl.java         | 109 ++++++++++
 .../brooklyn/entity/stock/SequenceEntity.java   |  77 +++++++
 .../entity/stock/SequenceEntityImpl.java        |  97 +++++++++
 .../entity/group/SequenceGroupTest.java         | 202 +++++++++++++++++++
 .../entity/stock/SequenceEntityTest.java        | 156 ++++++++++++++
 7 files changed, 876 insertions(+), 55 deletions(-)
----------------------------------------------------------------------



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

Posted by dr...@apache.org.
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);
+            }});
+    }
+}


[4/8] brooklyn-server git commit: Simplify and tidy up tests for both entities

Posted by dr...@apache.org.
Simplify and tidy up tests for both entities


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

Branch: refs/heads/master
Commit: 68a6f1465b01e6cb49163149af173d4680ac7a7f
Parents: 77d0ddb
Author: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Authored: Tue Feb 7 14:26:22 2017 +0000
Committer: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Committed: Tue Apr 11 14:34:26 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/group/SequenceGroup.java    |  7 +-
 .../entity/group/SequenceGroupImpl.java         | 14 ++-
 .../brooklyn/entity/stock/SequenceEntity.java   | 27 ++----
 .../entity/stock/SequenceEntityImpl.java        | 28 +-----
 .../entity/group/SequenceGroupTest.java         | 96 +++++++++++++-------
 .../entity/stock/SequenceEntityTest.java        | 55 ++++-------
 6 files changed, 98 insertions(+), 129 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/68a6f146/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
index 851d3b7..4d7d082 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroup.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroup.java
@@ -55,7 +55,7 @@ import com.google.common.reflect.TypeToken;
  *                 - $brooklyn:attributeWhenReady("application.id")
  *           - $brooklyn:object:
  *               type: org.apache.brooklyn.core.entity.EntityPredicates
- *               factoryMethod.name: "configEquelTo"
+ *               factoryMethod.name: "configEqualTo"
  *               factoryMethod.args:
  *                 - "sequence.set"
  *                 - true
@@ -65,11 +65,6 @@ import com.google.common.reflect.TypeToken;
  *     sequenceValueSensor: $brooklyn:sensor("entity.sequence")
  *     sequenceStringSensor: $brooklyn:sensor("entity.name")
  * }</pre>
- * <p>
- * <b>IMPORTANT</b> Relationship management will fail in unpredictable ways
- * if an entity is a member of multiple sequences that use the same
- * {@link #SEQUENCE_VALUE_SENSOR sensor}, however this usage will probably
- * not have the desired effect in any case.
  */
 @ImplementedBy(SequenceGroupImpl.class)
 public interface SequenceGroup extends DynamicGroup {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/68a6f146/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
index fc9c526..0e8c39c 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroupImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroupImpl.java
@@ -33,6 +33,11 @@ public class SequenceGroupImpl extends DynamicGroupImpl implements SequenceGroup
 
     public SequenceGroupImpl() { }
 
+    public void init() {
+        super.init();
+        reset();
+    }
+
     @Override
     public Void reset() {
         synchronized (memberChangeMutex) {
@@ -45,14 +50,6 @@ public class SequenceGroupImpl extends DynamicGroupImpl implements SequenceGroup
     }
 
     @Override
-    public void rescanEntities() {
-        synchronized (memberChangeMutex) {
-            reset();
-            super.rescanEntities();
-        }
-    }
-
-    @Override
     public boolean addMember(Entity member) {
         synchronized (memberChangeMutex) {
             boolean changed = super.addMember(member);
@@ -96,6 +93,7 @@ public class SequenceGroupImpl extends DynamicGroupImpl implements SequenceGroup
 
         entity.sensors().set(valueSensor, current);
         entity.sensors().set(stringSensor, string);
+        LOG.debug("Sequence on {} set to to {}", entity, current);
 
         sensors().set(SEQUENCE_CURRENT, entity);
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/68a6f146/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
index f53ba65..6b73d8f 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
@@ -28,6 +28,8 @@ import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.entity.group.SequenceGroup;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
 
+import com.google.common.base.Supplier;
+
 /**
  * An entity that supplies a sequence of values through an effector.
  * <p>
@@ -38,31 +40,23 @@ import org.apache.brooklyn.util.core.flags.SetFromFlag;
  *   brooklyn.config:
  *     sequence.start: 0
  *     sequence.increment: 1
- *     sequence.format: "global-%03d"
  * }</pre>
  */
 @ImplementedBy(SequenceEntityImpl.class)
-public interface SequenceEntity extends Entity, Startable {
+public interface SequenceEntity extends Entity, Startable, Supplier<Integer> {
 
     AttributeSensor<Integer> SEQUENCE_VALUE = SequenceGroup.SEQUENCE_VALUE;
 
-    AttributeSensor<String> SEQUENCE_STRING = SequenceGroup.SEQUENCE_STRING;
-
     @SetFromFlag("sequenceStart")
     ConfigKey<Integer> SEQUENCE_START = SequenceGroup.SEQUENCE_START;
 
     @SetFromFlag("sequenceIncrement")
     ConfigKey<Integer> SEQUENCE_INCREMENT = SequenceGroup.SEQUENCE_INCREMENT;
 
-    @SetFromFlag("sequenceFormat")
-    ConfigKey<String> SEQUENCE_FORMAT = SequenceGroup.SEQUENCE_FORMAT;
-
     MethodEffector<Void> RESET = new MethodEffector<Void>(SequenceEntity.class, "reset");
     MethodEffector<Void> INCREMENT = new MethodEffector<Void>(SequenceEntity.class, "increment");
-    MethodEffector<Integer> CURRENT_VALUE = new MethodEffector<Integer>(SequenceEntity.class, "currentValue");
-    MethodEffector<String> CURRENT_STRING = new MethodEffector<String>(SequenceEntity.class, "currentString");
-    MethodEffector<Integer> NEXT_VALUE = new MethodEffector<Integer>(SequenceEntity.class, "nextValue");
-    MethodEffector<String> NEXT_STRING = new MethodEffector<String>(SequenceEntity.class, "nextString");
+    MethodEffector<Integer> GET = new MethodEffector<Integer>(SequenceEntity.class, "get");
+    MethodEffector<Integer> INCREMENT_AND_GET = new MethodEffector<Integer>(SequenceEntity.class, "incrementAndGet");
 
     @Effector(description = "Reset the sequence to initial value")
     Void reset();
@@ -71,14 +65,9 @@ public interface SequenceEntity extends Entity, Startable {
     Void increment();
 
     @Effector(description = "Return the current numeric value of the sequence")
-    Integer currentValue();
-
-    @Effector(description = "Return the current string representation of the sequence")
-    String currentString();
+    Integer get();
 
-    @Effector(description = "Update and return the next numeric value of the sequence")
-    Integer nextValue();
+    @Effector(description = "Update and return the next value of the sequence")
+    Integer incrementAndGet();
 
-    @Effector(description = "Update and return the next string representation of the sequence")
-    String nextString();
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/68a6f146/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
index 0ddb134..b731dfe 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
@@ -52,32 +52,17 @@ public class SequenceEntityImpl extends AbstractEntity implements SequenceEntity
     }
 
     @Override
-    public Integer currentValue() {
+    public Integer get() {
         synchronized (mutex) {
             return sensors().get(SEQUENCE_VALUE);
         }
     }
 
     @Override
-    public String currentString() {
-        synchronized (mutex) {
-            return sensors().get(SEQUENCE_STRING);
-        }
-    }
-
-    @Override
-    public Integer nextValue() {
+    public Integer incrementAndGet() {
         synchronized (mutex) {
             increment();
-            return currentValue();
-        }
-    }
-
-    @Override
-    public String nextString() {
-        synchronized (mutex) {
-            increment();
-            return currentString();
+            return get();
         }
     }
 
@@ -85,7 +70,7 @@ public class SequenceEntityImpl extends AbstractEntity implements SequenceEntity
     public Void increment() {
         synchronized (mutex) {
             Integer increment = config().get(SEQUENCE_INCREMENT);
-            Integer current = currentValue();
+            Integer current = get();
             sequence(current + increment);
             return null;
         }
@@ -101,12 +86,7 @@ public class SequenceEntityImpl extends AbstractEntity implements SequenceEntity
     }
 
     private void sequence(Integer value) {
-        String format = config().get(SEQUENCE_FORMAT);
-        String string = String.format(format, value);
-
         sensors().set(SEQUENCE_VALUE, value);
-        sensors().set(SEQUENCE_STRING, string);
-
         LOG.debug("Sequence for {} set to {}", this, value);
     }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/68a6f146/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
index 148eb5b..d835c7d 100644
--- a/core/src/test/java/org/apache/brooklyn/entity/group/SequenceGroupTest.java
+++ b/core/src/test/java/org/apache/brooklyn/entity/group/SequenceGroupTest.java
@@ -18,22 +18,22 @@
  */
 package org.apache.brooklyn.entity.group;
 
+import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttribute;
+import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEquals;
+import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEqualsEventually;
 import static org.apache.brooklyn.test.Asserts.assertEqualsIgnoringOrder;
-import static org.apache.brooklyn.test.Asserts.*;
-import static org.apache.brooklyn.core.entity.EntityAsserts.*;
-
-import java.util.Set;
+import static org.apache.brooklyn.test.Asserts.assertTrue;
+import static org.apache.brooklyn.test.Asserts.succeedsEventually;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
-import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.core.location.SimulatedLocation;
 import org.apache.brooklyn.core.sensor.Sensors;
 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.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
@@ -43,7 +43,7 @@ import com.google.common.collect.ImmutableSet;
 
 public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
 
-    private TestApplication app;
+    private SimulatedLocation loc1;
     private SequenceGroup group;
     private TestEntity e1, e2, e3;
 
@@ -51,33 +51,35 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
     @Override
     public void setUp() throws Exception {
         super.setUp();
-
-        app = TestApplication.Factory.newManagedInstanceForTests();
-        group = app.createAndManageChild(EntitySpec.create(SequenceGroup.class)
-                .configure(SequenceGroup.SEQUENCE_STRING_SENSOR, Sensors.newStringSensor("test.sequence"))
-                .configure(SequenceGroup.SEQUENCE_FORMAT, "test-%02d"));
-        e1 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        e2 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        e3 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        loc1 = mgmt.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
     }
 
-    @AfterMethod(alwaysRun=true)
-    @Override
-    public void tearDown() throws Exception {
-        if (app != null) Entities.destroyAll(app.getManagementContext());
-
-        super.tearDown();
+    protected void createTestEntities() throws Exception {
+        e1 = app.createAndManageChild(EntitySpec.create(TestEntity.class).displayName("test-one"));
+        e2 = app.createAndManageChild(EntitySpec.create(TestEntity.class).displayName("test-two"));
+        e3 = app.createAndManageChild(EntitySpec.create(TestEntity.class).displayName("test-three"));
     }
 
-
     @Test
     public void testGroupDefaults() throws Exception {
+        group = app.addChild(EntitySpec.create(SequenceGroup.class));
+        createTestEntities();
+        app.start(ImmutableList.of(loc1));
+
+        assertAttributeEqualsEventually(group, SequenceGroup.RUNNING, true);
+
+        assertAttribute(group, SequenceGroup.SEQUENCE_VALUE, Predicates.isNull());
         assertTrue(group.getMembers().isEmpty());
     }
 
     @Test
     public void testGroupWithMatchingFilterReturnsOnlyMatchingMembers() throws Exception {
-        group.setEntityFilter(EntityPredicates.idEqualTo(e1.getId()));
+        group = app.addChild(EntitySpec.create(SequenceGroup.class)
+                .configure(SequenceGroup.ENTITY_FILTER, EntityPredicates.displayNameEqualTo("test-one")));
+        createTestEntities();
+        app.start(ImmutableList.of(loc1));
+
+        assertAttributeEqualsEventually(group, SequenceGroup.RUNNING, true);
 
         assertEqualsIgnoringOrder(group.getMembers(), ImmutableList.of(e1));
         assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 1);
@@ -86,9 +88,17 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
 
     @Test
     public void testGroupConfiguration() throws Exception {
-        group.setEntityFilter(EntityPredicates.idEqualTo(e1.getId()));
+        group = app.addChild(EntitySpec.create(SequenceGroup.class)
+                .configure(SequenceGroup.SEQUENCE_STRING_SENSOR,  Sensors.newStringSensor("test.sequence"))
+                .configure(SequenceGroup.SEQUENCE_FORMAT, "test-%02d")
+                .configure(SequenceGroup.ENTITY_FILTER, EntityPredicates.displayNameEqualTo("test-one")));
+        createTestEntities();
+        app.start(ImmutableList.of(loc1));
+
+        assertAttributeEqualsEventually(group, SequenceGroup.RUNNING, true);
 
         assertEqualsIgnoringOrder(group.getMembers(), ImmutableList.of(e1));
+        assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 1);
         assertAttributeEquals(e1, SequenceGroup.SEQUENCE_STRING, null);
         assertAttributeEquals(e1, Sensors.newStringSensor("test.sequence"), "test-01");
     }
@@ -97,13 +107,17 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
     public void testAlternateGroupConfiguration() throws Exception {
         AttributeSensor<Integer> value = Sensors.newIntegerSensor("test.value");
         AttributeSensor<String> string = Sensors.newStringSensor("test.string");
-        group = app.createAndManageChild(EntitySpec.create(SequenceGroup.class)
+        group = app.addChild(EntitySpec.create(SequenceGroup.class)
                 .configure(SequenceGroup.SEQUENCE_START, 12345)
                 .configure(SequenceGroup.SEQUENCE_INCREMENT, 678)
                 .configure(SequenceGroup.SEQUENCE_VALUE_SENSOR, value)
                 .configure(SequenceGroup.SEQUENCE_STRING_SENSOR, string)
-                .configure(SequenceGroup.SEQUENCE_FORMAT, "0x%04X"));
-        group.setEntityFilter(EntityPredicates.hasInterfaceMatching(".*TestEntity"));
+                .configure(SequenceGroup.SEQUENCE_FORMAT, "0x%04X")
+                .configure(SequenceGroup.ENTITY_FILTER, EntityPredicates.hasInterfaceMatching(".*TestEntity")));
+        createTestEntities();
+        app.start(ImmutableList.of(loc1));
+
+        assertAttributeEqualsEventually(group, SequenceGroup.RUNNING, true);
 
         assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e1, e2, e3));
         assertAttributeEquals(e1, value, 12345);
@@ -116,7 +130,12 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
 
     @Test
     public void testGroupWithMatchingFilterReturnsEverythingThatMatches() throws Exception {
-        group.setEntityFilter(Predicates.alwaysTrue());
+        group = app.addChild(EntitySpec.create(SequenceGroup.class)
+                .configure(SequenceGroup.ENTITY_FILTER, Predicates.alwaysTrue()));
+        createTestEntities();
+        app.start(ImmutableList.of(loc1));
+
+        assertAttributeEqualsEventually(group, SequenceGroup.RUNNING, true);
 
         assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e1, e2, e3, app, group));
         assertAttributeEquals(app, SequenceGroup.SEQUENCE_VALUE, 1);
@@ -130,8 +149,14 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
 
     @Test
     public void testGroupDetectsNewlyManagedMatchingMember() throws Exception {
-        group.setEntityFilter(EntityPredicates.displayNameEqualTo("myname"));
-        final Entity e = app.addChild(EntitySpec.create(TestEntity.class).displayName("myname"));
+        group = app.addChild(EntitySpec.create(SequenceGroup.class)
+                .configure(SequenceGroup.ENTITY_FILTER, EntityPredicates.displayNameEqualTo("test-four")));
+        createTestEntities();
+        app.start(ImmutableList.of(loc1));
+
+        assertAttributeEqualsEventually(group, SequenceGroup.RUNNING, true);
+
+        final Entity e = app.addChild(EntitySpec.create(TestEntity.class).displayName("test-four"));
 
         succeedsEventually(new Runnable() {
             public void run() {
@@ -142,8 +167,13 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
     }
 
     @Test
-    public void testGroupUsesNewFilter() throws Exception {
-        group.setEntityFilter(EntityPredicates.hasInterfaceMatching(".*TestEntity"));
+    public void testGroupAddsNewMatchingMember() throws Exception {
+        group = app.addChild(EntitySpec.create(SequenceGroup.class)
+                .configure(SequenceGroup.ENTITY_FILTER, EntityPredicates.hasInterfaceMatching(".*TestEntity")));
+        createTestEntities();
+        app.start(ImmutableList.of(loc1));
+
+        assertAttributeEqualsEventually(group, SequenceGroup.RUNNING, true);
 
         assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e1, e2, e3));
         assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 1);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/68a6f146/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java b/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
index 2ec8a79..c4b3b86 100644
--- a/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
+++ b/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
@@ -55,20 +55,17 @@ public class SequenceEntityTest extends BrooklynAppUnitTestSupport {
         assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
 
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 0);
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "0");
     }
 
     @Test
     public void testSequenceInitialConfig() throws Exception {
         sequence = app.addChild(EntitySpec.create(SequenceEntity.class)
-                .configure(SequenceEntity.SEQUENCE_START, 42)
-                .configure(SequenceEntity.SEQUENCE_FORMAT, "id-%02x"));
+                .configure(SequenceEntity.SEQUENCE_START, 42));
         app.start(ImmutableList.of(loc1));
 
         assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
 
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 42);
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "id-2a");
     }
 
     @Test
@@ -78,54 +75,41 @@ public class SequenceEntityTest extends BrooklynAppUnitTestSupport {
 
         EntityAsserts.assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
 
-        assertEquals(sequence.currentValue(), Integer.valueOf(1));
-        assertEquals(sequence.currentString(), "1");
+        assertEquals(sequence.get(), Integer.valueOf(1));
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 1);
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "1");
 
         sequence.increment();
 
-        assertEquals(sequence.currentValue(), Integer.valueOf(2));
-        assertEquals(sequence.currentString(), "2");
+        assertEquals(sequence.get(), Integer.valueOf(2));
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 2);
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "2");
 
         sequence.invoke(SequenceEntity.INCREMENT, ImmutableMap.<String, Object>of()).getUnchecked();
 
-        assertEquals(sequence.currentValue(), Integer.valueOf(3));
-        assertEquals(sequence.currentString(), "3");
+        assertEquals(sequence.get(), Integer.valueOf(3));
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 3);
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "3");
     }
 
     @Test
     public void testSequenceIncrementEffectorConfig() throws Exception {
         sequence = app.addChild(EntitySpec.create(SequenceEntity.class)
                 .configure(SequenceEntity.SEQUENCE_START, 0)
-                .configure(SequenceEntity.SEQUENCE_INCREMENT, 2)
-                .configure(SequenceEntity.SEQUENCE_FORMAT, "%03d"));
+                .configure(SequenceEntity.SEQUENCE_INCREMENT, 2));
         app.start(ImmutableList.of(loc1));
 
         EntityAsserts.assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
 
-        assertEquals(sequence.currentValue(), Integer.valueOf(0));
-        assertEquals(sequence.currentString(), "000");
+        assertEquals(sequence.get(), Integer.valueOf(0));
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 0);
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "000");
 
         sequence.increment();
 
-        assertEquals(sequence.currentValue(), Integer.valueOf(2));
-        assertEquals(sequence.currentString(), "002");
+        assertEquals(sequence.get(), Integer.valueOf(2));
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 2);
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "002");
 
         sequence.invoke(SequenceEntity.INCREMENT, ImmutableMap.<String, Object>of()).getUnchecked();
 
-        assertEquals(sequence.currentValue(), Integer.valueOf(4));
-        assertEquals(sequence.currentString(), "004");
+        assertEquals(sequence.get(), Integer.valueOf(4));
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 4);
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "004");
     }
 
     @Test
@@ -135,19 +119,16 @@ public class SequenceEntityTest extends BrooklynAppUnitTestSupport {
 
         assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
 
-        assertEquals(sequence.currentValue(), Integer.valueOf(1));
-        assertEquals(sequence.currentString(), "1");
+        assertEquals(sequence.get(), Integer.valueOf(1));
 
-        Integer nextValue = sequence.invoke(SequenceEntity.NEXT_VALUE, ImmutableMap.<String, Object>of()).getUnchecked();
+        Integer nextValue = sequence.invoke(SequenceEntity.INCREMENT_AND_GET, ImmutableMap.<String, Object>of()).getUnchecked();
         assertEquals(nextValue, Integer.valueOf(2));
 
-        String nextString = sequence.invoke(SequenceEntity.NEXT_STRING, ImmutableMap.<String, Object>of()).getUnchecked();
-        assertEquals(nextString, "3");
+        nextValue = sequence.invoke(SequenceEntity.INCREMENT_AND_GET, ImmutableMap.<String, Object>of()).getUnchecked();
+        assertEquals(nextValue, Integer.valueOf(3));
 
-        assertEquals(sequence.currentValue(), Integer.valueOf(3));
-        assertEquals(sequence.currentString(), "3");
+        assertEquals(sequence.get(), Integer.valueOf(3));
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 3);
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "3");
     }
 
     @Test
@@ -157,23 +138,19 @@ public class SequenceEntityTest extends BrooklynAppUnitTestSupport {
 
         assertAttributeEqualsEventually(sequence, Startable.SERVICE_UP, true);
 
-        assertEquals(sequence.currentValue(), Integer.valueOf(1));
+        assertEquals(sequence.get(), Integer.valueOf(1));
 
         sequence.increment();
         sequence.increment();
         sequence.increment();
 
-        assertEquals(sequence.currentValue(), Integer.valueOf(4));
-        assertEquals(sequence.currentString(), "4");
+        assertEquals(sequence.get(), Integer.valueOf(4));
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 4);
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "4");
 
         sequence.invoke(SequenceEntity.RESET, ImmutableMap.<String, Object>of()).getUnchecked();
 
-        assertEquals(sequence.currentValue(), Integer.valueOf(1));
-        assertEquals(sequence.currentString(), "1");
+        assertEquals(sequence.get(), Integer.valueOf(1));
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 1);
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "1");
     }
 
 }


[7/8] brooklyn-server git commit: Formatting

Posted by dr...@apache.org.
Formatting


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

Branch: refs/heads/master
Commit: 62989ac9d615d89d0e95e44fe6f772b116a9aaf8
Parents: ff6e7a3
Author: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Authored: Tue Feb 28 14:20:44 2017 +0000
Committer: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Committed: Tue Apr 11 14:34:27 2017 +0100

----------------------------------------------------------------------
 .../entity/lifecycle/ServiceStateLogic.java     | 150 ++++++++++++-------
 1 file changed, 95 insertions(+), 55 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/62989ac9/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java b/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
index 21d8b58..386eb56 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
@@ -50,6 +50,7 @@ import org.apache.brooklyn.core.entity.EntityAdjuncts;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.entity.EntityPredicates;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle.Transition;
+import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.enricher.stock.AbstractMultipleSensorAggregator;
 import org.apache.brooklyn.enricher.stock.Enrichers;
 import org.apache.brooklyn.enricher.stock.UpdatingMap;
@@ -58,6 +59,7 @@ import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.collections.QuorumCheck;
+import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks;
 import org.apache.brooklyn.util.core.task.ValueResolver;
 import org.apache.brooklyn.util.guava.Functionals;
 import org.apache.brooklyn.util.guava.Maybe;
@@ -77,15 +79,15 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.reflect.TypeToken;
 
-/** Logic, sensors and enrichers, and conveniences, for computing service status */ 
+/** Logic, sensors and enrichers, and conveniences, for computing service status */
 public class ServiceStateLogic {
 
     private static final Logger log = LoggerFactory.getLogger(ServiceStateLogic.class);
-    
+
     public static final AttributeSensor<Boolean> SERVICE_UP = Attributes.SERVICE_UP;
     public static final AttributeSensor<Map<String,Object>> SERVICE_NOT_UP_INDICATORS = Attributes.SERVICE_NOT_UP_INDICATORS;
     public static final AttributeSensor<Map<String,Object>> SERVICE_NOT_UP_DIAGNOSTICS = Attributes.SERVICE_NOT_UP_DIAGNOSTICS;
-    
+
     public static final AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = Attributes.SERVICE_STATE_ACTUAL;
     public static final AttributeSensor<Lifecycle.Transition> SERVICE_STATE_EXPECTED = Attributes.SERVICE_STATE_EXPECTED;
     public static final AttributeSensor<Map<String,Object>> SERVICE_PROBLEMS = Attributes.SERVICE_PROBLEMS;
@@ -98,7 +100,7 @@ public class ServiceStateLogic {
         if (map==null) return null;
         return map.get(key);
     }
-    
+
     @SuppressWarnings("unchecked")
     public static <TKey,TVal> void clearMapSensorEntry(Entity entity, AttributeSensor<Map<TKey,TVal>> sensor, TKey key) {
         updateMapSensorEntry(entity, sensor, key, (TVal)Entities.REMOVE);
@@ -109,9 +111,9 @@ public class ServiceStateLogic {
         /*
          * Important to *not* modify the existing attribute value; must make a copy, modify that, and publish.
          * This is because a Propagator enricher will set this same value on another entity. There was very
-         * strange behaviour when this was done for a SERVICE_UP_INDICATORS sensor - the updates done here 
+         * strange behaviour when this was done for a SERVICE_UP_INDICATORS sensor - the updates done here
          * applied to the attribute of both entities!
-         * 
+         *
          * Need to do this update atomically (i.e. sequentially) because there is no threading control for
          * what is calling updateMapSensorEntity. It is called directly on start, on initialising enrichers,
          * and in event listeners. These calls could be concurrent.
@@ -120,7 +122,7 @@ public class ServiceStateLogic {
             @Override public Maybe<Map<TKey, TVal>> apply(Map<TKey, TVal> map) {
                 boolean created = (map==null);
                 if (created) map = MutableMap.of();
-                
+
                 boolean changed;
                 if (v == Entities.REMOVE) {
                     changed = map.containsKey(key);
@@ -147,22 +149,22 @@ public class ServiceStateLogic {
                 }
             }
         };
-        
-        if (!Entities.isNoLongerManaged(entity)) { 
+
+        if (!Entities.isNoLongerManaged(entity)) {
             entity.sensors().modify(sensor, modifier);
         }
     }
-    
+
     public static void setExpectedState(Entity entity, Lifecycle state) {
         waitBrieflyForServiceUpIfStateIsRunning(entity, state);
         ((EntityInternal)entity).sensors().set(Attributes.SERVICE_STATE_EXPECTED, new Lifecycle.Transition(state, new Date()));
-        
+
         Maybe<Enricher> enricher = EntityAdjuncts.tryFindWithUniqueTag(entity.enrichers(), ComputeServiceState.DEFAULT_ENRICHER_UNIQUE_TAG);
         if (enricher.isPresent() && enricher.get() instanceof ComputeServiceState) {
             ((ComputeServiceState)enricher.get()).onEvent(null);
         }
     }
-    
+
     public static Lifecycle getExpectedState(Entity entity) {
         Transition expected = entity.getAttribute(Attributes.SERVICE_STATE_EXPECTED);
         if (expected==null) return null;
@@ -197,13 +199,13 @@ public class ServiceStateLogic {
     public static boolean isExpectedState(Entity entity, Lifecycle state) {
         return getExpectedState(entity)==state;
     }
-    
+
     public static class ServiceNotUpLogic {
         public static final String DEFAULT_ENRICHER_UNIQUE_TAG = "service.isUp if no service.notUp.indicators";
-        
+
         /** static only; not for instantiation */
         private ServiceNotUpLogic() {}
-        
+
         public static final EnricherSpec<?> newEnricherForServiceUpIfNotUpIndicatorsEmpty() {
             // The function means: if the serviceNotUpIndicators is not null, then serviceNotUpIndicators.size()==0;
             // otherwise return the default value.
@@ -217,8 +219,8 @@ public class ServiceStateLogic {
                 .uniqueTag(DEFAULT_ENRICHER_UNIQUE_TAG)
                 .build();
         }
-        
-        /** puts the given value into the {@link Attributes#SERVICE_NOT_UP_INDICATORS} map as if the 
+
+        /** puts the given value into the {@link Attributes#SERVICE_NOT_UP_INDICATORS} map as if the
          * {@link UpdatingMap} enricher for the given key */
         public static void updateNotUpIndicator(Entity entity, String key, Object value) {
             updateMapSensorEntry(entity, Attributes.SERVICE_NOT_UP_INDICATORS, key, value);
@@ -246,17 +248,17 @@ public class ServiceStateLogic {
             if (nodes==null || nodes.isEmpty()) ServiceNotUpLogic.updateNotUpIndicator(entity, mapSensor, "Should have at least one entry");
             else ServiceNotUpLogic.clearNotUpIndicator(entity, mapSensor);
         }
-        
+
     }
-    
-    /** Enricher which sets {@link Attributes#SERVICE_STATE_ACTUAL} on changes to 
+
+    /** Enricher which sets {@link Attributes#SERVICE_STATE_ACTUAL} on changes to
      * {@link Attributes#SERVICE_STATE_EXPECTED}, {@link Attributes#SERVICE_PROBLEMS}, and {@link Attributes#SERVICE_UP}
      * <p>
      * The default implementation uses {@link #computeActualStateWhenExpectedRunning(Map, Boolean)} if the last expected transition
-     * was to {@link Lifecycle#RUNNING} and 
+     * was to {@link Lifecycle#RUNNING} and
      * {@link #computeActualStateWhenNotExpectedRunning(Map, Boolean, org.apache.brooklyn.core.entity.lifecycle.Lifecycle.Transition)} otherwise.
      * If these methods return null, the {@link Attributes#SERVICE_STATE_ACTUAL} sensor will be cleared (removed).
-     * Either of these methods can be overridden for custom logic, and that custom enricher can be created using 
+     * Either of these methods can be overridden for custom logic, and that custom enricher can be created using
      * {@link ServiceStateLogic#newEnricherForServiceState(Class)} and added to an entity.
      */
     public static class ComputeServiceState extends AbstractEnricher implements SensorEventListener<Object> {
@@ -267,13 +269,13 @@ public class ServiceStateLogic {
 
         public ComputeServiceState() {}
         public ComputeServiceState(Map<?,?> flags) { super(flags); }
-            
+
         @Override
         public void init() {
             super.init();
             if (uniqueTag==null) uniqueTag = DEFAULT_ENRICHER_UNIQUE_TAG;
         }
-        
+
         @Override
         public void setEntity(EntityLocal entity) {
             super.setEntity(entity);
@@ -281,7 +283,7 @@ public class ServiceStateLogic {
                 // only publish on changes, unless it is configured otherwise
                 suppressDuplicates = true;
             }
-            
+
             Map<String, ?> notifyOfInitialValue = ImmutableMap.of("notifyOfInitialValue", Boolean.TRUE);
             subscriptions().subscribe(notifyOfInitialValue, entity, SERVICE_PROBLEMS, this);
             subscriptions().subscribe(notifyOfInitialValue, entity, SERVICE_UP, this);
@@ -291,11 +293,11 @@ public class ServiceStateLogic {
         @Override
         public void onEvent(@Nullable SensorEvent<Object> event) {
             Preconditions.checkNotNull(entity, "Cannot handle subscriptions or compute state until associated with an entity");
-            
+
             Map<String, Object> serviceProblems = entity.getAttribute(SERVICE_PROBLEMS);
             Boolean serviceUp = entity.getAttribute(SERVICE_UP);
             Lifecycle.Transition serviceExpected = entity.getAttribute(SERVICE_STATE_EXPECTED);
-            
+
             if (serviceExpected!=null && serviceExpected.getState()==Lifecycle.RUNNING) {
                 setActualState( computeActualStateWhenExpectedRunning(serviceProblems, serviceUp) );
             } else {
@@ -315,12 +317,12 @@ public class ServiceStateLogic {
                 return Maybe.of(Lifecycle.ON_FIRE);
             }
         }
-        
+
         protected Maybe<Lifecycle> computeActualStateWhenNotExpectedRunning(Map<String, Object> problems, Boolean up, Lifecycle.Transition stateTransition) {
             if (stateTransition!=null) {
                 // if expected state is present but not running, just echo the expected state (ignore problems and up-ness)
                 return Maybe.of(stateTransition.getState());
-                
+
             } else if (problems!=null && !problems.isEmpty()) {
                 // if there is no expected state, then if service is not up, say stopped, else say on fire (whether service up is true or not present)
                 if (Boolean.FALSE.equals(up)) {
@@ -335,7 +337,7 @@ public class ServiceStateLogic {
                 // if the problems map is non-null, then infer from service up;
                 // if there is no problems map, then leave unchanged (user may have set it explicitly)
                 if (problems!=null)
-                    return Maybe.of(up==null ? null /* remove if up is not set */ : 
+                    return Maybe.of(up==null ? null /* remove if up is not set */ :
                         (up ? Lifecycle.RUNNING : Lifecycle.STOPPED));
                 else
                     return Maybe.absent();
@@ -355,19 +357,57 @@ public class ServiceStateLogic {
         }
 
     }
-    
+
     public static final EnricherSpec<?> newEnricherForServiceStateFromProblemsAndUp() {
         return newEnricherForServiceState(ComputeServiceState.class);
     }
     public static final EnricherSpec<?> newEnricherForServiceState(Class<? extends Enricher> type) {
+        newEnricherForServiceUpFromChildren();
         return EnricherSpec.create(type);
     }
-    
+
+    /**
+     * An enricher that sets {@link Startable#SERVICE_UP servive.isUp} on an entity only when all children are
+     * also reporting as healthy.
+     * <p>
+     * Equivalent to the following YAML configuration.
+     * <pre>
+     * brooklyn.enrichers:
+     *   - type: org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic$ComputeServiceIndicatorsFromChildrenAndMembers
+     *     brooklyn.config:
+     *       enricher.aggregating.fromChildren: true
+     *       enricher.aggregating.fromMembers: false
+     *       enricher.suppressDuplicates: true
+     *       enricher.service_state.children_and_members.quorum.up: all
+     *       enricher.service_state.children_and_members.ignore_entities.service_state_values: [ "STOPPING", "STOPPED", "DESTROYED" ]
+     * </pre>
+     */
+    public static final EnricherSpec<?> newEnricherForServiceUpFromChildren() {
+        return newEnricherForServiceUp(Boolean.TRUE, Boolean.FALSE);
+    }
+    public static final EnricherSpec<?> newEnricherForServiceUpFromMembers() {
+        return newEnricherForServiceUp(Boolean.FALSE, Boolean.TRUE);
+    }
+    public static final EnricherSpec<?> newEnricherForServiceUpFromChildrenWithQuorumCheck(QuorumCheck quorumCheck) {
+        EnricherSpec<?> serviceUp = newEnricherForServiceUpFromChildren()
+                .configure(ComputeServiceIndicatorsFromChildrenAndMembers.RUNNING_QUORUM_CHECK, quorumCheck);
+        return serviceUp;
+    }
+    public static final EnricherSpec<?> newEnricherForServiceUp(Boolean fromChildren, Boolean fromMembers) {
+        EnricherSpec<?> serviceUp = EnricherSpec.create(ComputeServiceIndicatorsFromChildrenAndMembers.class)
+                .configure(ComputeServiceIndicatorsFromChildrenAndMembers.FROM_CHILDREN, fromChildren)
+                .configure(ComputeServiceIndicatorsFromChildrenAndMembers.FROM_MEMBERS, fromMembers)
+                .configure(ComputeServiceIndicatorsFromChildrenAndMembers.SUPPRESS_DUPLICATES, Boolean.TRUE)
+                .configure(ComputeServiceIndicatorsFromChildrenAndMembers.RUNNING_QUORUM_CHECK, QuorumChecks.all())
+                .configure(ComputeServiceIndicatorsFromChildrenAndMembers.IGNORE_ENTITIES_WITH_THESE_SERVICE_STATES, ImmutableSet.of(Lifecycle.STOPPING, Lifecycle.STOPPED, Lifecycle.DESTROYED));
+        return serviceUp;
+    }
+
     public static class ServiceProblemsLogic {
         /** static only; not for instantiation */
         private ServiceProblemsLogic() {}
-        
-        /** puts the given value into the {@link Attributes#SERVICE_PROBLEMS} map as if the 
+
+        /** puts the given value into the {@link Attributes#SERVICE_PROBLEMS} map as if the
          * {@link UpdatingMap} enricher for the given sensor reported this value */
         public static void updateProblemsIndicator(Entity entity, Sensor<?> sensor, Object value) {
             updateMapSensorEntry(entity, Attributes.SERVICE_PROBLEMS, sensor.getName(), value);
@@ -393,11 +433,11 @@ public class ServiceStateLogic {
             clearMapSensorEntry(entity, Attributes.SERVICE_PROBLEMS, key);
         }
     }
-    
+
     public static class ComputeServiceIndicatorsFromChildrenAndMembers extends AbstractMultipleSensorAggregator<Void> implements SensorEventListener<Object> {
         /** standard unique tag identifying instances of this enricher at runtime, also used for the map sensor if no unique tag specified */
         public final static String DEFAULT_UNIQUE_TAG = "service-lifecycle-indicators-from-children-and-members";
-        
+
         /** as {@link #DEFAULT_UNIQUE_TAG}, but when a second distinct instance is responsible for computing service up */
         public final static String DEFAULT_UNIQUE_TAG_UP = "service-not-up-indicators-from-children-and-members";
 
@@ -406,7 +446,7 @@ public class ServiceStateLogic {
             .defaultValue(QuorumCheck.QuorumChecks.atLeastOneUnlessEmpty())
             .runtimeInheritance(BasicConfigInheritance.NOT_REINHERITED)
             .build();
-        public static final ConfigKey<QuorumCheck> RUNNING_QUORUM_CHECK = ConfigKeys.builder(QuorumCheck.class, "enricher.service_state.children_and_members.quorum.running") 
+        public static final ConfigKey<QuorumCheck> RUNNING_QUORUM_CHECK = ConfigKeys.builder(QuorumCheck.class, "enricher.service_state.children_and_members.quorum.running")
             .description("Logic for checking whether this service is healthy, based on children and/or members running, defaulting to requiring none to be ON-FIRE")
             .defaultValue(QuorumCheck.QuorumChecks.all())
             .runtimeInheritance(BasicConfigInheritance.NOT_REINHERITED)
@@ -417,8 +457,8 @@ public class ServiceStateLogic {
         public static final ConfigKey<Boolean> IGNORE_ENTITIES_WITH_SERVICE_UP_NULL = ConfigKeys.newBooleanConfigKey("enricher.service_state.children_and_members.ignore_entities.service_up_null", "Whether to ignore children reporting null values for service up", true);
         @SuppressWarnings("serial")
         public static final ConfigKey<Set<Lifecycle>> IGNORE_ENTITIES_WITH_THESE_SERVICE_STATES = ConfigKeys.newConfigKey(new TypeToken<Set<Lifecycle>>() {},
-            "enricher.service_state.children_and_members.ignore_entities.service_state_values", 
-            "Service states (including null) which indicate an entity should be ignored when looking at children service states; anything apart from RUNNING not in this list will be treated as not healthy (by default just ON_FIRE will mean not healthy)", 
+            "enricher.service_state.children_and_members.ignore_entities.service_state_values",
+            "Service states (including null) which indicate an entity should be ignored when looking at children service states; anything apart from RUNNING not in this list will be treated as not healthy (by default just ON_FIRE will mean not healthy)",
             MutableSet.<Lifecycle>builder().addAll(Lifecycle.values()).add(null).remove(Lifecycle.RUNNING).remove(Lifecycle.ON_FIRE).build().asUnmodifiable());
 
         protected String getKeyForMapSensor() {
@@ -455,9 +495,9 @@ public class ServiceStateLogic {
 
         final static Set<ConfigKey<?>> RECONFIGURABLE_KEYS = ImmutableSet.<ConfigKey<?>>of(
             UP_QUORUM_CHECK, RUNNING_QUORUM_CHECK,
-            DERIVE_SERVICE_NOT_UP, DERIVE_SERVICE_NOT_UP, 
+            DERIVE_SERVICE_NOT_UP, DERIVE_SERVICE_NOT_UP,
             IGNORE_ENTITIES_WITH_SERVICE_UP_NULL, IGNORE_ENTITIES_WITH_THESE_SERVICE_STATES);
-        
+
         @Override
         protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) {
             if (RECONFIGURABLE_KEYS.contains(key)) {
@@ -466,14 +506,14 @@ public class ServiceStateLogic {
                 super.doReconfigureConfig(key, val);
             }
         }
-        
+
         @Override
         protected void onChanged() {
             super.onChanged();
             if (entity != null && isRunning())
                 onUpdated();
         }
-        
+
         private final List<Sensor<?>> SOURCE_SENSORS = ImmutableList.<Sensor<?>>of(SERVICE_UP, SERVICE_STATE_ACTUAL);
         @Override
         protected Collection<Sensor<?>> getSourceSensors() {
@@ -511,7 +551,7 @@ public class ServiceStateLogic {
                     continue;
                 entries++;
                 Lifecycle entityState = state.getKey().getAttribute(SERVICE_STATE_ACTUAL);
-                
+
                 if (Boolean.TRUE.equals(state.getValue())) numUp++;
                 else if (!ignoreStates.contains(entityState)) {
                     violators.add(state.getKey());
@@ -544,7 +584,7 @@ public class ServiceStateLogic {
             Set<Lifecycle> ignoreStates = getConfig(IGNORE_ENTITIES_WITH_THESE_SERVICE_STATES);
             for (Map.Entry<Entity,Lifecycle> state: values.entrySet()) {
                 if (state.getValue()==Lifecycle.RUNNING) numRunning++;
-                else if (!ignoreStates.contains(state.getValue())) 
+                else if (!ignoreStates.contains(state.getValue()))
                     onesNotHealthy.add(state.getKey());
             }
 
@@ -582,14 +622,14 @@ public class ServiceStateLogic {
             return null;
         }
     }
-    
+
     public static class ComputeServiceIndicatorsFromChildrenAndMembersSpec extends ExtensibleEnricherSpec<ComputeServiceIndicatorsFromChildrenAndMembers,ComputeServiceIndicatorsFromChildrenAndMembersSpec> {
         private static final long serialVersionUID = -607444925297963712L;
-        
+
         protected ComputeServiceIndicatorsFromChildrenAndMembersSpec() {
             this(ComputeServiceIndicatorsFromChildrenAndMembers.class);
         }
-        
+
         protected ComputeServiceIndicatorsFromChildrenAndMembersSpec(Class<? extends ComputeServiceIndicatorsFromChildrenAndMembers> clazz) {
             super(clazz);
         }
@@ -626,14 +666,14 @@ public class ServiceStateLogic {
             configure(ComputeServiceIndicatorsFromChildrenAndMembers.RUNNING_QUORUM_CHECK, check);
             return self();
         }
-        
+
         public ComputeServiceIndicatorsFromChildrenAndMembersSpec entityFilter(Predicate<? super Entity> val) {
             configure(ComputeServiceIndicatorsFromChildrenAndMembers.ENTITY_FILTER, val);
             return self();
         }
     }
 
-    /** provides the default {@link ComputeServiceIndicatorsFromChildrenAndMembers} enricher, 
+    /** provides the default {@link ComputeServiceIndicatorsFromChildrenAndMembers} enricher,
      * using the default unique tag ({@link ComputeServiceIndicatorsFromChildrenAndMembers#DEFAULT_UNIQUE_TAG}),
      * configured here to require none on fire, and either no children or at least one up child,
      * the spec can be further configured as appropriate */
@@ -642,10 +682,10 @@ public class ServiceStateLogic {
             .uniqueTag(ComputeServiceIndicatorsFromChildrenAndMembers.DEFAULT_UNIQUE_TAG);
     }
 
-    /** as {@link #newEnricherFromChildren()} but only publishing service not-up indicators, 
+    /** as {@link #newEnricherFromChildren()} but only publishing service not-up indicators,
      * using a different unique tag ({@link ComputeServiceIndicatorsFromChildrenAndMembers#DEFAULT_UNIQUE_TAG_UP}),
      * listening to children only, ignoring lifecycle/service-state,
-     * and using the same logic 
+     * and using the same logic
      * (viz looking only at children (not members) and requiring either no children or at least one child up) by default */
     public static ComputeServiceIndicatorsFromChildrenAndMembersSpec newEnricherFromChildrenUp() {
         return newEnricherFromChildren()
@@ -653,14 +693,14 @@ public class ServiceStateLogic {
             .checkChildrenOnly()
             .configure(ComputeServiceIndicatorsFromChildrenAndMembers.DERIVE_SERVICE_PROBLEMS, false);
     }
-    
+
     /** as {@link #newEnricherFromChildren()} but only publishing service problems,
      * listening to children and members, ignoring service up,
-     * and using the same logic 
+     * and using the same logic
      * (viz looking at children and members and requiring none are on fire) by default */
     public static ComputeServiceIndicatorsFromChildrenAndMembersSpec newEnricherFromChildrenState() {
         return newEnricherFromChildren()
             .configure(ComputeServiceIndicatorsFromChildrenAndMembers.DERIVE_SERVICE_NOT_UP, false);
     }
-    
+
 }


[5/8] brooklyn-server git commit: Use configuration definitions from SequenceGroup and add local mutex

Posted by dr...@apache.org.
Use configuration definitions from SequenceGroup and add local mutex


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

Branch: refs/heads/master
Commit: 77d0ddb7336e60dcf9ed9b1f3085c422c6811a3f
Parents: a5aff68
Author: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Authored: Tue Feb 7 09:44:37 2017 +0000
Committer: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Committed: Tue Apr 11 14:34:26 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/stock/SequenceEntity.java   | 44 +++---------
 .../entity/stock/SequenceEntityImpl.java        | 74 ++++++++++++--------
 2 files changed, 52 insertions(+), 66 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/77d0ddb7/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
index 5135f3c..f53ba65 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
@@ -23,15 +23,10 @@ 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.entity.trait.Startable;
-import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
-import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.entity.group.SequenceGroup;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
-import org.apache.brooklyn.util.text.StringPredicates;
-
-import com.google.common.base.Predicates;
 
 /**
  * An entity that supplies a sequence of values through an effector.
@@ -44,46 +39,23 @@ import com.google.common.base.Predicates;
  *     sequence.start: 0
  *     sequence.increment: 1
  *     sequence.format: "global-%03d"
- *     sequence.name: "global"
  * }</pre>
  */
 @ImplementedBy(SequenceEntityImpl.class)
 public interface SequenceEntity extends Entity, Startable {
 
+    AttributeSensor<Integer> SEQUENCE_VALUE = SequenceGroup.SEQUENCE_VALUE;
+
+    AttributeSensor<String> SEQUENCE_STRING = SequenceGroup.SEQUENCE_STRING;
+
     @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();
+    ConfigKey<Integer> SEQUENCE_START = SequenceGroup.SEQUENCE_START;
 
     @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();
+    ConfigKey<Integer> SEQUENCE_INCREMENT = SequenceGroup.SEQUENCE_INCREMENT;
 
     @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();
-
-    @SetFromFlag("sequenceName")
-    AttributeSensorAndConfigKey<String, String> SEQUENCE_NAME = ConfigKeys.newStringSensorAndConfigKey("sequence.name", "The name of the sequence", "sequence");
-
-    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();
+    ConfigKey<String> SEQUENCE_FORMAT = SequenceGroup.SEQUENCE_FORMAT;
 
     MethodEffector<Void> RESET = new MethodEffector<Void>(SequenceEntity.class, "reset");
     MethodEffector<Void> INCREMENT = new MethodEffector<Void>(SequenceEntity.class, "increment");

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/77d0ddb7/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
index 338b323..0ddb134 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
@@ -22,17 +22,16 @@ import java.util.Collection;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.core.entity.AbstractEntity;
-import org.apache.brooklyn.core.feed.ConfigToAttributes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class SequenceEntityImpl extends AbstractEntity implements SequenceEntity {
 
-    public SequenceEntityImpl() { }
+    private static final Logger LOG = LoggerFactory.getLogger(SequenceEntity.class);
 
-    public void init() {
-        super.init();
+    private Object mutex = new Object();
 
-        ConfigToAttributes.apply(this, SEQUENCE_NAME);
-    }
+    public SequenceEntityImpl() { }
 
     @Override
     public void start(Collection<? extends Location> locations) {
@@ -52,48 +51,63 @@ public class SequenceEntityImpl extends AbstractEntity implements SequenceEntity
         start(getLocations());
     }
 
-    protected void sequence(Integer value) {
-        String format = config().get(SEQUENCE_FORMAT);
-
-        sensors().set(SEQUENCE_VALUE, value);
-        sensors().set(SEQUENCE_STRING, String.format(format, value));
-    }
-
     @Override
     public Integer currentValue() {
-        return sensors().get(SEQUENCE_VALUE);
+        synchronized (mutex) {
+            return sensors().get(SEQUENCE_VALUE);
+        }
     }
 
     @Override
     public String currentString() {
-        return sensors().get(SEQUENCE_STRING);
+        synchronized (mutex) {
+            return sensors().get(SEQUENCE_STRING);
+        }
     }
 
     @Override
-    public synchronized Integer nextValue() {
-        increment();
-        return currentValue();
+    public Integer nextValue() {
+        synchronized (mutex) {
+            increment();
+            return currentValue();
+        }
     }
 
     @Override
-    public synchronized String nextString() {
-        increment();
-        return currentString();
+    public String nextString() {
+        synchronized (mutex) {
+            increment();
+            return currentString();
+        }
     }
 
     @Override
-    public synchronized Void increment() {
-        Integer increment = config().get(SEQUENCE_INCREMENT);
-        Integer current = currentValue();
-        sequence(current + increment);
-        return null;
+    public Void increment() {
+        synchronized (mutex) {
+            Integer increment = config().get(SEQUENCE_INCREMENT);
+            Integer current = currentValue();
+            sequence(current + increment);
+            return null;
+        }
     }
 
     @Override
-    public synchronized Void reset() {
-        Integer start = config().get(SEQUENCE_START);
-        sequence(start);
-        return null;
+    public Void reset() {
+        synchronized (mutex) {
+            Integer start = config().get(SEQUENCE_START);
+            sequence(start);
+            return null;
+        }
+    }
+
+    private void sequence(Integer value) {
+        String format = config().get(SEQUENCE_FORMAT);
+        String string = String.format(format, value);
+
+        sensors().set(SEQUENCE_VALUE, value);
+        sensors().set(SEQUENCE_STRING, string);
+
+        LOG.debug("Sequence for {} set to {}", this, value);
     }
 
 }


[6/8] brooklyn-server git commit: Use AtomicIntegers for internal sequencer state

Posted by dr...@apache.org.
Use AtomicIntegers for internal sequencer state


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

Branch: refs/heads/master
Commit: ff6e7a376a135ffd2e8e672d25bacefdb662b594
Parents: 68a6f14
Author: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Authored: Wed Feb 8 19:47:11 2017 +0000
Committer: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Committed: Tue Apr 11 14:34:27 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/group/SequenceGroup.java    |  5 +++--
 .../entity/group/SequenceGroupImpl.java         | 16 ++++++++--------
 .../brooklyn/entity/stock/SequenceEntity.java   |  4 ++++
 .../entity/stock/SequenceEntityImpl.java        | 20 ++++++++++++--------
 .../entity/group/SequenceGroupTest.java         | 19 +++++++++++++------
 .../entity/stock/SequenceEntityTest.java        |  2 +-
 6 files changed, 41 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ff6e7a37/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
index 4d7d082..170e90d 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroup.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroup.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.entity.group;
 
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.ImplementedBy;
@@ -123,8 +124,8 @@ public interface SequenceGroup extends DynamicGroup {
             .description("The current entity in the sequence")
             .build();
 
-    AttributeSensor<Integer> SEQUENCE_NEXT = Sensors.builder(Integer.class, "sequence.next")
-            .description("The next value of the sequence")
+    AttributeSensor<AtomicInteger> SEQUENCE_STATE = Sensors.builder(AtomicInteger.class, "sequence.state")
+            .description("The current state of the sequence")
             .build();
 
     AttributeSensor<Map<String, Integer>> SEQUENCE_CACHE = Sensors.builder(new TypeToken<Map<String, Integer>>() { }, "sequence.cache")

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ff6e7a37/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
index 0e8c39c..6aca7af 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroupImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroupImpl.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.entity.group;
 
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
@@ -44,7 +45,8 @@ public class SequenceGroupImpl extends DynamicGroupImpl implements SequenceGroup
             sensors().set(SEQUENCE_CACHE, Maps.<String, Integer>newConcurrentMap());
             sensors().set(SEQUENCE_CURRENT, null);
             Integer initial = config().get(SEQUENCE_START);
-            sensors().set(SEQUENCE_NEXT, initial);
+            AtomicInteger state = new AtomicInteger(initial);
+            sensors().set(SEQUENCE_STATE, state);
             return null;
         }
     }
@@ -86,7 +88,10 @@ public class SequenceGroupImpl extends DynamicGroupImpl implements SequenceGroup
 
     private Integer sequence(Entity entity) {
         String format = config().get(SEQUENCE_FORMAT);
-        Integer current = sensors().get(SEQUENCE_NEXT);
+        Integer increment = config().get(SEQUENCE_INCREMENT);
+        AtomicInteger state = sensors().get(SEQUENCE_STATE);
+        Integer current = state.getAndAdd(increment);
+
         String string = String.format(format, current);
         AttributeSensor<Integer> valueSensor = config().get(SEQUENCE_VALUE_SENSOR);
         AttributeSensor<String> stringSensor = config().get(SEQUENCE_STRING_SENSOR);
@@ -96,12 +101,7 @@ public class SequenceGroupImpl extends DynamicGroupImpl implements SequenceGroup
         LOG.debug("Sequence on {} set to to {}", entity, current);
 
         sensors().set(SEQUENCE_CURRENT, entity);
-
-        Integer increment = config().get(SEQUENCE_INCREMENT);
-        Integer next = current + increment;
-        LOG.debug("Sequence for {} incremented to {}", this, next);
-
-        sensors().set(SEQUENCE_NEXT, next);
+        LOG.debug("Sequence for {} incremented to {}", this, state.get());
 
         return current;
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ff6e7a37/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
index 6b73d8f..bc58f47 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntity.java
@@ -18,6 +18,8 @@
  */
 package org.apache.brooklyn.entity.stock;
 
+import java.util.concurrent.atomic.AtomicInteger;
+
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.ImplementedBy;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
@@ -47,6 +49,8 @@ public interface SequenceEntity extends Entity, Startable, Supplier<Integer> {
 
     AttributeSensor<Integer> SEQUENCE_VALUE = SequenceGroup.SEQUENCE_VALUE;
 
+    AttributeSensor<AtomicInteger> SEQUENCE_STATE = SequenceGroup.SEQUENCE_STATE;
+
     @SetFromFlag("sequenceStart")
     ConfigKey<Integer> SEQUENCE_START = SequenceGroup.SEQUENCE_START;
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ff6e7a37/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
index b731dfe..06fee53 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/SequenceEntityImpl.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.entity.stock;
 
 import java.util.Collection;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.core.entity.AbstractEntity;
@@ -61,7 +62,7 @@ public class SequenceEntityImpl extends AbstractEntity implements SequenceEntity
     @Override
     public Integer incrementAndGet() {
         synchronized (mutex) {
-            increment();
+            sequence();
             return get();
         }
     }
@@ -69,9 +70,7 @@ public class SequenceEntityImpl extends AbstractEntity implements SequenceEntity
     @Override
     public Void increment() {
         synchronized (mutex) {
-            Integer increment = config().get(SEQUENCE_INCREMENT);
-            Integer current = get();
-            sequence(current + increment);
+            sequence();
             return null;
         }
     }
@@ -80,14 +79,19 @@ public class SequenceEntityImpl extends AbstractEntity implements SequenceEntity
     public Void reset() {
         synchronized (mutex) {
             Integer start = config().get(SEQUENCE_START);
-            sequence(start);
+            AtomicInteger state =  new AtomicInteger(start);
+            sensors().set(SEQUENCE_STATE, state);
+            sensors().set(SEQUENCE_VALUE, state.get());
             return null;
         }
     }
 
-    private void sequence(Integer value) {
-        sensors().set(SEQUENCE_VALUE, value);
-        LOG.debug("Sequence for {} set to {}", this, value);
+    private void sequence() {
+        Integer increment = config().get(SEQUENCE_INCREMENT);
+        AtomicInteger state = sensors().get(SEQUENCE_STATE);
+        Integer current = state.addAndGet(increment);
+        sensors().set(SEQUENCE_VALUE, current);
+        LOG.debug("Sequence for {} set to {}", this, current);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ff6e7a37/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
index d835c7d..f1d85c5 100644
--- a/core/src/test/java/org/apache/brooklyn/entity/group/SequenceGroupTest.java
+++ b/core/src/test/java/org/apache/brooklyn/entity/group/SequenceGroupTest.java
@@ -22,9 +22,11 @@ import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttribute;
 import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEquals;
 import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEqualsEventually;
 import static org.apache.brooklyn.test.Asserts.assertEqualsIgnoringOrder;
-import static org.apache.brooklyn.test.Asserts.assertTrue;
+import static org.apache.brooklyn.test.Asserts.*;
 import static org.apache.brooklyn.test.Asserts.succeedsEventually;
 
+import java.util.concurrent.atomic.AtomicInteger;
+
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.location.LocationSpec;
@@ -83,7 +85,8 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
 
         assertEqualsIgnoringOrder(group.getMembers(), ImmutableList.of(e1));
         assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 1);
-        assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 2);
+        AtomicInteger state = group.sensors().get(SequenceGroup.SEQUENCE_STATE);
+        assertEquals(state.get(), 2);
     }
 
     @Test
@@ -143,8 +146,9 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
         assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 3);
         assertAttributeEquals(e2, SequenceGroup.SEQUENCE_VALUE, 4);
         assertAttributeEquals(e3, SequenceGroup.SEQUENCE_VALUE, 5);
-        assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 6);
         assertAttributeEquals(group, SequenceGroup.SEQUENCE_CURRENT, e3);
+        AtomicInteger state = group.sensors().get(SequenceGroup.SEQUENCE_STATE);
+        assertEquals(state.get(), 6);
     }
 
     @Test
@@ -162,7 +166,8 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
             public void run() {
                 assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e));
                 assertAttributeEquals(e, SequenceGroup.SEQUENCE_VALUE, 1);
-                assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 2);
+                AtomicInteger state = group.sensors().get(SequenceGroup.SEQUENCE_STATE);
+                assertEquals(state.get(), 2);
             }});
     }
 
@@ -179,8 +184,9 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
         assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 1);
         assertAttributeEquals(e2, SequenceGroup.SEQUENCE_VALUE, 2);
         assertAttributeEquals(e3, SequenceGroup.SEQUENCE_VALUE, 3);
-        assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 4);
         assertAttributeEquals(group, SequenceGroup.SEQUENCE_CURRENT, e3);
+        AtomicInteger state = group.sensors().get(SequenceGroup.SEQUENCE_STATE);
+        assertEquals(state.get(), 4);
 
         final Entity e = app.addChild(EntitySpec.create(TestEntity.class));
 
@@ -188,8 +194,9 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
             public void run() {
                 assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e1, e2, e3, e));
                 assertAttributeEquals(e, SequenceGroup.SEQUENCE_VALUE, 4);
-                assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 5);
                 assertAttributeEquals(group, SequenceGroup.SEQUENCE_CURRENT, e);
+                AtomicInteger state = group.sensors().get(SequenceGroup.SEQUENCE_STATE);
+                assertEquals(state.get(), 5);
             }});
     }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ff6e7a37/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java b/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
index c4b3b86..0acafaf 100644
--- a/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
+++ b/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
@@ -113,7 +113,7 @@ public class SequenceEntityTest extends BrooklynAppUnitTestSupport {
     }
 
     @Test
-    public void testSequenceNextEffectors() throws Exception {
+    public void testSequenceIncrementAndGetEffector() throws Exception {
         sequence = app.addChild(EntitySpec.create(SequenceEntity.class));
         app.start(ImmutableList.of(loc1));
 


[2/8] brooklyn-server git commit: Add support for configurable sequence sensors and update tests

Posted by dr...@apache.org.
Add support for configurable sequence sensors and update tests


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

Branch: refs/heads/master
Commit: a5aff6801dceb2df81b41aa8b178a8f570903cba
Parents: 2728745
Author: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Authored: Tue Feb 7 09:43:50 2017 +0000
Committer: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Committed: Tue Apr 11 14:34:26 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/group/SequenceGroup.java    |  70 +++++++++---
 .../entity/group/SequenceGroupImpl.java         |  32 +++++-
 .../entity/group/SequenceGroupTest.java         | 107 +++++++++++++------
 .../entity/stock/SequenceEntityTest.java        |   2 -
 4 files changed, 161 insertions(+), 50 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a5aff680/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
index a6dee56..851d3b7 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroup.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroup.java
@@ -20,6 +20,7 @@ package org.apache.brooklyn.entity.group;
 
 import java.util.Map;
 
+import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.ImplementedBy;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.config.ConfigKey;
@@ -27,10 +28,10 @@ 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.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.reflect.TypeToken;
 
@@ -44,18 +45,45 @@ import com.google.common.reflect.TypeToken;
  *   brooklyn.config:
  *     entityFilter:
  *       $brooklyn:object:
- *         type: org.apache.brooklyn.core.entity.EntityPredicates
- *         factoryMethod.name: "applicationIdEqualTo"
+ *         type: com.google.common.base.Predicates
+ *         factoryMethod.name: "and"
  *         factoryMethod.args:
- *           - $brooklyn:attributeWhenReady("application.id")
+ *           - $brooklyn:object:
+ *               type: org.apache.brooklyn.core.entity.EntityPredicates
+ *               factoryMethod.name: "applicationIdEqualTo"
+ *               factoryMethod.args:
+ *                 - $brooklyn:attributeWhenReady("application.id")
+ *           - $brooklyn:object:
+ *               type: org.apache.brooklyn.core.entity.EntityPredicates
+ *               factoryMethod.name: "configEquelTo"
+ *               factoryMethod.args:
+ *                 - "sequence.set"
+ *                 - true
  *     sequenceStart: 0
  *     sequenceIncrement: 1
- *     sequenceFormat: "entity%04x"
+ *     sequenceFormat: "Entity %04x"
+ *     sequenceValueSensor: $brooklyn:sensor("entity.sequence")
+ *     sequenceStringSensor: $brooklyn:sensor("entity.name")
  * }</pre>
+ * <p>
+ * <b>IMPORTANT</b> Relationship management will fail in unpredictable ways
+ * if an entity is a member of multiple sequences that use the same
+ * {@link #SEQUENCE_VALUE_SENSOR sensor}, however this usage will probably
+ * not have the desired effect in any case.
  */
 @ImplementedBy(SequenceGroupImpl.class)
 public interface SequenceGroup extends DynamicGroup {
 
+    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();
+
+    ConfigKey<Predicate<? super Entity>> ENTITY_FILTER = ConfigKeys.newConfigKeyWithDefault(DynamicGroup.ENTITY_FILTER, Predicates.alwaysFalse());
+
     @SetFromFlag("sequenceStart")
     ConfigKey<Integer> SEQUENCE_START = ConfigKeys.builder(Integer.class)
             .name("sequence.start")
@@ -65,7 +93,7 @@ public interface SequenceGroup extends DynamicGroup {
             .build();
 
     @SetFromFlag("sequenceIncrement")
-    ConfigKey<Integer> SEQUENCE_INCREMENT =  ConfigKeys.builder(Integer.class)
+    ConfigKey<Integer> SEQUENCE_INCREMENT = ConfigKeys.builder(Integer.class)
             .name("sequence.increment")
             .description("The sequence increment for the next value")
             .defaultValue(1)
@@ -80,6 +108,26 @@ public interface SequenceGroup extends DynamicGroup {
             .constraint(StringPredicates.containsRegex("%[-#+ 0,(]*[0-9]*[doxX]"))
             .build();
 
+    @SetFromFlag("sequenceValueSensor")
+    ConfigKey<AttributeSensor<Integer>> SEQUENCE_VALUE_SENSOR = ConfigKeys.builder(new TypeToken<AttributeSensor<Integer>>() { })
+            .name("sequence.sensor.value")
+            .description("The sensor for the sequence value")
+            .defaultValue(SEQUENCE_VALUE)
+            .constraint(Predicates.<AttributeSensor<Integer>>notNull())
+            .build();
+
+    @SetFromFlag("sequenceStringSensor")
+    ConfigKey<AttributeSensor<String>> SEQUENCE_STRING_SENSOR = ConfigKeys.builder(new TypeToken<AttributeSensor<String>>() { })
+            .name("sequence.sensor.string")
+            .description("The sensor for the sequence string")
+            .defaultValue(SEQUENCE_STRING)
+            .constraint(Predicates.<AttributeSensor<String>>notNull())
+            .build();
+
+    AttributeSensor<Entity> SEQUENCE_CURRENT = Sensors.builder(Entity.class, "sequence.current")
+            .description("The current entity in the sequence")
+            .build();
+
     AttributeSensor<Integer> SEQUENCE_NEXT = Sensors.builder(Integer.class, "sequence.next")
             .description("The next value of the sequence")
             .build();
@@ -88,17 +136,9 @@ public interface SequenceGroup extends DynamicGroup {
             .description("The current cache of entity ids to sequence numbers")
             .build();
 
-    MethodEffector<Void> RESET = new MethodEffector<Void>(SequenceEntity.class, "reset");
+    MethodEffector<Void> RESET = new MethodEffector<Void>(SequenceGroup.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/a5aff680/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
index f9fc04e..fc9c526 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroupImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/group/SequenceGroupImpl.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.entity.group;
 import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -28,7 +29,7 @@ import com.google.common.collect.Maps;
 
 public class SequenceGroupImpl extends DynamicGroupImpl implements SequenceGroup {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SequenceGroupImpl.class);
+    private static final Logger LOG = LoggerFactory.getLogger(SequenceGroup.class);
 
     public SequenceGroupImpl() { }
 
@@ -36,6 +37,7 @@ public class SequenceGroupImpl extends DynamicGroupImpl implements SequenceGroup
     public Void reset() {
         synchronized (memberChangeMutex) {
             sensors().set(SEQUENCE_CACHE, Maps.<String, Integer>newConcurrentMap());
+            sensors().set(SEQUENCE_CURRENT, null);
             Integer initial = config().get(SEQUENCE_START);
             sensors().set(SEQUENCE_NEXT, initial);
             return null;
@@ -58,6 +60,7 @@ public class SequenceGroupImpl extends DynamicGroupImpl implements SequenceGroup
                 Map<String, Integer> cache = sensors().get(SEQUENCE_CACHE);
                 if (!cache.containsKey(member.getId())) {
                     Integer value = sequence(member);
+
                     cache.put(member.getId(), value);
                 }
             }
@@ -65,13 +68,36 @@ public class SequenceGroupImpl extends DynamicGroupImpl implements SequenceGroup
         }
     }
 
+    @Override
+    public boolean removeMember(Entity member) {
+        synchronized (memberChangeMutex) {
+            boolean changed = super.removeMember(member);
+            if (changed) {
+                Map<String, Integer> cache = sensors().get(SEQUENCE_CACHE);
+                if (cache.containsKey(member.getId())) {
+                    cache.remove(member.getId());
+
+                    AttributeSensor<Integer> valueSensor = config().get(SEQUENCE_VALUE_SENSOR);
+                    AttributeSensor<String> stringSensor = config().get(SEQUENCE_STRING_SENSOR);
+                    member.sensors().set(valueSensor, null);
+                    member.sensors().set(stringSensor, null);
+                }
+            }
+            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);
+        AttributeSensor<Integer> valueSensor = config().get(SEQUENCE_VALUE_SENSOR);
+        AttributeSensor<String> stringSensor = config().get(SEQUENCE_STRING_SENSOR);
+
+        entity.sensors().set(valueSensor, current);
+        entity.sensors().set(stringSensor, string);
 
-        entity.sensors().set(SEQUENCE_VALUE, current);
-        entity.sensors().set(SEQUENCE_STRING,string);
+        sensors().set(SEQUENCE_CURRENT, entity);
 
         Integer increment = config().get(SEQUENCE_INCREMENT);
         Integer next = current + increment;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a5aff680/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
index afb5425..148eb5b 100644
--- a/core/src/test/java/org/apache/brooklyn/entity/group/SequenceGroupTest.java
+++ b/core/src/test/java/org/apache/brooklyn/entity/group/SequenceGroupTest.java
@@ -19,16 +19,20 @@
 package org.apache.brooklyn.entity.group;
 
 import static org.apache.brooklyn.test.Asserts.assertEqualsIgnoringOrder;
+import static org.apache.brooklyn.test.Asserts.*;
+import static org.apache.brooklyn.core.entity.EntityAsserts.*;
+
+import java.util.Set;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
 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.sensor.Sensors;
 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;
@@ -38,11 +42,10 @@ 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;
+    private TestEntity e1, e2, e3;
 
     @BeforeMethod(alwaysRun=true)
     @Override
@@ -50,9 +53,12 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
         super.setUp();
 
         app = TestApplication.Factory.newManagedInstanceForTests();
-        group = app.createAndManageChild(EntitySpec.create(SequenceGroup.class));
+        group = app.createAndManageChild(EntitySpec.create(SequenceGroup.class)
+                .configure(SequenceGroup.SEQUENCE_STRING_SENSOR, Sensors.newStringSensor("test.sequence"))
+                .configure(SequenceGroup.SEQUENCE_FORMAT, "test-%02d"));
         e1 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
         e2 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        e3 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
     }
 
     @AfterMethod(alwaysRun=true)
@@ -63,37 +69,75 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
         super.tearDown();
     }
 
+
+    @Test
+    public void testGroupDefaults() throws Exception {
+        assertTrue(group.getMembers().isEmpty());
+    }
+
     @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);
+        assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 1);
+        assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 2);
+    }
+
+    @Test
+    public void testGroupConfiguration() throws Exception {
+        group.setEntityFilter(EntityPredicates.idEqualTo(e1.getId()));
+
+        assertEqualsIgnoringOrder(group.getMembers(), ImmutableList.of(e1));
+        assertAttributeEquals(e1, SequenceGroup.SEQUENCE_STRING, null);
+        assertAttributeEquals(e1, Sensors.newStringSensor("test.sequence"), "test-01");
+    }
+
+    @Test
+    public void testAlternateGroupConfiguration() throws Exception {
+        AttributeSensor<Integer> value = Sensors.newIntegerSensor("test.value");
+        AttributeSensor<String> string = Sensors.newStringSensor("test.string");
+        group = app.createAndManageChild(EntitySpec.create(SequenceGroup.class)
+                .configure(SequenceGroup.SEQUENCE_START, 12345)
+                .configure(SequenceGroup.SEQUENCE_INCREMENT, 678)
+                .configure(SequenceGroup.SEQUENCE_VALUE_SENSOR, value)
+                .configure(SequenceGroup.SEQUENCE_STRING_SENSOR, string)
+                .configure(SequenceGroup.SEQUENCE_FORMAT, "0x%04X"));
+        group.setEntityFilter(EntityPredicates.hasInterfaceMatching(".*TestEntity"));
+
+        assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e1, e2, e3));
+        assertAttributeEquals(e1, value, 12345);
+        assertAttributeEquals(e1, string, "0x3039");
+        assertAttributeEquals(e2, value, 13023);
+        assertAttributeEquals(e2, string, "0x32DF");
+        assertAttributeEquals(e3, value, 13701);
+        assertAttributeEquals(e3, string, "0x3585");
     }
 
     @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);
+        assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e1, e2, e3, app, group));
+        assertAttributeEquals(app, SequenceGroup.SEQUENCE_VALUE, 1);
+        assertAttributeEquals(group, SequenceGroup.SEQUENCE_VALUE, 2);
+        assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 3);
+        assertAttributeEquals(e2, SequenceGroup.SEQUENCE_VALUE, 4);
+        assertAttributeEquals(e3, SequenceGroup.SEQUENCE_VALUE, 5);
+        assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 6);
+        assertAttributeEquals(group, SequenceGroup.SEQUENCE_CURRENT, e3);
     }
-    
+
     @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() {
+        final Entity e = app.addChild(EntitySpec.create(TestEntity.class).displayName("myname"));
+
+        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);
+                assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e));
+                assertAttributeEquals(e, SequenceGroup.SEQUENCE_VALUE, 1);
+                assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 2);
             }});
     }
 
@@ -101,18 +145,21 @@ public class SequenceGroupTest extends BrooklynAppUnitTestSupport {
     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);
+        assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e1, e2, e3));
+        assertAttributeEquals(e1, SequenceGroup.SEQUENCE_VALUE, 1);
+        assertAttributeEquals(e2, SequenceGroup.SEQUENCE_VALUE, 2);
+        assertAttributeEquals(e3, SequenceGroup.SEQUENCE_VALUE, 3);
+        assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 4);
+        assertAttributeEquals(group, SequenceGroup.SEQUENCE_CURRENT, e3);
 
-        final Entity e3 = app.addChild(EntitySpec.create(TestEntity.class));
+        final Entity e = app.addChild(EntitySpec.create(TestEntity.class));
 
-        Asserts.succeedsEventually(new Runnable() {
+        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);
+                assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(e1, e2, e3, e));
+                assertAttributeEquals(e, SequenceGroup.SEQUENCE_VALUE, 4);
+                assertAttributeEquals(group, SequenceGroup.SEQUENCE_NEXT, 5);
+                assertAttributeEquals(group, SequenceGroup.SEQUENCE_CURRENT, e);
             }});
     }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a5aff680/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java b/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
index 2b20f8d..2ec8a79 100644
--- a/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
+++ b/core/src/test/java/org/apache/brooklyn/entity/stock/SequenceEntityTest.java
@@ -61,7 +61,6 @@ public class SequenceEntityTest extends BrooklynAppUnitTestSupport {
     @Test
     public void testSequenceInitialConfig() throws Exception {
         sequence = app.addChild(EntitySpec.create(SequenceEntity.class)
-                .configure(SequenceEntity.SEQUENCE_NAME, "forty-two")
                 .configure(SequenceEntity.SEQUENCE_START, 42)
                 .configure(SequenceEntity.SEQUENCE_FORMAT, "id-%02x"));
         app.start(ImmutableList.of(loc1));
@@ -70,7 +69,6 @@ public class SequenceEntityTest extends BrooklynAppUnitTestSupport {
 
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_VALUE, 42);
         assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_STRING, "id-2a");
-        assertAttributeEquals(sequence, SequenceEntity.SEQUENCE_NAME, "forty-two");
     }
 
     @Test