Skip to content

Commit

Permalink
[KOGITO-8330] Passing iterationParam to subprocess
Browse files Browse the repository at this point in the history
  • Loading branch information
fjtirado committed Jan 2, 2023
1 parent 8f8f410 commit 3d085c7
Show file tree
Hide file tree
Showing 12 changed files with 262 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@
import java.util.Map;
import java.util.Optional;

import org.jbpm.compiler.canonical.descriptors.ExpressionUtils;
import org.jbpm.process.core.context.variable.Variable;
import org.jbpm.process.core.context.variable.VariableScope;
import org.jbpm.process.core.datatype.impl.coverter.CloneHelper;
import org.jbpm.ruleflow.core.factory.SubProcessNodeFactory;
import org.jbpm.workflow.core.impl.DataDefinition;
import org.jbpm.workflow.core.node.SubProcessNode;
import org.jbpm.workflow.instance.impl.NodeInstanceImpl;
import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcess;

import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.expr.AssignExpr;
Expand Down Expand Up @@ -115,6 +115,10 @@ public void visitNode(String factoryField, SubProcessNode node, BlockStmt body,
body.addStatement(getDoneMethod(getNodeId(node)));
}

private void addVariable(SubProcessNode node, BlockStmt body, Variable v) {
body.addStatement(getFactoryMethod(getNodeId(node), "variable", ExpressionUtils.getLiteralExpr(v)));
}

private BlockStmt bind(SubProcessNode subProcessNode, ModelMetaData subProcessModel) {
BlockStmt actionBody = new BlockStmt();
actionBody.addStatement(subProcessModel.newInstance("model"));
Expand All @@ -137,26 +141,7 @@ private BlockStmt bind(SubProcessNode subProcessNode, ModelMetaData subProcessMo
AssignExpr inputs = new AssignExpr(expr, processInputsExpr, AssignExpr.Operator.ASSIGN);
actionBody.addStatement(inputs);

// do the actual assignments
for (DataDefinition inputDefinition : subProcessNode.getIoSpecification().getDataInput().values()) {
// remove multiinstance data. It does not belong to this model it is just for calculations with
// data associations
String collectionInput = (String) subProcessNode.getMetaData().get("MICollectionInput");
if (collectionInput != null && collectionInput.equals(inputDefinition.getLabel())) {
continue;
}
DataDefinition multiInstance = subProcessNode.getMultiInstanceSpecification().getInputDataItem();
if (multiInstance != null && multiInstance.getLabel().equals(inputDefinition.getLabel())) {
continue;
}

Expression getValueExpr =
new MethodCallExpr(
new MethodCallExpr(new NameExpr(CloneHelper.class.getCanonicalName()), "get"), "clone", NodeList.nodeList(new MethodCallExpr(new NameExpr("inputs"),
"get", nodeList(new StringLiteralExpr(inputDefinition.getLabel())))));
actionBody.addStatement(subProcessModel.callSetter("model", inputDefinition.getLabel(), getValueExpr));
}

actionBody.addStatement(subProcessModel.callUpdateFromMap("model", "inputs"));
actionBody.addStatement(new ReturnStmt(new NameExpr("model")));
return actionBody;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ public MethodCallExpr callSetter(String targetVar, String destField, String valu
return callSetter(targetVar, destField, new NameExpr(value));
}

public MethodCallExpr callUpdateFromMap(String targetVar, String mapVar) {
return new MethodCallExpr(new NameExpr(targetVar), "update").addArgument(new NameExpr(mapVar));
}

public MethodCallExpr callSetter(String targetVar, String destField, Expression value) {
String name = variableScope.getTypes().get(destField).getSanitizedName();
String type = variableScope.getType(destField);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.expr.BooleanLiteralExpr;
import com.github.javaparser.ast.expr.CastExpr;
import com.github.javaparser.ast.expr.CharLiteralExpr;
import com.github.javaparser.ast.expr.ClassExpr;
import com.github.javaparser.ast.expr.DoubleLiteralExpr;
Expand Down Expand Up @@ -137,9 +138,10 @@ private static Expression convertExpression(Object object) {
}
if (objectClass != null) {
// will generate TypeConverterRegistry.get().forType("JsonNode.class").apply("{\"dog\":\"perro\"}"))
return new MethodCallExpr(new MethodCallExpr(new MethodCallExpr(new TypeExpr(StaticJavaParser.parseClassOrInterfaceType(TypeConverterRegistry.class.getName())), "get"), "forType",
NodeList.nodeList(new StringLiteralExpr(objectClass.getName()))), "apply",
NodeList.nodeList(new StringLiteralExpr().setString(TypeConverterRegistry.get().forTypeReverse(object).apply((object)))));
return new CastExpr(StaticJavaParser.parseClassOrInterfaceType(object.getClass().getName()),
new MethodCallExpr(new MethodCallExpr(new MethodCallExpr(new TypeExpr(StaticJavaParser.parseClassOrInterfaceType(TypeConverterRegistry.class.getName())), "get"), "forType",
NodeList.nodeList(new StringLiteralExpr(objectClass.getName()))), "apply",
NodeList.nodeList(new StringLiteralExpr().setString(TypeConverterRegistry.get().forTypeReverse(object).apply((object))))));
} else {
return new StringLiteralExpr().setString(object.toString());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2023 Red Hat, Inc. and/or its affiliates.
*
* Licensed 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.jbpm.process.core.datatype.impl.coverter;

import java.io.IOException;

import org.jbpm.process.core.datatype.DataType;
import org.jbpm.process.core.datatype.DataTypeResolver;

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

public class DataTypeDeserializer extends JsonDeserializer<DataType> {

@Override
public DataType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
return DataTypeResolver.fromType(p.getValueAsString(), Thread.currentThread().getContextClassLoader());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2023 Red Hat, Inc. and/or its affiliates.
*
* Licensed 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.jbpm.process.core.datatype.impl.coverter;

import java.io.IOException;

import org.jbpm.process.core.datatype.DataType;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

public class DataTypeSerializer extends JsonSerializer<DataType> {

@Override
public void serialize(DataType value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value.getStringType());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2023 Red Hat, Inc. and/or its affiliates.
*
* Licensed 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.jbpm.process.core.datatype.impl.coverter;

import java.util.function.Function;

import org.kie.kogito.jackson.utils.ObjectMapperFactory;

import com.fasterxml.jackson.core.JsonProcessingException;

public class JacksonConverter<T> implements Function<String, T> {

private Class<T> clazz;

public JacksonConverter(Class<T> clazz) {
this.clazz = clazz;
}

@Override
public T apply(String t) {
try {
return ObjectMapperFactory.get().readValue(t, clazz);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2023 Red Hat, Inc. and/or its affiliates.
*
* Licensed 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.jbpm.process.core.datatype.impl.coverter;

import java.util.function.Function;

import org.kie.kogito.jackson.utils.ObjectMapperFactory;

import com.fasterxml.jackson.core.JsonProcessingException;

public class JacksonUnconverter<T> implements Function<T, String> {

@Override
public String apply(T t) {
try {
return ObjectMapperFactory.get().writeValueAsString(t);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@
*/
package org.jbpm.process.core.datatype.impl.coverter;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import org.jbpm.process.core.context.variable.Variable;
import org.jbpm.process.core.datatype.DataType;
import org.kie.kogito.jackson.utils.JsonNodeConverter;
import org.kie.kogito.jackson.utils.ObjectMapperFactory;
import org.kie.kogito.jackson.utils.StringConverter;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.module.SimpleModule;

public class TypeConverterRegistry {

Expand All @@ -34,9 +38,20 @@ public class TypeConverterRegistry {
private Function<String, String> defaultConverter = new NoOpTypeConverter();

private TypeConverterRegistry() {
converters.put("java.util.Date", new DateTypeConverter());
converters.put(Date.class.getName(), new DateTypeConverter());
converters.put(JsonNode.class.getName(), new JsonNodeConverter(ObjectMapperFactory::listenerAware));
unconverters.put(JsonNode.class.getName(), new StringConverter());
addJacksonPair(Variable.class, Map.class);
}

private void addJacksonPair(Class<?>... classes) {
SimpleModule module = new SimpleModule();
module.addSerializer(DataType.class, new DataTypeSerializer()).addDeserializer(DataType.class, new DataTypeDeserializer());
ObjectMapperFactory.get().registerModule(module);
for (Class<?> clazz : classes) {
converters.put(clazz.getName(), new JacksonConverter<>(clazz));
unconverters.put(clazz.getName(), new JacksonUnconverter<>());
}
}

public boolean isRegistered(String type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
import org.jbpm.ruleflow.core.factory.AbstractCompositeNodeFactory;
import org.jbpm.ruleflow.core.factory.CompositeContextNodeFactory;
import org.jbpm.ruleflow.core.factory.NodeFactory;
import org.jbpm.ruleflow.core.factory.SubProcessNodeFactory;
import org.jbpm.ruleflow.core.factory.TimerNodeFactory;
import org.kie.kogito.serverless.workflow.parser.FunctionNamespaceFactory;
import org.kie.kogito.serverless.workflow.parser.FunctionTypeHandlerFactory;
import org.kie.kogito.serverless.workflow.parser.ParserContext;
import org.kie.kogito.serverless.workflow.parser.VariableInfo;
import org.kie.kogito.serverless.workflow.utils.JsonNodeContext;

import io.serverlessworkflow.api.Workflow;
import io.serverlessworkflow.api.actions.Action;
Expand Down Expand Up @@ -120,10 +122,12 @@ private TimerNodeFactory<?> createTimerNode(RuleFlowNodeContainerFactory<?, ?> f
SubFlowRef subFlowRef,
String inputVar,
String outputVar) {
return subprocessNode(
SubProcessNodeFactory<?> subProcessNode = subprocessNode(
factory.subProcessNode(parserContext.newId()).name(subFlowRef.getWorkflowId()).processId(subFlowRef.getWorkflowId()).waitForCompletion(true),
inputVar,
outputVar);
JsonNodeContext.getEvalVariables(factory.getNode()).forEach(v -> subProcessNode.inMapping(v.getName(), v.getName()));
return subProcessNode;
}

private NodeFactory<?, ?> getActionNode(RuleFlowNodeContainerFactory<?, ?> embeddedSubProcess,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ protected MakeNodeResult makeNode(RuleFlowNodeContainerFactory<?, ?> factory) {
factory.forEachNode(parserContext.newId()).sequential(false).waitForCompletion(true).expressionLanguage(workflow.getExpressionLang()).collectionExpression(state.getInputCollection())
.outputVariable(outputVarName, new ObjectDataType())
.metaData(Metadata.VARIABLE, ServerlessWorkflowParser.DEFAULT_WORKFLOW_VAR);
handleActions(result, state.getActions(), outputVarName, false);
if (state.getIterationParam() != null) {
result.variable(state.getIterationParam(), new ObjectDataType());
}
if (state.getOutputCollection() != null) {
result.completionAction(new CollectorActionSupplier(workflow.getExpressionLang(), state.getOutputCollection(), DEFAULT_WORKFLOW_VAR, ForEachNodeInstance.TEMP_OUTPUT_VAR));
}
handleActions(result, state.getActions(), outputVarName, false);
return new MakeNodeResult(result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,25 @@
*/
package org.kie.kogito.serverless.workflow.models;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.kie.kogito.MapInput;
import org.kie.kogito.MapInputId;
import org.kie.kogito.MapOutput;
import org.kie.kogito.MappableToModel;
import org.kie.kogito.Model;
import org.kie.kogito.jackson.utils.JsonObjectUtils;
import org.kie.kogito.serverless.workflow.SWFConstants;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.databind.JsonNode;

public class JsonNodeModel implements Model, MapInput, MapInputId, MapOutput, MappableToModel<JsonNodeModelOutput> {

private JsonNode workflowdata;
private String id;
private Map<String, Object> additionalProperties = Collections.emptyMap();

public JsonNodeModel() {
}
Expand All @@ -44,7 +50,6 @@ public void setId(String id) {
this.id = id;
}

@JsonAnyGetter
public JsonNode getWorkflowdata() {
return workflowdata;
}
Expand All @@ -57,4 +62,38 @@ public void setWorkflowdata(JsonNode workflowdata) {
public JsonNodeModelOutput toModel() {
return new JsonNodeModelOutput(id, workflowdata);
}

public void update(Map<String, Object> params) {
Map<String, Object> copy = mutableMap(params);
update((String) copy.remove("id"), copy);
}

public void fromMap(String id, Map<String, Object> params) {
update(id, mutableMap(params));
}

public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
map.put(SWFConstants.DEFAULT_WORKFLOW_VAR, workflowdata);
if (id != null) {
map.put("id", id);
}
map.putAll(additionalProperties);
return map;
}

private void update(String id, Map<String, Object> params) {
this.id = id;
if (params.containsKey(SWFConstants.DEFAULT_WORKFLOW_VAR)) {
this.workflowdata = JsonObjectUtils.fromValue(params.remove(SWFConstants.DEFAULT_WORKFLOW_VAR)).deepCopy();
this.additionalProperties = params;
} else {
this.workflowdata = JsonObjectUtils.fromValue(params);
this.additionalProperties = Collections.emptyMap();
}
}

private static Map<String, Object> mutableMap(Map<String, Object> map) {
return map instanceof HashMap ? map : new HashMap<>(map);
}
}
Loading

0 comments on commit 3d085c7

Please sign in to comment.