I've been experimenting with more reliable ways to parse inputs from a Conductor workflow. To do this, I've created a composed class structure with a person and their vehicles.
Person.java
public class Person {
protected String firstName;
protected String lastName;
protected List<Vehicle> vehicles;
// Constructors, getters, setters
}
Vehicle.java
public class Vehicle {
public String make;
public String model;
public String trim;
public short year;
// Constructors, getters, setters
}
I then created a worker which uses Jackson to convert Conductor Client's Task.getInputData()
Map to the above POJOs.
ExperimentalStepWorker.java
public class ExperimentStepWorker implements Worker {
protected ObjectMapper objectMapper = new ObjectMapper();
// Other @Overloaded methods per the Worker interface
/**
* Executes a task and returns the updated task.
*
* @param task Task to be executed.
* @return the {@link TaskResult} object
* If the task is not completed yet, return with the status as IN_PROGRESS.
*/
@Override
public TaskResult execute(Task task) {
TaskResult result = new TaskResult(task);
Person person = objectMapper.convertValue(task.getInputData(), Person.class);
System.out.println("The map's contents were:");
System.out.println(task.getInputData());
System.out.println();
System.out.println("The objects's contents were:");
System.out.println(person);
result.setStatus(TaskResult.Status.COMPLETED);
return result;
}
}
I then submitted the following workflow to Conductor. The first one has no inputs. The second has only a Person object, and the last and final one has an array of three vehicles:
Workflow input from Postman to Conductor Server
{
"name": "see_if_jackson_works",
"workflowDef": {
"ownerApp": "postman",
"createdBy": "postman_user",
"name": "do_all_the_things_2",
"description": "Test Http Task Workflow",
"version": 1,
"tasks": [
{
"name": "worker_1",
"taskReferenceName": "First Experiment",
"inputParameters": {},
"type": "SIMPLE",
"startDelay": 15,
"optional": false
},
{
"name": "worker_2",
"taskReferenceName": "Second Experiment",
"inputParameters": {
"firstName": "Kirkland",
"lastName": "Yuknis"
},
"type": "SIMPLE",
"startDelay": 30,
"optional": false
},
{
"name": "worker_3",
"taskReferenceName": "Third experiment",
"inputParameters": {
"firstName": "Kirkland",
"lastName": "Yuknis",
"vehicles": [
{"make": "Subaru", "model": "Outback", "trim": "Limited", "year": 2018},
{"make": "Subaru", "model": "Outback", "trim": "Limited", "year": 2018},
{"make": "Subaru", "model": "Outback", "trim": "Limited", "year": 2018}
]
},
"type": "SIMPLE",
"startDelay": 45,
"optional": false
}
],
"schemaVersion": 2,
"restartable": true,
"workflowStatusListenerEnabled": false
},
"input": {}
}
Things seemed to work well for the first two experiments, but in the last one Conductor failed to execute the task. After reading the documentation, I'm wondering if it is possible to have a composed object as an input for a Conductor task. Is anyone able to confirm or deny this? If you can, where might I be going about it wrong?
Worker output from execute()
method when the server calls the worker
The map's contents were:
{}
The objects's contents were:
Person{firstName='null', lastName='null', vehicles=null}
The map's contents were:
{firstName=Kirkland, lastName=Yuknis}
The objects's contents were:
Person{firstName='Kirkland', lastName='Yuknis', vehicles=null}
Update: I just found the following error, which makes it look like a Jackson issue
{
"taskType": "worker_3",
"status": "FAILED",
"inputData": {
"firstName": "Kirkland",
"lastName": "Yuknis",
"vehicles": [
{
"make": "Subaru",
"model": "Outback",
"trim": "Limited",
"Year": 2018
},
{
"make": "Subaru",
"model": "Outback",
"trim": "Limited",
"Year": 2018
},
{
"make": "Subaru",
"model": "Outback",
"trim": "Limited",
"Year": 2018
}
]
},
"referenceTaskName": "Third experiment",
"retryCount": 0,
"seq": 3,
"pollCount": 1,
"taskDefName": "worker_3",
"scheduledTime": 1559838042119,
"startTime": 1559838087887,
"endTime": 1559838088012,
"updateTime": 1559838087888,
"startDelayInSeconds": 45,
"retried": true,
"executed": false,
"callbackFromWorker": true,
"responseTimeoutSeconds": 1200,
"workflowInstanceId": "08a7bf21-6f4b-4265-97b0-0ce9cc93d1a4",
"workflowType": "do_all_the_things_2",
"taskId": "3b4906df-7136-4669-a8c8-a12a9b7c2f6a",
"reasonForIncompletion": "Error while executing the task: java.lang.IllegalArgumentException: Unrecognized field \"vehicles\" (class com.yuknis.egypt.models.Person), not marked as ignorable (2 known properties: \"lastName\", \"firstName\"])\n at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.yuknis.egypt.models.Person[\"vehicles\"])",
"callbackAfterSeconds": 0,
"workerId": "Kirklands-MBP.hsd1.fl.comcast.net",
"workflowTask": {
"name": "worker_3",
"taskReferenceName": "Third experiment",
"inputParameters": {
"firstName": "Kirkland",
"lastName": "Yuknis",
"vehicles": [
{
"make": "Subaru",
"model": "Outback",
"trim": "Limited",
"Year": 2018
},
{
"make": "Subaru",
"model": "Outback",
"trim": "Limited",
"Year": 2018
},
{
"make": "Subaru",
"model": "Outback",
"trim": "Limited",
"Year": 2018
}
]
},
"type": "SIMPLE",
"startDelay": 45,
"optional": false,
"taskDefinition": {
"createTime": 1559837812844,
"name": "worker_3",
"retryCount": 3,
"timeoutSeconds": 0,
"timeoutPolicy": "TIME_OUT_WF",
"retryLogic": "FIXED",
"retryDelaySeconds": 600,
"responseTimeoutSeconds": 1200,
"concurrentExecLimit": 100,
"rateLimitPerFrequency": 50,
"rateLimitFrequencyInSeconds": 60
},
"asyncComplete": false
},
"rateLimitPerFrequency": 0,
"rateLimitFrequencyInSeconds": 0,
"taskDefinition": {
"present": true
},
"queueWaitTime": 45768,
"taskStatus": "FAILED",
"logs": [
"06/06/19, 16:21:28:008 : java.lang.IllegalArgumentException: Unrecognized field \"vehicles\" (class com.yuknis.egypt.models.Person), not marked as ignorable (2 known properties: \"lastName\", \"firstName\"])\n at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.yuknis.egypt.models.Person[\"vehicles\"])\n\tat com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3750)\n\tat com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:3668)\n\tat com.yuknis.egypt.workers.ExperimentStepWorker.execute(ExperimentStepWorker.java:58)\n\tat com.netflix.conductor.client.task.WorkflowTaskCoordinator.execute(WorkflowTaskCoordinator.java:379)\n\tat com.netflix.conductor.client.task.WorkflowTaskCoordinator.lambda$pollForTask$4(WorkflowTaskCoordinator.java:340)\n\tat java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat java.base/java.lang.Thread.run(Thread.java:835)\nCaused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field \"vehicles\" (class com.yuknis.egypt.models.Person), not marked as ignorable (2 known properties: \"lastName\", \"firstName\"])\n at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.yuknis.egypt.models.Person[\"vehicles\"])\n\tat com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)\n\tat com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:823)\n\tat com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1153)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1589)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1567)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:294)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)\n\tat com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3745)\n\t... 9 more\n"
]
},
I found the problem. As a habit I do not create accessors for non-primitive data members on a class. Since List is a non-primitive, I (without thinking) did not create accessors for it. In addition to this, vehicles was protected and not public. I've added the accessor and it solved the issue. Thanks all who looked at this!