From 0e0484abeaf9a8e533ce91e6c1c2f244eb65a51b Mon Sep 17 00:00:00 2001 From: Yaron Dayagi Date: Sun, 12 Mar 2023 18:32:58 +0200 Subject: [PATCH] Add a workflow task that sends a message to topic in TIBCO FLPATH-145 https://issues.redhat.com/browse/FLPATH-145 Signed-off-by: Yaron Dayagi --- external-dependencies/download-tibco.sh | 10 ++ external-dependencies/pom.xml | 60 +++++++++ pom.xml | 2 + prebuilt-tasks/pom.xml | 59 +++++++++ .../tasks/tibco/TibcoWorkFlowTask.java | 74 +++++++++++ .../redhat/parodos/tasks/tibco/Tibjms.java | 8 ++ .../parodos/tasks/tibco/TibjmsImpl.java | 30 +++++ .../tasks/tibco/TibcoWorkFlowTaskTest.java | 81 ++++++++++++ workflow-examples/README.md | 18 ++- workflow-examples/pom.xml | 5 + workflow-examples/run_tibco.sh | 124 ++++++++++++++++++ .../examples/TibcoWorkFlowConfiguration.java | 33 +++++ 12 files changed, 502 insertions(+), 2 deletions(-) create mode 100755 external-dependencies/download-tibco.sh create mode 100644 external-dependencies/pom.xml create mode 100644 prebuilt-tasks/pom.xml create mode 100644 prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/TibcoWorkFlowTask.java create mode 100644 prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/Tibjms.java create mode 100644 prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/TibjmsImpl.java create mode 100644 prebuilt-tasks/src/test/java/com/redhat/parodos/tasks/tibco/TibcoWorkFlowTaskTest.java create mode 100755 workflow-examples/run_tibco.sh create mode 100644 workflow-examples/src/main/java/com/redhat/parodos/examples/TibcoWorkFlowConfiguration.java diff --git a/external-dependencies/download-tibco.sh b/external-dependencies/download-tibco.sh new file mode 100755 index 000000000..7385f47f7 --- /dev/null +++ b/external-dependencies/download-tibco.sh @@ -0,0 +1,10 @@ +#!/bin/bash +if [ -f "tibjms.jar" ]; then exit 0; fi +rm -rf /tmp/tibco +mkdir /tmp/tibco +curl -s -o /tmp/tibco/tibco.zip "https://edownloads.tibco.com/Installers/tap/EMS-CE/10.2.1/TIB_ems-ce_10.2.1_linux_x86_64.zip?SJCDPTPG=1681227113_1e3fe1f60cbbe9ee810af68e0b4256bb&ext=.zip" +cd /tmp/tibco +unzip tibco.zip TIB_ems-ce_10.2.1/tar/TIB_ems-ce_10.2.1_linux_x86_64-java_client.tar.gz +tar -zxf TIB_ems-ce_10.2.1/tar/TIB_ems-ce_10.2.1_linux_x86_64-java_client.tar.gz opt/tibco/ems/10.2/lib/tibjms.jar +cd - +cp /tmp/tibco/opt/tibco/ems/10.2/lib/tibjms.jar . \ No newline at end of file diff --git a/external-dependencies/pom.xml b/external-dependencies/pom.xml new file mode 100644 index 000000000..d2438fc7f --- /dev/null +++ b/external-dependencies/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + dev.parodos + parados-parent + ${revision} + + + external-dependencies + + + 11 + 11 + UTF-8 + + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + + download-tibco + exec + clean + + + + ${project.basedir}/download-tibco.sh + + + + org.apache.maven.plugins + maven-install-plugin + 2.4 + + com.tibco + tibjms + 10.2 + ${project.basedir}/tibjms.jar + jar + + + + install_tibco + + install-file + + clean + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 29245ced9..864949ee4 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,8 @@ notification-service pattern-detection-library coverage + external-dependencies + prebuilt-tasks diff --git a/prebuilt-tasks/pom.xml b/prebuilt-tasks/pom.xml new file mode 100644 index 000000000..d2dfcc688 --- /dev/null +++ b/prebuilt-tasks/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + dev.parodos + parados-parent + ${revision} + + + prebuilt-tasks + + + 11 + 11 + UTF-8 + + + + dev.parodos + external-dependencies + ${revision} + + + org.projectlombok + lombok + 1.18.24 + + + dev.parodos + parodos-model-api + ${revision} + compile + + + jakarta.jms + jakarta.jms-api + 2.0.3 + + + com.tibco + tibjms + 10.2 + + + junit + junit + 4.13.1 + test + + + org.mockito + mockito-core + 3.9.0 + test + + + \ No newline at end of file diff --git a/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/TibcoWorkFlowTask.java b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/TibcoWorkFlowTask.java new file mode 100644 index 000000000..5f1c44d1f --- /dev/null +++ b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/TibcoWorkFlowTask.java @@ -0,0 +1,74 @@ +package com.redhat.parodos.tasks.tibco; + +import com.redhat.parodos.workflow.task.BaseWorkFlowTask; +import com.redhat.parodos.workflow.task.WorkFlowTaskOutput; +import com.redhat.parodos.workflow.task.parameter.WorkFlowTaskParameter; +import com.redhat.parodos.workflow.task.parameter.WorkFlowTaskParameterType; +import com.redhat.parodos.workflows.work.DefaultWorkReport; +import com.redhat.parodos.workflows.work.WorkContext; +import com.redhat.parodos.workflows.work.WorkReport; +import com.redhat.parodos.workflows.work.WorkStatus; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +import java.util.LinkedList; +import java.util.List; + +@Slf4j +public class TibcoWorkFlowTask extends BaseWorkFlowTask { + + private final Tibjms service; + + private final String url; + + private final String caFile; + + private final String username; + + private final String password; + + public TibcoWorkFlowTask(String url, String caFile, String username, String password) { + this(new TibjmsImpl(), url, username, password, caFile); + } + + TibcoWorkFlowTask(Tibjms service, String url, String caFile, String username, String password) { + this.service = service; + this.url = url; + this.caFile = caFile; + this.username = username; + this.password = password; + } + + @Override + public @NonNull List getWorkFlowTaskParameters() { + LinkedList params = new LinkedList(); + params.add(WorkFlowTaskParameter.builder().key("topic").type(WorkFlowTaskParameterType.TEXT).optional(false) + .description("Topic to send to").build()); + params.add(WorkFlowTaskParameter.builder().key("message").type(WorkFlowTaskParameterType.TEXT).optional(false) + .description("Message to send to topic").build()); + return params; + } + + @Override + public @NonNull List getWorkFlowTaskOutputs() { + return List.of(WorkFlowTaskOutput.OTHER); + } + + @Override + public WorkReport execute(WorkContext workContext) { + try { + String topic = getParameterValue(workContext, "topic"); + String message = getParameterValue(workContext, "message"); + + service.sendMessage(url, caFile, username, password, topic, message); + + } + catch (Exception e) { + log.error("TIBCO task failed", e); + return new DefaultWorkReport(WorkStatus.FAILED, workContext, e); + } + + return new DefaultWorkReport(WorkStatus.COMPLETED, workContext); + } + +} diff --git a/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/Tibjms.java b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/Tibjms.java new file mode 100644 index 000000000..516d14afe --- /dev/null +++ b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/Tibjms.java @@ -0,0 +1,8 @@ +package com.redhat.parodos.tasks.tibco; + +public interface Tibjms { + + public void sendMessage(String url, String caFile, String username, String password, String topic, String message) + throws javax.jms.JMSException; + +} diff --git a/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/TibjmsImpl.java b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/TibjmsImpl.java new file mode 100644 index 000000000..0edd0a627 --- /dev/null +++ b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/TibjmsImpl.java @@ -0,0 +1,30 @@ +package com.redhat.parodos.tasks.tibco; + +import com.tibco.tibjms.TibjmsConnectionFactory; + +import javax.jms.JMSException; +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.Session; +import javax.jms.MessageProducer; +import javax.jms.TextMessage; + +public class TibjmsImpl implements Tibjms { + + @Override + public void sendMessage(String url, String caFile, String username, String password, String topic, String message) + throws JMSException { + TibjmsConnectionFactory factory = new TibjmsConnectionFactory(url); + if (!caFile.isEmpty()) { + factory.setSSLTrustedCertificate(caFile); + } + try (Connection connection = factory.createConnection(username, password); + Session session = connection.createSession(javax.jms.Session.AUTO_ACKNOWLEDGE);) { + Destination destination = session.createTopic(topic); + MessageProducer producer = session.createProducer(destination); + TextMessage textMessage = session.createTextMessage(message); + producer.send(textMessage); + } + } + +} diff --git a/prebuilt-tasks/src/test/java/com/redhat/parodos/tasks/tibco/TibcoWorkFlowTaskTest.java b/prebuilt-tasks/src/test/java/com/redhat/parodos/tasks/tibco/TibcoWorkFlowTaskTest.java new file mode 100644 index 000000000..888970c9f --- /dev/null +++ b/prebuilt-tasks/src/test/java/com/redhat/parodos/tasks/tibco/TibcoWorkFlowTaskTest.java @@ -0,0 +1,81 @@ +package com.redhat.parodos.tasks.tibco; + +import com.redhat.parodos.workflow.context.WorkContextDelegate; +import com.redhat.parodos.workflow.exception.MissingParameterException; +import com.redhat.parodos.workflows.work.WorkContext; +import com.redhat.parodos.workflows.work.WorkReport; +import com.redhat.parodos.workflows.work.WorkStatus; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import javax.jms.JMSException; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class TibcoWorkFlowTaskTest { + + private final static String caFile = "/cafile"; + + private final static String url = "ssl://localhost:7222"; + + private final static String username = "username"; + + private final static String passowrd = "password"; + + private final static String topic = "topic"; + + private final static String message = "test message"; + + private final static Tibjms tibjms = Mockito.mock(Tibjms.class); + + private final static TibcoWorkFlowTask task = new TibcoWorkFlowTask(tibjms, url, caFile, username, passowrd); + + @Before + public void setUp() { + task.setBeanName("Test"); + } + + @Test + public void executeMissingParameter() { + WorkContext ctx = getWorkContext(false); + WorkReport result = task.execute(ctx); + assertEquals(WorkStatus.FAILED, result.getStatus()); + assertEquals(MissingParameterException.class, result.getError().getClass()); + } + + @Test + public void executeErrorInTibco() throws JMSException { + WorkContext ctx = getWorkContext(true); + doThrow(JMSException.class).when(tibjms).sendMessage(any(), any(), any(), any(), any(), any()); + WorkReport result = task.execute(ctx); + assertEquals(WorkStatus.FAILED, result.getStatus()); + assertEquals(JMSException.class, result.getError().getClass()); + } + + @Test + public void executeSuccess() throws JMSException { + WorkContext ctx = getWorkContext(true); + WorkReport result = task.execute(ctx); + assertEquals(WorkStatus.COMPLETED, result.getStatus()); + verify(tibjms, times(1)).sendMessage(url, caFile, username, passowrd, topic, message); + } + + private WorkContext getWorkContext(boolean withParams) { + WorkContext ctx = new WorkContext(); + HashMap map = new HashMap<>(); + if (withParams) { + map.put("topic", topic); + map.put("message", message); + } + + WorkContextDelegate.write(ctx, WorkContextDelegate.ProcessType.WORKFLOW_TASK_EXECUTION, task.getName(), + WorkContextDelegate.Resource.ARGUMENTS, map); + return ctx; + } + +} diff --git a/workflow-examples/README.md b/workflow-examples/README.md index 5ee3e0814..cd02c376e 100644 --- a/workflow-examples/README.md +++ b/workflow-examples/README.md @@ -11,7 +11,7 @@ the 'workflow-service' will explain how to execute the workflow. In this version of Parodos, Workflow projects are Java project. All configuration is done using the Spring Framework. These samples are built with Maven, however Gradle could also be used. -The examples in this project can be found in two different packages. +These are the examples in this project: ### Simple @@ -23,6 +23,10 @@ Parallel workflows. This package shows how to work with WorkflowTasks that create a manual process that needs to be monitored before further WorkFlowTasks can be created. In this project WorkflowCheckers and other related concepts are created and configured. +### TIBCO + +A workflow configuration that demonstrates use of TibcoWorkflowTask + ## Compiling The Code To get the Parodos dependencies you will need to run a maven install from the **root** of the project folder. This will @@ -42,10 +46,15 @@ Once the Jar has been compiled, ensure it's added to the pom.xml of the workflow ```xml - com.redhat.parodos + dev.parodos workflow-examples ${parodos.version} + + dev.parodos + prebuilt-tasks + ${revision} + ``` @@ -353,6 +362,11 @@ hours (or even days). As all state is persisted, when the workflow-service is re WorkflowCheckers can be used to determine if required manual processes, outside the scope of Parodos, have completed. +### TIBCO workflow + +'TibcoWorkFlowConfiguration.java' is the workflow code. You'll find details inside the file. +Use the script 'run_tibco.sh' to execute the workflow. + #### A Note On Defining WorkflowTasks for Usage In A Workflow ***Creating a Single Instance Of The Same Workflow Tasks*** diff --git a/workflow-examples/pom.xml b/workflow-examples/pom.xml index 425a85c2e..8690f1ed4 100644 --- a/workflow-examples/pom.xml +++ b/workflow-examples/pom.xml @@ -81,6 +81,11 @@ ${mockito.version} test + + dev.parodos + prebuilt-tasks + ${revision} + diff --git a/workflow-examples/run_tibco.sh b/workflow-examples/run_tibco.sh new file mode 100755 index 000000000..b86d2f73f --- /dev/null +++ b/workflow-examples/run_tibco.sh @@ -0,0 +1,124 @@ +#!/bin/bash +# Author: ydayagi +# Sample Script to run the TIBCO WorkFlow Example + + +SERVERIP=${SERVERIP:-127.0.0.1} +SERVERPORT=${SERVERPORT:-8080} +export TARGET_URL="http://${SERVERIP}:${SERVERPORT}" + +echo "Starting example with '${TARGET_URL}' server" + +echo_red() { + COLOR="\e[31m"; + ENDCOLOR="\e[0m"; + printf "$COLOR%b$ENDCOLOR\n" "$1"; +} + +echo_green() { + COLOR="\e[32m"; + ENDCOLOR="\e[0m"; + printf "$COLOR%b$ENDCOLOR\n" "$1"; +} + +echo_yellow() { + COLOR="\e[33m"; + ENDCOLOR="\e[0m"; + printf "$COLOR%b$ENDCOLOR\n" "$1"; +} + +echo_blue() { + COLOR="\e[34m"; + ENDCOLOR="\e[0m"; + printf "$COLOR%b$ENDCOLOR\n" "$1"; +} + +@fail() { + echo_red "ERROR: $1" + exit 1 +} + +get_workflow_id() { + curl -X 'GET' -s \ + "${TARGET_URL}/api/v1/workflowdefinitions" \ + -H 'accept: */*' \ + -H 'Authorization: Basic dGVzdDp0ZXN0' \ + -H 'Content-Type: application/json' | jq '.[] | select(.name=="'$1'")' | jq -r '.id' +} + +run_tibco_flow() { + echo " " + echo_blue "******** Checking project is running ********" + for i in {1..100} + do + CODE=$(curl -LI -s "${TARGET_URL}/api/v1/projects" \ + -H 'accept: */*' \ + -o /dev/null \ + -H 'Authorization: Basic dGVzdDp0ZXN0' \ + -H 'Content-Type: application/json' \ + -w '%{http_code}\n') + [ $CODE -eq "200" ] && break + sleep 2s + [ $i -eq "100" ] && @fail "Project didn't started yet" + done + echo "Project is ✔️ on ${TARGET_URL}" + echo " " + + echo_blue "******** Create Project ********" + echo " " + PROJECT_ID=$(curl -X 'POST' -s \ + "${TARGET_URL}/api/v1/projects" \ + -H 'accept: */*' \ + -H 'Authorization: Basic dGVzdDp0ZXN0' \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "project-1", + "description": "TIBCO example project" + }' | jq -r '.id') + [ ${#PROJECT_ID} -eq "36" ] || @fail "Project ID ${PROJECT_ID} is not present" + echo "Project id is " $(echo_green $PROJECT_ID) + + echo " " + echo_blue "******** Running The TIBCO WorkFlow ********" + + echo " " + echo_blue "Running the TIBCO WorkFlow" + echo "" + + TIBCO_WORKFLOW_NAME="tibcoWorkFlow" + TIBCO_WORKFLOW_ID=$(get_workflow_id $TIBCO_WORKFLOW_NAME) + [ ${#TIBCO_WORKFLOW_ID} -eq "36" ] || @fail "There is no valid TIBCO_WORKFLOW_ID: '${TIBCO_WORKFLOW_ID}'" + [ ${#TIBCO_WORKFLOW_NAME} -gt "10" ] || @fail "There is no valid TIBCO_WORKFLOW_NAME: '${TIBCO_WORKFLOW_NAME}'" + + echo "- TIBCO_WORKFLOW_ID: " $(echo_green TIBCO_WORKFLOW_ID) + echo "- TIBCO_WORKFLOW_NAME: " $(echo_green TIBCO_WORKFLOW_NAME) + EXECUTION_ID="$(curl -X 'POST' -s \ + "${TARGET_URL}/api/v1/workflows/" \ + -H 'accept: */*' \ + -H 'Authorization: Basic dGVzdDp0ZXN0' \ + -H 'Content-Type: application/json' \ + -d '{ + "projectId": "'$PROJECT_ID'", + "workFlowName": "'$TIBCO_WORKFLOW_NAME'", + "workFlowTasks": [ + { + "name": "tibcoTask", + "arguments": [ + { + "key": "topic", + "value": "tibcoTest" + }, + { + "key": "message", + "value": "testing tibco task" + } + ] + } + ] + }' | jq -r '.workFlowExecutionId')" + echo " " + echo " " + echo "TIBCO workflow execution id:" $(echo_green $EXECUTION_ID) + [ ${#EXECUTION_ID} -eq "36" ] || @fail "There is no valid EXECUTION_ID: '${EXECUTION_ID}'" +} +run_tibco_flow diff --git a/workflow-examples/src/main/java/com/redhat/parodos/examples/TibcoWorkFlowConfiguration.java b/workflow-examples/src/main/java/com/redhat/parodos/examples/TibcoWorkFlowConfiguration.java new file mode 100644 index 000000000..9b83c52de --- /dev/null +++ b/workflow-examples/src/main/java/com/redhat/parodos/examples/TibcoWorkFlowConfiguration.java @@ -0,0 +1,33 @@ +// @formatter:off +package com.redhat.parodos.examples; + +import com.redhat.parodos.tasks.tibco.TibcoWorkFlowTask; +import com.redhat.parodos.workflow.annotation.Infrastructure; +import com.redhat.parodos.workflows.workflow.SequentialFlow; +import com.redhat.parodos.workflows.workflow.WorkFlow; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Slf4j +@Configuration +public class TibcoWorkFlowConfiguration { + + @Bean + TibcoWorkFlowTask tibcoTask() { + // TIBCO's default installation: + // 1. creates a CA cert PEM file at the location specified below + // 2. creates a server cert with hostname 'server' + // Add the hostname 'server' to 127.0.0.1 entry in /etc/hosts + // Set the admin password in TIBCO to 'admin' or edit the username and password below + return new TibcoWorkFlowTask("ssl://server:7243", "admin", "admin", "/opt/tibco/ems/10.2/samples/certs/server_root.cert.pem"); + } + + @Bean + @Infrastructure + WorkFlow tibcoWorkFlow(@Qualifier("tibcoTask") TibcoWorkFlowTask tibcoTask) { + return SequentialFlow.Builder.aNewSequentialFlow().named("tibcoWorkFlow").execute(tibcoTask).build(); + } + +}