This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 4dc4514abcb14b837e6e66160928a71e47729f20
Author: Alex Heneveld <[email protected]>
AuthorDate: Tue Dec 6 09:27:42 2022 +0000

    add a "workflow startable" which uses workflow to start/stop entities
---
 .../core/entity/trait/StartableMethods.java        |   2 +-
 .../core/workflow/steps/CustomWorkflowStep.java    |   6 ++
 ...artableImpl.java => AbstractStartableImpl.java} |  50 +++------
 .../brooklyn/entity/stock/BasicStartable.java      |  16 ---
 .../brooklyn/entity/stock/BasicStartableImpl.java  |  74 ++++---------
 .../brooklyn/entity/stock/WorkflowStartable.java   |  67 ++++++++++++
 .../entity/stock/WorkflowStartableImpl.java        |  64 ++++++++++++
 .../entity/stock/WorkflowStartableTest.java        | 114 +++++++++++++++++++++
 karaf/init/src/main/resources/catalog.bom          |   9 ++
 .../software/base/WorkflowSoftwareProcess.java     |  20 ++--
 10 files changed, 304 insertions(+), 118 deletions(-)

diff --git 
a/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java
 
b/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java
index d887c334ca..00c5e8022d 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java
@@ -67,7 +67,7 @@ public class StartableMethods {
         log.debug("Restarted children of entity "+e);
     }
     
-    private static <T extends Entity> Iterable<T> 
filterStartableManagedEntities(Iterable<T> contenders) {
+    public static <T extends Entity> Iterable<T> 
filterStartableManagedEntities(Iterable<T> contenders) {
         return Iterables.filter(contenders, 
Predicates.and(Predicates.instanceOf(Startable.class), 
EntityPredicates.isManaged()));
     }
 
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java
 
b/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java
index 76697cc02c..0c87777f38 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java
@@ -65,6 +65,12 @@ public class CustomWorkflowStep extends 
WorkflowStepDefinition implements Workfl
 
     private static final String WORKFLOW_SETTING_SHORTHAND = "[ \"replayable\" 
${replayable...} ] [ \"retention\" ${retention...} ] ";
 
+    public CustomWorkflowStep() {}
+    public CustomWorkflowStep(String name, List<Object> steps) {
+        this.name = name;
+        this.steps = steps;
+    }
+
     String shorthand;
     @Override
     public void populateFromShorthand(String value) {
diff --git 
a/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java 
b/core/src/main/java/org/apache/brooklyn/entity/stock/AbstractStartableImpl.java
similarity index 66%
copy from 
core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
copy to 
core/src/main/java/org/apache/brooklyn/entity/stock/AbstractStartableImpl.java
index c6431b0244..0e799690cc 100644
--- 
a/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
+++ 
b/core/src/main/java/org/apache/brooklyn/entity/stock/AbstractStartableImpl.java
@@ -18,24 +18,14 @@
  */
 package org.apache.brooklyn.entity.stock;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Predicates;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.entity.Attributes;
 import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.EntityPredicates;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
 import org.apache.brooklyn.core.entity.trait.Startable;
@@ -43,10 +33,16 @@ import 
org.apache.brooklyn.core.entity.trait.StartableMethods;
 import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.util.collections.QuorumCheck;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
-public class BasicStartableImpl extends AbstractEntity implements 
BasicStartable {
+public abstract class AbstractStartableImpl extends AbstractEntity implements 
BasicStartable {
 
-    private static final Logger log = 
LoggerFactory.getLogger(BasicStartableImpl.class);
+    private static final Logger log = 
LoggerFactory.getLogger(AbstractStartableImpl.class);
 
     @Override
     public void start(Collection<? extends Location> locations) {
@@ -61,37 +57,21 @@ public class BasicStartableImpl extends AbstractEntity 
implements BasicStartable
             locations = Locations.getLocationsCheckingAncestors(locations, 
this);
             log.info("Starting entity "+this+" at "+locations);
 
-            // essentially does StartableMethods.start(this, locations),
-            // but optionally filters locations for each child
-
-            Locations.LocationsFilter filter = getConfig(LOCATIONS_FILTER);
-            Iterable<Entity> startables = 
filterStartableManagedEntities(getChildren());
-            if (!Iterables.isEmpty(startables)) {
-                List<Task<?>> tasks = 
Lists.newArrayListWithCapacity(Iterables.size(startables));
-                for (final Entity entity : startables) {
-                    Collection<? extends Location> l2 = locations;
-                    if (filter != null) {
-                        l2 = filter.filterForContext(new 
ArrayList<Location>(locations), entity);
-                        log.debug("Child " + entity + " of " + this + " being 
started in filtered location list: " + l2);
-                    }
-                    tasks.add(Entities.invokeEffectorWithArgs(this, entity, 
Startable.START, l2));
-                }
-                for (Task<?> t : tasks) {
-                    t.getUnchecked();
-                }
-            }
+            doStart(locations);
             sensors().set(Attributes.SERVICE_UP, true);
         } finally {
             ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING);
         }
     }
 
+    protected abstract void doStart(Collection<? extends Location> locations);
+
     @Override
     public void stop() {
         ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPING);
         sensors().set(SERVICE_UP, false);
         try {
-            StartableMethods.stop(this);
+            doStop();
             ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPED);
         } catch (Exception e) {
             ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE);
@@ -99,6 +79,8 @@ public class BasicStartableImpl extends AbstractEntity 
implements BasicStartable
         }
     }
 
+    protected abstract void doStop();
+
     @Override
     public void restart() {
         StartableMethods.restart(this);
@@ -113,8 +95,4 @@ public class BasicStartableImpl extends AbstractEntity 
implements BasicStartable
                 .suppressDuplicates(true));
     }
 
-    // TODO make public in StartableMethods
-    private static Iterable<Entity> 
filterStartableManagedEntities(Iterable<Entity> contenders) {
-        return Iterables.filter(contenders, 
Predicates.and(Predicates.instanceOf(Startable.class), 
EntityPredicates.isManaged()));
-    }
 }
diff --git 
a/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartable.java 
b/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartable.java
index c2263db004..873bc45038 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartable.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartable.java
@@ -46,20 +46,4 @@ public interface BasicStartable extends Entity, Startable {
             "brooklyn.locationsFilter", 
             "Provides a hook for customizing locations to be used for a given 
context");
 
-    /** @deprecated since 0.7.0; use {@link Locations#LocationFilter} */
-    @Deprecated
-    public interface LocationsFilter extends Locations.LocationsFilter {
-        /** @deprecated since 0.7.0; use {@link Locations#USE_FIRST_LOCATION} 
*/
-        @Deprecated
-        public static final LocationsFilter USE_FIRST_LOCATION = new 
LocationsFilter() {
-            private static final long serialVersionUID = 3100091615409115890L;
-
-            @Override
-            public List<Location> filterForContext(List<Location> locations, 
Object context) {
-                if (locations.size()<=1) return locations;
-                return ImmutableList.of(locations.get(0));
-            }
-        };
-    }
-
 }
diff --git 
a/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java 
b/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
index c6431b0244..820787d520 100644
--- 
a/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
+++ 
b/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
@@ -44,59 +44,36 @@ import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.util.collections.QuorumCheck;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 
-public class BasicStartableImpl extends AbstractEntity implements 
BasicStartable {
+public class BasicStartableImpl extends AbstractStartableImpl implements 
BasicStartable {
 
     private static final Logger log = 
LoggerFactory.getLogger(BasicStartableImpl.class);
 
     @Override
-    public void start(Collection<? extends Location> locations) {
-        try {
-            ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
+    protected void doStart(Collection<? extends Location> locations) {
+        // essentially does StartableMethods.start(this, locations),
+        // but optionally filters locations for each child
 
-            // Opportunity to block startup until other dependent components 
are available
-            Object val = config().get(START_LATCH);
-            if (val != null) log.debug("{} finished waiting for start-latch; 
continuing...", this);
-
-            addLocations(locations);
-            locations = Locations.getLocationsCheckingAncestors(locations, 
this);
-            log.info("Starting entity "+this+" at "+locations);
-
-            // essentially does StartableMethods.start(this, locations),
-            // but optionally filters locations for each child
-
-            Locations.LocationsFilter filter = getConfig(LOCATIONS_FILTER);
-            Iterable<Entity> startables = 
filterStartableManagedEntities(getChildren());
-            if (!Iterables.isEmpty(startables)) {
-                List<Task<?>> tasks = 
Lists.newArrayListWithCapacity(Iterables.size(startables));
-                for (final Entity entity : startables) {
-                    Collection<? extends Location> l2 = locations;
-                    if (filter != null) {
-                        l2 = filter.filterForContext(new 
ArrayList<Location>(locations), entity);
-                        log.debug("Child " + entity + " of " + this + " being 
started in filtered location list: " + l2);
-                    }
-                    tasks.add(Entities.invokeEffectorWithArgs(this, entity, 
Startable.START, l2));
-                }
-                for (Task<?> t : tasks) {
-                    t.getUnchecked();
+        Locations.LocationsFilter filter = getConfig(LOCATIONS_FILTER);
+        Iterable<Entity> startables = 
StartableMethods.filterStartableManagedEntities(getChildren());
+        if (!Iterables.isEmpty(startables)) {
+            List<Task<?>> tasks = 
Lists.newArrayListWithCapacity(Iterables.size(startables));
+            for (final Entity entity : startables) {
+                Collection<? extends Location> l2 = locations;
+                if (filter != null) {
+                    l2 = filter.filterForContext(new 
ArrayList<Location>(locations), entity);
+                    log.debug("Child " + entity + " of " + this + " being 
started in filtered location list: " + l2);
                 }
+                tasks.add(Entities.invokeEffectorWithArgs(this, entity, 
Startable.START, l2));
+            }
+            for (Task<?> t : tasks) {
+                t.getUnchecked();
             }
-            sensors().set(Attributes.SERVICE_UP, true);
-        } finally {
-            ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING);
         }
     }
 
     @Override
-    public void stop() {
-        ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPING);
-        sensors().set(SERVICE_UP, false);
-        try {
-            StartableMethods.stop(this);
-            ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPED);
-        } catch (Exception e) {
-            ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE);
-            throw Exceptions.propagate(e);
-        }
+    protected void doStop() {
+        StartableMethods.stop(this);
     }
 
     @Override
@@ -104,17 +81,4 @@ public class BasicStartableImpl extends AbstractEntity 
implements BasicStartable
         StartableMethods.restart(this);
     }
 
-    @Override
-    protected void initEnrichers() {
-        super.initEnrichers();
-        enrichers().add(ServiceStateLogic.newEnricherFromChildrenUp()
-                .checkChildrenOnly()
-                .requireUpChildren(QuorumCheck.QuorumChecks.all())
-                .suppressDuplicates(true));
-    }
-
-    // TODO make public in StartableMethods
-    private static Iterable<Entity> 
filterStartableManagedEntities(Iterable<Entity> contenders) {
-        return Iterables.filter(contenders, 
Predicates.and(Predicates.instanceOf(Startable.class), 
EntityPredicates.isManaged()));
-    }
 }
diff --git 
a/core/src/main/java/org/apache/brooklyn/entity/stock/WorkflowStartable.java 
b/core/src/main/java/org/apache/brooklyn/entity/stock/WorkflowStartable.java
new file mode 100644
index 0000000000..467b092dcb
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/WorkflowStartable.java
@@ -0,0 +1,67 @@
+/*
+ * 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.stock;
+
+import com.google.common.base.Predicates;
+import org.apache.brooklyn.api.catalog.CatalogConfig;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.config.ConfigInheritance;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigInheritance;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.ConfigPredicates;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.Locations;
+import org.apache.brooklyn.core.workflow.steps.CustomWorkflowStep;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+
+import java.util.Arrays;
+
+/**
+ * Provides a Startable entity that runs workflow to start/stop and optionally 
restart
+ */
+@ImplementedBy(WorkflowStartableImpl.class)
+public interface WorkflowStartable extends BasicStartable {
+
+    @CatalogConfig(label = "Start Workflow", priority=5)
+    ConfigKey<CustomWorkflowStep> START_WORKFLOW = 
ConfigKeys.builder(CustomWorkflowStep.class, "start")
+            .description("workflow to start the entity")
+            .runtimeInheritance(BasicConfigInheritance.NEVER_INHERITED)
+            .constraint(Predicates.notNull())
+            .build();
+
+    @CatalogConfig(label = "Stop Workflow", priority=1)
+    ConfigKey<CustomWorkflowStep> STOP_WORKFLOW = 
ConfigKeys.builder(CustomWorkflowStep.class, "stop")
+            .description("workflow to stop the entity")
+            .runtimeInheritance(BasicConfigInheritance.NEVER_INHERITED)
+            .constraint(Predicates.notNull())
+            .build();
+
+    static final CustomWorkflowStep DEFAULT_RESTART_WORKFLOW = new 
CustomWorkflowStep("Restart (default workflow)", Arrays.asList("invoke-effector 
stop", "invoke-effector start"));
+
+    @CatalogConfig(label = "Restart Workflow", priority=1)
+    ConfigKey<CustomWorkflowStep> RESTART_WORKFLOW = 
ConfigKeys.builder(CustomWorkflowStep.class, "restart")
+            .description("workflow to restart the entity; default is to stop 
then start")
+            .runtimeInheritance(BasicConfigInheritance.NEVER_INHERITED)
+            .defaultValue(DEFAULT_RESTART_WORKFLOW)
+            .build();
+
+}
diff --git 
a/core/src/main/java/org/apache/brooklyn/entity/stock/WorkflowStartableImpl.java
 
b/core/src/main/java/org/apache/brooklyn/entity/stock/WorkflowStartableImpl.java
new file mode 100644
index 0000000000..054374a564
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/entity/stock/WorkflowStartableImpl.java
@@ -0,0 +1,64 @@
+/*
+ * 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.stock;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
+import org.apache.brooklyn.core.entity.trait.StartableMethods;
+import org.apache.brooklyn.core.workflow.WorkflowExecutionContext;
+import org.apache.brooklyn.core.workflow.steps.CustomWorkflowStep;
+import org.apache.brooklyn.util.collections.QuorumCheck;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+
+public class WorkflowStartableImpl extends AbstractStartableImpl implements 
WorkflowStartable {
+
+    private static final Logger log = 
LoggerFactory.getLogger(WorkflowStartableImpl.class);
+
+    protected Maybe<Object> runWorkflow(ConfigKey<CustomWorkflowStep> key) {
+        CustomWorkflowStep workflow = getConfig(key);
+        if (workflow==null) return Maybe.absent("No workflow defined for: 
"+key.getName());
+
+        WorkflowExecutionContext workflowContext = 
workflow.newWorkflowExecution(this, key.getName().toLowerCase(),
+                null /* could getInput from workflow, and merge shell 
environment here */);
+
+        return Maybe.of(DynamicTasks.queueIfPossible( 
workflowContext.getTask(true).get() 
).orSubmitAsync(this).getTask().getUnchecked());
+    }
+
+    @Override
+    protected void doStart(Collection<? extends Location> locations) {
+        runWorkflow(START_WORKFLOW).get();
+    }
+
+    @Override
+    protected void doStop() {
+        runWorkflow(STOP_WORKFLOW).get();
+    }
+
+    @Override
+    public void restart() {
+        runWorkflow(RESTART_WORKFLOW).get();
+    }
+
+}
diff --git 
a/core/src/test/java/org/apache/brooklyn/entity/stock/WorkflowStartableTest.java
 
b/core/src/test/java/org/apache/brooklyn/entity/stock/WorkflowStartableTest.java
new file mode 100644
index 0000000000..dbd4918fa8
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/entity/stock/WorkflowStartableTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.stock;
+
+import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.core.entity.*;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.location.Locations.LocationsFilter;
+import org.apache.brooklyn.core.location.SimulatedLocation;
+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.core.workflow.WorkflowBasicTest;
+import org.apache.brooklyn.core.workflow.WorkflowEffector;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+public class WorkflowStartableTest extends BrooklynAppUnitTestSupport {
+
+    @Override
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        super.setUp();
+        WorkflowBasicTest.addWorkflowStepTypes(mgmt);
+    }
+
+    @Test
+    public void testSettingSensors() throws Exception {
+        WorkflowStartable entity = 
app.addChild(EntitySpec.create(WorkflowStartable.class)
+                .configure(WorkflowStartable.START_WORKFLOW.getName(), 
MutableMap.of("steps", MutableList.of("set-sensor started = yes")))
+                .configure(WorkflowStartable.STOP_WORKFLOW.getName(), 
MutableMap.of("steps", MutableList.of("set-sensor started = no", "set-sensor 
stopped = yes")))
+        );
+        app.start(null);
+        EntityAsserts.assertAttributeEquals(entity, 
Sensors.newStringSensor("started"), "yes");
+        EntityAsserts.assertAttributeEquals(entity, Attributes.SERVICE_UP, 
true);
+        EntityAsserts.assertAttributeEquals(entity, 
Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+
+        entity.invoke(entity.getEntityType().getEffectorByName("stop").get(), 
null).getUnchecked();
+        EntityAsserts.assertAttributeEquals(entity, 
Sensors.newStringSensor("started"), "no");
+        EntityAsserts.assertAttributeEquals(entity, 
Sensors.newStringSensor("stopped"), "yes");
+        EntityAsserts.assertAttributeEquals(entity, Attributes.SERVICE_UP, 
false);
+        EntityAsserts.assertAttributeEquals(entity, 
Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED);
+
+        entity.sensors().set(Sensors.newStringSensor("stopped"), "manually 
no");  // make sure this is cleared
+        
entity.invoke(entity.getEntityType().getEffectorByName("restart").get(), 
null).getUnchecked();
+        EntityAsserts.assertAttributeEquals(entity, 
Sensors.newStringSensor("started"), "yes");
+        EntityAsserts.assertAttributeEquals(entity, 
Sensors.newStringSensor("stopped"), "yes");  // because stop ran before start
+        EntityAsserts.assertAttributeEquals(entity, Attributes.SERVICE_UP, 
true);
+        EntityAsserts.assertAttributeEquals(entity, 
Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+
+        WorkflowEffector eff = new WorkflowEffector(ConfigBag.newInstance()
+                .configure(WorkflowEffector.EFFECTOR_NAME, "make-problem")
+                .configure(WorkflowEffector.STEPS, MutableList.of("set-sensor 
service.problems = { some_problem: Testing }")) );
+//                .configure(WorkflowEffector.STEPS, 
MutableList.of("set-sensor service.problems['some_problem'] = Testing a 
problem")) );
+        eff.apply((EntityLocal)entity);
+        
entity.invoke(entity.getEntityType().getEffectorByName("make-problem").get(), 
null).getUnchecked();
+
+        Time.sleep(Duration.ONE_SECOND);
+        Dumper.dumpInfo(entity);
+        EntityAsserts.assertAttributeEqualsEventually(entity, 
Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE);
+
+        eff = new WorkflowEffector(ConfigBag.newInstance()
+                .configure(WorkflowEffector.EFFECTOR_NAME, "fix-problem")
+                .configure(WorkflowEffector.STEPS, MutableList.of("set-sensor 
service.problems = {}")) );
+//                .configure(WorkflowEffector.STEPS, 
MutableList.of("clear-sensor service.problems['some_problem']")) );
+        eff.apply((EntityLocal)entity);
+        
entity.invoke(entity.getEntityType().getEffectorByName("fix-problem").get(), 
null).getUnchecked();
+
+        Time.sleep(Duration.ONE_SECOND);
+        Dumper.dumpInfo(entity);
+        EntityAsserts.assertAttributeEqualsEventually(entity, 
Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+    }
+
+}
diff --git a/karaf/init/src/main/resources/catalog.bom 
b/karaf/init/src/main/resources/catalog.bom
index 3f0ccfe386..6e7975c5ed 100644
--- a/karaf/init/src/main/resources/catalog.bom
+++ b/karaf/init/src/main/resources/catalog.bom
@@ -260,3 +260,12 @@ brooklyn.catalog:
     itemType: policy
     item:
       type: org.apache.brooklyn.core.workflow.WorkflowPolicy
+
+  - id: workflow-entity
+    itemType: entity
+    item:
+      type: org.apache.brooklyn.entity.stock.WorkflowStartable
+  - id: workflow-software-process
+    itemType: entity
+    item:
+      type: org.apache.brooklyn.entity.software.base.WorkflowSoftwareProcess
diff --git 
a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/WorkflowSoftwareProcess.java
 
b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/WorkflowSoftwareProcess.java
index 0949601e72..09ab9aef31 100644
--- 
a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/WorkflowSoftwareProcess.java
+++ 
b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/WorkflowSoftwareProcess.java
@@ -33,33 +33,33 @@ import 
org.apache.brooklyn.core.workflow.steps.CustomWorkflowStep;
 @ImplementedBy(WorkflowSoftwareProcessImpl.class)
 public interface WorkflowSoftwareProcess extends SoftwareProcess {
 
-    @CatalogConfig(label = "Install Command", priority=5)
+    @CatalogConfig(label = "Install Workflow", priority=5)
     ConfigKey<CustomWorkflowStep> INSTALL_WORKFLOW = 
ConfigKeys.builder(CustomWorkflowStep.class, "install.workflow")
-            .description("command to run during the install phase")
+            .description("workflow to run during the software install phase")
             .runtimeInheritance(ConfigInheritance.NONE)
             .build();
 
-    @CatalogConfig(label = "Customize command", priority=4)
+    @CatalogConfig(label = "Customize Workflow", priority=4)
     ConfigKey<CustomWorkflowStep> CUSTOMIZE_WORKFLOW = 
ConfigKeys.builder(CustomWorkflowStep.class, "customize.workflow")
-            .description("command to run during the customization phase")
+            .description("workflow to run during the software customization 
phase")
             .runtimeInheritance(ConfigInheritance.NONE)
             .build();
 
-    @CatalogConfig(label = "Launch Command", priority=3)
+    @CatalogConfig(label = "Launch Workflow", priority=3)
     ConfigKey<CustomWorkflowStep> LAUNCH_WORKFLOW = 
ConfigKeys.builder(CustomWorkflowStep.class, "launch.workflow")
-            .description("command to run to launch the process")
+            .description("workflow to run to launch the software process")
             .runtimeInheritance(ConfigInheritance.NONE)
             .build();
 
-    @CatalogConfig(label = "Check-running Command", priority=2)
+    @CatalogConfig(label = "Check-running Workflow", priority=2)
     ConfigKey<CustomWorkflowStep> CHECK_RUNNING_WORKFLOW = 
ConfigKeys.builder(CustomWorkflowStep.class, "checkRunning.workflow")
-            .description("command to determine whether the process is running")
+            .description("workflow to determine whether the software process 
is running")
             .runtimeInheritance(ConfigInheritance.NONE)
             .build();
 
-    @CatalogConfig(label = "Stop Command", priority=1)
+    @CatalogConfig(label = "Stop Workflow", priority=1)
     ConfigKey<CustomWorkflowStep> STOP_WORKFLOW = 
ConfigKeys.builder(CustomWorkflowStep.class, "stop.workflow")
-            .description("command to run to stop the process")
+            .description("workflow to run to stop the software process")
             .runtimeInheritance(ConfigInheritance.NONE)
             .build();
 

Reply via email to