workflow app fixes
Change-Id: Ia3b06b7947671e4e38ca37eb832e6a969ed1e6af
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java
index 941ba70..381b2d5 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/cli/WorkFlowTestCommand.java
@@ -37,15 +37,16 @@
public class WorkFlowTestCommand extends AbstractShellCommand {
static final String INVOKE_SAMPLE = "invoke-sample";
+ static final String EXCEPTION_SAMPLE = "exception-sample";
@Argument(index = 0, name = "test-name",
- description = "Test name (" + INVOKE_SAMPLE + ")",
+ description = "Test name (" + INVOKE_SAMPLE + " | " + EXCEPTION_SAMPLE + ")",
required = true)
@Completion(WorkFlowTestCompleter.class)
private String testName = null;
@Argument(index = 1, name = "arg1",
- description = "number of test for " + INVOKE_SAMPLE,
+ description = "number of test for (" + INVOKE_SAMPLE + " | " + EXCEPTION_SAMPLE + ")",
required = false)
private String arg1 = null;
@@ -76,6 +77,26 @@
invokeSampleTest(num);
break;
+
+ case EXCEPTION_SAMPLE:
+ if (Objects.isNull(arg1)) {
+ error("arg1 is required for test " + EXCEPTION_SAMPLE);
+ return;
+ }
+ int count;
+ try {
+ count = Integer.parseInt(arg1);
+ } catch (NumberFormatException e) {
+ error("arg1 should be an integer value");
+ return;
+ } catch (Exception e) {
+ error(e.getMessage() + ", trace: " + Arrays.asList(e.getStackTrace()));
+ return;
+ }
+
+ invokeExceptionTest(count);
+ break;
+
default:
print("Unsupported test-name: " + testName);
}
@@ -83,6 +104,7 @@
/**
* Workflow invoke test_name.
+ *
* @param num the arg1 of workflow to test_name
*/
private void invokeSampleTest(int num) {
@@ -95,8 +117,22 @@
}
/**
+ * Workflow datatype exception test.
+ *
+ * @param num the number of workflow to test
+ */
+ private void invokeExceptionTest(int num) {
+ for (int i = 0; i <= num; i++) {
+ String wpName = "test-" + i;
+ invoke("sample.workflow-3", wpName);
+ }
+ }
+
+
+ /**
* Invokes workflow.
- * @param workflowId workflow id
+ *
+ * @param workflowId workflow id
* @param workplaceName workplace name
*/
private void invoke(String workflowId, String workplaceName) {
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkflowManager.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkflowManager.java
index 5866932..8eb7c0c 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkflowManager.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/WorkflowManager.java
@@ -15,22 +15,27 @@
*/
package org.onosproject.workflow.impl;
+
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.workflow.api.DefaultWorkplace;
-import org.onosproject.workflow.api.JsonDataModelTree;
-import org.onosproject.workflow.api.Workflow;
-import org.onosproject.workflow.api.WorkflowContext;
-import org.onosproject.workflow.api.WorkflowDescription;
-import org.onosproject.workflow.api.WorkflowException;
import org.onosproject.workflow.api.WorkflowService;
import org.onosproject.workflow.api.WorkflowExecutionService;
-import org.onosproject.workflow.api.WorkflowStore;
-import org.onosproject.workflow.api.Workplace;
-import org.onosproject.workflow.api.WorkplaceDescription;
import org.onosproject.workflow.api.WorkplaceStore;
+import org.onosproject.workflow.api.WorkflowStore;
+import org.onosproject.workflow.api.WorkplaceDescription;
+import org.onosproject.workflow.api.WorkflowException;
+import org.onosproject.workflow.api.DefaultWorkplace;
+import org.onosproject.workflow.api.JsonDataModelTree;
+import org.onosproject.workflow.api.WorkflowDescription;
+import org.onosproject.workflow.api.Workplace;
+import org.onosproject.workflow.api.WorkflowDataModelException;
+import org.onosproject.workflow.api.Workflow;
+import org.onosproject.workflow.api.Worklet;
+import org.onosproject.workflow.api.WorkflowContext;
+import org.onosproject.workflow.api.JsonDataModel;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
@@ -38,8 +43,15 @@
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
import java.net.URI;
+import java.util.Map;
+import java.util.HashMap;
import java.util.Objects;
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import static org.slf4j.LoggerFactory.getLogger;
@@ -118,21 +130,121 @@
@Override
public void invokeWorkflow(JsonNode worklowDescJson) throws WorkflowException {
log.info("invokeWorkflow: {}", worklowDescJson);
-
Workplace workplace = workplaceStore.getWorkplace(Workplace.SYSTEM_WORKPLACE);
if (Objects.isNull(workplace)) {
throw new WorkflowException("Invalid system workplace");
}
- Workflow workflow = workflowStore.get(URI.create(WorkplaceWorkflow.WF_CREATE_WORKFLOW));
+ Workflow workflow = workflowStore.get(URI.create(worklowDescJson.get("id").asText()));
if (Objects.isNull(workflow)) {
+ throw new WorkflowException("Invalid Workflow");
+ }
+
+ if (!checkWorkflowSchema(workflow, worklowDescJson)) {
+ throw new WorkflowException("Invalid Workflow " + worklowDescJson.get("id").asText());
+ }
+
+ Workflow wfCreationWf = workflowStore.get(URI.create(WorkplaceWorkflow.WF_CREATE_WORKFLOW));
+ if (Objects.isNull(wfCreationWf)) {
throw new WorkflowException("Invalid workflow " + WorkplaceWorkflow.WF_CREATE_WORKFLOW);
}
- WorkflowContext context = workflow.buildSystemContext(workplace, new JsonDataModelTree(worklowDescJson));
+ WorkflowContext context = wfCreationWf.buildSystemContext(workplace, new JsonDataModelTree(worklowDescJson));
workflowExecutionService.execInitWorklet(context);
}
+ /**
+ * Checks if the type of worklet is same as that of wfdesc Json.
+ *
+ * @param workflow workflow
+ * @param jsonNode jsonNode
+ * @throws WorkflowException workflow exception
+ */
+
+ private boolean checkWorkflowSchema(Workflow workflow, JsonNode jsonNode) throws WorkflowException {
+
+ Map<String, Map<String, String>> workletDataTypeMap = new HashMap<>();
+ for (String workletType : workflow.getWorkletTypeList()) {
+ Map<String, String> jsonDataModelMap = new HashMap<>();
+ if (Objects.equals(workletType, Worklet.Common.INIT.tag())
+ || (Objects.equals(workletType, Worklet.Common.COMPLETED.tag()))) {
+ continue;
+ }
+ Worklet worklet = workflow.getWorkletInstance(workletType);
+ Class cls = worklet.getClass();
+ for (Field field : cls.getDeclaredFields()) {
+ if (field.isSynthetic()) {
+ continue;
+ }
+ Annotation[] annotations = field.getAnnotations();
+ for (Annotation annotation : annotations) {
+ if (annotation instanceof JsonDataModel) {
+ JsonDataModel jsonDataModel = (JsonDataModel) annotation;
+ Matcher matcher = Pattern.compile("(\\w+)").matcher(jsonDataModel.path());
+ if (!matcher.find()) {
+ throw new WorkflowException("Invalid Json Data Model Path");
+ }
+ String path = matcher.group(1);
+ if (checkJsonNodeDataType(jsonNode, field, path)) {
+ jsonDataModelMap.put(path, field.getType().getName());
+ }
+ }
+ }
+ }
+ if (!jsonDataModelMap.isEmpty()) {
+ workletDataTypeMap.put(worklet.tag(), jsonDataModelMap);
+ }
+
+ }
+ if (!workletDataTypeMap.isEmpty()) {
+ throw new WorkflowDataModelException("invalid workflow ", workflow.id().toString(), workletDataTypeMap);
+ }
+ return true;
+ }
+
+
+ private boolean checkJsonNodeDataType(JsonNode jsonNode, Field field, String path) throws WorkflowException {
+ if (!Objects.nonNull(jsonNode.get("data")) && !Objects.nonNull(jsonNode.get("data").get(path))) {
+ throw new WorkflowException("Invalid Json");
+ }
+ JsonNodeType jsonNodeType = jsonNode.get("data").get(path).getNodeType();
+ if (jsonNodeType != null) {
+ switch (jsonNodeType) {
+ case NUMBER:
+ if (!(field.getType().isAssignableFrom(Integer.class))) {
+ return true;
+ }
+ break;
+ case STRING:
+ if (!(field.getType().isAssignableFrom(String.class))) {
+ return true;
+ }
+ break;
+ case OBJECT:
+ if (!(field.getType().isAssignableFrom(Objects.class))) {
+ return true;
+ }
+ break;
+ case BOOLEAN:
+ if (!(field.getType().isAssignableFrom(Boolean.class))) {
+ return true;
+ }
+ break;
+ case ARRAY:
+ if (!(field.getType().isAssignableFrom(Arrays.class))) {
+ return true;
+ }
+ break;
+ default:
+ return true;
+ }
+ } else {
+ return false;
+
+ }
+ return false;
+ }
+
@Override
public void terminateWorkflow(WorkflowDescription wfDesc) throws WorkflowException {
log.info("terminateWorkflow: {}", wfDesc);
diff --git a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java
index 252c17d..2a9b9f6 100644
--- a/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java
+++ b/apps/workflow/app/src/main/java/org/onosproject/workflow/impl/example/SampleWorkflow.java
@@ -116,6 +116,15 @@
.chain(SampleWorklet5.class.getName())
.build();
workflowStore.register(workflow);
+
+ // registering new workflow definition
+ uri = URI.create("sample.workflow-3");
+ workflow = ImmutableListWorkflow.builder()
+ .id(uri)
+ .chain(SampleWorklet6.class.getName())
+ .build();
+ workflowStore.register(workflow);
+
}
/**
@@ -308,4 +317,29 @@
}
}
+ /**
+ * Class for sample worklet-6 to test workflow datamodel exception.
+ */
+ public static class SampleWorklet6 extends AbsSampleWorklet {
+
+ @JsonDataModel(path = MODEL_COUNT)
+ String str;
+
+ @Override
+ public void process(WorkflowContext context) throws WorkflowException {
+ ObjectNode node = getDataModel(context);
+ node.put("work6", "done");
+ log.info("workflow-process {}-{}", context.workplaceName(), this.getClass().getSimpleName());
+ sleep(10);
+ context.completed();
+ }
+
+ @Override
+ public boolean isNext(WorkflowContext context) throws WorkflowException {
+ ObjectNode node = allocOrGetModel(context);
+ log.info("workflow-isNext {}-{}", context.workplaceName(), this.getClass().getSimpleName());
+ sleep(10);
+ return !node.has("work6");
+ }
+ }
}