Repository: brooklyn-server Updated Branches: refs/heads/master 9b27d0d45 -> d961de340
Add suppressDuplicates to yaml sensor initializers Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/406581c5 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/406581c5 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/406581c5 Branch: refs/heads/master Commit: 406581c556f804be7df0cdff7824adbcfb2e6564 Parents: 9b27d0d Author: Aled Sage <[email protected]> Authored: Mon Aug 14 13:54:34 2017 +0100 Committer: Aled Sage <[email protected]> Committed: Mon Aug 14 14:57:04 2017 +0100 ---------------------------------------------------------------------- .../camp/brooklyn/SshCommandSensorYamlTest.java | 46 +++++++ .../core/sensor/http/HttpRequestSensor.java | 6 + .../core/sensor/ssh/SshCommandSensor.java | 9 ++ .../core/sensor/http/HttpRequestSensorTest.java | 29 ++++ .../entity/java/JmxAttributeSensor.java | 11 +- .../entity/java/JmxAttributeSensorTest.java | 134 +++++++++++++++++++ .../core/sensor/windows/WinRmCommandSensor.java | 26 ++-- .../feed/windows/WinRmCommandSensorTest.java | 33 +++++ 8 files changed, 284 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/406581c5/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SshCommandSensorYamlTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SshCommandSensorYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SshCommandSensorYamlTest.java index cb4473b..09c99ac 100644 --- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SshCommandSensorYamlTest.java +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SshCommandSensorYamlTest.java @@ -18,16 +18,23 @@ */ package org.apache.brooklyn.camp.brooklyn; +import org.apache.brooklyn.api.entity.Application; 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.EntityAsserts; +import org.apache.brooklyn.core.entity.RecordingSensorEventListener; import org.apache.brooklyn.core.sensor.Sensors; +import org.apache.brooklyn.core.test.entity.TestApplication; import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess; +import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import com.google.common.base.Predicates; import com.google.common.collect.Iterables; public class SshCommandSensorYamlTest extends AbstractYamlTest { @@ -65,6 +72,45 @@ public class SshCommandSensorYamlTest extends AbstractYamlTest { EntityAsserts.assertAttributeEqualsEventually(entity, Sensors.newStringSensor("mySensor"), "myResponse"); } + // "Integration" because takes a second + @Test(groups="Integration") + public void testSupressingDuplicates() throws Exception { + AttributeSensor<String> mySensor = Sensors.newStringSensor("mySensor"); + + RecordingSensorEventListener<String> listener = new RecordingSensorEventListener<>(); + Application tmpApp = mgmt().getEntityManager().createEntity(EntitySpec.create(TestApplication.class)); + tmpApp.subscriptions().subscribe(null, mySensor, listener); + + RecordingSshTool.setCustomResponse(".*myCommand.*", new RecordingSshTool.CustomResponse(0, "myResponse", null)); + + Entity app = createAndStartApplication( + "location:", + " localhost:", + " sshToolClass: "+RecordingSshTool.class.getName(), + "services:", + "- type: " + VanillaSoftwareProcess.class.getName(), + " brooklyn.config:", + " onbox.base.dir.skipResolution: true", + " brooklyn.initializers:", + " - type: org.apache.brooklyn.core.sensor.ssh.SshCommandSensor", + " brooklyn.config:", + " name: mySensor", + " command: myCommand", + " suppressDuplicates: true", + " period: 10ms", + " onlyIfServiceUp: false"); + waitForApplicationTasks(app); + + VanillaSoftwareProcess entity = (VanillaSoftwareProcess) Iterables.getOnlyElement(app.getChildren()); + EntityAsserts.assertAttributeEqualsEventually(entity, mySensor, "myResponse"); + listener.assertHasEventEventually(Predicates.alwaysTrue()); + + Asserts.succeedsContinually(new Runnable() { + @Override public void run() { + listener.assertEventCount(1); + }}); + } + @Override protected Logger getLogger() { return log; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/406581c5/core/src/main/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensor.java b/core/src/main/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensor.java index 5485471..842bcb4 100644 --- a/core/src/main/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensor.java +++ b/core/src/main/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensor.java @@ -62,6 +62,10 @@ public final class HttpRequestSensor<T> extends AddSensor<T> { public static final ConfigKey<String> USERNAME = ConfigKeys.newStringConfigKey("username", "Username for HTTP request, if required"); public static final ConfigKey<String> PASSWORD = ConfigKeys.newStringConfigKey("password", "Password for HTTP request, if required"); public static final ConfigKey<Map<String, String>> HEADERS = new MapConfigKey<>(String.class, "headers"); + 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<Boolean> PREEMPTIVE_BASIC_AUTH = ConfigKeys.newBooleanConfigKey( "preemptiveBasicAuth", @@ -96,6 +100,7 @@ public final class HttpRequestSensor<T> extends AddSensor<T> { final String password = EntityInitializers.resolve(allConfig, PASSWORD); final Map<String, String> headers = EntityInitializers.resolve(allConfig, HEADERS); final Boolean preemptiveBasicAuth = EntityInitializers.resolve(allConfig, PREEMPTIVE_BASIC_AUTH); + final Boolean suppressDuplicates = EntityInitializers.resolve(allConfig, SUPPRESS_DUPLICATES); Function<? super HttpToolResponse, T> successFunction; if (Strings.isBlank(jsonPath)) { @@ -109,6 +114,7 @@ public final class HttpRequestSensor<T> extends AddSensor<T> { .checkSuccess(HttpValueFunctions.responseCodeEquals(200)) .onFailureOrException(Functions.constant((T) null)) .onSuccess(successFunction) + .suppressDuplicates(Boolean.TRUE.equals(suppressDuplicates)) .period(period); HttpFeed.Builder httpRequestBuilder = HttpFeed.builder().entity(entity) http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/406581c5/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java b/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java index fc93d74..a5052df 100644 --- a/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java +++ b/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java @@ -40,6 +40,7 @@ import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.config.MapConfigKey; import org.apache.brooklyn.core.effector.AddSensor; import org.apache.brooklyn.core.entity.BrooklynConfigKeys; +import org.apache.brooklyn.core.entity.EntityInitializers; import org.apache.brooklyn.core.entity.EntityInternal; import org.apache.brooklyn.core.sensor.http.HttpRequestSensor; import org.apache.brooklyn.feed.AbstractCommandFeed; @@ -72,6 +73,11 @@ public final class SshCommandSensor<T> extends AddSensor<T> { + "use '~' to always execute in the home dir, or 'custom-feed/' to execute in a custom-feed dir relative to the run dir"); public static final MapConfigKey<Object> SENSOR_SHELL_ENVIRONMENT = BrooklynConfigKeys.SHELL_ENVIRONMENT; + 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); + protected final String command; protected final String executionDir; protected final Map<String,Object> sensorEnv; @@ -93,6 +99,8 @@ public final class SshCommandSensor<T> extends AddSensor<T> { LOG.debug("Adding SSH sensor {} to {}", name, entity); } + final Boolean suppressDuplicates = EntityInitializers.resolve(params, SUPPRESS_DUPLICATES); + Supplier<Map<String,String>> envSupplier = new Supplier<Map<String,String>>() { @Override public Map<String, String> get() { @@ -125,6 +133,7 @@ public final class SshCommandSensor<T> extends AddSensor<T> { .period(period) .env(envSupplier) .command(commandSupplier) + .suppressDuplicates(Boolean.TRUE.equals(suppressDuplicates)) .checkSuccess(SshValueFunctions.exitStatusEquals(0)) .onFailureOrException(Functions.constant((T) null)) .onSuccess(Functions.compose(new Function<String, T>() { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/406581c5/core/src/test/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensorTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensorTest.java b/core/src/test/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensorTest.java index bf6ff1e..636c1c1 100644 --- a/core/src/test/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensorTest.java +++ b/core/src/test/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensorTest.java @@ -27,10 +27,12 @@ import org.apache.brooklyn.api.sensor.AttributeSensor; import org.apache.brooklyn.core.entity.Attributes; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.entity.EntityAsserts; +import org.apache.brooklyn.core.entity.RecordingSensorEventListener; import org.apache.brooklyn.core.sensor.Sensors; import org.apache.brooklyn.core.test.entity.TestApplication; import org.apache.brooklyn.core.test.entity.TestEntity; import org.apache.brooklyn.feed.http.HttpFeedTest; +import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.test.http.RecordingHttpRequestHandler; import org.apache.brooklyn.test.http.TestHttpRequestHandler; import org.apache.brooklyn.test.http.TestHttpServer; @@ -42,6 +44,7 @@ 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.Iterables; @@ -96,6 +99,32 @@ public class HttpRequestSensorTest { EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_STRING, "myValue"); } + // "Integration" because takes a second + @Test(groups="Integration") + @SuppressWarnings("deprecation") + public void testHttpSensorSuppressingDuplicates() throws Exception { + RecordingSensorEventListener<String> listener = new RecordingSensorEventListener<>(); + entity.subscriptions().subscribe(entity, SENSOR_STRING, listener); + + HttpRequestSensor<Integer> sensor = new HttpRequestSensor<Integer>(ConfigBag.newInstance() + .configure(HttpRequestSensor.SUPPRESS_DUPLICATES, true) + .configure(HttpRequestSensor.SENSOR_PERIOD, Duration.millis(1)) + .configure(HttpRequestSensor.SENSOR_NAME, SENSOR_STRING.getName()) + .configure(HttpRequestSensor.SENSOR_TYPE, STRING_TARGET_TYPE) + .configure(HttpRequestSensor.JSON_PATH, "$.myKey") + .configure(HttpRequestSensor.SENSOR_URI, serverUrl + "/myKey/myValue")); + sensor.apply((org.apache.brooklyn.api.entity.EntityLocal)entity); + entity.sensors().set(Attributes.SERVICE_UP, true); + + EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_STRING, "myValue"); + listener.assertHasEventEventually(Predicates.alwaysTrue()); + + Asserts.succeedsContinually(new Runnable() { + @Override public void run() { + listener.assertEventCount(1); + }}); + } + // TODO Fails because doesn't pick up default value of `JSON_PATH`, which is `$` @Test(groups="Broken") @SuppressWarnings("deprecation") http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/406581c5/software/base/src/main/java/org/apache/brooklyn/entity/java/JmxAttributeSensor.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/java/JmxAttributeSensor.java b/software/base/src/main/java/org/apache/brooklyn/entity/java/JmxAttributeSensor.java index 851a589..21046ad 100644 --- a/software/base/src/main/java/org/apache/brooklyn/entity/java/JmxAttributeSensor.java +++ b/software/base/src/main/java/org/apache/brooklyn/entity/java/JmxAttributeSensor.java @@ -28,6 +28,7 @@ import org.apache.brooklyn.api.mgmt.Task; 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.core.sensor.DependentConfiguration; import org.apache.brooklyn.core.sensor.http.HttpRequestSensor; import org.apache.brooklyn.core.sensor.ssh.SshCommandSensor; @@ -60,10 +61,15 @@ public final class JmxAttributeSensor<T> extends AddSensor<T> { public static final ConfigKey<String> ATTRIBUTE = ConfigKeys.newStringConfigKey("attribute", "JMX attribute to poll in object"); public static final ConfigKey<Object> DEFAULT_VALUE = ConfigKeys.newConfigKey(Object.class, "defaultValue", "Default value for sensor; normally null"); + 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); + protected final String objectName; protected final String attribute; protected final Object defaultValue; - + public JmxAttributeSensor(final ConfigBag params) { super(params); @@ -82,6 +88,8 @@ public final class JmxAttributeSensor<T> extends AddSensor<T> { public void apply(final EntityLocal entity) { super.apply(entity); + final Boolean suppressDuplicates = EntityInitializers.resolve(params, SUPPRESS_DUPLICATES); + if (entity instanceof UsesJmx) { if (LOG.isDebugEnabled()) { LOG.debug("Submitting task to add JMX sensor {} to {}", name, entity); @@ -102,6 +110,7 @@ public final class JmxAttributeSensor<T> extends AddSensor<T> { .pollAttribute(new JmxAttributePollConfig<T>(sensor) .objectName(objectName) .attributeName(attribute) + .suppressDuplicates(Boolean.TRUE.equals(suppressDuplicates)) .onFailureOrException(Functions.<T>constant((T) defaultValue))) .build(); entity.addFeed(feed); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/406581c5/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxAttributeSensorTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxAttributeSensorTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxAttributeSensorTest.java new file mode 100644 index 0000000..22e748e --- /dev/null +++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxAttributeSensorTest.java @@ -0,0 +1,134 @@ +/* + * 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.java; + +import org.apache.brooklyn.api.entity.EntitySpec; +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.entity.RecordingSensorEventListener; +import org.apache.brooklyn.core.location.SimulatedLocation; +import org.apache.brooklyn.core.sensor.Sensors; +import org.apache.brooklyn.core.sensor.http.HttpRequestSensor; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.entity.software.base.test.jmx.GeneralisedDynamicMBean; +import org.apache.brooklyn.entity.software.base.test.jmx.JmxService; +import org.apache.brooklyn.feed.jmx.JmxFeedTest.TestEntityWithJmx; +import org.apache.brooklyn.feed.jmx.JmxHelper; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.time.Duration; +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.ImmutableMap; + +public class JmxAttributeSensorTest extends BrooklynAppUnitTestSupport { + final static AttributeSensor<String> SENSOR_STRING = Sensors.newStringSensor("aString"); + final static AttributeSensor<Object> SENSOR_OBJ = Sensors.newSensor(Object.class, "anObj"); + final static String STRING_TARGET_TYPE = "java.lang.String"; + final static String OBJECT_TARGET_TYPE = "java.lang.Object"; + + private TestEntity entity; + + private JmxService jmxService; + private JmxHelper jmxHelper; + + private String objectName = "Brooklyn:type=MyTestMBean,name=myname"; + private String attributeName = "myattrib"; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + + // Create an entity and configure it with the above JMX service + entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).impl(TestEntityWithJmx.class).additionalInterfaces(UsesJmx.class)); + app.start(ImmutableList.of(new SimulatedLocation())); + + jmxHelper = new JmxHelper(entity); + jmxService = new JmxService(entity); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + try { + if (jmxHelper != null) jmxHelper.disconnect(); + if (jmxService != null) jmxService.shutdown(); + } finally { + super.tearDown(); + } + } + + @Test + public void testSensor() throws Exception { + GeneralisedDynamicMBean mbean = jmxService.registerMBean(ImmutableMap.of(attributeName, 42), objectName); + + JmxAttributeSensor<Integer> sensor = new JmxAttributeSensor<Integer>(ConfigBag.newInstance() + .configure(JmxAttributeSensor.SENSOR_PERIOD, Duration.millis(10)) + .configure(JmxAttributeSensor.SENSOR_NAME, SENSOR_STRING.getName()) + .configure(JmxAttributeSensor.SENSOR_TYPE, STRING_TARGET_TYPE) + .configure(JmxAttributeSensor.OBJECT_NAME, objectName) + .configure(JmxAttributeSensor.ATTRIBUTE, attributeName)); + sensor.apply(entity); + entity.sensors().set(Attributes.SERVICE_UP, true); + + EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_STRING, "42"); + + // Change the value and check it updates + mbean.updateAttributeValue(attributeName, 64); + EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_STRING, "64"); + } + + // "Integration" because takes a second + @Test(groups="Integration") + public void testSensorSupressingDuplicates() throws Exception { + GeneralisedDynamicMBean mbean = jmxService.registerMBean(ImmutableMap.of(attributeName, 42), objectName); + + RecordingSensorEventListener<String> listener = new RecordingSensorEventListener<>(); + entity.subscriptions().subscribe(entity, SENSOR_STRING, listener); + + JmxAttributeSensor<Integer> sensor = new JmxAttributeSensor<Integer>(ConfigBag.newInstance() + .configure(HttpRequestSensor.SUPPRESS_DUPLICATES, true) + .configure(JmxAttributeSensor.SENSOR_PERIOD, Duration.millis(10)) + .configure(JmxAttributeSensor.SENSOR_NAME, SENSOR_STRING.getName()) + .configure(JmxAttributeSensor.SENSOR_TYPE, STRING_TARGET_TYPE) + .configure(JmxAttributeSensor.OBJECT_NAME, objectName) + .configure(JmxAttributeSensor.ATTRIBUTE, attributeName)); + sensor.apply(entity); + entity.sensors().set(Attributes.SERVICE_UP, true); + + EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_STRING, "42"); + listener.assertHasEventEventually(Predicates.alwaysTrue()); + + Asserts.succeedsContinually(new Runnable() { + @Override public void run() { + listener.assertEventCount(1); + }}); + EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_STRING, "42"); + + // Change the value and check it updates + mbean.updateAttributeValue(attributeName, 64); + EntityAsserts.assertAttributeEqualsEventually(entity, SENSOR_STRING, "64"); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/406581c5/software/winrm/src/main/java/org/apache/brooklyn/core/sensor/windows/WinRmCommandSensor.java ---------------------------------------------------------------------- diff --git a/software/winrm/src/main/java/org/apache/brooklyn/core/sensor/windows/WinRmCommandSensor.java b/software/winrm/src/main/java/org/apache/brooklyn/core/sensor/windows/WinRmCommandSensor.java index ecc0d59..2fcc315 100644 --- a/software/winrm/src/main/java/org/apache/brooklyn/core/sensor/windows/WinRmCommandSensor.java +++ b/software/winrm/src/main/java/org/apache/brooklyn/core/sensor/windows/WinRmCommandSensor.java @@ -18,18 +18,16 @@ */ package org.apache.brooklyn.core.sensor.windows; -import com.google.common.annotations.Beta; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.base.Preconditions; -import com.google.common.base.Supplier; +import java.util.Map; +import java.util.concurrent.ExecutionException; + import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntityInitializer; 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.BrooklynConfigKeys; +import org.apache.brooklyn.core.entity.EntityInitializers; import org.apache.brooklyn.core.entity.EntityInternal; import org.apache.brooklyn.core.sensor.http.HttpRequestSensor; import org.apache.brooklyn.feed.CommandPollConfig; @@ -42,13 +40,15 @@ import org.apache.brooklyn.util.core.internal.winrm.WinRmTool; import org.apache.brooklyn.util.core.json.ShellEnvironmentSerializer; import org.apache.brooklyn.util.core.task.Tasks; import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.os.Os; import org.apache.brooklyn.util.text.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Map; -import java.util.concurrent.ExecutionException; +import com.google.common.annotations.Beta; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; /** * Configurable {@link EntityInitializer} which adds an WinRm sensor feed running the <code>command</code> supplied @@ -70,6 +70,11 @@ public final class WinRmCommandSensor<T> extends AddSensor<T> { + "use '~' to always execute in the home dir, or 'custom-feed/' to execute in a custom-feed dir relative to the run dir"); public static final ConfigKey<Map<String, String>> SENSOR_ENVIRONMENT = WinRmTool.ENVIRONMENT; + 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); + protected final String command; protected final String executionDir; protected final Map<String,String> sensorEnv; @@ -91,6 +96,8 @@ public final class WinRmCommandSensor<T> extends AddSensor<T> { LOG.debug("Adding WinRM sensor {} to {}", name, entity); } + final Boolean suppressDuplicates = EntityInitializers.resolve(params, SUPPRESS_DUPLICATES); + Supplier<Map<String,String>> envSupplier = new Supplier<Map<String,String>>() { @Override public Map<String, String> get() { @@ -123,6 +130,7 @@ public final class WinRmCommandSensor<T> extends AddSensor<T> { .period(period) .env(envSupplier) .command(commandSupplier) + .suppressDuplicates(Boolean.TRUE.equals(suppressDuplicates)) .checkSuccess(SshValueFunctions.exitStatusEquals(0)) .onFailureOrException(Functions.constant((T) null)) .onSuccess(Functions.compose(new Function<String, T>() { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/406581c5/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WinRmCommandSensorTest.java ---------------------------------------------------------------------- diff --git a/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WinRmCommandSensorTest.java b/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WinRmCommandSensorTest.java index dcadca0..7bf481c 100644 --- a/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WinRmCommandSensorTest.java +++ b/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WinRmCommandSensorTest.java @@ -22,18 +22,23 @@ import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.api.location.Location; import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.api.sensor.AttributeSensor; import org.apache.brooklyn.core.entity.EntityAsserts; +import org.apache.brooklyn.core.entity.RecordingSensorEventListener; import org.apache.brooklyn.core.mgmt.rebind.RebindTestFixtureWithApp; import org.apache.brooklyn.core.sensor.Sensors; +import org.apache.brooklyn.core.sensor.http.HttpRequestSensor; import org.apache.brooklyn.core.sensor.windows.WinRmCommandSensor; import org.apache.brooklyn.core.test.entity.TestEntity; import org.apache.brooklyn.location.winrm.WinRmMachineLocation; +import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.core.internal.winrm.RecordingWinRmTool; 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.ImmutableMap; @@ -78,4 +83,32 @@ public class WinRmCommandSensorTest extends RebindTestFixtureWithApp { RecordingWinRmTool.setCustomResponse(".*mycommand.*", new RecordingWinRmTool.CustomResponse(0, "myval2", "")); EntityAsserts.assertAttributeEqualsEventually(entity, Sensors.newStringSensor("mysensor"), "myval2"); } + + // "Integration" because takes a second + @Test(groups="Integration") + public void testSupressingDuplicates() throws Exception { + AttributeSensor<String> mySensor = Sensors.newStringSensor("mysensor"); + + RecordingSensorEventListener<String> listener = new RecordingSensorEventListener<>(); + app().subscriptions().subscribe(null, mySensor, listener); + + RecordingWinRmTool.setCustomResponse(".*mycommand.*", new RecordingWinRmTool.CustomResponse(0, "myval", "")); + + Entity entity = app().createAndManageChild(EntitySpec.create(TestEntity.class) + .addInitializer(new WinRmCommandSensor<String>(ConfigBag.newInstance(ImmutableMap.of( + HttpRequestSensor.SUPPRESS_DUPLICATES, true, + WinRmCommandSensor.SENSOR_PERIOD, "1ms", + WinRmCommandSensor.SENSOR_COMMAND, "mycommand", + WinRmCommandSensor.SENSOR_NAME, mySensor.getName()))))); + + app().start(ImmutableList.of(loc)); + + EntityAsserts.assertAttributeEqualsEventually(entity, mySensor, "myval"); + listener.assertHasEventEventually(Predicates.alwaysTrue()); + + Asserts.succeedsContinually(new Runnable() { + @Override public void run() { + listener.assertEventCount(1); + }}); + } }
