Adds FunctionSensor (for yaml FunctionFeed) Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/3ef6742d Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/3ef6742d Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/3ef6742d
Branch: refs/heads/master Commit: 3ef6742d002a7be6ac5f483c339e7a3aa344d440 Parents: 001730b Author: Aled Sage <aled.s...@gmail.com> Authored: Fri Sep 15 09:11:45 2017 +0100 Committer: Aled Sage <aled.s...@gmail.com> Committed: Fri Sep 15 18:56:41 2017 +0100 ---------------------------------------------------------------------- .../camp/brooklyn/FunctionSensorYamlTest.java | 89 +++++++++++++++++++ .../core/sensor/function/FunctionSensor.java | 91 ++++++++++++++++++++ .../sensor/function/FunctionSensorTest.java | 77 +++++++++++++++++ 3 files changed, 257 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3ef6742d/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/FunctionSensorYamlTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/FunctionSensorYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/FunctionSensorYamlTest.java new file mode 100644 index 0000000..14402ec --- /dev/null +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/FunctionSensorYamlTest.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.camp.brooklyn; + +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.brooklyn.api.entity.Application; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.sensor.AttributeSensor; +import org.apache.brooklyn.core.entity.EntityAsserts; +import org.apache.brooklyn.core.sensor.Sensors; +import org.apache.brooklyn.core.sensor.function.FunctionSensor; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import com.google.common.collect.Iterables; + +public class FunctionSensorYamlTest extends AbstractYamlRebindTest { + private static final Logger log = LoggerFactory.getLogger(FunctionSensorYamlTest.class); + + final static AttributeSensor<String> SENSOR_STRING = Sensors.newStringSensor("aString"); + final static String TARGET_TYPE = "java.lang.String"; + + public static class MyCallable implements Callable<Object> { + public static AtomicReference<Object> val = new AtomicReference<>(); + + @Override public Object call() throws Exception { + return val.get(); + } + } + + @Test + public void testFunctionSensor() throws Exception { + MyCallable.val.set("first"); + + Entity app = createAndStartApplication( + "services:", + "- type: " + TestEntity.class.getName(), + " brooklyn.config:", + " onbox.base.dir.skipResolution: true", + " brooklyn.initializers:", + " - type: "+FunctionSensor.class.getName(), + " brooklyn.config:", + " "+FunctionSensor.SENSOR_PERIOD.getName()+": 100ms", + " "+FunctionSensor.SENSOR_NAME.getName()+": " + SENSOR_STRING.getName(), + " "+FunctionSensor.SENSOR_TYPE.getName()+": " + TARGET_TYPE, + " "+FunctionSensor.FUNCTION.getName()+":", + " $brooklyn:object:", + " type: "+MyCallable.class.getName()); + waitForApplicationTasks(app); + Entity entity = Iterables.getOnlyElement(app.getChildren()); + + EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_STRING, "first"); + + MyCallable.val.set("second"); + EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_STRING, "second"); + + // Rebind, and confirm that it resumes polling + Application newApp = rebind(); + Entity newEntity = Iterables.getOnlyElement(newApp.getChildren()); + + MyCallable.val.set("third"); + EntityAsserts.assertAttributeEqualsEventually(newEntity, SENSOR_STRING, "third"); + } + + @Override + protected Logger getLogger() { + return log; + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3ef6742d/core/src/main/java/org/apache/brooklyn/core/sensor/function/FunctionSensor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/function/FunctionSensor.java b/core/src/main/java/org/apache/brooklyn/core/sensor/function/FunctionSensor.java new file mode 100644 index 0000000..cc77f02 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/sensor/function/FunctionSensor.java @@ -0,0 +1,91 @@ +/* + * 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.core.sensor.function; + +import java.util.concurrent.Callable; + +import org.apache.brooklyn.api.entity.EntityLocal; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.effector.AddSensor; +import org.apache.brooklyn.core.entity.EntityInitializers; +import org.apache.brooklyn.feed.function.FunctionFeed; +import org.apache.brooklyn.feed.function.FunctionPollConfig; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.core.flags.TypeCoercions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.Beta; +import com.google.common.base.Functions; +import com.google.common.reflect.TypeToken; + +/** + * Configurable {@link org.apache.brooklyn.api.entity.EntityInitializer} which adds a function sensor feed. + * This calls the function periodically, to compute the sensor's value. + * + * @see FunctionFeed + */ +@Beta +public final class FunctionSensor<T> extends AddSensor<T> { + + private static final Logger LOG = LoggerFactory.getLogger(FunctionSensor.class); + + public static final ConfigKey<Boolean> SUPPRESS_DUPLICATES = ConfigKeys.newBooleanConfigKey( + "suppressDuplicates", + "Whether to publish the sensor value again, if it is the same as the previous value", + Boolean.FALSE); + + public static final ConfigKey<Callable<?>> FUNCTION = ConfigKeys.newConfigKey( + new TypeToken<Callable<?>>() {}, + "function", + "The callable to be executed periodically", + null); + + public FunctionSensor(final ConfigBag params) { + super(params); + } + + @Override + public void apply(final EntityLocal entity) { + super.apply(entity); + + if (LOG.isDebugEnabled()) { + LOG.debug("Adding HTTP JSON sensor {} to {}", name, entity); + } + + final ConfigBag allConfig = ConfigBag.newInstanceCopying(this.params).putAll(params); + + final Callable<?> function = EntityInitializers.resolve(allConfig, FUNCTION); + final Boolean suppressDuplicates = EntityInitializers.resolve(allConfig, SUPPRESS_DUPLICATES); + + FunctionPollConfig<?, T> pollConfig = new FunctionPollConfig<Object, T>(sensor) + .callable(function) + .onSuccess(TypeCoercions.function((Class<T>)sensor.getType())) + .onFailureOrException(Functions.constant((T) null)) + .suppressDuplicates(Boolean.TRUE.equals(suppressDuplicates)) + .period(period); + + FunctionFeed feed = FunctionFeed.builder().entity(entity) + .poll(pollConfig) + .build(); + + entity.addFeed(feed); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3ef6742d/core/src/test/java/org/apache/brooklyn/core/sensor/function/FunctionSensorTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/sensor/function/FunctionSensorTest.java b/core/src/test/java/org/apache/brooklyn/core/sensor/function/FunctionSensorTest.java new file mode 100644 index 0000000..3d554bd --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/sensor/function/FunctionSensorTest.java @@ -0,0 +1,77 @@ +/* + * 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.core.sensor.function; + +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.sensor.AttributeSensor; +import org.apache.brooklyn.core.entity.Attributes; +import org.apache.brooklyn.core.entity.EntityAsserts; +import org.apache.brooklyn.core.sensor.Sensors; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.time.Duration; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +public class FunctionSensorTest extends BrooklynAppUnitTestSupport { + final static AttributeSensor<String> SENSOR_STRING = Sensors.newStringSensor("aString"); + final static String STRING_TARGET_TYPE = "java.lang.String"; + + TestEntity entity; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + super.setUp(); + entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + app.start(ImmutableList.<Location>of()); + } + + @Test + public void testFunction() throws Exception { + AtomicReference<String> val = new AtomicReference<String>("first"); + Callable<String> callable = new Callable<String>() { + @Override public String call() throws Exception { + return val.get(); + } + }; + + FunctionSensor<Integer> initializer = new FunctionSensor<Integer>(ConfigBag.newInstance() + .configure(FunctionSensor.SENSOR_PERIOD, Duration.millis(10)) + .configure(FunctionSensor.SENSOR_NAME, SENSOR_STRING.getName()) + .configure(FunctionSensor.SENSOR_TYPE, STRING_TARGET_TYPE) + .configure(FunctionSensor.FUNCTION, callable)); + initializer.apply(entity); + entity.sensors().set(Attributes.SERVICE_UP, true); + + initializer.apply(entity); + entity.sensors().set(Attributes.SERVICE_UP, true); + + EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_STRING, "first"); + + val.set("second"); + EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_STRING, "second"); + } +}