From 1b60873aa4042490d512335b82bb99fa05007b69 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 | 65 +++++++++ .../tasks/tibco/TibcoWorkFlowTask.java | 73 +++++++++++ .../redhat/parodos/tasks/tibco/Tibjms.java | 8 ++ .../parodos/tasks/tibco/TibjmsImpl.java | 24 ++++ .../tasks/tibco/TibcoWorkFlowTaskTest.java | 75 +++++++++++ workflow-examples/README.md | 18 ++- workflow-examples/pom.xml | 6 + workflow-examples/run_tibco.sh | 124 ++++++++++++++++++ .../examples/TibcoWorkFlowConfiguration.java | 33 +++++ 12 files changed, 496 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..a7afec118 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,8 @@ notification-service pattern-detection-library coverage + prebuilt-tasks + external-dependencies diff --git a/prebuilt-tasks/pom.xml b/prebuilt-tasks/pom.xml new file mode 100644 index 000000000..41ade115a --- /dev/null +++ b/prebuilt-tasks/pom.xml @@ -0,0 +1,65 @@ + + + 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 + + + org.springframework + spring-context + 5.3.14 + compile + + + 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..b253e1d98 --- /dev/null +++ b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/TibcoWorkFlowTask.java @@ -0,0 +1,73 @@ +// @formatter:off +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 com.tibco.tibjms.TibjmsConnectionFactory; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +import java.util.LinkedList; +import java.util.List; + +@Slf4j +public class TibcoWorkFlowTask extends BaseWorkFlowTask { + + private Tibjms service; + private String url; + private String caFile; + private String username; + private 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() { + List 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"); + + log.debug("sending to topic " + topic + " message: " + 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..3de7d0499 --- /dev/null +++ b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/tibco/TibjmsImpl.java @@ -0,0 +1,24 @@ +package com.redhat.parodos.tasks.tibco; + +import com.tibco.tibjms.TibjmsConnectionFactory; + +import javax.jms.*; + +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); + } + 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..9e29d96b3 --- /dev/null +++ b/prebuilt-tasks/src/test/java/com/redhat/parodos/tasks/tibco/TibcoWorkFlowTaskTest.java @@ -0,0 +1,75 @@ +// @formatter:off +package com.redhat.parodos.tasks.tibco; + +import com.redhat.parodos.tasks.tibco.TibcoWorkFlowTask; +import com.redhat.parodos.tasks.tibco.Tibjms; +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 { + + String caFile = "/cafile"; + String url = "ssl://localhost:7222"; + String username = "username"; + String passowrd = "password"; + String topic = "topic"; + String message = "test message"; + Tibjms tibjms = Mockito.mock(Tibjms.class); + TibcoWorkFlowTask task = new TibcoWorkFlowTask(tibjms, url, caFile, username, passowrd); + + @Before + public void before() { + 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(); + Map map; + if ( withParams == true ) { + map = Map.of("topic",topic,"message",message); + } else { + map = new HashMap(); + } + 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..c8f269050 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 + 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..e16e5f1d7 100644 --- a/workflow-examples/pom.xml +++ b/workflow-examples/pom.xml @@ -81,6 +81,12 @@ ${mockito.version} test + + dev.parodos + prebuilt-tasks + ${revision} + compile + 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(); + } + +}