Add StopAfterDurationPolicy
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/e162d209 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/e162d209 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/e162d209 Branch: refs/heads/master Commit: e162d2091c7ad744af33e59a0b5a3441a784a92b Parents: d2cdb41 Author: Sam Corbett <sam.corb...@cloudsoftcorp.com> Authored: Thu Feb 11 12:28:37 2016 +0000 Committer: Sam Corbett <sam.corb...@cloudsoftcorp.com> Committed: Tue Feb 16 16:55:46 2016 +0000 ---------------------------------------------------------------------- .../core/sensor/DurationSinceSensor.java | 18 ++- .../policy/action/StopAfterDurationPolicy.java | 135 +++++++++++++++++++ .../action/StopAfterDurationPolicyTest.java | 46 +++++++ .../org/apache/brooklyn/util/time/Duration.java | 6 +- 4 files changed, 200 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e162d209/core/src/main/java/org/apache/brooklyn/core/sensor/DurationSinceSensor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/DurationSinceSensor.java b/core/src/main/java/org/apache/brooklyn/core/sensor/DurationSinceSensor.java index 9dc89a5..4be57d8 100644 --- a/core/src/main/java/org/apache/brooklyn/core/sensor/DurationSinceSensor.java +++ b/core/src/main/java/org/apache/brooklyn/core/sensor/DurationSinceSensor.java @@ -41,13 +41,15 @@ public class DurationSinceSensor extends AddSensor<Duration> { public static final ConfigKey<Supplier<Long>> EPOCH_SUPPLIER = ConfigKeys.builder(new TypeToken<Supplier<Long>>() {}) .name("duration.since.epochsupplier") - .description("The source of time from which durations are measured. Defaults to System.currentTimeMillis.") + .description("The source of time from which durations are measured. Defaults to System.currentTimeMillis when " + + "if no supplier is given or the configured supplier returns null.") .defaultValue(CURRENT_TIME_SUPPLIER) .build(); public static final ConfigKey<Supplier<Long>> TIME_SUPPLIER = ConfigKeys.builder(new TypeToken<Supplier<Long>>() {}) .name("duration.since.timesupplier") - .description("The source of the current time. Defaults to System.currentTimeMillis.") + .description("The source of the current time. Defaults to System.currentTimeMillis if unconfigured or the " + + "supplier returns null.") .defaultValue(CURRENT_TIME_SUPPLIER) .build(); @@ -67,7 +69,11 @@ public class DurationSinceSensor extends AddSensor<Duration> { super.apply(entity); if (entity.sensors().get(epochSensor) == null) { - entity.sensors().set(epochSensor, epochSupplier.get()); + Long epoch = epochSupplier.get(); + if (epoch == null) { + epoch = CURRENT_TIME_SUPPLIER.get(); + } + entity.sensors().set(epochSensor, epoch); } FunctionFeed feed = FunctionFeed.builder() @@ -98,7 +104,11 @@ public class DurationSinceSensor extends AddSensor<Duration> { Long referencePoint = entity.sensors().get(epochSensor); // Defensive check. Someone has done something silly if this is false. if (referencePoint != null) { - return Duration.millis(timeSupplier.get() - referencePoint); + Long time = timeSupplier.get(); + if (time == null) { + time = CURRENT_TIME_SUPPLIER.get(); + } + return Duration.millis(time - referencePoint); } else { throw new IllegalStateException("Cannot calculate duration since sensor: " + entity + " missing required value for " + epochSensor); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e162d209/policy/src/main/java/org/apache/brooklyn/policy/action/StopAfterDurationPolicy.java ---------------------------------------------------------------------- diff --git a/policy/src/main/java/org/apache/brooklyn/policy/action/StopAfterDurationPolicy.java b/policy/src/main/java/org/apache/brooklyn/policy/action/StopAfterDurationPolicy.java new file mode 100644 index 0000000..3b5eefa --- /dev/null +++ b/policy/src/main/java/org/apache/brooklyn/policy/action/StopAfterDurationPolicy.java @@ -0,0 +1,135 @@ +/* + * 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.policy.action; + +import java.util.Set; + +import org.apache.brooklyn.api.entity.EntityLocal; +import org.apache.brooklyn.api.sensor.SensorEvent; +import org.apache.brooklyn.api.sensor.SensorEventListener; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.entity.Attributes; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; +import org.apache.brooklyn.core.entity.trait.Startable; +import org.apache.brooklyn.core.policy.AbstractPolicy; +import org.apache.brooklyn.core.sensor.DurationSinceSensor; +import org.apache.brooklyn.core.sensor.Sensors; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.core.flags.SetFromFlag; +import org.apache.brooklyn.util.time.Duration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +public class StopAfterDurationPolicy extends AbstractPolicy { + + private static final Logger LOG = LoggerFactory.getLogger(StopAfterDurationPolicy.class); + + public static final ConfigKey<Duration> LIFETIME = ConfigKeys.builder(Duration.class) + .name("lifetime") + .description("The duration the entity is allowed to remain running") + .constraint(Predicates.notNull()) + .build(); + + public static final ConfigKey<Lifecycle> STATE = ConfigKeys.builder(Lifecycle.class) + .name("state") + .description("The state the entity must enter before the stop-timer is started") + .defaultValue(Lifecycle.RUNNING) + .constraint(Predicates.notNull()) + .build(); + + public static final ConfigKey<Duration> POLL_PERIOD = ConfigKeys.builder(Duration.class) + .name("pollPeriod") + .description("Period in which duration-since sensor should be updated") + .defaultValue(Duration.THIRTY_SECONDS) + .constraint(Predicates.notNull()) + .build(); + + public static final ConfigKey<Boolean> HAS_STARTED_TIMER = ConfigKeys.builder(Boolean.class) + .name("timer-started") + .description("For internal use only") + .defaultValue(false) + .build(); + + public static final ConfigKey<Boolean> FIRED_STOP = ConfigKeys.builder(Boolean.class) + .name("fired-stop") + .description("For internal use only") + .defaultValue(false) + .build(); + + private final Object eventLock = new Object[0]; + + public void setEntity(final EntityLocal entity) { + super.setEntity(entity); + entity.subscriptions().subscribe(entity, Attributes.SERVICE_STATE_ACTUAL, new LifecycleListener()); + entity.subscriptions().subscribe(entity, Sensors.newSensor(Duration.class, getSensorName()), new TimeIsUpListener()); + } + + @Override + protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) { + Set<ConfigKey<?>> accepted = ImmutableSet.<ConfigKey<?>>of( + HAS_STARTED_TIMER, + FIRED_STOP, + LIFETIME); + if (!accepted.contains(key)) { + super.doReconfigureConfig(key, val); + } + } + + private String getSensorName() { + return "duration.since.first-" + config().get(STATE).name().toLowerCase(); + } + + private class LifecycleListener implements SensorEventListener<Lifecycle> { + @Override + public void onEvent(SensorEvent<Lifecycle> event) { + synchronized (eventLock) { + if (!config().get(HAS_STARTED_TIMER) && config().get(STATE).equals(event.getValue())) { + DurationSinceSensor sensor = new DurationSinceSensor(ConfigBag.newInstance(ImmutableMap.of( + DurationSinceSensor.SENSOR_NAME, getSensorName(), + DurationSinceSensor.SENSOR_PERIOD, config().get(POLL_PERIOD), + DurationSinceSensor.SENSOR_TYPE, Duration.class.getName()))); + sensor.apply(entity); + config().set(HAS_STARTED_TIMER, true); + } + } + } + } + + private class TimeIsUpListener implements SensorEventListener<Duration> { + @Override + public void onEvent(SensorEvent<Duration> event) { + synchronized (eventLock) { + if (!config().get(FIRED_STOP)) { + if (config().get(LIFETIME).subtract(event.getValue()).isNegative()) { + LOG.debug("Stopping {}: lifetime ({}) has expired", entity, config().get(LIFETIME)); + entity.invoke(Startable.STOP, ImmutableMap.<String, Object>of()); + config().set(FIRED_STOP, true); + } + } + } + } + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e162d209/policy/src/test/java/org/apache/brooklyn/policy/action/StopAfterDurationPolicyTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/org/apache/brooklyn/policy/action/StopAfterDurationPolicyTest.java b/policy/src/test/java/org/apache/brooklyn/policy/action/StopAfterDurationPolicyTest.java new file mode 100644 index 0000000..927d5aa --- /dev/null +++ b/policy/src/test/java/org/apache/brooklyn/policy/action/StopAfterDurationPolicyTest.java @@ -0,0 +1,46 @@ +/* + * 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.policy.action; + +import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEqualsEventually; + +import org.apache.brooklyn.api.policy.PolicySpec; +import org.apache.brooklyn.core.entity.Attributes; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.util.time.Duration; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +public class StopAfterDurationPolicyTest extends BrooklynAppUnitTestSupport { + + @Test + public void testAppStoppedWhenDurationExpires() { + PolicySpec<StopAfterDurationPolicy> policy = PolicySpec.create(StopAfterDurationPolicy.class) + .configure(StopAfterDurationPolicy.LIFETIME, Duration.ONE_MILLISECOND) + .configure(StopAfterDurationPolicy.POLL_PERIOD, Duration.ONE_MILLISECOND); + app.policies().add(policy); + app.start(ImmutableList.of(app.newSimulatedLocation())); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e162d209/utils/common/src/main/java/org/apache/brooklyn/util/time/Duration.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/time/Duration.java b/utils/common/src/main/java/org/apache/brooklyn/util/time/Duration.java index e8dee7d..a5042cf 100644 --- a/utils/common/src/main/java/org/apache/brooklyn/util/time/Duration.java +++ b/utils/common/src/main/java/org/apache/brooklyn/util/time/Duration.java @@ -275,7 +275,11 @@ public class Duration implements Comparable<Duration>, Serializable { } public boolean isPositive() { - return nanos()>0; + return nanos() > 0; + } + + public boolean isNegative() { + return nanos() < 0; } public boolean isLongerThan(Duration x) {