refactor Enrichers.builder() types to give correct generics for published sensor
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/3c714ed1 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/3c714ed1 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/3c714ed1 Branch: refs/heads/master Commit: 3c714ed12259c3909f0244f83f504e6a62be7bdb Parents: 141751b Author: Alex Heneveld <[email protected]> Authored: Wed Aug 6 22:09:15 2014 -0400 Committer: Alex Heneveld <[email protected]> Committed: Wed Aug 27 02:07:48 2014 -0400 ---------------------------------------------------------------------- .../main/java/brooklyn/enricher/Enrichers.java | 132 ++++++++++--------- .../java/brooklyn/enricher/EnrichersTest.java | 55 +++++++- 2 files changed, 115 insertions(+), 72 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/3c714ed1/core/src/main/java/brooklyn/enricher/Enrichers.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/enricher/Enrichers.java b/core/src/main/java/brooklyn/enricher/Enrichers.java index 6ddc780..0d3a0d5 100644 --- a/core/src/main/java/brooklyn/enricher/Enrichers.java +++ b/core/src/main/java/brooklyn/enricher/Enrichers.java @@ -56,8 +56,8 @@ public class Enrichers { private Enrichers() {} - public static InitialBuilder<?> builder() { - return new ConcreteInitialBuilder(); + public static InitialBuilder builder() { + return new InitialBuilder(); } public abstract static class Builder<B extends Builder<B>> { @@ -67,36 +67,46 @@ public class Enrichers { } } - public abstract static class InitialBuilder<B extends InitialBuilder<B>> extends Builder<B> { - public PropagatorBuilder<?> propagating(Map<? extends Sensor<?>, ? extends Sensor<?>> vals) { - return new ConcretePropagatorBuilder(vals); + protected abstract static class AbstractInitialBuilder<B extends AbstractInitialBuilder<B>> extends Builder<B> { + public PropagatorBuilder propagating(Map<? extends Sensor<?>, ? extends Sensor<?>> vals) { + return new PropagatorBuilder(vals); } - public PropagatorBuilder<?> propagating(Iterable<? extends Sensor<?>> vals) { - return new ConcretePropagatorBuilder(vals); + public PropagatorBuilder propagating(Iterable<? extends Sensor<?>> vals) { + return new PropagatorBuilder(vals); } - public PropagatorBuilder<?> propagating(Sensor<?>... vals) { - return new ConcretePropagatorBuilder(vals); + public PropagatorBuilder propagating(Sensor<?>... vals) { + return new PropagatorBuilder(vals); } - public PropagatorBuilder<?> propagatingAll() { - return new ConcretePropagatorBuilder(true, null); + public PropagatorBuilder propagatingAll() { + return new PropagatorBuilder(true, null); } - public PropagatorBuilder<?> propagatingAllBut(Sensor<?>... vals) { - return new ConcretePropagatorBuilder(true, ImmutableSet.copyOf(vals)); + public PropagatorBuilder propagatingAllBut(Sensor<?>... vals) { + return new PropagatorBuilder(true, ImmutableSet.copyOf(vals)); } - public PropagatorBuilder<?> propagatingAllBut(Iterable<? extends Sensor<?>> vals) { - return new ConcretePropagatorBuilder(true, vals); + public PropagatorBuilder propagatingAllBut(Iterable<? extends Sensor<?>> vals) { + return new PropagatorBuilder(true, vals); } - public <S> TransformerBuilder<S, Object, ?> transforming(AttributeSensor<S> val) { - return new ConcreteTransformerBuilder<S, Object>(val); - } - public <S> CombinerBuilder<S, Object, ?> combining(AttributeSensor<? extends S>... vals) { - return new ConcreteCombinerBuilder<S, Object>(vals); - } - public <S> CombinerBuilder<S, Object, ?> combining(Collection<AttributeSensor<? extends S>> vals) { - return new ConcreteCombinerBuilder<S, Object>(vals); - } - public <S> AggregatorBuilder<S, Object, ?> aggregating(AttributeSensor<S> val) { - return new ConcreteAggregatorBuilder<S,Object>(val); + + /** builds an enricher which transforms a given sensor: + * <li> applying a (required) function ({@link TransformerBuilder#computing(Function)}, or {@link TransformerBuilder#computingAverage()}/{@link TransformerBuilder#computingSum()}, mandatory); + * <li> and publishing it on the entity where the enricher is attached; + * <li> optionally taking the sensor from a different source entity ({@link TransformerBuilder#from(Entity)}); + * <li> and optionally publishing it as a different sensor ({@link TransformerBuilder#publishing(AttributeSensor)}); + * <p> (You must supply at least one of the optional values, of course, otherwise the enricher may loop endlessly!) */ + public <S> TransformerBuilder<S, Object> transforming(AttributeSensor<S> val) { + return new TransformerBuilder<S, Object>(val); + } + /** as {@link #transforming(AttributeSensor)} but accepting multiple sensors, with the function acting on the set of values */ + public <S> CombinerBuilder<S, Object> combining(Collection<AttributeSensor<? extends S>> vals) { + return new CombinerBuilder<S, Object>(vals); + } + /** as {@link #combining(Collection)} */ + public <S> CombinerBuilder<S, Object> combining(AttributeSensor<? extends S>... vals) { + return new CombinerBuilder<S, Object>(vals); + } + /** as {@link #combining(Collection)} but the collection of values comes from the given sensor on multiple entities */ + public <S> AggregatorBuilder<S, Object> aggregating(AttributeSensor<S> val) { + return new AggregatorBuilder<S,Object>(val); } /** creates an {@link UpdatingMap} enricher: * {@link UpdatingMapBuilder#from(AttributeSensor)} and {@link UpdatingMapBuilder#computing(Function)} are required @@ -107,7 +117,7 @@ public class Enrichers { } - public abstract static class AggregatorBuilder<S, T, B extends AggregatorBuilder<S, T, B>> extends Builder<B> { + protected abstract static class AbstractAggregatorBuilder<S, T, B extends AbstractAggregatorBuilder<S, T, B>> extends Builder<B> { protected final AttributeSensor<S> aggregating; protected AttributeSensor<T> publishing; protected Entity fromEntity; @@ -121,15 +131,13 @@ public class Enrichers { protected Object defaultValueForUnreportedSensors; protected Object valueToReportIfNoSensors; - public AggregatorBuilder(AttributeSensor<S> aggregating) { + public AbstractAggregatorBuilder(AttributeSensor<S> aggregating) { this.aggregating = aggregating; } - // TODO change the signature of this to have correct type info as done for UpdatingMapBuilder.from(Sensor) - // (including change *Builder to Abstract*Builder and Concrete*Builder to *Builder, for all other enricher types) @SuppressWarnings({ "unchecked", "rawtypes" }) - public B publishing(AttributeSensor<? extends T> val) { + public <T2 extends T> AggregatorBuilder<S,T2> publishing(AttributeSensor<? extends T2> val) { this.publishing = (AttributeSensor) checkNotNull(val); - return self(); + return (AggregatorBuilder) self(); } public B from(Entity val) { this.fromEntity = checkNotNull(val); @@ -236,7 +244,7 @@ public class Enrichers { } } - public abstract static class CombinerBuilder<S, T, B extends CombinerBuilder<S, T, B>> extends Builder<B> { + protected abstract static class AbstractCombinerBuilder<S, T, B extends AbstractCombinerBuilder<S, T, B>> extends Builder<B> { protected final List<AttributeSensor<? extends S>> combining; protected AttributeSensor<T> publishing; protected Entity fromEntity; @@ -248,17 +256,17 @@ public class Enrichers { // For summing/averaging protected Object defaultValueForUnreportedSensors; - public CombinerBuilder(AttributeSensor<? extends S>... vals) { + public AbstractCombinerBuilder(AttributeSensor<? extends S>... vals) { this(ImmutableList.copyOf(vals)); } - public CombinerBuilder(Collection<AttributeSensor<? extends S>> vals) { + public AbstractCombinerBuilder(Collection<AttributeSensor<? extends S>> vals) { checkArgument(checkNotNull(vals).size() > 0, "combining-sensors must be non-empty"); this.combining = ImmutableList.<AttributeSensor<? extends S>>copyOf(vals); } @SuppressWarnings({ "unchecked", "rawtypes" }) - public B publishing(AttributeSensor<? extends T> val) { + public <T2 extends T> CombinerBuilder<S,T2> publishing(AttributeSensor<? extends T2> val) { this.publishing = (AttributeSensor) checkNotNull(val); - return self(); + return (CombinerBuilder) this; } public B from(Entity val) { this.fromEntity = checkNotNull(val); @@ -325,26 +333,20 @@ public class Enrichers { } } - /** builds an enricher which transforms a given sensor: - * <li> applying a function ({@link #computing(Function)}, or {@link #computingAverage()}/{@link #computingSum()}, mandatory); - * <li> and publishing it on the entity where the enricher is attached; - * <li> optionally taking the sensor from a different source entity ({@link #from(Entity)}); - * <li> and optionally publishing it as a different sensor ({@link #publishing(AttributeSensor)}); - * <p> (You should supply at least one of the optional values, of course, otherwise the enricher may loop endlessly!) */ - public abstract static class TransformerBuilder<S, T, B extends TransformerBuilder<S, T, B>> extends Builder<B> { + protected abstract static class AbstractTransformerBuilder<S, T, B extends AbstractTransformerBuilder<S, T, B>> extends Builder<B> { protected final AttributeSensor<S> transforming; protected AttributeSensor<T> publishing; protected Entity fromEntity; protected Function<? super S, ?> computing; protected Function<? super SensorEvent<S>, ?> computingFromEvent; - public TransformerBuilder(AttributeSensor<S> val) { + public AbstractTransformerBuilder(AttributeSensor<S> val) { this.transforming = checkNotNull(val); } @SuppressWarnings({ "unchecked", "rawtypes" }) - public B publishing(AttributeSensor<? extends T> val) { + public <T2 extends T> TransformerBuilder<S,T2> publishing(AttributeSensor<? extends T2> val) { this.publishing = (AttributeSensor) checkNotNull(val); - return self(); + return (TransformerBuilder) this; } public B from(Entity val) { this.fromEntity = checkNotNull(val); @@ -382,25 +384,25 @@ public class Enrichers { } } - public abstract static class PropagatorBuilder<B extends PropagatorBuilder<B>> extends Builder<B> { + protected abstract static class AbstractPropagatorBuilder<B extends AbstractPropagatorBuilder<B>> extends Builder<B> { protected final Map<? extends Sensor<?>, ? extends Sensor<?>> propagating; protected final Boolean propagatingAll; protected final Iterable<? extends Sensor<?>> propagatingAllBut; protected Entity fromEntity; - public PropagatorBuilder(Map<? extends Sensor<?>, ? extends Sensor<?>> vals) { + public AbstractPropagatorBuilder(Map<? extends Sensor<?>, ? extends Sensor<?>> vals) { checkArgument(checkNotNull(vals).size() > 0, "propagating-sensors must be non-empty"); this.propagating = vals; this.propagatingAll = null; this.propagatingAllBut = null; } - public PropagatorBuilder(Iterable<? extends Sensor<?>> vals) { + public AbstractPropagatorBuilder(Iterable<? extends Sensor<?>> vals) { this(newIdentityMap(ImmutableSet.copyOf(vals))); } - public PropagatorBuilder(Sensor<?>... vals) { + public AbstractPropagatorBuilder(Sensor<?>... vals) { this(newIdentityMap(ImmutableSet.copyOf(vals))); } - public PropagatorBuilder(boolean propagatingAll, Iterable<? extends Sensor<?>> butVals) { + public AbstractPropagatorBuilder(boolean propagatingAll, Iterable<? extends Sensor<?>> butVals) { // Ugly constructor! Taking boolean to differentiate it from others; could use a static builder // but feels like overkill having a builder for a builder, being called by a builder! checkArgument(propagatingAll, "Not propagating all; use PropagatingAll(vals)"); @@ -507,41 +509,41 @@ public class Enrichers { } } - private static class ConcreteInitialBuilder extends InitialBuilder<ConcreteInitialBuilder> { + public static class InitialBuilder extends AbstractInitialBuilder<InitialBuilder> { } - private static class ConcreteAggregatorBuilder<S, T> extends AggregatorBuilder<S, T, ConcreteAggregatorBuilder<S, T>> { - public ConcreteAggregatorBuilder(AttributeSensor<S> aggregating) { + public static class AggregatorBuilder<S, T> extends AbstractAggregatorBuilder<S, T, AggregatorBuilder<S, T>> { + public AggregatorBuilder(AttributeSensor<S> aggregating) { super(aggregating); } } - private static class ConcretePropagatorBuilder extends PropagatorBuilder<ConcretePropagatorBuilder> { - public ConcretePropagatorBuilder(Map<? extends Sensor<?>, ? extends Sensor<?>> vals) { + public static class PropagatorBuilder extends AbstractPropagatorBuilder<PropagatorBuilder> { + public PropagatorBuilder(Map<? extends Sensor<?>, ? extends Sensor<?>> vals) { super(vals); } - public ConcretePropagatorBuilder(Iterable<? extends Sensor<?>> vals) { + public PropagatorBuilder(Iterable<? extends Sensor<?>> vals) { super(vals); } - public ConcretePropagatorBuilder(Sensor<?>... vals) { + public PropagatorBuilder(Sensor<?>... vals) { super(vals); } - public ConcretePropagatorBuilder(boolean propagatingAll, Iterable<? extends Sensor<?>> butVals) { + public PropagatorBuilder(boolean propagatingAll, Iterable<? extends Sensor<?>> butVals) { super(propagatingAll, butVals); } } - private static class ConcreteCombinerBuilder<S, T> extends CombinerBuilder<S, T, ConcreteCombinerBuilder<S, T>> { - public ConcreteCombinerBuilder(AttributeSensor<? extends S>... vals) { + public static class CombinerBuilder<S, T> extends AbstractCombinerBuilder<S, T, CombinerBuilder<S, T>> { + public CombinerBuilder(AttributeSensor<? extends S>... vals) { super(vals); } - public ConcreteCombinerBuilder(Collection<AttributeSensor<? extends S>> vals) { + public CombinerBuilder(Collection<AttributeSensor<? extends S>> vals) { super(vals); } } - private static class ConcreteTransformerBuilder<S, T> extends TransformerBuilder<S, T, ConcreteTransformerBuilder<S, T>> { - public ConcreteTransformerBuilder(AttributeSensor<S> val) { + public static class TransformerBuilder<S, T> extends AbstractTransformerBuilder<S, T, TransformerBuilder<S, T>> { + public TransformerBuilder(AttributeSensor<S> val) { super(val); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/3c714ed1/core/src/test/java/brooklyn/enricher/EnrichersTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/enricher/EnrichersTest.java b/core/src/test/java/brooklyn/enricher/EnrichersTest.java index 8bfd2bb..ab3f199 100644 --- a/core/src/test/java/brooklyn/enricher/EnrichersTest.java +++ b/core/src/test/java/brooklyn/enricher/EnrichersTest.java @@ -28,12 +28,15 @@ import org.testng.annotations.Test; import brooklyn.entity.BrooklynAppUnitTestSupport; import brooklyn.entity.basic.BasicGroup; import brooklyn.entity.basic.Entities; +import brooklyn.entity.basic.EntitySubscriptionTest.RecordingSensorEventListener; import brooklyn.entity.proxying.EntitySpec; import brooklyn.event.AttributeSensor; import brooklyn.event.SensorEvent; import brooklyn.event.basic.Sensors; +import brooklyn.test.Asserts; import brooklyn.test.EntityTestUtils; import brooklyn.test.entity.TestEntity; +import brooklyn.util.collections.CollectionFunctionals; import brooklyn.util.collections.MutableMap; import brooklyn.util.collections.MutableSet; import brooklyn.util.guava.Functionals; @@ -41,6 +44,7 @@ import brooklyn.util.text.StringFunctions; import com.google.common.base.Function; import com.google.common.base.Functions; +import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -107,7 +111,7 @@ public class EnrichersTest extends BrooklynAppUnitTestSupport { public void testCombiningRespectsUnchanged() { entity.addEnricher(Enrichers.builder() .combining(NUM1, NUM2) - .publishing(NUM3) + .<Object>publishing(NUM3) .computing(new Function<Iterable<Integer>, Object>() { @Override public Object apply(Iterable<Integer> input) { if (input != null && Iterables.contains(input, 123)) { @@ -156,7 +160,7 @@ public class EnrichersTest extends BrooklynAppUnitTestSupport { entity.addEnricher(Enrichers.builder() .transforming(NUM1) .publishing(LONG1) - .computing(Functions.constant(Integer.valueOf(1))) + .computing(Functions.constant(Long.valueOf(1))) .build()); entity.setAttribute(NUM1, 123); @@ -179,23 +183,60 @@ public class EnrichersTest extends BrooklynAppUnitTestSupport { } @Test(groups="Integration") // because takes a second - public void testTransformingRespectsUnchanged() { + public void testTransformingRespectsUnchangedButWillRepublish() { + RecordingSensorEventListener record = new RecordingSensorEventListener(); + app.getManagementContext().getSubscriptionManager().subscribe(entity, STR2, record); + entity.addEnricher(Enrichers.builder() .transforming(STR1) - .publishing(STR2) + .<Object>publishing(STR2) .computing(new Function<String, Object>() { @Override public Object apply(String input) { return ("ignoredval".equals(input)) ? Entities.UNCHANGED : input; }}) .build()); + Asserts.assertThat(record.events, CollectionFunctionals.sizeEquals(0)); entity.setAttribute(STR1, "myval"); - EntityTestUtils.assertAttributeEqualsEventually(entity, STR2, "myval"); + Asserts.eventually(Suppliers.ofInstance(record.events), CollectionFunctionals.sizeEquals(1)); + EntityTestUtils.assertAttributeEquals(entity, STR2, "myval"); entity.setAttribute(STR1, "ignoredval"); EntityTestUtils.assertAttributeEqualsContinually(entity, STR2, "myval"); + + entity.setAttribute(STR1, "myval2"); + Asserts.eventually(Suppliers.ofInstance(record.events), CollectionFunctionals.sizeEquals(2)); + EntityTestUtils.assertAttributeEquals(entity, STR2, "myval2"); + + entity.setAttribute(STR1, "myval2"); + entity.setAttribute(STR1, "myval2"); + entity.setAttribute(STR1, "myval3"); + Asserts.eventually(Suppliers.ofInstance(record.events), CollectionFunctionals.sizeEquals(5)); } + // TODO if we had something like suppressDuplicates(true) : +// public void testTransformingSuppressDuplicates() { +// RecordingSensorEventListener record = new RecordingSensorEventListener(); +// app.getManagementContext().getSubscriptionManager().subscribe(entity, STR2, record); +// +// entity.addEnricher(Enrichers.builder() +// .transforming(STR1) +// .publishing(STR2) +// .computing(Functions.<String>identity()) +// .suppressDuplicates(true) +// .build()); +// +// entity.setAttribute(STR1, "myval"); +// Asserts.eventually(Suppliers.ofInstance(record.events), CollectionFunctionals.sizeEquals(1)); +// EntityTestUtils.assertAttributeEquals(entity, STR2, "myval"); +// +// entity.setAttribute(STR1, "myval2"); +// entity.setAttribute(STR1, "myval2"); +// entity.setAttribute(STR1, "myval3"); +// EntityTestUtils.assertAttributeEqualsContinually(entity, STR2, "myval3"); +// Asserts.assertThat(record.events, CollectionFunctionals.sizeEquals(3)); +// } + @Test public void testPropagating() { entity.addEnricher(Enrichers.builder() @@ -321,7 +362,7 @@ public class EnrichersTest extends BrooklynAppUnitTestSupport { .aggregating(NUM1) .publishing(LONG1) .fromMembers() - .computing(Functions.constant(Integer.valueOf(1))) + .computing(Functions.constant(Long.valueOf(1))) .build()); entity.setAttribute(NUM1, 123); @@ -333,7 +374,7 @@ public class EnrichersTest extends BrooklynAppUnitTestSupport { group.addMember(entity); group.addEnricher(Enrichers.builder() .aggregating(NUM1) - .publishing(LONG1) + .<Object>publishing(LONG1) .fromMembers() .computing(new Function<Iterable<Integer>, Object>() { @Override public Object apply(Iterable<Integer> input) {
