better resolution of workflow inputs
- prevent confusion with subkeys having dot qualifiers and being omitted when used in sensor/policy definition
- don't coerce a supplier to a map
- allow unknown types under type key in maps, since we now use our deserializers for it
- allow access to selected workflow.util vars even outwith workflow
- return entity predicate where required, and better coercion from boolean to conditions
- remove deprecated findKeys method
- better error messages
- more tests
diff --git a/api/src/main/java/org/apache/brooklyn/api/objs/Configurable.java b/api/src/main/java/org/apache/brooklyn/api/objs/Configurable.java
index 5187d00..2f7fb3c 100644
--- a/api/src/main/java/org/apache/brooklyn/api/objs/Configurable.java
+++ b/api/src/main/java/org/apache/brooklyn/api/objs/Configurable.java
@@ -96,10 +96,6 @@
*/
<T> T set(HasConfigKey<T> key, Task<T> val);
- /** @deprecated since 0.11.0 see {@link ConfigMap#findKeys(Predicate)} */
- @Deprecated
- Set<ConfigKey<?>> findKeys(Predicate<? super ConfigKey<?>> filter);
-
/** see {@link ConfigMap#findKeysDeclared(Predicate)} */
public Set<ConfigKey<?>> findKeysDeclared(Predicate<? super ConfigKey<?>> filter);
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java
index 3274989..31cf646 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ObjectsYamlTest.java
@@ -194,11 +194,6 @@
return set(key.getConfigKey(), val);
}
- @Override @Deprecated
- public Set<ConfigKey<?>> findKeys(Predicate<? super ConfigKey<?>> filter) {
- return findKeysDeclared(filter);
- }
-
@Override
public Set<ConfigKey<?>> findKeysDeclared(Predicate<? super ConfigKey<?>> filter) {
return MutableSet.copyOf(Iterables.filter(bag.getAllConfigAsConfigKeyMap().keySet(), filter));
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java
index f5d93fb..02f9517 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/WorkflowYamlTest.java
@@ -88,6 +88,7 @@
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool;
+import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.time.Duration;
import org.apache.brooklyn.util.time.Time;
@@ -425,6 +426,33 @@
if (extraChecks!=null) extraChecks.accept(policy);
}
+ @Test
+ public void testWorkflowPolicyInputs() throws Exception {
+ Entity app = createAndStartApplication(
+ "services:",
+ "- type: " + BasicEntity.class.getName(),
+ " id: main_entity",
+ " brooklyn.policies:",
+ " - type: workflow-policy",
+ " brooklyn.config:",
+ " name: Set myWorkflowSensor",
+ " input:",
+ " x: 42",
+ " id: set-my-workflow-sensor",
+ " steps:",
+ " - set-sensor myWorkflowSensor = ${x}",
+ " - return ${x}",
+ "");
+
+ Stopwatch sw = Stopwatch.createStarted();
+ waitForApplicationTasks(app);
+
+ Entity entity = app.getChildren().iterator().next();
+ WorkflowPolicy policy = (WorkflowPolicy) entity.policies().asList().stream().filter(p -> p instanceof WorkflowPolicy).findAny().get();
+ Asserts.assertEquals(Entities.submit(entity, Tasks.create("test", policy::runOnceNow)).getUnchecked(), 42);
+ EntityAsserts.assertAttributeEquals(entity, Sensors.newSensor(Object.class, "myWorkflowSensor"), 42);
+ }
+
ClassLogWatcher lastLogWatcher;
Object invokeWorkflowStepsWithLogging(String ...stepLines) throws Exception {
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/ConfigUtils.java b/core/src/main/java/org/apache/brooklyn/core/config/ConfigUtils.java
index 617fa46..3ea73ec 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/ConfigUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/ConfigUtils.java
@@ -21,17 +21,15 @@
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
-import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
+import com.google.common.base.Predicate;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
-import org.apache.brooklyn.core.config.ConfigUtils;
-import org.apache.brooklyn.core.config.WrappedConfigKey;
import org.apache.brooklyn.core.internal.BrooklynProperties;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.exceptions.Exceptions;
@@ -41,8 +39,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Predicate;
-
@SuppressWarnings({"unchecked"})
public class ConfigUtils {
@@ -83,7 +79,7 @@
public static BrooklynProperties filterFor(BrooklynProperties properties, Predicate<? super String> filter) {
BrooklynProperties result = BrooklynProperties.Factory.newEmpty();
- Set<ConfigKey<?>> keys = properties.findKeys(ConfigPredicates.nameSatisfies(filter));
+ Set<ConfigKey<?>> keys = properties.findKeysPresent(ConfigPredicates.nameSatisfies(filter));
for (ConfigKey<?> key : keys) {
result.put(key, properties.getConfig(key));
}
@@ -92,7 +88,7 @@
public static BrooklynProperties filterForPrefix(BrooklynProperties properties, String prefix) {
BrooklynProperties result = BrooklynProperties.Factory.newEmpty();
- Set<ConfigKey<?>> keys = properties.findKeys(ConfigPredicates.nameSatisfies(StringPredicates.startsWith(prefix)));
+ Set<ConfigKey<?>> keys = properties.findKeysPresent(ConfigPredicates.nameSatisfies(StringPredicates.startsWith(prefix)));
for (ConfigKey<?> key : keys) {
result.put(key, properties.getConfig(key));
}
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java b/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java
index 80ba2f8..f7610e5 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java
@@ -18,16 +18,25 @@
*/
package org.apache.brooklyn.core.config.internal;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Future;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.reflect.TypeToken;
-import java.util.function.Consumer;
import org.apache.brooklyn.api.mgmt.ExecutionContext;
import org.apache.brooklyn.api.mgmt.TaskFactory;
import org.apache.brooklyn.api.objs.BrooklynObject;
import org.apache.brooklyn.config.ConfigInheritance;
-import org.apache.brooklyn.config.ConfigInheritance.ConfigInheritanceContext;
import org.apache.brooklyn.config.ConfigInheritances;
import org.apache.brooklyn.config.ConfigInheritances.BasicConfigValueAtContainer;
import org.apache.brooklyn.config.ConfigKey;
@@ -57,11 +66,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import java.util.*;
-import java.util.concurrent.Future;
-
public abstract class AbstractConfigMapImpl<TContainer extends BrooklynObject> implements ConfigMapWithInheritance<TContainer> {
/*
@@ -526,11 +530,6 @@
return result;
}
- @Override @Deprecated
- public Set<ConfigKey<?>> findKeys(Predicate<? super ConfigKey<?>> filter) {
- return findKeys(filter, KeyFindingMode.PRESENT_NOT_RESOLVED);
- }
-
@Override
public Set<ConfigKey<?>> findKeysDeclared(Predicate<? super ConfigKey<?>> filter) {
return findKeys(filter, KeyFindingMode.DECLARED_OR_PRESENT);
@@ -569,7 +568,7 @@
switch (mode) {
case DECLARED_OR_PRESENT: inherited = getParentInternal().config().getInternalConfigMap().findKeysDeclared(filter); break;
case PRESENT_AND_RESOLVED: inherited = getParentInternal().config().getInternalConfigMap().findKeysPresent(filter); break;
- case PRESENT_NOT_RESOLVED: inherited = getParentInternal().config().getInternalConfigMap().findKeys(filter); break;
+ case PRESENT_NOT_RESOLVED: inherited = ((AbstractConfigMapImpl) getParentInternal().config().getInternalConfigMap()).findKeys(filter, KeyFindingMode.PRESENT_NOT_RESOLVED); break;
default:
throw new IllegalStateException("Unsupported key finding mode: "+mode);
}
diff --git a/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynPropertiesImpl.java b/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynPropertiesImpl.java
index 026c8c0..6e4704e 100644
--- a/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynPropertiesImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynPropertiesImpl.java
@@ -508,11 +508,6 @@
return getAllConfigLocalRaw();
}
- @Override @Deprecated
- public Set<ConfigKey<?>> findKeys(Predicate<? super ConfigKey<?>> filter) {
- return findKeysDeclared(filter);
- }
-
@Override
public Set<ConfigKey<?>> findKeysDeclared(Predicate<? super ConfigKey<?>> filter) {
Set<ConfigKey<?>> result = new LinkedHashSet<ConfigKey<?>>();
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java
index b391bb2..526071e 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java
@@ -235,11 +235,6 @@
return new DeferredBrooklynProperties(submap, mgmt);
}
- @Override @Deprecated
- public Set<ConfigKey<?>> findKeys(Predicate<? super ConfigKey<?>> filter) {
- return delegate.findKeys(filter);
- }
-
@Override
public Set<ConfigKey<?>> findKeysDeclared(Predicate<? super ConfigKey<?>> filter) {
return delegate.findKeysDeclared(filter);
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
index 0391458..b1ae127 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
@@ -245,11 +245,6 @@
getConfigsInternal().setLocalConfig(MutableMap.<ConfigKey<?>,Object>of());
}
- @Override @Deprecated
- public Set<ConfigKey<?>> findKeys(Predicate<? super ConfigKey<?>> filter) {
- return getConfigsInternal().findKeys(filter);
- }
-
@Override
public Set<ConfigKey<?>> findKeysDeclared(Predicate<? super ConfigKey<?>> filter) {
return getConfigsInternal().findKeysDeclared(filter);
@@ -272,7 +267,7 @@
@SuppressWarnings("deprecation")
@Override
- // see super; we aspire to depreate this due to poor treatment of inheritance
+ // see super; we aspire to depreate this due to poor treatment of inheritance, and ambiguity about map subkeys;
public ConfigBag getBag() {
ConfigBag result = ConfigBag.newInstance();
AbstractConfigMapImpl<? extends BrooklynObject> ci = getConfigsInternal();
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/BasicConfigurableObject.java b/core/src/main/java/org/apache/brooklyn/core/objs/BasicConfigurableObject.java
index 3c28fc0..d81383b 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/BasicConfigurableObject.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/BasicConfigurableObject.java
@@ -20,6 +20,8 @@
import java.util.Set;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.api.objs.Configurable;
@@ -34,16 +36,13 @@
import org.apache.brooklyn.util.core.flags.SetFromFlag;
import org.apache.brooklyn.util.text.Identifiers;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-
/**
* A parent class for ancilliary objects that do not require the full heavy lifting of {@link AbstractBrooklynObject}
* or similar, but wish to use {@link ConfigKey} and {@link Configurable} in their construction, via the
* {@code $brooklyn:object} method of the CAMP DSL.
* <p>
* Type coercion of values will occur when the {@link ConfigMap} is accessed, but resolving of {@link Task tasks} and other
- * deferred operations are assumed to have occurred prior to calling {@link #setConfig(ConfigKey, Object)} i.e. at
+ * deferred operations are assumed to have occurred prior to calling {@link org.apache.brooklyn.api.objs.Configurable.ConfigurationSupport#set(ConfigKey, Object)} i.e. at
* object construction.
*/
public class BasicConfigurableObject implements Configurable, Identifiable, ManagementContextInjectable, HasBrooklynManagementContext {
@@ -118,11 +117,6 @@
return set(key.getConfigKey(), val);
}
- @Override @Deprecated
- public Set<ConfigKey<?>> findKeys(Predicate<? super ConfigKey<?>> filter) {
- return findKeysDeclared(filter);
- }
-
@Override
public Set<ConfigKey<?>> findKeysDeclared(Predicate<? super ConfigKey<?>> filter) {
return MutableSet.copyOf(Iterables.filter(config.getAllConfigAsConfigKeyMap().keySet(), filter));
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
index 953f106..84820a0 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
@@ -80,8 +80,12 @@
* It does not identify the container where it is defined, meaning URLs and deferred config values
* cannot be resolved in the context of the appropriate ancestor.
* <p>
+ * Also there is ambiguity about map subkeys (caller probably wants map = { key = val } but this gives map.key = val.
+ * <p>
* For these reasons it is recommended to use a different accessor,
- * and callers should be advised this beta method may be removed.
+ * and callers should be advised this beta method may be removed.
+ * <p>
+ * Consider code such as WorkflowPollCallable.getFlatBag
*/
@Beta
// TODO deprecate. used fairly extensively, mostly in tests. a bit more care will be needed to refactor.
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/AsPropertyIfAmbiguous.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/AsPropertyIfAmbiguous.java
index b266cd2..2eccb9f 100644
--- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/AsPropertyIfAmbiguous.java
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/AsPropertyIfAmbiguous.java
@@ -66,10 +66,10 @@
/** @deprecated since 1.1 now use transform fn, and prefer wrapped in parens */
public static final String CONFLICTING_TYPE_NAME_PROPERTY_PREFIX = "@";
- // we can change this to false to allow e.g. deserialize "{type: unknown}" as a map when an object is expected;
- // however it is probably more useful to return that as an error, because usually it is an error,
- // and have a special way of permitting it in places
- public static final boolean THROW_ON_OBJECT_EXPECTED_AND_INVALID_TYPE_KEY_SUPPLIED = true;
+ // almost always this can be true, but sometimes if deserializing to an object or map,
+ // more often now that we modify map deserializers, it might be necessary to ignore a "type":"unknown" entry,
+ // rather than failing because 'unknown' isn't known as a type
+ public static final boolean THROW_ON_OBJECT_EXPECTED_AND_INVALID_TYPE_KEY_SUPPLIED = false;
public interface HasBaseType {
JavaType getBaseType();
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java
index 5df6c02..a48870f 100644
--- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java
@@ -196,7 +196,18 @@
try {
// needed for a few things, mainly where a bean has a type field that conflicts with the type here,
// tryCoercer -20-wrong-bean uses this
- return convertDeeply(mgmt, mapOrListToSerializeThenDeserialize, type, allowRegisteredTypes, loader, allowJavaTypes);
+ T result = convertDeeply(mgmt, mapOrListToSerializeThenDeserialize, type, allowRegisteredTypes, loader, allowJavaTypes);
+ if (wrongTypeResult!=null) {
+ if (result==null || !type.getRawType().isInstance(result)) {
+ LOG.warn("Wrong type returned coercing "+mapOrListToSerializeThenDeserialize+" to "+type+"; got "+wrongTypeResult+" / "+result);
+ // prefer the original if both get it wrong
+ return wrongTypeResult;
+ } else {
+ LOG.warn("Wrong type returned coercing "+mapOrListToSerializeThenDeserialize+" to "+type+" shallow, but succeeded on deep; got "+wrongTypeResult+" / "+result);
+ return result;
+ }
+ }
+ return result;
} catch (Exception e2) {
Exceptions.propagateIfFatal(e2);
if (wrongTypeResult!=null) return wrongTypeResult;
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/CommonTypesSerialization.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/CommonTypesSerialization.java
index f294ee8..f070956 100644
--- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/CommonTypesSerialization.java
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/CommonTypesSerialization.java
@@ -460,7 +460,9 @@
return super.convertStringToObject(value, p, ctxt);
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
- if (BrooklynObjectSerialization.this.mgmt instanceof NonDeploymentManagementContext) {
+ if (BrooklynObjectSerialization.this.mgmt==null) {
+ LOG.warn("Reference to BrooklynObject " + value + " outwith task context or without mgmt set; replacing with 'null': "+e);
+ } else if (BrooklynObjectSerialization.this.mgmt instanceof NonDeploymentManagementContext) {
LOG.warn("Reference to BrooklynObject " + value + " which is unknown or not yet known, using NonDeployment context; replacing with 'null': "+e);
} else {
LOG.warn("Reference to BrooklynObject " + value + " which is unknown or no longer available; replacing with 'null': "+e);
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/JsonSymbolDependentDeserializer.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/JsonSymbolDependentDeserializer.java
index 5b2c5ff..18bf756 100644
--- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/JsonSymbolDependentDeserializer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/JsonSymbolDependentDeserializer.java
@@ -32,11 +32,15 @@
import org.apache.brooklyn.util.core.units.Range;
import org.apache.brooklyn.util.core.xstream.ImmutableSetConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import static org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.createBeanDeserializer;
public abstract class JsonSymbolDependentDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer {
+ private static final Logger log = LoggerFactory.getLogger(JsonSymbolDependentDeserializer.class);
+
public static final Set<JsonToken> SIMPLE_TOKENS = ImmutableSet.of(
JsonToken.VALUE_STRING,
JsonToken.VALUE_NUMBER_FLOAT,
@@ -71,7 +75,13 @@
type = ctxt.getContextualType();
}
if (isTypeReplaceableByDefault()) {
- type = getDefaultType();
+ JavaType type2 = getDefaultType();
+ if (type!=null && !type.getRawClass().isAssignableFrom(type2.getRawClass())) {
+ // keep old type if it's not compatible; it might fail subsequently, but at least it won't be incompatible
+ log.warn("Default type for "+type+" reported as "+type2+", in "+this+", which is not compatible; may fail subsequently");
+ // but change it, as if we leave it as an interface it will almost certainly fail
+ }
+ type = type2;
}
return this;
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 9831f2d..7443d91 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
@@ -143,6 +143,25 @@
return null;
}
+ /** allow access to selected static things in a non-workflow context; e.g. time */
+ public static class WorkflowWithoutContextFreemarkerModel implements TemplateHashModel {
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+ if ("workflow".equals(key)) {
+ return new TemplateHashModel() {
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+ if ("util".equals(key)) return new WorkflowStaticUtilModel();
+ return null;
+ }
+ @Override public boolean isEmpty() throws TemplateModelException { return false; }
+ };
+ }
+ return null;
+ }
+ @Override public boolean isEmpty() throws TemplateModelException { return false; }
+ }
+
public class WorkflowFreemarkerModel implements TemplateHashModel, TemplateProcessor.UnwrappableTemplateModel {
@Override
public Maybe<Object> unwrap() {
@@ -358,9 +377,9 @@
}
}
- class WorkflowUtilModel implements TemplateHashModel {
+ static class WorkflowStaticUtilModel implements TemplateHashModel {
- WorkflowUtilModel() {}
+ WorkflowStaticUtilModel() {}
@Override
public TemplateModel get(String key) throws TemplateModelException {
@@ -373,7 +392,7 @@
if ("now_nice".equals(key)) return TemplateProcessor.wrapAsTemplateModel(Time.makeDateString(Instant.now()));
if ("random".equals(key)) return TemplateProcessor.wrapAsTemplateModel(Math.random());
- return ifNoMatches();
+ return null;
}
@Override
@@ -382,6 +401,20 @@
}
}
+ class WorkflowUtilModel extends WorkflowStaticUtilModel {
+
+ WorkflowUtilModel() {}
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+
+ // could put workflow-specific context things here
+
+ TemplateModel result = super.get(key);
+ if (result!=null) return result;
+ return ifNoMatches();
+ }
+ }
+
AllowBrooklynDslMode defaultAllowBrooklynDsl = null;
//AllowBrooklynDslMode.ALL;
@@ -406,6 +439,7 @@
return inResolveStackEntry("resolve-coercing", expression, () -> {
boolean triedCoercion = false;
List<Exception> exceptions = MutableList.of();
+ List<Exception> deferredExceptions = MutableList.of();
if (expression instanceof String) {
try {
// prefer simple coercion if it's a string coming in
@@ -424,7 +458,7 @@
RegisteredTypes.getClassLoadingContext(context.getEntity()), true /* needed for wrapped resolved holders */);
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
- exceptions.add(e);
+ deferredExceptions.add(e);
}
}
@@ -439,6 +473,7 @@
}
}
+ exceptions.addAll(deferredExceptions);
throw Exceptions.propagate(exceptions.iterator().next());
});
}
@@ -660,6 +695,10 @@
return new WorkflowFreemarkerModel();
}
+ public static TemplateHashModel newWorkflowFreemarkerModel(WorkflowExpressionResolution context) {
+ return context==null ? new WorkflowWithoutContextFreemarkerModel() : context.newWorkflowFreemarkerModel();
+ }
+
public WorkflowExecutionContext getWorkflowExecutionContext() {
return context;
}
@@ -689,7 +728,7 @@
BiFunction<String, WorkflowExpressionResolution, Object> fn = context.getManagementContext().getScratchpad().get(WORKFLOW_CUSTOM_INTERPOLATION_FUNCTION);
if (fn!=null) result = fn.apply(expression, this);
else result = TemplateProcessor.processTemplateContentsForWorkflow("workflow", expression,
- newWorkflowFreemarkerModel(), true, false, errorMode);
+ newWorkflowFreemarkerModel(this), true, false, errorMode);
} finally {
if (ourWait) interruptClear();
diff --git a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowPolicy.java b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowPolicy.java
index 58e445e..11333f2 100644
--- a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowPolicy.java
+++ b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowPolicy.java
@@ -153,7 +153,7 @@
}
protected WorkflowPollCallable newWorkflowPollCallable() {
- return new WorkflowPollCallable(WorkflowContextType.POLICY, getDisplayName() + " (policy)", config().getBag(), this);
+ return new WorkflowPollCallable(WorkflowContextType.POLICY, getDisplayName() + " (policy)", this);
}
@Override
diff --git a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowSensor.java b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowSensor.java
index 2a9c08b..fc564d9 100644
--- a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowSensor.java
+++ b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowSensor.java
@@ -32,10 +32,13 @@
import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.SubElementConfigKey;
+import org.apache.brooklyn.core.config.internal.AbstractConfigMapImpl;
import org.apache.brooklyn.core.effector.AddSensorInitializer;
import org.apache.brooklyn.core.effector.AddSensorInitializerAbstractProto;
import org.apache.brooklyn.core.entity.EntityInitializers;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+import org.apache.brooklyn.core.objs.BrooklynObjectInternal.ConfigurationSupportInternal;
import org.apache.brooklyn.core.sensor.AbstractAddTriggerableSensor;
import org.apache.brooklyn.core.sensor.Sensors;
import org.apache.brooklyn.core.workflow.steps.appmodel.EntityValueToSet;
@@ -158,6 +161,17 @@
private final Map<String,Object> params;
private final WorkflowExecutionContext.WorkflowContextType wcType;
+ protected WorkflowPollCallable(WorkflowExecutionContext.WorkflowContextType wcType, String workflowCallableName, BrooklynObject entityOrAdjunct) {
+ this(wcType, workflowCallableName, getFlatBag(entityOrAdjunct), entityOrAdjunct);
+ }
+ static ConfigBag getFlatBag(BrooklynObject entityOrAdjunct) {
+ ConfigBag result = ConfigBag.newInstance();
+ entityOrAdjunct.config().findKeysPresent(x -> true).forEach(k -> {
+ while (k instanceof SubElementConfigKey) k = ((SubElementConfigKey)k).parent;
+ result.put((ConfigKey)k, ((ConfigurationSupportInternal)entityOrAdjunct.config()).getRaw(k).or(k.getDefaultValue()) );
+ });
+ return result;
+ }
protected WorkflowPollCallable(WorkflowExecutionContext.WorkflowContextType wcType, String workflowCallableName, ConfigBag params, BrooklynObject entityOrAdjunct) {
this.wcType = wcType;
this.workflowCallableName = workflowCallableName;
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java b/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
index 35b1752..51cee03 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
@@ -36,6 +36,7 @@
import org.apache.brooklyn.util.JavaGroovyEquivalents;
import org.apache.brooklyn.util.core.ClassLoaderUtils;
import org.apache.brooklyn.util.core.predicates.DslPredicates;
+import org.apache.brooklyn.util.core.task.DeferredSupplier;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.guava.Maybe;
@@ -388,6 +389,9 @@
if (input instanceof Map || input instanceof Collection || Boxing.isPrimitiveOrBoxedObject(input)) {
return null;
}
+ if (input instanceof DeferredSupplier) {
+ return null;
+ }
// input is a complex type / bean
boolean toMap = Map.class.isAssignableFrom(type.getRawType());
boolean toBeanWithType = !toMap && Reflections.findFieldMaybe(type.getRawType(), "type").isPresentAndNonNull();
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java b/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java
index d185c7a..e28bab8 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java
@@ -18,10 +18,30 @@
*/
package org.apache.brooklyn.util.core.predicates;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import javax.annotation.Nullable;
+
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import com.google.common.annotations.Beta;
@@ -68,18 +88,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nullable;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.*;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.regex.Pattern;
-
public class DslPredicates {
private static final Logger LOG = LoggerFactory.getLogger(DslPredicates.class);
@@ -95,9 +103,7 @@
TypeCoercions.registerAdapter(String.class, DslPredicate.class, DslPredicates::implicitlyEqualTo);
TypeCoercions.registerAdapter(Boolean.class, DslPredicate.class, DslPredicates::always);
-// TypeCoercions.registerAdapter(DeferredSupplier.class, DslPredicate.class, DslPredicates::implicitlyEqualTo);
-// TypeCoercions.registerAdapter(WorkflowExpressionResolution.WrappedUnresolvedExpression.class, DslPredicate.class, DslPredicates::implicitlyEqualTo);
- // not sure why above don't work, but below does
+ // use this to map more types of objects
TypeCoercions.registerAdapter("60-expression-to-predicate", new TryCoercer() {
@Override
public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> type) {
@@ -127,8 +133,14 @@
/** always returns false */ NEVER,
}
- static <T> T unwrapped(WrappedValue<T> t) {
- return WrappedValue.get(t);
+ static <T> T unwrapped(WrappedValue<T> t, Class<T> type) {
+ return TypeCoercions.coerce(WrappedValue.get(t), type);
+ }
+ static <T> T unwrapped(WrappedValue<T> t, TypeToken<T> type) {
+ return TypeCoercions.coerce(WrappedValue.get(t), type);
+ }
+ static DslPredicate<?> unwrappedPredicate(WrappedValue t) {
+ return unwrapped(t, DslPredicate.class);
}
static Object unwrappedObject(Object t) {
@@ -345,7 +357,7 @@
}
}
- public Object implicitEqualsUnwrapped() { return unwrapped(implicitEquals); }
+ public Object implicitEqualsUnwrapped() { return unwrapped(implicitEquals, Object.class); }
public boolean apply(T input) {
Maybe<Object> result = resolveTargetAgainstInput(input);
@@ -497,9 +509,8 @@
if (assertCondition!=null) failOnAssertCondition(result, checker);
checker.check(implicitEquals, result, (implicitTestSpec, value) -> {
-
// if a condition somehow gets put into the implicit equals, e.g. via an expression returning an expression, then recognize it as a condition
- Object test = unwrapped(implicitTestSpec);
+ Object test = unwrapped(implicitTestSpec, Object.class);
if (test instanceof DslPredicate) {
return nestedPredicateCheck((DslPredicate) test, result);
}
@@ -517,8 +528,8 @@
return DslPredicates.coercedEqual(implicitTestSpec, value);
});
checker.check(equals, result, DslPredicates::coercedEqual);
- checker.check(regex, result, (test, value) -> asStringTestOrFalse(value, v -> Pattern.compile(unwrapped(test), Pattern.DOTALL).matcher(v).matches()));
- checker.check(glob, result, (test, value) -> asStringTestOrFalse(value, v -> WildcardGlobs.isGlobMatched(unwrapped(test), v)));
+ checker.check(regex, result, (test, value) -> asStringTestOrFalse(value, v -> Pattern.compile(unwrapped(test, String.class), Pattern.DOTALL).matcher(v).matches()));
+ checker.check(glob, result, (test, value) -> asStringTestOrFalse(value, v -> WildcardGlobs.isGlobMatched(unwrapped(test, String.class), v)));
checker.check(inRange, result, (test,value) ->
// current Range only supports Integer, but this code will support any
@@ -549,10 +560,10 @@
return nestedPredicateCheck(test, Maybe.of(computedSize));
});
- checker.checkTest(not, test -> !nestedPredicateCheck(unwrapped(test), result));
- checker.checkTest(check, test -> nestedPredicateCheck(unwrapped(test), result));
- checker.checkTest(any, test -> test.stream().anyMatch(p -> nestedPredicateCheck(unwrapped(p), result)));
- checker.checkTest(all, test -> test.stream().allMatch(p -> nestedPredicateCheck(unwrapped(p), result)));
+ checker.checkTest(not, test -> !nestedPredicateCheck(unwrapped(test, DslPredicate.class), result));
+ checker.checkTest(check, test -> nestedPredicateCheck(unwrapped(test, DslPredicate.class), result));
+ checker.checkTest(any, test -> test.stream().anyMatch(p -> nestedPredicateCheck(unwrapped(p, DslPredicate.class), result)));
+ checker.checkTest(all, test -> test.stream().allMatch(p -> nestedPredicateCheck(unwrapped(p, DslPredicate.class), result)));
checker.check(javaInstanceOf, result, this::checkJavaInstanceOf);
}
@@ -963,6 +974,7 @@
@Override
public JavaType getDefaultType() {
+ if (type!=null && DslEntityPredicate.class.isAssignableFrom(type.getRawClass())) return ctxt.constructType(DslEntityPredicateDefault.class);
return ctxt.constructType(DslPredicateDefault.class);
}
diff --git a/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowInputOutputExtensionTest.java b/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowInputOutputExtensionTest.java
index 508b735..c52e315 100644
--- a/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowInputOutputExtensionTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowInputOutputExtensionTest.java
@@ -19,6 +19,7 @@
package org.apache.brooklyn.core.workflow;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.common.collect.Iterables;
import org.apache.brooklyn.api.entity.EntityLocal;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.mgmt.Task;
@@ -81,8 +82,14 @@
}
@Test
- public void testMapOutputAndInputFromLastStep() {
- doTestMapOutputAndInput(cfg -> {
+ public void testMapOutputFromExplicitOutput() {
+ doTestMapOutput(cfg -> cfg.put(WorkflowEffector.OUTPUT,
+ MutableMap.of("c", "${c}", "d", "${d}", "e", "${e}") ));
+ }
+
+ @Test
+ public void testMapOutputFromLastStep() {
+ doTestMapOutput(cfg -> {
List<Object> step = cfg.get(WorkflowEffector.STEPS);
step.add("let map result = { c: ${c}, d: ${d}, e: ${e} }");
cfg.put(WorkflowEffector.STEPS, step);
@@ -91,12 +98,27 @@
}
@Test
- public void testMapOutputAndInputFromExplicitOutput() {
- doTestMapOutputAndInput(cfg -> cfg.put(WorkflowEffector.OUTPUT,
- MutableMap.of("c", "${c}", "d", "${d}", "e", "${e}") ));
+ public void testMapOutputFromReturn() {
+ doTestMapOutput(cfg -> {
+ List<Object> step = cfg.get(WorkflowEffector.STEPS);
+ step.add("let map result = { c: ${c}, d: ${d}, e: ${e} }");
+ step.add("return ${result}");
+ cfg.put(WorkflowEffector.STEPS, step);
+ });
}
- public void doTestMapOutputAndInput(Consumer<ConfigBag> mod) {
+ @Test
+ public void testMapOutputFromExplicitInput() {
+ doTestMapOutput(cfg -> {
+ List<Object> step = cfg.get(WorkflowEffector.STEPS);
+ step.add("let map result = { c: ${c}, d: ${d}, e: ${E} }");
+ cfg.put(WorkflowEffector.STEPS, step);
+ cfg.put(WorkflowEffector.OUTPUT, "${result}");
+ cfg.put(WorkflowEffector.INPUT, MutableMap.of("E", "b"));
+ });
+ }
+
+ public void doTestMapOutput(Consumer<ConfigBag> mod) {
loadTypes();
BasicApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(BasicApplication.class));
diff --git a/core/src/test/java/org/apache/brooklyn/util/core/internal/FlagUtilsTest.java b/core/src/test/java/org/apache/brooklyn/util/core/internal/FlagUtilsTest.java
index f22db62..e915702 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/internal/FlagUtilsTest.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/internal/FlagUtilsTest.java
@@ -404,11 +404,6 @@
return set(key.getConfigKey(), val);
}
- @Override @Deprecated
- public Set<ConfigKey<?>> findKeys(Predicate<? super ConfigKey<?>> filter) {
- return findKeysDeclared(filter);
- }
-
@Override
public Set<ConfigKey<?>> findKeysDeclared(Predicate<? super ConfigKey<?>> filter) {
return MutableSet.copyOf(Iterables.filter(bag.getAllConfigAsConfigKeyMap().keySet(), filter));
diff --git a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigMap.java b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigMap.java
index 2045c69..147b6ec 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigMap.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigMap.java
@@ -64,16 +64,6 @@
/** returns a read-only map of all local config keys with their raw (unresolved+uncoerced) contents */
public Map<ConfigKey<?>,Object> getAllConfigLocalRaw();
- /** @deprecated since 0.11.0 use {@link #findKeysDeclared(Predicate)} or {@link #findKeysPresent(Predicate)}
- * <p>
- * this method is like the above but it does not compare against reference keys in the container / type context.
- * there are no known cases where that is the desired behaviour, so callers are being forced to migrate to
- * one of the above other two methods. if keys in the map are needed and not the reference keys,
- * let the brooklyn developers know to keep this functionality! */
- @Deprecated
- // XXX
- public Set<ConfigKey<?>> findKeys(Predicate<? super ConfigKey<?>> filter);
-
/** returns all keys present in the map matching the given filter predicate; see ConfigPredicates for common predicates.
* if the map is associated with a container or type context where reference keys are defined,
* those keys are included in the result whether or not present in the map (unlike {@link #findKeysPresent(Predicate)}) */