Click here to Skip to main content
13,703,494 members
Click here to Skip to main content
Add your own
alternative version

Stats

238.3K views
5.3K downloads
354 bookmarked
Posted 5 Jan 2017
Licenced MIT

Wexflow: Open source workflow engine in C#

, 22 Nov 2017
Rate this:
Please Sign up or sign in to vote.
An extensible workflow engine with a cross-platform manager and designer.

Table of contents

  1. Features
  2. How to install Wexflow?
  3. How to uninstall Wexflow?
  4. How to use Wexflow?
    1. General
    2. Wexflow Manager
    3. Wexflow Web Manager
    4. Wexflow Web Designer
    5. Wexflow Android Manager
  5. Workflow samples
    1. Sequential workflows
    2. Execution graph
    3. Flowchart workflows
    4. Workflow events
  6. How to create a custom task?
  7. How to debug Wexflow?
  8. Using the code
    1. Wexflow Windows Service
    2. Wexflow Engine
  9. Libraries used by Wexflow
  10. History

Features

Wexflow is an open source extensible workflow engine with a cross-platform manager and designer. The goal of Wexflow is to automate recurring tasks without user intervention. Wexflow aims to make automations, workflow processes, long-running processes and interactions between systems, applications and folks easy, straightforward and clean.

Wexflow provides the following features:

Discover more about the features in details.

Here are the benefits of using Wexflow:

  • Gain time by automating repetitive tasks.
  • Save money by avoiding re-work and corrections.
  • Reduce human error.
  • Become more efficient and effective in completing your tasks.
  • Become more productive in what you do.
  • Become consistent in what you do.

How to install Wexflow?

Windows

Wexflow can be installed on Windows XP, Windows server 2003 and higher. Wexflow supports .NET Framework 4.0 and higher.

To install Wexflow, proceed as follows:

1. Install Microsoft .NET Framework 4.0 or higher.

2. Install Microsoft Sync Framework 2.1 Synchronization Redistributables (Synchronization-v2.1-x86-ENU.msi).

3. Install Microsoft Sync Framework 2.1 Provider Services Redistributables (ProviderServices-v2.1-x86-ENU.msi).

4. Install WexflowSetup.exe:

5. You can choose to create a desktop shortcut:

6. Click on install to perform the installation:

7. Finally, click on finish to finish the installation:

The following menus are added in the start menu:

  • The "Manager" menu opens Wexflow Manager GUI. 
  • The "Web Manager" menu opens Wexflow Web Manager. 
  • The "Web Designer" menu opens Wexflow Web Designer. 
  • The "Documentation" menu opens the documentation folder of Wexflow. 
  • The "Configuration" menu opens the configuration folder of Wexflow.
  • The "Logs" menu opens the log file of the day.

After Wexflow is installed a Windows Service named Wexflow is installed and starts automatically. To start Wexflow Manager, this Windows Service must be running. However, If you want to stop it you can do it from Windows Services console:

macOS

Wexflow provides a GUI for managing workflows that can be installed on a macOS system.

To run Wexflow on macOS, Wexflow Windows Service must be installed on a Windows machine. Wexflow Windows Service provides a self hosted web service that allows to query Wexflow Engine.

After Wexflow Windows Service is installed on a Windows Machine, proceed as follows to install Wexflow Manager on macOS:

1. Install Mono.

2. Open Wexflow.dmg:

3. Drag and drop Wexflow in Applications folder to install.

4. Configure Wexflow Web Service URI by modifying the setting option WexflowWebServiceUri in /Applications/Wexflow.app/Contents/MonoBundle/Wexflow.Clients.Eto.Manager.exe.config

If the installation succeeds, the following window will appear when launching the application:

Linux

Wexflow provides a GUI for managing workflows that can be installed on a Linux system.

To run Wexflow on Linux, Wexflow Windows Service must be installed on a Windows machine. Wexflow Windows Service provides a self hosted web service that allows to query Wexflow Engine.

After Wexflow Windows Service is installed on a Windows Machine, proceed as follows to install Wexflow Manager on Linux:

1. Download wexflow.tar.gz (available in Wexflow_setup_linux.zip)

2. Install mono-complete:

sudo apt install mono-complete

3. Install Wexflow Manager:

sudo mv wexflow.tar.gz /opt/
cd /opt/
sudo tar -zxvf wexflow.tar.gz
sudo chmod +x /opt/wexflow/wexflow.sh
sudo ln -s /opt/wexflow/wexflow.sh /usr/local/bin/wexflow

5. Configure Wexflow web service uri by modifying the settings option WexflowWebServiceUri in Wexflow Manager configuration file:

sudo vim /opt/wexflow/Wexflow.Clients.Eto.Manager.exe.config 

6. Run Wexflow Manager:

wexflow

The following window will appear:

Android

Wexflow provides a GUI for managing workflows that can be installed on an Android device.

To run Wexflow on Andoird, Wexflow Windows Service must be installed on a Windows machine. Wexflow Windows Service provides a self hosted web service that allows to query Wexflow Engine.

After Wexflow Windows Service is installed on a Windows Machine, proceed as follows to install Wexflow Manager on an Android device:

1. Download wexflow.apk (available in Wexflow_setup_android.zip)

2. Copy wexflow.apk into the Android device

3. Install wexflow.apk

4. Launch Wexflow application and open the application settings through the settings menu:

5. Configure Wexflow Web Service Uri: 

That's it. Wexflow application is ready for work: 

How to uninstall Wexflow?

Windows

To uninstall Wexflow, simply click on "Uninstall" menu from "Windows Start menu > Wexflow".

Or go to "Configuration Panel > Add/remove programs" then select "Wexflow version 1.0.1" and click on uninstall:

After Wexflow is uninstalled, the folders C:\Wexflow\ and C:\WexflowTesting\ are not deleted to prevent user defined workflows and testing scenarios from being deleted. However, If you do not need them you can delete them manually.

The log file C:\Program Files\Wexflow\Wexflow.log is also not deleted to keep track of the last operations done by Wexflow. However, If you do not need the logs you can delete the log files.

macOS

To uninstall Wexflow Manager from a macOS machine, simply delete Wexflow application from Applications folder.

Linux

To uninstall Wexflow Manager from a Linux machine, proceed as follows:

sudo rm /usr/local/bin/wexflow
sudo rm -rf /opt/wexflow

Android

To uninstall Wexflow from an Android device, simply open Settings>Applications>Wexflow then uninstall it.

How to use Wexflow?

General

After installing Wexflow, the folders C:\Wexflow\ and C:\WexflowTesting\ are created. The folder C:\Wexflow\ contains the following elements:

  • Wexflow.xml which is the main configuration file of Wexflow engine. Its path can be configured from C:\Program Files\Wexflow\Wexflow.Clients.WindowsService.exe.config
  • Workflows/ which contains the workflows in XML format.
  • Trash/ which is the trash folder.
  • Temp/ which is the temporary foler of Wexflow.

The folder C:\WexflowTesting\ contains data of testing scenarios.

The logs are written in C:\Program Files\Wexflow\Wexflow.log. There is one log file per day. The old log files are saved in this format C:\Program Files\Wexflow\Wexflow.logyyyyMMdd.

Below the configuration file of a workflow:

<?xml version="1.0" encoding="utf-8" ?>
<!--
    This is the configuration file of a workflow. 
    A workflow is composed of:
      - An id which is an integer that must be unique.
      - A name which is a string that must be unique.
      - A description wich is a string.
      - A settings section which is composed of the following elements:
        - A launchType which is one of the following options:
          - startup: The workflow is launched when Wexflow engine starts.
          - trigger: The workflow is launched manually from the Wexflow manager.
          - periodic: The workflow is lauched periodically.
        - A period which is necessary for the periodic launchType. It is 
          a timeSpan in this format dd.hh:mm:ss. For example the period
          00.00:02:00 will launch the workflow every 2 minutes.
        - The enabled option which allows to enable or disable a workflow.
          The possible values are true or false.
      - A Tasks section which contains the tasks that will be executed by
        the workflow one after the other.
        - A Task is composed of:
          - An id which is an integer that must be unique.
          - A name wich is one of the options described in the tasks documentation.
          - A description which is a string.
          - The enable option which allows to enable or disable a task. The possible 
            values are true or false.
          - A collection of settings.
        - An ExecutionGraph section which contains the execution graph of the workflow.
          This section is optional and described in the samples section.
-->
<Workflow xmlns="urn:wexflow-schema" id="$int" name="$string" description="$string">
  <Settings>
    <Setting name="launchType" value="startup|trigger|periodic" />
    <Setting name="period" value="dd.hh:mm:ss" />
    <Setting name="enabled" value="true|false" />
  </Settings>
  <Tasks>
    <Task id="$int" name="$string" description="$string" enabled="true|false">
      <Setting name="$string" value="$string" />
      <Setting name="$string" value="$string" />
      <!-- You can add as many settings as you want. -->
    </Task>
    <Task id="$int" name="$string" description="$string" enabled="true|false">
      <Setting name="$string" value="$string" />
      <Setting name="$string" value="$string" />
    </Task>
    <!-- You can add as many tasks as you want. -->
  </Tasks>
  <!-- This node is optional and described in the samples section. -->
  <ExecutionGraph />
</Workflow>

The name option of a Task must be one of the followings.

The execution graph is explained in the samples section.

To learn how to make your own workflows, you can check out the workflow samples availabe in C:\Wexflow\Workflows\ and read the tasks documentation.

If a new workflow is created in C:\Wexflow\Workflows\ or if an existing workflow is deleted or modified, you don't have to restart Wexflow Windows Service so that these modifications take effect. Wexflow engine will automatically detect the changes and reload, add or delete the workflow.

To disable a workflow, you can set the enabled settings option of the workflow to false. If you want to make a workflow disappears from the list of the workflows loaded by Wexflow engine, you can create a directory named disabled within C:\Wexflow\Workflows\ and move that workflow to that directory.

How tasks communicate between each other?

State is transferred between tasks through selectFiles and through selectEntities settings.

This works the following way:

  1. A task in a workflow does its job and produces files which it stores in a collection.
  2. Another task (must be in the same workflow) can afterwards reference those files with the selectFiles XML property, specifying the ID of the task that produced the required files. It then can use these files to do its own job.

More visually (from the examples):

<Workflow xmlns="urn:wexflow-schema" id="1" name="Workflow_Invoices" description="Workflow_Invoices">
    <Settings>
        <Setting name="launchType" value="trigger" />
        <Setting name="enabled" value="true" />
    </Settings>
    <Tasks>
        <Task id="1" name="FilesLoader" description="Loading invoices" enabled="true">
            <Setting name="folder" value="C:\WexflowTesting\Invoices\" />
        </Task>
        <!-- some more tasks here -->
        <Task id="6" name="FilesMover" description="Moving invoices" enabled="true">
            <Setting name="selectFiles" value="1" />
            <Setting name="destFolder" value="C:\WexflowTesting\Invoices_sent\" />
        </Task>
    </Tasks>
</Workflow>

selectEntities setting works the same way as selectFiles. The only difference is that selectEntities is designed to be used for tasks that manipulate custom objects from a database or from web services for example. To go further, read this documentation regarding entities.

Wexflow Manager

Wexflow Manager is a simple application that allows the user to do the following things:

  • See all the workflows loaded by Wexflow Engine.
  • See the status of the selected workflow (running, suspended or disabled).
  • Start a workflow.
  • Stop a workflow.
  • Suspend a workflow.
  • Resume a workflow.
  • Open the designer.
  • View the logs.
  • Open the help page.
  • Check if a new version is available.

To see what's going on in Wexflow, open the log file C:\Program Files\Wexflow\Wexflow.log in a text editor like Notepad++. Notepad++ will update the log file as it fills up.

Wexflow Web Manager

Wexflow provides a lightweight JavaScript API (~6Kb) that allows Wexflow Manager to be hosted on any website.

Wexflow Web Manager allows the user to do the following things:

  • See all the workflows loaded by Wexflow Engine.
  • See the status of the selected workflow (running, suspended or disabled).
  • Start a workflow.
  • Stop a workflow.
  • Suspend a workflow.
  • Resume a workflow.

To host Wexflow Web Manager in a website, simply proceed as follows:

  1. Reference wexflow-manager.min.css and wexflow-manager.min.js:
    These files are located in C:\Program Files\Wexfow\Web Manager\
  2. Create an instance of WexflowManager.

The HTML source code should look like as follows:

<!DOCTYPE html>
<html>
<head>
    <title>Wexflow</title>
    <link rel="stylesheet" type="text/css" href="css/wexflow-manager.min.css" />
    <script type="text/javascript" src="js/wexflow-manager.min.js"></script>
</head>
<body>
    <div id="wf-manager" style="position: absolute; top:0; right:0; bottom:0; left:0;"></div>
    <script type="text/javascript">
        window.onload = function () {
            new WexflowManager("wf-manager", "http://localhost:8000/wexflow/");
        };
    </script>
</body>
</html>

Wexflow Web Designer

Wexflow provides a lightweight JavaScript API that allows Wexflow Designer to be hosted on any website.

Wexflow Web Designer allows the user to do the following things:

  • See all the workflows loaded by Wexflow Engine.
  • See the status of the selected workflow (running, suspended or disabled).
  • See the configuration of the selected workflow.
  • See all the tasks and their settings.
  • See the workflow in XML format.
  • Edit the configuration of the selected workflow.
  • Edit the configuration of the tasks.
  • Edit the settings of the tasks.
  • Add a task setting.
  • Remove a task setting.
  • Add an attribute to selectFiles and selectAttachments task settings.
  • Remove an attribute from selectFiles and selectAttachments task settings.
  • Add a task to a linear workflow.
  • Remove a task from a linear workflow.
  • Create a new linear workflow.
  • Delete a workflow (The workflow file is moved to the Trash folder which by default is C:\Wexflow\Trash).
  • View the execution graph of a linear workflow.
  • View the execution graph of a non linear workflow.

To add or edit a task, you need to read the documentation of the task.

Wexflow Designer does not provide the edition of the execution graph. To do so, you'll have to open the workflow file available in  C:\Wexflow\Workflows in a text editor and edit the execution graph section. To understand how the execution graph section works, you'll have to read the documentation regarding the execution graph.

To host Wexflow Web Designer in a website, simply proceed as follows:

  1. Reference wexflow-designer.min.css and wexflow-designer.min.js:
    These files are located in C:\Program Files (x86)\Wexflow\Web Designer\
  2. Create an instance of WexflowDesigner.

The HTML source code should look like as follows:

<!DOCTYPE html>
<html>
<head>
    <title>Wexflow Designer</title>
    <link rel="stylesheet" type="text/css" href="css/wexflow-designer.min.css" />
    <script type="text/javascript" src="js/wexflow-designer.min.js"></script>
</head>
<body>
<div id="wf-designer" style="position: absolute; top: 0; right: 0; bottom: 0; left: 0;"></div>
<script type="text/javascript">
  window.onload = function () {
    new WexflowDesigner("wf-designer", "http://localhost:8000/wexflow/");
  };
</script>
</body>
</html>

Wexflow Android Manager

Wexflow provides an Android application for managing workflows.

Wexflow Android Manager allows the user to do the following things:

  • See all the workflows loaded by Wexflow Engine.
  • See the status of the selected workflow (running, suspended or disabled).
  • Start a workflow.
  • Stop a workflow.
  • Suspend a workflow.
  • Resume a workflow.

Workflow samples

In this section, few workflow samples will be presented in order to make the end user familiar with Wexflow workflows synthax.

Sequential workflows

A sequential workflow executes a set of tasks in order, one by one. Tasks are executed in a sequential manner until the last task finishes. The order of the execution of the tasks can be altered by modifying the execution graph of the workflow.

Workflow 1

This workflow uploads invoices to an SFTP server, then waits for 2 days and then notifies the customers.

<Workflow xmlns="urn:wexflow-schema" id="1" name="Workflow_Invoices" description="Workflow_Invoices">
    <Settings>
        <Setting name="launchType" value="trigger" />
        <Setting name="enabled" value="true" />
    </Settings>
    <Tasks>
        <Task id="1" name="FilesLoader" description="Loading invioces" enabled="true">
            <Setting name="folder" value="C:\WexflowTesting\Invoices\" />
        </Task>
        <Task id="2" name="Ftp" description="Uploading invoices" enabled="true">
            <Setting name="protocol" value="sftp" /> <!-- ftp|ftps|sftp -->
            <Setting name="command" value="upload" /> <!-- list|upload|download|delete -->
            <Setting name="server" value="127.0.1" />
            <Setting name="port" value="21" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
            <Setting name="path" value="/" />
            <Setting name="selectFiles" value="1" />
        </Task>
        <Task id="3" name="Wait" description="Waiting for 2 days" enabled="true">
            <Setting name="duration" value="2.00:00:00" />
        </Task>
        <Task id="4" name="FilesLoader" description="Loading emails" enabled="true">
            <Setting name="file" value="C:\WexflowTesting\Emails\Invoices.xml" />
        </Task>
       <Task id="5" name="MailsSender" description="Notifying customers" enabled="true">
            <Setting name="selectFiles" value="4" />
            <Setting name="host" value="127.0.0.1" />
            <Setting name="port" value="587" />
            <Setting name="enableSsl" value="true" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
        </Task>
        <Task id="6" name="FilesMover" description="Moving invoices" enabled="true">
            <Setting name="selectFiles" value="1" />
            <Setting name="destFolder" value="C:\WexflowTesting\Invoices_sent\" />
        </Task>
    </Tasks>
</Workflow>

First of all, the FilesLoader task loads all the invoices located in the folder C:\WexflowTesting\Invoices\, then the Ftp task uploads them to the SFTP server, then the Wait task waits for 2 days, then the FilesLoader task loads the emails in XML format and then the MailsSender task sends the emails. Finally, the FilesMover task moves the invoices to the folder C:\WexflowTesting\Invoices_sent\.

Workflow 2

This workflow waits for files to arrive in C:\WexflowTesting\Watchfolder1\ and C:\WexflowTesting\Watchfolder2\ then uploads them to an FTP server then moves them to C:\WexflowTesting\Sent\ folder. This workflow starts every 2 minutes.

<Workflow xmlns="urn:wexflow-schema" id="2" name="Workflow_FilesSender" description="Workflow_FilesSender">
    <Settings>
        <Setting name="launchType" value="periodic" />
        <Setting name="period" value="00.00:02:00.00" />
        <Setting name="enabled" value="true" />
    </Settings>
    <Tasks>
        <Task id="1" name="FilesLoader" description="Loading files" enabled="true">
            <Setting name="folder" value="C:\WexflowTesting\Watchfolder1\" />
            <Setting name="folder" value="C:\WexflowTesting\Watchfolder2\" />
        </Task>
        <Task id="2" name="Ftp" description="Uploading files" enabled="true">
            <Setting name="protocol" value="ftp" /> <!-- ftp|ftps|sftp -->
            <Setting name="command" value="upload" /> <!-- list|upload|download|delete -->
            <Setting name="server" value="127.0.1" />
            <Setting name="port" value="21" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
            <Setting name="path" value="/" />
            <Setting name="selectFiles" value="1" />
        </Task>
        <Task id="3" name="FilesMover" description="Moving files to Sent folder" enabled="true">
            <Setting name="selectFiles" value="1" />
            <Setting name="destFolder" value="C:\WexflowTesting\Sent\" />
        </Task>
    </Tasks>
</Workflow>

First of all, the FilesLoader task loads all the files located in the folders C:\WexflowTesting\Watchfolder1\ and C:\WexflowTesting\Watchfolder2\ then the Ftp task loads the files and uploads them to the FTP server. Finally, the FilesMover task moves the files to the folder C:\WexflowTesting\Sent\.

Workflow 3

This workflow transcodes the WAV files located in C:\WexflowTesting\WAV\ to MP3 format through FFMPEG and moves the transcoded files to C:\WexflowTesting\MP3\.

<Workflow xmlns="urn:wexflow-schema" id="3" name="Workflow_ffmpeg" description="Workflow_ffmpeg">
    <Settings>
        <Setting name="launchType" value="trigger" />
        <Setting name="enabled" value="true" />
    </Settings>
    <Tasks>
        <Task id="1" name="FilesLoader" description="Loading WAV files" enabled="true">
            <Setting name="folder" value="C:\WexflowTesting\WAV\" />
        </Task>
        <Task id="2" name="ProcessLauncher" description="WAV to MP3" enabled="true">
            <Setting name="selectFiles" value="1" />
            <!-- You need to install FFMPEG -->
            <Setting name="processPath" value="C:\Program Files\ffmpeg\bin\ffmpeg.exe" />
            <!-- variables: {$filePath},{$fileName},{$fileNameWithoutExtension}-->
            <Setting name="processCmd" value="-i {$filePath} -codec:a libmp3lame -qscale:a 2 {$output:$fileNameWithoutExtension.mp3}" /> 
            <Setting name="hideGui" value="true" />
            <Setting name="generatesFiles" value="true" /> 
        </Task>
        <Task id="3" name="FilesMover" description="Moving MP3 files from temp folder" enabled="true">
            <Setting name="selectFiles" value="2" />
            <Setting name="destFolder" value="C:\WexflowTesting\MP3\" />
        </Task>
    </Tasks>
</Workflow>

First of all, the FilesLoader task loads all the files located in the folder C:\WexflowTesting\WAV\ then the ProcessLauncher task launches FFMPEG process on every file by specifying the right command in order to create the MP3 file. Finally, the FilesMover task moves the MP3 files to the folder C:\WexflowTesting\MP3\.

Workflow 4

This workflow waits for WAV files to arrive in C:\WexflowTesting\WAV\ then transcodes them to MP3 files through VLC then uploads the MP3 files to an FTP server then moves the WAV files to C:\WexflowTesting\WAV_processed\. This workflow starts every 2 minutes.

<Workflow xmlns="urn:wexflow-schema" id="4" name="Workflow_vlc" description="Workflow_vlc">
    <Settings>
        <Setting name="launchType" value="periodic" />
        <Setting name="period" value="00.00:02:00.00" />
        <Setting name="enabled" value="true" />
    </Settings>
    <Tasks>
        <Task id="1" name="FilesLoader" description="Loading WAV files" enabled="true">
            <Setting name="folder" value="C:\WexflowTesting\WAV\" />
        </Task>
        <Task id="2" name="ProcessLauncher" description="WAV to MP3" enabled="true">
            <Setting name="selectFiles" value="1" />
            <!-- You need to install VLC-->
            <Setting name="processPath" value="C:\Program Files\VideoLAN\VLC\vlc.exe" />
            <!-- variables: {$filePath},{$fileName},{$fileNameWithoutExtension}-->
            <Setting name="processCmd" value="-I dummy {$filePath} :sout=#transcode{acodec=mpga}:std{dst={$output:$fileNameWithoutExtension.mp3},access=file} vlc://quit" />
            <Setting name="hideGui" value="true" />
            <Setting name="generatesFiles" value="true" />
        </Task>
        <Task id="3" name="Ftp" description="Uploading MP3 files" enabled="true">
            <Setting name="protocol" value="ftp" />
            <Setting name="command" value="upload" />
            <Setting name="server" value="127.0.1" />
            <Setting name="port" value="21" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
            <Setting name="path" value="/" />
            <Setting name="selectFiles" value="2" />
        </Task>
        <Task id="4" name="FilesMover" description="Moving WAV files" enabled="true">
            <Setting name="selectFiles" value="1" />
            <Setting name="destFolder" value="C:\WexflowTesting\WAV_processed\" />
        </Task>
    </Tasks>
</Workflow>

First of all, the FilesLoader task loads all the files located in the folder C:\WexflowTesting\WAV\ then the ProcessLauncher task launches VLC process on every file by specifying the right command in order to create the MP3 file. Then, the Ftp task loads the MP3 files generated by the ProcessLauncher task and then uploads them to the FTP server. Finally, the FilesMover task moves the processed WAV files to the folder C:\WexflowTesting\WAV_processed\.

Workflow 5

This workflow downloads specific files from an FTP server. This workflow starts by listing all the files located at the root folder of the server, then the specific files that will be downloaded are tagged through an XSLT (LisFiles.xslt), then the files are downloaded by the Ftp task through todo="toDownload" and from="app4" tags, then the downloaded files are moved to the folder C:\WexflowTesting\Ftp_download\.

<Workflow xmlns="urn:wexflow-schema" id="5" name="Workflow_Ftp_download_tag" description="Workflow_Ftp_download_tag">
    <Settings>
        <Setting name="launchType" value="trigger" /> <!-- startup|trigger|periodic -->
        <Setting name="enabled" value="true" /> <!-- true|false -->
    </Settings>
    <Tasks>
        <Task id="1" name="Ftp" description="Listing files (FTP)" enabled="true">
            <Setting name="command" value="list" />
            <Setting name="protocol" value="ftp" /> <!-- ftp|ftps|sftp -->
            <Setting name="server" value="127.0.1" />
            <Setting name="port" value="21" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
            <Setting name="path" value="/" />
        </Task>
        <Task id="2" name="ListFiles" description="Listing files" enabled="true">
        </Task>
        <Task id="3" name="Xslt" description="Renaming and tagging files" enabled="true">
            <Setting name="selectFiles" value="2" />
            <Setting name="xsltPath" value="C:\Wexflow\Xslt\ListFiles.xslt" />
            <Setting name="version" value="2.0" /> <!-- 1.0|2.0 -->
            <Setting name="removeWexflowProcessingNodes" value="false" />
        </Task>
        <Task id="4" name="Ftp" description="Downloading files" enabled="true">
            <Setting name="command" value="download" />
            <Setting name="protocol" value="ftp" /> <!-- ftp|ftps|sftp -->
            <Setting name="server" value="127.0.1" />
            <Setting name="port" value="21" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
            <Setting name="path" value="/" />
            <Setting name="selectFiles" todo="toDownload" from="app4" />
        </Task>
        <Task id="5" name="FilesMover" description="Moving files to Ftp_download" enabled="true">
            <Setting name="selectFiles" value="4" />
            <Setting name="destFolder" value="C:\WexflowTesting\Ftp_download\" />
            <Setting name="overwrite" value="true" />
        </Task>
    </Tasks>
</Workflow>

Roughly speaking, the Ftp task loads the list of files located at the root folder of the FTP server in the running instance of the workflow, then the ListFiles task outputs and XML file that contains all the files loaded then the Xslt task takes as input this XML and generates an XML wich contains a system node called <WexflowProcessing> wich contains the list of files to be tagged and/or renamed.

To understand how tagging and renaming files work, refer to the documentation of the ListFiles and Xslt tasks.

Below is the XSLT ListFiles.xslt used for tagging files:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/">
    <root>
      <WexflowProcessing>
        <xsl:for-each select="//WexflowProcessing/Workflow/Files//File">
          <xsl:choose>
            <xsl:when test="@name = 'file1.txt'">
              <File taskId="{@taskId}" name="{@name}" renameTo="file1_renamed.txt" 
                    todo="toRename" 
                    from="app1" />
            </xsl:when>
            <xsl:when test="@name = 'file2.txt'">
              <File taskId="{@taskId}" name="{@name}" renameTo="file2_renamed.txt" 
                    todo="toSend" 
                    from="app2" />
            </xsl:when>
            <xsl:when test="@name = 'file3.txt'">
              <File taskId="{@taskId}" name="{@name}" renameTo="file3_renamed.txt" 
                    todo="toDownload" 
                    from="app3" />
            </xsl:when>
            <xsl:when test="@name = 'file4.txt'">
              <File taskId="{@taskId}" name="{@name}" renameTo="file4_renamed.txt"
                    todo="toDownload" 
                    from="app4" />
            </xsl:when>
          </xsl:choose>
        </xsl:for-each>
      </WexflowProcessing>
    </root>
  </xsl:template>
</xsl:stylesheet>

Execution graph

This workflow loads the file C:\WexflowTesting\file1.txt then uploads it to an FTP server then moves it to C:\WexflowTesting\Sent\ folder.

<Workflow xmlns="urn:wexflow-schema" id="6" name="Workflow_Ftp_upload" description="Workflow_Ftp_upload">
    <Settings>
        <Setting name="launchType" value="trigger" />
        <Setting name="enabled" value="true" />
    </Settings>
    <Tasks>
        <Task id="1" name="FilesLoader" description="Loading files" enabled="true">
            <Setting name="file" value="C:\WexflowTesting\file1.txt" />
        </Task>
        <Task id="2" name="Ftp" description="Uploading files" enabled="true">
            <Setting name="protocol" value="ftp" />
            <Setting name="command" value="upload" />
            <Setting name="server" value="127.0.1" />
            <Setting name="port" value="21" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
            <Setting name="path" value="/" />
            <Setting name="selectFiles" value="1" />
        </Task>
        <Task id="3" name="FilesMover" description="Moving files to Sent folder" enabled="true">
            <Setting name="selectFiles" value="1" />
            <Setting name="destFolder" value="C:\WexflowTesting\Sent\" />
        </Task>
    </Tasks>
    <ExecutionGraph>
      <Task id="1"><Parent id="-1" /></Task>
      <Task id="2"><Parent id="1"  /></Task>
      <Task id="3"><Parent id="2"  /></Task>
    </ExecutionGraph>
</Workflow>

First of all, the FilesLoader task loads the file C:\WexflowTesting\file1.txt then the Ftp task loads that file and uploads it to the FTP server. Finally, the FilesMover task moves that file to the folder C:\WexflowTesting\Sent\.

By convention, the parent task id of first task to be executed must always be -1. The execution graph of this workflow will execute the tasks in the following order:

However, if the execution graph is modified as follows:

<ExecutionGraph>
  <Task id="1"><Parent id="-1" /></Task>
  <Task id="3"><Parent id="1"  /></Task>
  <Task id="2"><Parent id="3"  /></Task>
</ExecutionGraph>

The tasks will be executed as follows:

If the execution graph is modified as follows:

<ExecutionGraph>
  <Task id="3"><Parent id="-1" /></Task>
  <Task id="2"><Parent id="3"  /></Task>
  <Task id="1"><Parent id="2"  /></Task>
</ExecutionGraph>

The tasks will be executed as follows:

Two things are forbidden in the execution graph:

  1. Infinite loops.
  2. Parallel tasks.

Here is an example of an infinite loop:

<ExecutionGraph>
  <Task id="1"><Parent id="-1" /></Task>
  <Task id="2"><Parent id="1"  /></Task>
  <Task id="1"><Parent id="2"  /></Task>
</ExecutionGraph>

Here is an example of parallel tasks:

<ExecutionGraph>
  <Task id="1"><Parent id="-1" /></Task>
  <Task id="2"><Parent id="1"  /></Task>
  <Task id="3"><Parent id="1"  /></Task>
</ExecutionGraph>

Flowchart workflows

A flowchart workflow is a workflow that contains at least one flowchart node (If/While/Switch) in its execution graph. A flow chart node takes as input a flowchart task (A task that returns either true or false after performing its job) and a set of tasks to execute in order, one by one. The order of the execution of the tasks can be altered by modifying the execution graph of the flowchart node.

If

The following workflow is a flowchart workflow that is triggered by the file file.trigger. If the file file.trigger is found on the file system then this workflow will upload the file file1.txt to an FTP server then it will notify customers that the upload was successful. Otherwise, if the trigger file.trigger is not found on the file system then the workflow will notify customers that the upload failed.

<Workflow xmlns="urn:wexflow-schema" id="7" name="Workflow_If" description="Workflow_If">
    <Settings>
        <Setting name="launchType" value="trigger" />
        <Setting name="enabled" value="true" />
    </Settings>
    <Tasks>
        <Task id="1" name="FilesLoader" description="Loading files" enabled="true">
            <Setting name="file" value="C:\WexflowTesting\file1.txt" />
        </Task>
        <Task id="2" name="Ftp" description="Uploading files" enabled="true">
            <Setting name="protocol" value="ftp" />
            <Setting name="command" value="upload" />
            <Setting name="server" value="127.0.1" />
            <Setting name="port" value="21" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
            <Setting name="path" value="/" />
            <Setting name="selectFiles" value="1" />
        </Task>
        <Task id="3" name="FilesLoader" description="Loading emails (OK)" enabled="true">
            <Setting name="file" value="C:\WexflowTesting\Emails\Emails.xml" />
        </Task>
       <Task id="4" name="MailsSender" description="Notifying customers (OK)" enabled="true">
            <Setting name="selectFiles" value="3" />
            <Setting name="host" value="127.0.0.1" />
            <Setting name="port" value="587" />
            <Setting name="enableSsl" value="true" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
        </Task>
        <Task id="5" name="FilesLoader" description="Loading emails (KO)" enabled="true">
            <Setting name="file" value="C:\WexflowTesting\Emails\Emails.xml" />
        </Task>
       <Task id="6" name="MailsSender" description="Notifying customers (KO)" enabled="true">
            <Setting name="selectFiles" value="5" />
            <Setting name="host" value="127.0.0.1" />
            <Setting name="port" value="587" />
            <Setting name="enableSsl" value="true" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
        </Task>
        <Task id="99" name="FileExists" description="Checking trigger file" enabled="true">
            <Setting name="file" value="C:\WexflowTesting\file.trigger" />
        </Task>
    </Tasks>
    <ExecutionGraph>
      <If id="100" parent="-1" if="99">
         <Do>
            <Task id="1"><Parent id="-1" /></Task>
            <Task id="2"><Parent id="1"  /></Task>
            <Task id="3"><Parent id="2"  /></Task>
            <Task id="4"><Parent id="3"  /></Task>
         </Do>
         <Else>
            <Task id="5"><Parent id="-1" /></Task>
            <Task id="6"><Parent id="5"  /></Task>
         </Else>
      </If>
    </ExecutionGraph>
</Workflow>

By convention, the parent task id of the first task to execute in <Do> and <Else> nodes must always be -1.

You can add If flowchart nodes pretty much wherever you want in the execution graph. Also, you can add as mush as you want. You can also add them in the event nodes OnSuccess, OnWarning and OnError.

An If can be inside an If, a While and a Switch.

While

This workflow is triggered by the file file.trigger. While the file file.trigger exists, this workflow will upload the file file1.txt to an FTP server then it will notify customers then it will wait for 2 days then it will start again.

<Workflow xmlns="urn:wexflow-schema" id="8" name="Workflow_While" description="Workflow_While">
    <Settings>
        <Setting name="launchType" value="trigger" />
        <Setting name="enabled" value="true" />
    </Settings>
    <Tasks>
        <Task id="1" name="FilesLoader" description="Loading files" enabled="true">
            <Setting name="file" value="C:\WexflowTesting\file1.txt" />
        </Task>
        <Task id="2" name="Ftp" description="Uploading files" enabled="true">
            <Setting name="protocol" value="ftp" />
            <Setting name="command" value="upload" />
            <Setting name="server" value="127.0.1" />
            <Setting name="port" value="21" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
            <Setting name="path" value="/" />
            <Setting name="selectFiles" value="1" />
        </Task>
        <Task id="3" name="FilesLoader" description="Loading emails" enabled="true">
            <Setting name="file" value="C:\WexflowTesting\Emails\Emails.xml" />
        </Task>
       <Task id="4" name="MailsSender" description="Notifying customers" enabled="true">
            <Setting name="selectFiles" value="3" />
            <Setting name="host" value="127.0.0.1" />
            <Setting name="port" value="587" />
            <Setting name="enableSsl" value="true" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
        </Task>
        <Task id="5" name="Wait" description="Waiting for 2 days..." enabled="true">
            <Setting name="duration" value="02.00:00:00" />
        </Task>
        <Task id="99" name="FileExists" description="Checking trigger file" enabled="true">
            <Setting name="file" value="C:\WexflowTesting\file.trigger" />
        </Task>
    </Tasks>
    <ExecutionGraph>
      <While id="100" parent="-1" while="99">
        <Task id="1"><Parent id="-1" /></Task>
        <Task id="2"><Parent id="1"  /></Task>
        <Task id="3"><Parent id="2"  /></Task>
        <Task id="4"><Parent id="3"  /></Task>
        <Task id="5"><Parent id="4"  /></Task>
      </While>
    </ExecutionGraph>
</Workflow>

By convention, the parent task id of the first task to be executed in the <While> node must always be -1.

You can add While flowchart nodes pretty much wherever you want in the execution graph. Also, you can add as mush as you want. You can also add them in the event nodes OnSuccess, OnWarning and OnError.

A While can be inside a While, an If and a Switch.

Switch

This workflow starts every 24 hours. On Monday, it uploads files to an FTP server and on Wednesday it notifies customers.

<Workflow xmlns="urn:wexflow-schema" id="43" name="Workflow_Switch" description="Workflow_Switch">
  <Settings>
    <Setting name="launchType" value="periodic" />
	<Setting name="period" value="1.00:00:00" />
    <Setting name="enabled" value="true" />
  </Settings>
  <Tasks>
    <Task id="1" name="Now" description="Getting current day" enabled="true">
      <Setting name="culture" value="en-US" />  
      <Setting name="format" value="dddd" />
    </Task>
	<Task id="2" name="FilesLoader" description="Loading files" enabled="true">
      <Setting name="file" value="C:\WexflowTesting\file1.txt" />
    </Task>
    <Task id="3" name="Ftp" description="Uploading files" enabled="true">
      <Setting name="protocol" value="ftp" />
      <Setting name="command" value="upload" />
      <Setting name="server" value="127.0.1" />
      <Setting name="port" value="21" />
      <Setting name="user" value="user" />
      <Setting name="password" value="password" />
      <Setting name="path" value="/" />
      <Setting name="selectFiles" value="1" />
    </Task>
   <Task id="4" name="FilesLoader" description="Loading emails" enabled="true">
      <Setting name="file" value="C:\WexflowTesting\Emails\Emails.xml" />
   </Task>
   <Task id="5" name="MailsSender" description="Notifying customers" enabled="true">
        <Setting name="selectFiles" value="3" />
        <Setting name="host" value="127.0.0.1" />
        <Setting name="port" value="587" />
        <Setting name="enableSsl" value="true" />
        <Setting name="user" value="user" />
        <Setting name="password" value="password" />
    </Task>
  </Tasks>
  <ExecutionGraph>
    <Switch id="100" parent="-1" switch="1">
      <Case value="Monday">
        <Task id="2"><Parent id="-1" /></Task>
		<Task id="3"><Parent id="2" /></Task>
      </Case>
      <Case value="Wednesday">
        <Task id="4"><Parent id="-1" /></Task>
        <Task id="5"><Parent id="4" /></Task>
      </Case>
      <Default />
    </Switch>
  </ExecutionGraph>
</Workflow>

By convention, the parent task id of the first task to be executed in the Case/Default nodes must always be -1.

You can add Switch flowchart nodes pretty much wherever you want in the execution graph. Also, you can add as mush as you want. You can also add them in the event nodes OnSuccess, OnWarning and OnError.

A Switch can be inside a While, an If and a Switch.

Workflow events

After a workflow finishes its job, its final result is either success, or warning or error. If its final result is success, the OnSuccess event is triggered. If its final result is warning, the OnWarning event is triggered. If its final result is error, the OnError event is triggered. An event contains a set of tasks and/or flowchart nodes to execute in order, one by one. The order of the execution of the tasks and/or flowchart nodes can be altered by modifying the execution graph of the event.

This workflow uploads the file1.txt to an FTP server then notifies customers in case of success.

<Workflow xmlns="urn:wexflow-schema" id="9" name="Workflow_Events" description="Workflow_Events">
    <Settings>
        <Setting name="launchType" value="trigger" />
        <Setting name="enabled" value="true" />
    </Settings>
    <Tasks>
        <Task id="1" name="FilesLoader" description="Loading files" enabled="true">
            <Setting name="file" value="C:\WexflowTesting\file1.txt" />
        </Task>
        <Task id="2" name="Ftp" description="Uploading files" enabled="true">
            <Setting name="protocol" value="ftp" />
            <Setting name="command" value="upload" />
            <Setting name="server" value="127.0.1" />
            <Setting name="port" value="21" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
            <Setting name="path" value="/" />
            <Setting name="selectFiles" value="1" />
        </Task>
       <Task id="3" name="FilesLoader" description="Loading emails" enabled="true">
            <Setting name="file" value="C:\WexflowTesting\Emails\Emails.xml" />
        </Task>
       <Task id="4" name="MailsSender" description="Notifying customers" enabled="true">
            <Setting name="selectFiles" value="3" />
            <Setting name="host" value="127.0.0.1" />
            <Setting name="port" value="587" />
            <Setting name="enableSsl" value="true" />
            <Setting name="user" value="user" />
            <Setting name="password" value="password" />
        </Task>
    </Tasks>
    <ExecutionGraph>
      <Task id="1"><Parent id="-1" /></Task>
      <Task id="2"><Parent id="1"  /></Task>
      <OnSuccess>
        <Task id="3"><Parent id="-1" /></Task>
        <Task id="4"><Parent id="3"  /></Task>
      </OnSuccess>
    </ExecutionGraph>
</Workflow>

The flowchart event nodes <OnWarning> and <OnError> can be used in the same way. You can put If, While and Switch flowchart nodes in event nodes.

These are simple and basic workflows to give an idea on how to make your own workflows. However, if you have multiple systems, applications and automations involved in a workflow, the workflow could be very interesting.

How to create a custom task?

General

Custom tasks are a must in a workflow engine and allows systems and applications to interact.

To create a custom task MyTask for example you will need to proceed as follows:

  1. Create a class library project in Visual Studio and name it Wexflow.Tasks.MyTask.
  2. Reference the assemblies Wexflow.Core.dll and log4net.dll.These assemblies are located in the installation folder of Wexflow C:\Program Files\Wexflow\.

You can also reference these assemblies through nuget package manager:

PM> Install-Package Wexflow -Version 2.3.0

     3. Create a public class MyTask that implements the abstract class Wexflow.Core.Task.

Wexflow.Tasks.MyTask code should look like as follows:

namespace Wexflow.Tasks.MyTask
{
    public class MyTask : Task
    {
        public MyTask(XElement xe, Workflow wf) : base(xe, wf)
        {
            // Task settings goes here
        }

        public override TaskStatus Run()
        {
            try
            {
                // Task logic goes here

                return new TaskStatus(Status.Success);
            }
            catch (ThreadAbortException)
            {
                throw;
            }
        }
    }
}

Each task returns a TaskStatus object when it finishes performing its job. TaskStatus is composed of the following elements:

public Status Status { get; set; }
public bool Condition { get; set; }
public string SwitchValue { get; set; }

The Status can be one of the followings:

public enum Status
{
  Success,
  Warning,
  Error
}

For example, if a task performs an opetation on a collection of files and if this operation succeeds for all the files then its Status should be Success. Otherwise if this operation succeeds for some files and fails for others then its Status should be Warning. Otherwise if this operation fails for all the files then its Status should be Error.

The Condition property is designed for flowchart tasks. In addition to the Status of the task, a flowchart task returns either true or false after performing its operation.

The Condition property should always be set to false for sequential tasks.

The SwitchValue is designed to be used by Switch flowchart nodes. If you set a value in the SwitchValue property and use this task in a Switch flowchart node, the case corresponding to the value will be executed. Otherwise, if the Default case is set, it will be executed.

You can use the TaskStatus constructor that suits your needs.

To retrieve settings, you can use the following methods:

string settingValue = this.GetSetting("settingName");
string settingValue = this.GetSetting("settingName", defaultValue);
string[] settingValues = this.GetSettings("settingName");

To select the files loaded by the running instance of a workflow through the selectFiles settings option, you can do it as follows:

FileInf[] files = this.SelectFiles();

To select entities loaded by the running instance of a workflow through the selectEntities settings option, you can do it as follows:

Entity[] entities = this.SelectEntities();

The Entity class could be very useful when working with custom tasks that manipulate objects from a database or Web Services for example.

To load a file within a task, you can do it as follows:

this.Files.Add(new FileInf(path, this.Id));

To load an entity within a task, you can do it as follows:

this.Entities.Add(myEntity);

Finally if you finished coding your custom task, compile the class library project and copy the assembly Wexflow.Tasks.MyTask.dll in C:\Program Files\Wexflow\. Your custom task is then ready to be used as follows:

<Task id="$int" name="MyTask" description="My task description" enabled="true">
    <Setting name="settingName" value="settingValue" />
</Task>

That's it. That's all the things you need to know to start coding your own custom tasks.

To test the custom task, create a new workflow (new XML file) and put the configuration of the custom task in it as follows:

<Workflow xmlns="urn:wexflow-schema" id="99" name="Workflow_MyWorkflow" description="Workflow_MyWorkflow">
	<Settings>
		<Setting name="launchType" value="trigger" /> <!-- startup|trigger|periodic -->
		<Setting name="enabled" value="true" /> <!-- true|false -->
	</Settings>
	<Tasks>
        <Task id="1" name="MyTask" description="My task description" enabled="true">
            <Setting name="settingName" value="settingValue" />
        </Task>
	</Tasks>
</Workflow>

Then, place that XML file in C:\Wexflow\Workflows\.

The workflow will then appear in the list of workflows in Wexflow Manager or Wexflow Web Manager. You can then launch it from there.

Logging

The following methods are available from the Task class for logging:

public void Info(string msg);
public void InfoFormat(string msg, params object[] args);
public void Debug(string msg);
public void DebugFormat(string msg, params object[] args);
public void Error(string msg);
public void ErrorFormat(string msg, params object[] args);
public void Error(string msg, Exception e);
public void ErrorFormat(string msg, Exception e, params object[] args);

Files

Files can be loaded in a task by calling the methods Add or AddRange:

this.Files.Add(myFile);
this.Files.AddRange(myFiles);

Then the files loaded can be selected in other tasks by their task Id as follows:

<Setting name="selectFiles" value="$taskId" />

To select the files loaded by the running instance of a workflow through the selectFiles settings option, you can do it as follows:

FileInf[] files = this.SelectFiles();

Entities

Entity is an abstract class having the Id of the task as property:

namespace Wexflow.Core
{
    public abstract class Entity
    {
        public int TaskId { get; set; }
    }
}

The entity class is designed to be inherited by other classes such as objects retrieved from a database or a web service or an API or whatever. Then, these objects can be loaded in a task by calling the methods Add or AddRange:

this.Entities.Add(myEntity);
this.Entities.AddRange(myEntities);

Then, the entities loaded can be selected in other tasks by their task Id as follows:

<Setting name="selectEntities" value="$taskId" />

Entities are designed to be used in custom tasks.

To select entities loaded by the running instance of a workflow through the selectEntities settings option, you can do it as follows:

Entity[] entities = this.SelectEntities();

The Entity class could be very useful when working with custom tasks that manipulate objects from a database or Web Services for example.

Hashtable

Tasks contains a Hashtable that can be used as a shared memory between them.

To add an object to the Hashtable, simply proceed as follows:

this.Hashtable.Add("myKey", myObject);

To retrieve an object from the Hashtable, simply proceed as follows:

var myObject = this.Hashtable["myKey"];

To remove an object from the Hashtable, simply proceed as follows:

this.Hashtable.Remove("myKey");

Designer

To make your custom task MyTask appear in the dropdown of the designer, simply open the file C:\Wexflow\TasksNames.json and add "MyTask" in it as follows:

[
...
"MyTask"
]

You must also add the settings by opening the file C:\Wexflow\TasksSettings.json and adding your custom settings as follows:

{
...
"MyTask": ["settingName"]
}

That's it. MyTask will show up in the designer dropdows and when selected its settings will show up too.

Debugging

To debug custom tasks, you can use logging.

You can also clone this repository and open Wexflow.vs2017.sln or Wexflow.vs2010.sln in Visual Studio and follow these guidelines to debug Wexflow Windows Service. Then, you can create your custom task in the solution and debug it. Of course, to debug it you have to proceed as follows:

  1. Create your custom task.
  2. Reference your custom task in Wexflow.Clients.WindowsService.
  3. Create a workflow using your custom task (see the documentation).
  4. Place your workflow in C:\Wexflow\Workflows.
  5. Open Wexflow Manager or Wexflow Web Manager and trigger your workflow from there.

How to debug Wexflow?

To debug Wexflow, proceed as follows:

  • Install Microsoft .NET Framework 4.0 or higher.
  • Install Microsoft Sync Framework 2.1 SDK. You can download it from here.
  • Install Visual Studio 2010 or higher.
  • Copy the folders "Wexflow" and "WexflowTesting" in C:\. You can download them from here.

Using the code

In this section, the source code of the version 1.0.1 will be explained in details. The source code has changed since this release but this section will give you a clear idea on how Wexflow works.

Wexflow source code is very simple to understand. The source code projects are organized as follows:

The Wexflow.Clients solution folder contains Wexflow WIndows Service and Wexflow Manager. The Wexflow.Core solution folder contains the core assembly of Wexflow Wexflow.Core, the WCF service contracts in Wexflow.Core.Service.Contracts and the WCF proxy in Wexflow.Core.Service.Client. The Wexflow.Tasks contains Wexflow tasks.

Wexflow Windows Service

Wexflow uses a Windows Service that hosts a WCF Web service. When Wexflow Windows Service starts a new instance of WexflowEngine is created then runned through the method Run(). The WCF Web service allows to do the following things:

  • Get the list of workflows loaded by WexflowEngine.
  • Get a WorkflowInfo object from a workflow id.
  • Start a workflow from a workflow id.
  • Stop a workflow from a workflow id.
  • Suspend a workflow from a workflow id.
  • Resume a workflow from a workflow id.

Below the source code of Wexflow Windows Service.

public partial class WexflowWindowsService : ServiceBase
{
    public static string SETTINGS_FILE = ConfigurationManager.AppSettings["WexflowSettingsFile"];
    public static WexflowEngine WEXFLOW_ENGINE = new WexflowEngine(SETTINGS_FILE);

    private ServiceHost _serviceHost = null;
    
    public WexflowWindowsService()
    {
        InitializeComponent();
        this.ServiceName = "Wexflow";
        WEXFLOW_ENGINE.Run();
    }

    public void OnDebug()
    {
        this.OnStart(null);
    }

    protected override void OnStart(string[] args)
    {
        if (this._serviceHost != null)
        {
            this._serviceHost.Close();
        }

        // Create a ServiceHost for the WexflowService type and 
        // provide the base address.
        this._serviceHost = new ServiceHost(typeof(WexflowService));
            
        // Open the ServiceHostBase to create listeners and start 
        // listening for messages.
        this._serviceHost.Open();
    }

    protected override void OnStop()
    {
        if (this._serviceHost != null)
        {
            this._serviceHost.Close();
            this._serviceHost = null;
        }
    }
}

WexflowSettingsFile defaults to C:\Wexflow\Wexflow.xml.

ServiceHost is used to host WexflowService WCF Web service in Wexflow Windows service.

Below the source code of WexflowService.

[ServiceContract(Namespace = "http://WexflowService/")]
public interface IWexflowService
{
    [OperationContract]
    string Hello();

    [OperationContract]
    WorkflowInfo[] GetWorkflows();

    [OperationContract]
    void StartWorkflow(int workflowId);

    [OperationContract]
    void StopWorkflow(int workflowId);

    [OperationContract]
    void SuspendWorkflow(int workflowId);

    [OperationContract]
    void ResumeWorkflow(int workflowId);

    [OperationContract]
    WorkflowInfo GetWorkflow(int workflowId);
}

[ServiceBehavior(IncludeExceptionDetailInFaults=true)]
public class WexflowService:IWexflowService
{
    public string Hello()
    {
        return "Hello!";
    }

    public WorkflowInfo[] GetWorkflows()
    {
        List<WorkflowInfo> wfis = new List<WorkflowInfo>();
        foreach (Workflow wf in WexflowWindowsService.WEXFLOW_ENGINE.Workflows)
        {
            wfis.Add(new WorkflowInfo(wf.Id, wf.Name, wf.LaunchType, wf.IsEnabled, wf.Description, wf.IsRunning, wf.IsPaused));
        }
        return wfis.ToArray();
    }

    public void StartWorkflow(int workflowId)
    {
        WexflowWindowsService.WEXFLOW_ENGINE.StartWorkflow(workflowId);
    }

    public void StopWorkflow(int workflowId)
    {
        WexflowWindowsService.WEXFLOW_ENGINE.StopWorkflow(workflowId);
    }

    public void SuspendWorkflow(int workflowId)
    {
        WexflowWindowsService.WEXFLOW_ENGINE.PauseWorkflow(workflowId);
    }

    public void ResumeWorkflow(int workflowId)
    {
        WexflowWindowsService.WEXFLOW_ENGINE.ResumeWorkflow(workflowId);
    }

    public WorkflowInfo GetWorkflow(int workflowId)
    {
        Workflow wf = WexflowWindowsService.WEXFLOW_ENGINE.GetWorkflow(workflowId);
        return new WorkflowInfo(wf.Id, wf.Name, wf.LaunchType, wf.IsEnabled, wf.Description, wf.IsRunning, wf.IsPaused);
    }
}

Below the system.serviceModel configuration of Wexflow Windows Service.

<system.serviceModel>
    <services>
      <service behaviorConfiguration="WexflowServiceBehavior" name="Wexflow.Clients.WindowsService.WexflowService">
        <endpoint address="" binding="wsHttpBinding" contract="Wexflow.Clients.WindowsService.IWexflowService" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000/WexflowService/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="WexflowServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="False"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

SvcUtil.exe was used to generate the WCF proxy as follows:

SvcUtil.exe http://localhost:8000/WexflowService/ /out:WexflowServiceClient.cs /config:App.config

To debug Wexflow Windows service, the following trick has been used:

namespace Wexflow.Clients.WindowsService
{
    static class Program
    {
        static void Main(string[] args)
        {
            if (args.Length > 0 && args[0].Equals("debug"))
            {
                WexflowWindowsService service = new WexflowWindowsService();
                service.OnDebug();
                Thread.Sleep(Timeout.Infinite);
            }
            else
            {
                ServiceBase[] servicesToRun = new ServiceBase[] { new WexflowWindowsService() };
                ServiceBase.Run(servicesToRun);
            }
        }
    }
}

Wexflow Engine

The class diagram below describes the general architecture of Wexflow Engine.

WexflowEngine class is composed of a collection of workflows and other properties. A workflow is composed of a collection of tasks and other properties. Task is an abstract class inherited by Wexflow tasks.

Roughly speaking, WexflowEngine class parses all the XML files of the workflows into Workflow objects through LINQ To XML. Then, when the method Run() of this class is called, the workflows having startup as launchType are launched automatically, then the workflows having periodic as launchType are launched through a timer.

When a new instance of WexflowEngine is created the following things happen:

public WexflowEngine(string settingsFile) 
{
    this.SettingsFile = settingsFile;
    LoadSettings();
    LoadWorkflows();
}

The workflow engine settings are loaded then the workflows are loaded from their XML files.

The settings are loaded as follows:

private void LoadSettings()
{
    XDocument xdoc = XDocument.Load(this.SettingsFile);
    this.WorkflowsFolder = GetWexflowSetting(xdoc, "workflowsFolder");
    this.TempFolder = GetWexflowSetting(xdoc, "tempFolder");
}

private string GetWexflowSetting(XDocument xdoc, string name)
{
    return xdoc.XPathSelectElement(string.Format("/Wexflow/Setting[@name='{0}']", name)).Attribute("value").Value;
}

The workflows are loaded as follows:

private void LoadWorkflows()
{ 
    List<Workflow> workflows = new List<Workflow>();
    foreach (string file in Directory.GetFiles(this.WorkflowsFolder))
    {
        try
        {
            Workflow workflow = new Workflow(file, this.TempFolder);
            workflows.Add(workflow);
            Logger.InfoFormat("Workflow loaded: {0}", workflow);
        }
        catch (Exception e)
        {
            Logger.ErrorFormat("An error occured while loading the workflow : {0} Please check the workflow configuration.", file);
        }
    }
    this.Workflows = workflows.ToArray();
}

When a new instance of a workflow is created the following things happen:

public Workflow(string path, string wexflowTempFolder)
{
    this.JobId = 1;
    this._thread = null;
    this.WorkflowFilePath = path;
    this.WexflowTempFolder = wexflowTempFolder;
    this.FilesPerTask = new Dictionary<int, List<FileInf>>();
    this.EntitiesPerTask = new Dictionary<int, List<Entity>>();
    Load();
}

private void Load()
{
    XDocument xdoc = XDocument.Load(this.WorkflowFilePath);
    this.Id = int.Parse(GetWorkflowAttribute(xdoc, "id"));
    this.Name = GetWorkflowAttribute(xdoc, "name");
    this.Description = GetWorkflowAttribute(xdoc, "description");
    this.LaunchType = (LaunchType)Enum.Parse(typeof(LaunchType), GetWorkflowSetting(xdoc, "launchType"), true);
    if(this.LaunchType == Core.LaunchType.Periodic) this.Period = TimeSpan.Parse(GetWorkflowSetting(xdoc, "period"));
    this.IsEnabled = bool.Parse(GetWorkflowSetting(xdoc, "enabled"));

    List<Task> tasks = new List<Task>();
    foreach (XElement xTask in xdoc.XPathSelectElements("/Workflow/Tasks/Task"))
    {
        string name = xTask.Attribute("name").Value;
        string assemblyName = "Wexflow.Tasks." + name;
        string typeName = "Wexflow.Tasks." + name + "." + name + ", " + assemblyName;
        Task task = (Task)Activator.CreateInstance(Type.GetType(typeName), xTask, this);
        tasks.Add(task);
    }
    this.Taks = tasks.ToArray();
}

private string GetWorkflowAttribute(XDocument xdoc, string attr)
{
    return xdoc.XPathSelectElement("/Workflow").Attribute(attr).Value;
}

private string GetWorkflowSetting(XDocument xdoc, string name)
{
    return xdoc.XPathSelectElement(string.Format("/Workflow[@id='{0}']/Settings/Setting[@name='{1}']", this.Id, name)).Attribute("value").Value;
}

First of all, the workflow settings are retrieved then the tasks are built by reflection.

When Wexflow engine starts through the method Run() the following things happen:

public void Run()
{
    foreach (Workflow workflow in this.Workflows)
    {
        if (workflow.IsEnabled)
        {
            if (workflow.LaunchType == LaunchType.Startup)
            {
                workflow.Start();
            }
            else if (workflow.LaunchType == LaunchType.Periodic)
            {
                Action<object> callback = o =>
                {
                    Workflow wf = (Workflow)o;
                    if (!wf.IsRunning) wf.Start();
                };
                
                WexflowTimer timer = new WexflowTimer(new TimerCallback(callback), workflow, workflow.Period);
                timer.Start();
            }
        }
    }
}

For each workflow, if the workflow is enabled, Wexflow will launch it if its launchType is startup otherwise if its launchType is periodic Wexflow will create a WexflowTimer that will launch the workflow every period.

The WexflowTimer class is very simple and looks like as follows:

public class WexflowTimer
{
    public TimerCallback TimerCallback { get; private set; }
    public object State { get; private set; }
    public TimeSpan Period { get; private set; }

    public WexflowTimer(TimerCallback timerCallback, object state, TimeSpan period)
    {
        this.TimerCallback = timerCallback;
        this.State = state;
        this.Period = period;
    }

    public void Start()
    {
        Thread thread = new Thread(new ThreadStart(() =>
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            for(;;)
            {
                if (stopwatch.ElapsedMilliseconds >= this.Period.TotalMilliseconds)
                {
                    stopwatch.Reset();
                    stopwatch.Start();
                    this.TimerCallback.Invoke(this.State);
                }
                Thread.Sleep(100);
            }
        }));

        thread.Start();
    }
}

I've used a custom timer because I've faced a lot of issues whith System.Threading.Timer while testing complex periodic workflows.

When a workflow is started the following things happen:

public void Start()
{
    Thread thread = new Thread(new ThreadStart(() =>
        {
            try
            {
                this.IsRunning = true;
                Logger.InfoFormat("{0} Workflow started.", this.LogTag);

                // Create the temp folder
                CreateTempFolder();

                // Run the tasks
                foreach (Task task in this.Taks)
                {
                    if (task.IsEnabled)
                    {
                        task.Run();
                    }
                }
            }
            catch (ThreadAbortException)
            {
            }
            catch (Exception e)
            {
                Logger.ErrorFormat("An error occured while running the workflow : {0}", e, this);
            }
            finally
            {
                // Cleanup
                foreach (List<FileInf> files in this.FilesPerTask.Values) files.Clear();
                foreach (List<Entity> entities in this.EntitiesPerTask.Values) entities.Clear();
                this._thread = null;
                this.IsRunning = false;
                GC.Collect();

                Logger.InfoFormat("{0} Workflow finished.", this.LogTag);
                this.JobId++;
            }
        }));

    this._thread = thread;
    thread.Start();
}

private void CreateTempFolder()
{ 
    // WorkflowId/dd-MM-yyyy/HH-mm-ss-fff
    string wfTempFolder = Path.Combine(this.WexflowTempFolder, this.Id.ToString());
    if (!Directory.Exists(wfTempFolder)) Directory.CreateDirectory(wfTempFolder);
    
    string wfDayTempFolder = Path.Combine(wfTempFolder, string.Format("{0:yyyy-MM-dd}", DateTime.Now));
    if (!Directory.Exists(wfDayTempFolder)) Directory.CreateDirectory(wfDayTempFolder);

    
    string wfJobTempFolder = Path.Combine(wfDayTempFolder, string.Format("{0:HH-mm-ss-fff}", DateTime.Now));
    if (!Directory.Exists(wfJobTempFolder)) Directory.CreateDirectory(wfJobTempFolder);

    this.WorkflowTempFolder = wfJobTempFolder;
}

A new thread is created and launched. In this thread, a temporary folder is created then the tasks are launched one by one. At the end of the workflow the files and entities loaded by the tasks are cleared.

Below the FileInf class:

public class FileInf
{
    public string Path { get; private set; }
    public string FileName { get; private set; }
    public int TaskId { get; private set; }

    public FileInf(string path, int taskId)
    {
        this.Path = path;
        this.FileName = System.IO.Path.GetFileName(this.Path);
        this.TaskId = taskId;
    }
}

Below the Entity class:

public abstract class Entity
{
    public int TaskId { get; private set; }
}

The workflows having trigger as launchType are launched from the Wexflow Manager by calling Workflow.Start() method.

A workflow can be stopped, suspended and resumed from the Wexflow Manager.

Finally, each task implements the abstract class Wexflow.Core.Task:

public abstract class Task
{
    public int Id { get; private set; }
    public string Name { get; private set; }
    public string Description { get; private set; }
    public bool IsEnabled { get; private set; }
    public Workflow Workflow { get; private set; }
    public List<FileInf> Files 
    { 
        get
        {
            return this.Workflow.FilesPerTask[this.Id];
        }
    }
    public List<Entity> Entities
    {
        get
        {
            return this.Workflow.EntitiesPerTask[this.Id];
        }
    }

    private XElement _xElement;

    public Task(XElement xe, Workflow wf) 
    {
        this._xElement = xe;
        this.Id = int.Parse(xe.Attribute("id").Value);
        this.Name = xe.Attribute("name").Value;
        this.Description = xe.Attribute("description").Value;
        this.IsEnabled = bool.Parse(xe.Attribute("enabled").Value);
        this.Workflow = wf;
        this.Workflow.FilesPerTask.Add(this.Id, new List<FileInf>());
        this.Workflow.EntitiesPerTask.Add(this.Id, new List<Entity>());
    }

    public abstract void Run();

    public string GetSetting(string name)
    {
        return this._xElement.XPathSelectElement(string.Format("Setting[@name='{0}']", name)).Attribute("value").Value;
    }

    public string GetSetting(string name, string defaultValue)
    {
        XElement xe = this._xElement.XPathSelectElement(string.Format("Setting[@name='{0}']", name));
        if (xe == null) return defaultValue;
        return xe.Attribute("value").Value;
    }

    public string[] GetSettings(string name)
    {
        List<string> settings = new List<string>();
        foreach (XElement xe in this._xElement.XPathSelectElements(string.Format("Setting[@name='{0}']", name)))
        {
            settings.Add(xe.Attribute("value").Value);
        }
        return settings.ToArray();
    }

    public FileInf[] SelectFiles() 
    {
        List<FileInf> files = new List<FileInf>();
        foreach (string id in this.GetSettings("selectFiles"))
        {
            int taskId = int.Parse(id);
            files.AddRange(this.Workflow.FilesPerTask[taskId]);
        }
        return files.ToArray();
    }

    public Entity[] SelectEntities()
    {
        List<Entity> entities = new List<Entity>();
        foreach (string id in this.GetSettings("selectEntities"))
        {
            int taskId = int.Parse(id);
            entities.AddRange(this.Workflow.EntitiesPerTask[taskId]);
        }
        return entities.ToArray();
    }

   // ...
}

Each task implements a custom logic in the Run() method. Each task can load a collection of files through the Files property and a collection of entities through the Entities property. The source code of each task will not be detailed in this article but you can have a look at the source code of each task so you can see how to build custom tasks.

That's it. I hope you enjoyed reading this article. If you have any thoughts to improve Wexflow or if you face any issues or if you want to contribute to this project please let me know in the comments.

Libraries used by Wexflow

Here is the list of the libraries used by Wexflow:

  • FluentFTP: An FTP client supporting FTP and FTPS(exmplicit/implicit) written in C# and under MIT license.
  • SSH.NET: An SSH library for .NET written in C# and under MIT license.
  • SharpZipLib: A Zip, GZip, Tar and BZip2 library written in C# and under MIT license.
  • Saxon-HE: An XSLT and XQuery Processor that provides implementations of XSLT (2.0), XQuery (1.0, 3.0, and 3.1), and XPath (2.0, 3.0, and 3.1) at the basic level of conformance defined by W3C. It's an open source library available under the Mozilla Public License version 1.0.
  • log4net: A port of the famous Apache log4j framework to the Microsoft .NET runtime. It's under the Apache license version 2.0.
  • TweetSharp: A fast and clean wrapper around the Twitter AP written in C#.
  • Microsoft Sync Framework 2.1: A data synchronization platform that allows to synchronize data across multiple data stores.
  • Json.NET: A high-performance JSON framework for .NET written in C# and under MIT license.
  • Hammock: an HTTP library that simplifies consuming and wrapping RESTful services.
  • Mono.Security: A library that provides the missing pieces to .NET security.
  • Oracle Data Access Components (ODAC): Oracle database client for .NET.
  • MySQL Connector/Net: A fully-managed ADO.NET driver for MySQL.
  • System.Data.SQLite: An ADO.NET provider for SQLite.
  • Npgsql: An open source ADO.NET Data Provider for PostgreSQL written in C# and under the PostgreSQL License, a liberal OSI-approved open source license.
  • .NET Data Provider for Teradata: An ADO.NET provider for Teradata.
  • Eto.Forms: A cross platform GUI framework for desktop and mobile applications in .NET.
  • highlight.js: Javascript syntax highlighter.
  • Cytoscape.js: Graph theory / network library for analysis and visualization.
  • OpenPop.NET: An open source implementation of a POP3 client and a robust MIME parser written in C#. It allows developers easy access to email on a POP3 server in a matter of minutes.
  • iTextSharp: .NET port of the iText library.

History

05 Jan 2017:

  • Released version 1.0.
  • 09 Jan 2017: 
    • Released version 1.0.1.
    • Created Wexflow Windows Service.
    • Created Tar, Tgz and Sql tasks.
    • Updated Wexflow Manager.
    • Fixed some bugs.
  • 16 Jan 2017:
  • 23 Jan 2017:
    • Released version 1.0.3.
    • Created HttpSyncFilesRenamerFilesExist and Wait tasks.
    • Created file tags functionality.
    • Added list, download and delete commands to Ftp task (FTP/FTPS(explicit/implicit)/SFTP).
    • Added retryCount and retryTimeout setting options to Ftp task.
    • Updated Wexflow manager.
    • Updated Wexflow engine.
    • Fixed some bugs.
    • Updated setup file.
    • Updated article content.
  • 26 Jan 2017:
    • Released version 1.0.4.
    • Created XSD validation of worklfow files before loading them.
    • Created tasks execution graph.
    • Created flowchart workflows (DoIf and DoWhile).
    • Created workflow events (OnSuccess, OnWarning and OnError).
    • Created FileExists flowchart task.
    • Updated setup.
    • Updated article content.
  • 30 Jan 2017:
    • Released version 1.0.5.
    • Created Wexflow Web Manager: a lightweight JavaScript API (~6Kb) for managing workflows.
    • Created Wexflow Manager GUI for Linux.
    • Updated Wexflow Manager for Windows.
    • Updated setup for Windows.
    • Created a setup for Linux.
  • 06 Feb 2017:
    • Released version 1.0.6.
    • Created Wexflow Android Manager: an Android application for managing workflows.
    • Updated Wexflow Web Manager.
    • Updated Wexflow Manager (Windows and Linux).
    • Updated Wexflow Engine.
  • 17 Feb 2017:
    • Released version 1.0.7.
    • Created Movedir task.
    • Updated Wexflow Web Manager.
    • Updated Wexflow Manager (Windows, Linux and Android).
    • Updated Wexflow Engine.
    • Fixed some bugs.
  • 06 Mar 2017:
    • Released version 1.0.8.
    • Created Swictch/Case flowchart node.
    • Created Now task.
    • Created Wexflow Manager for macOS.
    • Updated If and While flowchart nodes syntax.
    • Updated setup projects.
    • Now, an If flowchart node can be inside an If, a While and a Switch flowchart nodes.
    • Now, a While flowchart node can be inside an If, a While and a Switch flowchart nodes.
    • Now, a Switch flowchart node can be inside an If, a While and a Switch flowchart nodes.
    • Code refactoring.
    • Fixed some bugs.
  • 07 Apr 2017:
    • Released version 1.0.9.
    • Fixed Switch flowchart node children execution.
    • Fixed WCF service configuration issue.
  • 22 May 2017:
    • Released version 1.1.
    • Created Workflow task. This task allows to start, suspend, resume or stop a list of workflows.
    • Performance optimization.
    • Code refactoring.
  • 07 Oct 2017:
    • Released version 1.2.
    • Implemented mail attachments in MailsSender task.
    • Changed encoding to UTF-8 in logging and Wexflow Web Service client.
    • Hot reload of workflows:
      • If a workflow XML file is added to Workflows folder, It is automatically loaded without having to restart Wexflow Windows service.
      • If a workflow XML file is deleted from Workflows folder, It is automatically stopped and removed without having to restart Wexflow Windows service.
      • If a workflow XML file is changed. It is automatically reloaded without having to restart Wexflow Windows service.
    • Fixed empty Default node issue in Switch flowchart node.
    • Updated Windows, macOS and Linux manager apps.
  • 11 Oct 2017:
  • 13 Oct 2017:
    • Released version 1.4.
    • Implemented Wexflow Designer edit mode:
      • Now it is possible to edit the workflow configuration through Wexflow Designer.
      • Now it is possible to edit tasks configuration and settings through Wexflow Designer.
  • 15 Oct 2017:
    • Released version 1.5.
    • Added new features to Wexflow Designer:
      • Add a task setting.
      • Remove a task setting.
      • Add an attribute to selectFiles and selectAttachments task settings.
      • Remove an attribute from selectFiles and selectAttachments task settings.
      • Add a task to a linear workflow.
      • Remove a task from a linear workflow.
  • 16 Oct 2017:
  • 17 Oct 2017:
    • Released version 1.7.
    • Now we can delete workflows in Wexflow Designer.
    • Enhanced task settings edition.
    • Updated the documentation of MailsSender task.
    • Fixed some bugs in Wexflow Designer.
    • Updated Windows setup project.
  • 18 Oct 2017:
    • Released version 1.8.
    • Implemented the visualization of the execution graph of a linear workflow in Wexflow Designer.
    • Updated Windows setup project.
  • 19 Oct 2017:
    • Released version 1.9.
    • Added new features and fixed some issues in Wexflow Designer:
      • Implemented the visualization of the execution graph of a non lienar workflow.
      • Displayed the xml button after saving a new workflow.
      • Fixed an issue of editing the id of a new workflow after saving it.
      • Fixed an issue regarding the value of the workflow file path after saving a new workflow and editing its name.
      • Fixed an issue of changing the id of a selected workflow.
  • 20 Oct 2017:
    • Released version 2.0.
    • Added a new feature and fixed some issues in Wexflow Web Designer:
      • Changed the style of Wexflow Web Designer.
      • Fixed an issue when saving an existing workflow.
      • Fixed an issue when changing the id of an existing workflow multiple times.
    • Added new features to Wexflow Web Manager:
      • Changed the style of Wexflow Web Manager.
      • Added a confirm dialog when stopping a workflow.
    • Added regexPattern option in the task FilesLoader.
    • Added recursive load option in the task FilesLoader.
    • Updated the documentation of the task FilesLoader.
    • Updated Wexflow web service.
    • Updated the workflow Workflow_FilesLoader.
    • Updated the folder samples\WexflowTesing.
    • Updated Windows setup project.
    • Updated the documentation of the task FilesRenamer.
  • 27 Oct 2017:
    • Released version 2.1.
    • Added new tasks:
      • Sha1: This task generates SHA-1 hashes of a collection of files and writes the results in an XML file.
      • Sha256: This task generates SHA-256 hashes of a collection of files and writes the results in an XML file.
      • Sha512: This task generates SHA-512 hashes of a collection of files and writes the results in an XML file.
      • FilesConcat: This task concatenates a collection of files.
      • FilesSplitter: This task splits files into chunks.
      • FilesInfo: This task generates files information of a collection of files and writes the results in an XML file.
      • MediaInfo: This task generates the most relevant technical and tag data for video and audio files and outputs the results in an XML file.
      • MailsReceiver: This task fetches a collection of emails.
      • ProcessKiller: This task kills a process.
    • Implemented unit tests (vs2017).
    • Updated samples\WexflowTesting folder.
    • Updated samples\Workflows folder.
    • Updated the documenttaion of the tasks FilesExist and Xslt.
    • Updated Windows installer.
  • 05 Nov 2017:
    • Released version 2.2.
    • Added Designer and Logs buttons in Wexflow Manager.
    • Added Help and About menus in Wexflow Manager.
    • Added the task description in the execution graph in Wexflow Designer.
    • Fixed an issue in Wexflow Designer: We cannot save a workflow because of a typo in wexflow-designer.js
    • Created Wexflow.Core nuget package.
    • Added new tasks:
      • Unzip: This task extracts ZIP archives.
      • Untar: This task extracts TAR archives.
      • Untgz: This task extracts TAR.GZ archives.
      • ProcessInfo: This task shows information about a process.
      • TextToPdf: This task generates PDF files from TEXT files.
      • HtmlToPdf: This task generates PDF files from HTML files.
      • SqlToXml: This task executes a collection of SQL scripts (select queries) and outputs the results in XML files.
      • SqlToCsv: This task executes a collection of SQL scripts (select queries) and outputs the results in CSV files.
      • Guid: This task generates Guids and outputs the result in an XML file.
    • Added extension setting in the task Xslt.
    • Updated the task FilesCopier.
    • Fixed an issue in Task.GetSetting.
    • Added new unit tests.
    • Updated ImagesTransformer and ProcessKiller unit tests.
    • Updated samples\WexflowTesting folder.
    • Updated samples\Workflows folder.
    • Updated Windows installer.
    • Code refactoring.
  • 14 Nov 2017:
    • Released version 2.3.
    • Wexflow Designer:
      • Moved tasks names and tasks settings to external files (C:\Wexflow\TasksNames.json and C:\Wexflow\TasksSettings.json).
      • Added Xml and Documentation buttons for tasks.
    • Added Hashtable as shared memory for tasks.
    • Added deleteMessages setting to the task MailsReceiver.
    • Fixed some issues in Sql, SqlToCsv and SqlToXml tasks.
    • Documented Wexflow.Core.
    • Updated Wexflow.Core nuget package.
    • Updated the way we debug Wexflow Windows Service and Wexflow Manager.
    • Fixed the issue #36.
    • Fixed an issue in Wexflow engine.

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Akram El Assas
Engineer
Morocco Morocco
No Biography provided

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionWill this cool project going on? Pin
LightTempler28-Aug-18 10:52
memberLightTempler28-Aug-18 10:52 
QuestionNested If Pin
Member 135060348-Mar-18 5:27
memberMember 135060348-Mar-18 5:27 
GeneralMy vote of 5 Pin
Member 108362408-Mar-18 4:21
memberMember 108362408-Mar-18 4:21 
Questiondifference between file and files task Pin
Member 1257470020-Dec-17 10:19
memberMember 1257470020-Dec-17 10:19 
GeneralMessage Closed Pin
6-Dec-17 20:11
memberMember 135627586-Dec-17 20:11 
PraiseMessage Closed Pin
5-Dec-17 20:30
memberMember 135558495-Dec-17 20:30 
PraiseMessage Closed Pin
5-Dec-17 20:29
memberMember 135558495-Dec-17 20:29 
QuestionParent and child task Pin
Member 1350603415-Nov-17 10:16
memberMember 1350603415-Nov-17 10:16 
AnswerRe: Parent and child task Pin
Akram El Assas15-Nov-17 11:07
memberAkram El Assas15-Nov-17 11:07 
GeneralRe: Parent and child task Pin
Member 1350603429-Nov-17 9:56
memberMember 1350603429-Nov-17 9:56 
QuestionCall the start button progrmatically Pin
Member 1350603415-Nov-17 10:16
memberMember 1350603415-Nov-17 10:16 
AnswerRe: Call the start button progrmatically Pin
Akram El Assas15-Nov-17 11:04
memberAkram El Assas15-Nov-17 11:04 
GeneralRe: Call the start button progrmatically Pin
Member 1350603416-Nov-17 5:15
memberMember 1350603416-Nov-17 5:15 
QuestionForm-based approval workflows? Pin
Member 135112308-Nov-17 17:12
memberMember 135112308-Nov-17 17:12 
AnswerRe: Form-based approval workflows? Pin
Akram El Assas9-Nov-17 1:13
memberAkram El Assas9-Nov-17 1:13 
QuestionAfter creating custom task Pin
Member 135060346-Nov-17 5:12
memberMember 135060346-Nov-17 5:12 
AnswerRe: After creating custom task Pin
Akram El Assas6-Nov-17 7:45
memberAkram El Assas6-Nov-17 7:45 
GeneralRe: After creating custom task Pin
Member 135060346-Nov-17 8:46
memberMember 135060346-Nov-17 8:46 
GeneralRe: After creating custom task Pin
Akram El Assas6-Nov-17 8:58
memberAkram El Assas6-Nov-17 8:58 
GeneralRe: After creating custom task Pin
Member 135060346-Nov-17 9:04
memberMember 135060346-Nov-17 9:04 
GeneralRe: After creating custom task Pin
Akram El Assas6-Nov-17 9:17
memberAkram El Assas6-Nov-17 9:17 
GeneralRe: After creating custom task Pin
Member 135060346-Nov-17 9:54
memberMember 135060346-Nov-17 9:54 
GeneralRe: After creating custom task Pin
Akram El Assas6-Nov-17 10:18
memberAkram El Assas6-Nov-17 10:18 
GeneralRe: After creating custom task Pin
Akram El Assas7-Nov-17 10:09
memberAkram El Assas7-Nov-17 10:09 
GeneralRe: After creating custom task Pin
Member 135060349-Nov-17 4:11
memberMember 135060349-Nov-17 4:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01-2016 | 2.8.180920.1 | Last Updated 22 Nov 2017
Article Copyright 2017 by Akram El Assas
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid