Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public static <T> TaskAdaptable<T> invocationPossiblySubWorkflow(Entity entity,
if (eff2 != eff) {
if (eff2 instanceof EffectorWithBody) {
log.debug("Replacing invocation of {} on {} with {} which is the impl defined at that entity", new Object[] { eff, entity, eff2 });
return ((EffectorWithBody<T>)eff2).getBody().newTask(entity, eff2, getConfigBagWithParametersCoerced(eff2, parameters, false));
return ((EffectorWithBody<T>)eff2).getBody().newTask(entity, eff2, getConfigBagWithParametersCoerced(entity, eff2, parameters, false));
} else {
log.warn("Effector {} defined on {} has no body; invoking caller-supplied {} instead", new Object[] { eff2, entity, eff });
}
Expand All @@ -194,16 +194,36 @@ public static <T> TaskAdaptable<T> invocationPossiblySubWorkflow(Entity entity,

if (eff instanceof EffectorWithBody) {
if (eff instanceof WorkflowEffector.WorkflowEffectorAndBody) {
return (TaskAdaptable<T>) ((WorkflowEffector.WorkflowEffectorAndBody) eff).getBody().newSubWorkflowTask(entity, eff, getConfigBagWithParametersCoerced(eff, parameters, false), parent, parentWorkflowInitializer);
return (TaskAdaptable<T>) ((WorkflowEffector.WorkflowEffectorAndBody) eff).getBody().newSubWorkflowTask(entity, eff, getConfigBagWithParametersCoerced(entity, eff, parameters, false), parent, parentWorkflowInitializer);
} else {
return ((EffectorWithBody<T>) eff).getBody().newTask(entity, eff, getConfigBagWithParametersCoerced(eff, parameters, false));
return ((EffectorWithBody<T>) eff).getBody().newTask(entity, eff, getConfigBagWithParametersCoerced(entity, eff, parameters, false));
}
}

throw new UnsupportedOperationException("No implementation registered for effector "+eff+" on "+entity);
}

public static ConfigBag getConfigBagWithParametersCoerced(Effector<?> eff, @Nullable Map<?,?> map, boolean requireParameter) {
return getConfigBagWithParametersCoerced(null, eff, map, requireParameter);
}

public static ConfigBag getConfigBagWithParametersCoerced(@Nullable Entity entity, Effector<?> eff, @Nullable Map<?,?> map, boolean requireParameter) {
// Coercing a parameter whose declared type is a registered type (e.g. a TOSCA data type) needs a
// management context, which the bean coercer obtains from the current task's context entity. When the
// effector is invoked outside the entity's task (e.g. from REST) there is no such context, and the
// coercion fails with "... without a management context". Run the coercion in the entity's execution
// context so the context entity (and hence the management context) is available.
if (entity != null && BrooklynTaskTags.getContextEntity(Tasks.current()) == null) {
return ((EntityInternal) entity).getExecutionContext().get(
Tasks.<ConfigBag>builder().dynamic(false)
.displayName("Coercing parameters for effector " + eff.getName())
.body(() -> coerceParametersIntoConfigBag(eff, map, requireParameter))
.build());
}
return coerceParametersIntoConfigBag(eff, map, requireParameter);
}

private static ConfigBag coerceParametersIntoConfigBag(Effector<?> eff, @Nullable Map<?,?> map, boolean requireParameter) {
ConfigBag bag = ConfigBag.newInstance();
if (map!=null) {
map.forEach( (ko,v) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,27 @@ public void testYamlMapsDontGoTooFarWhenWantingListOfString() {
assertEquals(s, ImmutableList.of("a: 1", "b : 2"));
}

@SuppressWarnings("serial")
@Test
public void testYamlBlockListCoercionToListOfMaps() {
// YAML block list of maps (e.g. an effector param of type list<custom-data-type> supplied as block
// YAML) must parse to a list of maps rather than collapsing the whole block into a single element.
List<?> s = TypeCoercions.coerce(
"- name: element1\n password: p1\n a_list:\n - sub1\n - sub2\n- name: element2\n password: p2",
new TypeToken<List<Map<String, Object>>>() {});
assertEquals(s, ImmutableList.of(
MutableMap.of("name", "element1", "password", "p1", "a_list", ImmutableList.of("sub1", "sub2")),
MutableMap.of("name", "element2", "password", "p2")));

// single block-list entry
s = TypeCoercions.coerce("- name: only\n password: p", new TypeToken<List<Map<String, Object>>>() {});
assertEquals(s, ImmutableList.of(MutableMap.of("name", "only", "password", "p")));

// flow/bracket form still works for list of maps
s = TypeCoercions.coerce("[ a: 1, b: 2 ]", new TypeToken<List<Map<String, Object>>>() {});
assertEquals(s, ImmutableList.of(MutableMap.of("a", 1), MutableMap.of("b", 2)));
}

@SuppressWarnings("serial")
@Test
public void testYamlBlockListCoercionToStringList() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,9 +461,25 @@ public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> type) {
result = JavaStringEscapes.unwrapJsonishListStringIfPossible(inputS);
}
} else {
// any other type, use YAMLish parse
resultM = JavaStringEscapes.tryUnwrapJsonishList(inputS);
result = (Collection<?>) resultM.orNull();
// for a list of non-strings: if input uses YAML block list syntax ("- item" lines),
// parse it directly with YAML, keeping maps/collections so the element coercer can
// recurse into them. Wrapping in brackets (as the jsonish parser does) kills the block
// sequence markers and collapses the whole block into a single string element.
if (inputS.trim().startsWith("- ")) {
try {
Object yamlDoc = Iterables.getOnlyElement(Yamls.parseAll(inputS));
if (yamlDoc instanceof List) {
result = (Collection<?>) yamlDoc;
}
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
}
}
if (result == null) {
// any other type, use YAMLish parse
resultM = JavaStringEscapes.tryUnwrapJsonishList(inputS);
result = (Collection<?>) resultM.orNull();
}
}
if (result==null) {
if (resultM!=null) return Maybe.Absent.castAbsent(resultM);
Expand Down