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 b4fd8d634b44981f28d3c323b55acfdf3930478f
Author: Alex Heneveld <[email protected]>
AuthorDate: Fri Jun 30 13:08:45 2023 +0100

    make attributeWhenReady in workflow not timeout/abort on start/stop or abort
    
    previous impl did it for all templates; now we do it just for workflow 
templates
---
 .../camp/brooklyn/WorkflowExpressionsYamlTest.java |  4 +-
 .../core/sensor/DependentConfiguration.java        | 18 ++++----
 .../workflow/WorkflowExpressionResolution.java     |  2 +-
 .../brooklyn/util/core/text/TemplateProcessor.java | 48 ++++++++++++++++++----
 4 files changed, 53 insertions(+), 19 deletions(-)

diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowExpressionsYamlTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowExpressionsYamlTest.java
index 3036d535b1..0b11eabba1 100644
--- 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowExpressionsYamlTest.java
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowExpressionsYamlTest.java
@@ -226,7 +226,7 @@ public class WorkflowExpressionsYamlTest extends 
AbstractYamlTest {
     }
 
     @Test
-    public void testWorkflowExpressionAllowsOnFire() throws Exception {
+    public void testWorkflowTemplateExpressionAllowsOnFire() throws Exception {
         Entity entity = createEntity();
         WorkflowExecutionContext workflow = invocationWorkflowOnLastEntity(
                 "    - step: transform ${entity.attributeWhenReady.foo} | wait 
| set foo_in_workflow",
@@ -248,7 +248,7 @@ public class WorkflowExpressionsYamlTest extends 
AbstractYamlTest {
     }
 
     @Test
-    public void testDSLExpressionAbortWhenOnFire() throws Exception {
+    public void testBrooklynDslExpressionAbortWhenOnFire() throws Exception {
         createEntity();
         WorkflowExecutionContext workflow = invocationWorkflowOnLastEntity(
                 "    - step: transform $brooklyn:attributeWhenReady(\"foo\") | 
wait | set foo_in_workflow",
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
 
b/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
index 351677bcb0..eb4ee249fa 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
@@ -114,14 +114,16 @@ public class DependentConfiguration {
     private DependentConfiguration() {}
 
     /**
-     * Default readiness is Groovy truth.
-     * 
+     * Default readiness is Groovy truth, with timeout.
      * @see #attributeWhenReady(Entity, AttributeSensor, Predicate)
      */
     public static <T> Task<T> attributeWhenReady(Entity source, 
AttributeSensor<T> sensor) {
         return attributeWhenReady(source, sensor, 
JavaGroovyEquivalents.groovyTruthPredicate());
     }
 
+    /**
+     * @see #attributeWhenReadyAllowingOnFire(Entity, AttributeSensor, 
Predicate)
+     */
     public static <T> Task<T> attributeWhenReadyAllowingOnFire(Entity source, 
AttributeSensor<T> sensor) {
         return attributeWhenReadyAllowingOnFire(source, sensor, 
JavaGroovyEquivalents.groovyTruthPredicate());
     }
@@ -136,7 +138,11 @@ public class DependentConfiguration {
     }
     
     /** returns an unsubmitted {@link Task} which blocks until the given 
sensor on the given source entity gives a value that satisfies ready, then 
returns that value;
-     * particular useful in Entity configuration where config will block until 
Tasks have a value
+     * particular useful in Entity configuration where config will block until 
Tasks have a value.
+     * This task will fail if the entity goes on fire and timeout after 1 
minute if starting or stopping,
+     * so is suitable for use in contexts where there is not a straightforward 
way to bail out in the case of those events
+     * (such as resolving template files as part of an upload).
+     * If this is not desired see {@link 
#attributeWhenReadyAllowingOnFire(Entity, AttributeSensor, Predicate)}.
      */
     public static <T> Task<T> attributeWhenReady(final Entity source, final 
AttributeSensor<T> sensor, final Predicate<? super T> ready) {
         Builder<T, T> builder = builder().attributeWhenReady(source, sensor);
@@ -145,11 +151,9 @@ public class DependentConfiguration {
 
     }
 
-    /** returns an unsubmitted {@link Task} which blocks until the given 
sensor on the given source entity gives a value that satisfies ready, then 
returns that value;
-     * particular useful in Entity configuration where config will block until 
Tasks have a value
-     */
+    /** as {@link #attributeWhenReady(Entity, AttributeSensor, Predicate)} but 
with no timeout and not aborting if the entity goes on fire. */
     public static <T> Task<T> attributeWhenReadyAllowingOnFire(final Entity 
source, final AttributeSensor<T> sensor, final Predicate<? super T> ready) {
-        Builder<T, T> builder = 
builder().attributeWhenReadyAllowingOnFire(source, 
sensor).timeoutIfStoppingOrDestroyed(Duration.ONE_MINUTE);
+        Builder<T, T> builder = 
builder().attributeWhenReadyAllowingOnFire(source, sensor);
         if (ready != null) builder.readiness(ready);
         return builder.build();
 
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExpressionResolution.java
 
b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExpressionResolution.java
index 76bc842329..14dde15a3a 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExpressionResolution.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExpressionResolution.java
@@ -604,7 +604,7 @@ public class WorkflowExpressionResolution {
 
         boolean ourWait = interruptSetIfNeededToPreventWaiting();
         try {
-            result = TemplateProcessor.processTemplateContents("workflow", 
expression, model, true, false, errorMode);
+            result = 
TemplateProcessor.processTemplateContentsForWorkflow("workflow", expression, 
model, true, false, errorMode);
         } catch (Exception e) {
             Exception e2 = e;
             if (wrappingMode.deferAndRetryErroneousExpressions) {
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java 
b/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java
index 771fe150fb..ba65c16f7a 100644
--- 
a/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java
@@ -41,7 +41,6 @@ import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.effector.EffectorBase;
 import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
 import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.EntityAsserts;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.location.internal.LocationInternal;
 import org.apache.brooklyn.core.sensor.DependentConfiguration;
@@ -87,6 +86,7 @@ public class TemplateProcessor {
 
     static ThreadLocalStack<Map<TemplateModel,Object>> 
TEMPLATE_MODEL_UNWRAP_CACHE = new ThreadLocalStack<>(true);
     static ThreadLocalStack<String> TEMPLATE_FILE_WANTING_LEGACY_SYNTAX = new 
ThreadLocalStack<>(true);
+    static ThreadLocalStack<Boolean> IS_FOR_WORKFLOW = new 
ThreadLocalStack<>(true);
 
     public interface UnwrappableTemplateModel {
         Maybe<Object> unwrap();
@@ -600,16 +600,19 @@ public class TemplateProcessor {
             return Maybe.of(entity);
         }
 
-        enum SensorResolutionMode { SENSOR_DEFINITION, ATTRIBUTE_VALUE, 
ATTRIBUTE_WHEN_READY }
+        enum SensorResolutionMode { SENSOR_DEFINITION,
+            ATTRIBUTE_VALUE,
+            ATTRIBUTE_WHEN_READY_FOR_TEMPLATES,
+            ATTRIBUTE_WHEN_READY_FOR_WORKFLOW }
 
         protected EntityAttributeTemplateModel(EntityInternal entity, 
SensorResolutionMode mode) {
             this.entity = entity;
             if 
(TEMPLATE_FILE_WANTING_LEGACY_SYNTAX.peek().isPresentAndNonNull()) {
                 // in templates, we have only ever supported attribute when 
ready. preserve that for now, but warn of deprecation.
-                if (mode != SensorResolutionMode.ATTRIBUTE_WHEN_READY) {
+                if (mode != 
SensorResolutionMode.ATTRIBUTE_WHEN_READY_FOR_TEMPLATES) {
                     log.warn("Using deprecated legacy attributeWhenReady 
behaviour of ${entity.attribute...} or ${entity.sensor...}. Template should be 
updated to use ${entity.attributeWhenReady...} if that is required: "
                         + TEMPLATE_FILE_WANTING_LEGACY_SYNTAX.peek());
-                    mode = SensorResolutionMode.ATTRIBUTE_WHEN_READY;
+                    mode = 
SensorResolutionMode.ATTRIBUTE_WHEN_READY_FOR_TEMPLATES;
                 }
             }
             this.mode = mode;
@@ -625,9 +628,12 @@ public class TemplateProcessor {
             Object result;
             try {
                 result =
-                        mode == SensorResolutionMode.ATTRIBUTE_WHEN_READY ?
-                                
((EntityInternal)entity).getExecutionContext().get( 
DependentConfiguration.attributeWhenReadyAllowingOnFire(entity,
+                        mode == 
SensorResolutionMode.ATTRIBUTE_WHEN_READY_FOR_TEMPLATES ?
+                                
((EntityInternal)entity).getExecutionContext().get( 
DependentConfiguration.attributeWhenReady(entity,
                                     Sensors.builder(Object.class, 
key).persistence(AttributeSensor.SensorPersistenceMode.NONE).build()))
+                        : mode == 
SensorResolutionMode.ATTRIBUTE_WHEN_READY_FOR_WORKFLOW ?
+                                
((EntityInternal)entity).getExecutionContext().get( 
DependentConfiguration.attributeWhenReadyAllowingOnFire(entity,
+                                        Sensors.builder(Object.class, 
key).persistence(AttributeSensor.SensorPersistenceMode.NONE).build()))
                         : mode == SensorResolutionMode.ATTRIBUTE_VALUE ?
                                 entity.sensors().get( 
Sensors.newSensor(Object.class, key) )
                         : mode == SensorResolutionMode.SENSOR_DEFINITION ?
@@ -679,8 +685,9 @@ public class TemplateProcessor {
         protected final EntityDriver driver;
         protected final ManagementContext mgmt;
         protected final DotSplittingTemplateModel extraSubstitutionsModel;
+        protected boolean isForWorkflow = false;
 
-        // TODO the extra substitutions here (and in 
LocationAndMapTemplateModel) could be replaced with
+        // note: the extra substitutions here (and in 
LocationAndMapTemplateModel) could be replaced with
         // FirstAvailableTemplateModel(entityModel, mapHashModel)
 
         protected EntityAndMapTemplateModel(ManagementContext mgmt, 
EntityInternal entity, EntityDriver driver) {
@@ -690,6 +697,11 @@ public class TemplateProcessor {
             extraSubstitutionsModel = new DotSplittingTemplateModel(null);
         }
 
+        public EntityAndMapTemplateModel setIsForWorkflow(boolean 
isForWorkflow) {
+            this.isForWorkflow = isForWorkflow;
+            return this;
+        }
+
         @Override
         public Maybe<Object> unwrap() {
             return Maybe.ofDisallowingNull(entity!=null ? entity : mgmt!=null 
? mgmt : extraSubstitutionsModel.unwrap().orNull());
@@ -700,7 +712,15 @@ public class TemplateProcessor {
         }
 
         public static TemplateHashModel forEntity(Entity entity, Map<String,? 
extends Object> extraSubstitutions) {
-            return new FirstAvailableTemplateModel(new 
EntityAndMapTemplateModel(null, (EntityInternal) entity, null), 
wrappedBeanToHashOrNull(entity), dotOrNull(extraSubstitutions));
+            EntityAndMapTemplateModel entityModel = new 
EntityAndMapTemplateModel(null, (EntityInternal) entity, null);
+            if (Boolean.TRUE.equals(IS_FOR_WORKFLOW.peek().orNull())) 
entityModel.setIsForWorkflow(true);
+            return new FirstAvailableTemplateModel(entityModel, 
wrappedBeanToHashOrNull(entity), dotOrNull(extraSubstitutions));
+        }
+
+        public static TemplateHashModel forEntityPossiblyInWorkflow(Entity 
entity, Map<String,? extends Object> extraSubstitutions, boolean isInWorkflow) {
+            return new FirstAvailableTemplateModel(
+                    new EntityAndMapTemplateModel(null, (EntityInternal) 
entity, null).setIsForWorkflow(isInWorkflow),
+                    wrappedBeanToHashOrNull(entity), 
dotOrNull(extraSubstitutions));
         }
 
         public static TemplateHashModel forManagementContext(ManagementContext 
mgmt, Map<String,? extends Object> extraSubstitutions) {
@@ -765,7 +785,8 @@ public class TemplateProcessor {
                 return wrapAsTemplateModel( entity instanceof Group ? 
((Group)entity).getMembers() : MutableList.of() );
             if ("sensor".equals(key)) return new 
EntityAttributeTemplateModel(entity, 
EntityAttributeTemplateModel.SensorResolutionMode.ATTRIBUTE_VALUE);
             if ("attribute".equals(key)) return new 
EntityAttributeTemplateModel(entity, 
EntityAttributeTemplateModel.SensorResolutionMode.ATTRIBUTE_VALUE);
-            if ("attributeWhenReady".equals(key)) return new 
EntityAttributeTemplateModel(entity, 
EntityAttributeTemplateModel.SensorResolutionMode.ATTRIBUTE_WHEN_READY);
+            if ("attributeWhenReady".equals(key)) return new 
EntityAttributeTemplateModel(entity,
+                    isForWorkflow ? 
EntityAttributeTemplateModel.SensorResolutionMode.ATTRIBUTE_WHEN_READY_FOR_WORKFLOW
 : 
EntityAttributeTemplateModel.SensorResolutionMode.ATTRIBUTE_WHEN_READY_FOR_TEMPLATES);
             // new option
             if ("sensor_definition".equals(key)) return new 
EntityAttributeTemplateModel(entity, 
EntityAttributeTemplateModel.SensorResolutionMode.SENSOR_DEFINITION);
             if ("effector".equals(key)) return 
wrapAsTemplateModel(Maps.transformValues(entity.getMutableEntityType().getEffectors(),
 EffectorBase::of));
@@ -983,6 +1004,15 @@ public class TemplateProcessor {
         }
     }
 
+    public static Object processTemplateContentsForWorkflow(String context, 
String templateContents, final TemplateHashModel substitutions, boolean 
allowSingleVariableObject, boolean logErrors, InterpolationErrorMode errorMode) 
{
+        try {
+            IS_FOR_WORKFLOW.push(true);
+            return processTemplateContents(context, templateContents, 
substitutions, allowSingleVariableObject, logErrors, errorMode);
+        } finally {
+            IS_FOR_WORKFLOW.pop();
+        }
+    }
+
     public enum InterpolationErrorMode {
         FAIL,
         BLANK,

Reply via email to