0% found this document useful (0 votes)
52 views

t09 Workflow Connector

The Workflow Connector Pattern aims to solve how web services can support complex, long-running business processes. It considers process complexity, flexibility for different use cases, and ease of use. The pattern hides complexity rather than reducing it and can make testing and process changes more difficult. It uses a process model with service tasks to invoke services, user tasks for human work, and gateways like exclusive, inclusive, and parallel to control flow.

Uploaded by

Brent Liang
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
52 views

t09 Workflow Connector

The Workflow Connector Pattern aims to solve how web services can support complex, long-running business processes. It considers process complexity, flexibility for different use cases, and ease of use. The pattern hides complexity rather than reducing it and can make testing and process changes more difficult. It uses a process model with service tasks to invoke services, user tasks for human work, and gateways like exclusive, inclusive, and parallel to control flow.

Uploaded by

Brent Liang
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 15

The Workflow

Connector Pattern

SOEN 487 - Web Services and Applications


By: Nicholas Nagy
What is the Workflow Connector Pattern?

Tries to solve:

How can web services be used to


support complex and long-running
business processes?

Winter 2021
Considerations

1. Process Complexity
a. Best for large scale processes
2. Variety of choices
a. Flexible for different use cases
b. The best are very expensive
3. Ease of use
a. Hides complexity instead of reducing it
b. Testing can also be difficult
c. Process definition changes are much simpler to change than the
underlying code that depends on it

Winter 2021
Process Model

Winter 2021
Required Resources

Camunda:
Open source workflow engine and modeller.
Download link + instructions: https://camunda.com/download/
Modeler download link + instructions: https://camunda.com/download/modeler/

Node JS download link: https://nodejs.org/en/

Winter 2021
Tasks

Service Task: User Task:

A Service Task is used to invoke services. In A User Task is used to model work that
Camunda this is done by calling Java code or needs to be done by a human actor. When
providing a work item for an external worker the process execution arrives at such a User
to complete asynchronously or invoking a Task, a new task is created in the task list of
logic which is implemented in form of the user(s) or group(s) assigned to that task.
webservices.

Winter 2021
Gateways

Data-based Exclusive Gateway Inclusive Gateway:


(XOR):

Parallel Gateway:

Winter 2021
/**

API For User Tasks -


* For reading the response of an HTTP request
* @param response to read
* @return String of the response
* @throws IOException

Part 1
*/
public static String readResponse(CloseableHttpResponse response)
throws IOException {
// Handling the IO Stream from the response using scanner
package FormApp; Scanner sc = new Scanner(response.getEntity().getContent());
StringBuilder stringResponse = new StringBuilder();
import org.apache.http.client.methods.CloseableHttpResponse; while (sc.hasNext()) {
import org.apache.http.client.methods.HttpGet; stringResponse.append(sc.nextLine());
import org.apache.http.client.methods.HttpPost; stringResponse.append("\n");
import org.apache.http.entity.StringEntity; }
import org.apache.http.impl.client.CloseableHttpClient; response.close();
import org.apache.http.impl.client.HttpClients; return stringResponse.toString();
import org.json.JSONArray; }
import org.json.JSONObject;
/**
import java.io.IOException; * Extract ID from JSON String
import java.util.Scanner; * @param json String
* @return ID of JSON Object
public class API { */
public static String getIdFromJSON(String json) {
/** JSONObject jsonObject = new JSONObject(json);
* Create university application process return jsonObject.getString("id");
* @return String of the process ID }
*/
public static String createUniApplicationProcess() { /**
// Create closeable http client to execute requests with * Method for creating the application (completing the create
try (CloseableHttpClient client = HttpClients.createDefault()) { application task)
// Creating the request to execute * @param processId ID for the process to create the application for
HttpPost httpPost = new HttpPost( * @param gpa Grade of the application
"http://localhost:8080/engine-rest/process- * @param essay Essay of the application
definition/key/UniversityApplication/submit-form"); */
httpPost.setHeader("Accept", "application/json"); public static void createApplication(String processId, double gpa,
httpPost.setHeader("Content-type", "application/json"); String essay) {
httpPost.setEntity(new StringEntity("{}")); String taskId = getTaskID(processId);
String body = createBodyForVariables(gpa, essay);
// Executing the request using the http client and obtaining the completeTask(taskId, body);
response }
CloseableHttpResponse response = client.execute(httpPost);
String jsonResponse = readResponse(response); /**
String applicationId = getIdFromJSON(jsonResponse); * Get task ID of the first task of a given process
System.out.printf("Your application ID is: %s\nPlease save it * @param processID of the process to check the first task with
somewhere.", applicationId); * @return id of the first task
return applicationId; */
} catch (IOException e) { public static String getTaskID(String processID) {

}
e.printStackTrace();
return "Failed to get customers"; Winter 2021
JSONArray jsonArray = getTaskList(processID);
JSONObject obj = (JSONObject) jsonArray.get(0);
return obj.getString("id");
} }
/**
* Complete tasks for a specified taskID
* @param taskId to complete

API For User Tasks -


* @param body to post
*/
public static void completeTask(String taskId, String body) {

Part 2
try (CloseableHttpClient client = HttpClients.createDefault()) {
String URL = String.format("http://localhost:8080/engine-
rest/task/%s/complete", taskId);
// Creating the request to execute
HttpPost httpPost = new HttpPost(URL);
httpPost.setHeader("Accept", "application/json");
/** httpPost.setHeader("Content-type", "application/json");
* Getting the task list of a given process
* @param processID of the process httpPost.setEntity(new StringEntity(body));
* @return JSON Array of tasks
*/ // Executing the request using the http client and obtaining the
private static JSONArray getTaskList(String processID) { response
try (CloseableHttpClient client = HttpClients.createDefault()) { client.execute(httpPost);
String URL = String.format("http://localhost:8080/engine- } catch (IOException e) {
rest/task/?processInstanceId=%s", e.printStackTrace();
processID); }
HttpGet httpGet = new HttpGet(URL);
CloseableHttpResponse response = client.execute(httpGet); }
String jsonResponse = readResponse(response);
return new JSONArray(jsonResponse); /**
} catch (IOException e ) { * Check whether a process exists
return new JSONArray("[]"); * @param processID to use to check whether the process exists
} * @return whether it exists
} */
public static boolean processExists(String processID) {
/**
* Create the JSON body for the process variables to be posted when try (CloseableHttpClient client = HttpClients.createDefault()) {
completing the create application task String url = String.format("http://localhost:8080/engine-
* @param gpa process variable rest/process-instance/%s", processID);
* @param essay process variable HttpGet httpGet = new HttpGet(url);
* @return JSON String to be posted CloseableHttpResponse httpResponse = client.execute(httpGet);
*/ String jsonResponse = readResponse(httpResponse);
private static String createBodyForVariables(double gpa, String essay) { JSONObject obj = new JSONObject(jsonResponse);
JSONObject variables = new JSONObject(); boolean t = (obj.has("type") &&
JSONObject gpaJson = new JSONObject();
gpaJson.put("value", gpa); obj.getString("type").equals("InvalidRequestException"));
gpaJson.put("type", "double"); return !(obj.has("type") &&
variables.put("gpa", gpaJson); obj.getString("type").equals("InvalidRequestException"));
} catch (IOException e) {
JSONObject essayJson = new JSONObject(); return false;
essayJson.put("value", essay); }
essayJson.put("type", "string"); }
variables.put("essay", essayJson);

Winter 2021
JSONObject body = new JSONObject();
body.put("variables", variables);
return body.toString();
}
API For User Tasks - Part 3

/**
* Check whether the process is at the confirm acceptance task of
the process
* @param processID to check process
* @return whether the current task is confirm acceptance
*/
public static boolean canConfirmAcceptance(String processID) {
return validateInCurrentActivity(processID,
"ConfirmAcceptance");
}

/**
* Check whether the process is at the create application task of
the process
* @param processID to check process
* @return whether the current task is create application
*/
public static boolean canCreateApplication(String processID) {
return validateInCurrentActivity(processID,
"CreateApplication");
}

/**
* Validate if the first task in a task list of a process is the
specified activity
* @param processID of the process to look into
* @param activityID to validate the current activity
* @return whether the current activity is the one passed
*/
private static boolean validateInCurrentActivity(String processID,
String activityID) {
JSONArray taskList = getTaskList(processID);
if (taskList.length() != 1) {
return false;
} else {
JSONObject task = taskList.getJSONObject(0);
return
task.getString("taskDefinitionKey").equals(activityID);

}
}
Winter 2021
}
/**
* Console app portion for handling editing of the task of a process that was already
created

Console for User Task -


* @param input scanner for taking user input
*/
private static void editApplication(Scanner input) {
System.out.println("Please enter your application ID:");

Part 1
String applicationId = input.nextLine();
if (!API.processExists(applicationId)) {
System.out.println("Your application does not exist in our system.\nPlease try
to create an application " +
"first.");
return;
}
package FormApp; if (!API.canCreateApplication(applicationId)) {
System.out.println("Your application is unable to be edited at the moment as
import java.util.Scanner; it may be in the process of " +
"being reviewed.\nIf your application has already been confirmed,
public class Console { please select the appropriate" +
public static void main(String[] args) { " choice when starting up this application.\n");
System.out.println("Welcome!"); return;
System.out.println("Pick your choice and press enter."); }
System.out.println("1 - Create University application"); createApplication(input, applicationId);
System.out.println("2 - Confirm Acceptance to University"); }
System.out.println("3 - Edit Application");
Scanner input = new Scanner(System.in); /**
String choice = input.nextLine(); * Console app portion for handling creation of the application/completing that task
* @param input scanner for taking user input
switch (choice) { * @param processID processID to complete the task for
case "1": */
handleApplication(input); private static void createApplication(Scanner input, String processID) {
break; System.out.println("Creating University Application.\n");
case "2": System.out.print("State your GPA: ");
handleConfirmation(input); double gpa = input.nextDouble();
break; input.nextLine();
case "3": System.out.printf("GPA: %.2f\n", gpa);
editApplication(input); System.out.println("Write a small essay about why you want to join this
break; university.\n" +
default: "Press Enter when you are finished.");
System.out.println("That isn't part of the available choices."); String essay = input.nextLine();
break; System.out.printf("Essay: %s\n", essay);
} API.createApplication(processID, gpa, essay);
input.close();
} System.out.printf("Your Application ID is: %s.\nPlease write it down and keep
it.", processID);
/** }
* Used to create the process and then trigger the console function for
creating/completing the create application
* task
* @param input scanner for taking user input
*/
private static void handleApplication(Scanner input) { Winter 2021
// Start Application Process and get the task ID for the first task.
String processID = API.createUniApplicationProcess();
createApplication(input, processID);
}
Console for User Task - Part 2

/**
* Console app portion for handling confirmation of university
acceptance
* @param input Scanner for taking user input
*/
private static void handleConfirmation(Scanner input) {
System.out.println("Please enter your Application ID.");
String applicationId = input.nextLine();

if (!API.processExists(applicationId)) {
System.out.println("Your application does not exist in our
system.\nPlease try to create an application " +
"first.");
return;
}

if (!API.canConfirmAcceptance(applicationId)) {
System.out.println("Unfortunately, you weren't accepted to
this University.\nYou may try to submit a new" +
" application.");
return;
}

String taskId = API.getTaskID(applicationId);


String body = "{}";
API.completeTask(taskId, body);
System.out.println("You've officially been accepted to
University!.");
}

Winter 2021
import java.util.logging.Logger;

import org.camunda.bpm.client.ExternalTaskClient;

Service Task import


import
import
org.camunda.bpm.client.variable.ClientValues;
org.camunda.bpm.engine.variable.VariableMap;
org.camunda.bpm.engine.variable.Variables;

Workers public class GradeValidator {


private final static Logger LOGGER =
Logger.getLogger(GradeValidator.class.getName());

public static void main(String[] args) {


const { Client, logger, Variables } = require("camunda-external- ExternalTaskClient client = ExternalTaskClient.create()
task-client-js"); .baseUrl("http://localhost:8080/engine-rest")
.asyncResponseTimeout(10000) // long polling
// configuration for the Client: timeout
// - 'baseUrl': url to the Workflow Engine .build();
// - 'logger': utility to automatically log important events
const config = { baseUrl: "http://localhost:8080/engine-rest",
// subscribe to an external task topic as specified in the
use: logger };
process
// create a Client instance with custom configuration
client.subscribe("ValidateGrades")
const client = new Client(config); .lockDuration(1000) // the default lock duration is
20 seconds, but you can override this
// susbscribe to the topic: 'ReviewEssay' .handler((externalTask, externalTaskService) -> {
client.subscribe("ReviewEssay", async function({ task,
taskService }) { // Get a process variable
// Put your business logic double gpa = externalTask.getVariable("gpa");
// set a process variable 'winning'
const essay = task.variables.get("essay"); LOGGER.info("Student GPA is " + gpa);

const processVariables = new Variables(); VariableMap processVariables =


const essayIsGood = essay.split(' ').length > 5; Variables.createVariables().putValue("validatedGrades",
processVariables.set("essayIsGood", essayIsGood);
ClientValues.booleanValue(gpa > 1.0));
// complete the task
// Complete the task
await taskService.complete(task, processVariables);
}); externalTaskService.complete(externalTask,
processVariables);

Winter 2021
})
.open();
}
}
References
Camunda Rest API: https://docs.camunda.org/manual/latest/reference/rest/

JavaScript Task Client: https://github.com/camunda/camunda-external-task-client-js

Java Task Client: https://github.com/camunda/camunda-external-task-client-java

Task Types: https://docs.camunda.org/manual/7.14/reference/bpmn20/tasks/

Gateway Types: https://docs.camunda.org/manual/7.14/reference/bpmn20/gateways/

Book: Service Design Patterns Fundamental Design Solutions for SOAP/WSDL and
RESTful Web Services

Winter 2021
Extra Resources
Gettings Started: https://www.youtube.com/watch?v=2XeTJQfz_YQ

Running Java in Camunda: https://www.youtube.com/watch?v=HxtZf5VD6lQ

Distributed System: https://www.youtube.com/watch?v=l6pMXr8Jf6k

REST API: https://www.youtube.com/watch?v=ypX90aQScOQ

Advanced Topics: https://www.youtube.com/watch?v=gFY1UuiCVl4

Winter 2021

You might also like