You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sj...@apache.org on 2016/02/16 18:40:58 UTC

[08/11] brooklyn-server git commit: Add StopAfterDurationPolicy

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 <sa...@cloudsoftcorp.com>
Authored: Thu Feb 11 12:28:37 2016 +0000
Committer: Sam Corbett <sa...@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) {