How To Build & Configure A Custom DFS Connector

Last published at: June 27th, 2023

Introduction

Distributed File System (DFS) connector is used by FlowWright to connect to a distributed file system for storage.  By default, FlowWright connects to the File system DFS connector, which is backward compatible with previous versions of FlowWright.  With distributed servers, a distributed file system is necessary, multiple servers should and can access the same file storage system.  FlowWright ships with the following DFS connectors:

  • File system connector
  • Database connector
  • Azure File system connector
  • Azure Blog storage connector

The DFS connector is created by simply implementing a Class based on an Interface.

Building & Configuring a DFS connector

This sample will illustrate how to create a new DFS connector, how to configure it using the Configuration Manager, and the use of DFS connectors for file storage.

 

 

Here's the example code the File system DFS connector, it implements the interface IFWDFSStorageConnector.

public FWDFSFileStoreConfig Config { getset; }
 /// <summary>
 /// Gets or sets the o user session.
 /// </summary>
 /// <value>The o user session.</value>
 public FWUserSession UserSession { getset; }
 /// <summary>
 /// Gets or sets the s error.
 /// </summary>
 /// <value>The s error.</value>
 public string Error { getset; }
 
 /// <summary>
 /// Initializes a new instance of the <see cref="clsFileSystemConnector"/> class.
 /// </summary>
 /// <param name="oConnConfig">The o connection configuration.</param>
 /// <param name="oConnUserSession">The o connection user session.</param>
 public clsFileSystemConnector(FWDFSFileStoreConfig oConnConfig, FWUserSession oConnUserSession)
 {
     Config = oConnConfig;
     UserSession = oConnUserSession;
 }

/// <summary>  /// Creates the file.  /// </summary>  /// <param name="filePath">The file path.</param>  /// <param name="oFileContents">The o file contents.</param>  /// <param name="fileID">The file identifier.</param>  /// <param name="oFileType">Type of the o file.</param>  /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>  public bool CreateFile(string filePathbyte[] oFileContentsout string fileID, DFSFileStoreFileType oFileType)  {      try      {          fileID = filePath;          filePath = Path.Combine(Config.Parms["FileRootPath"], filePath);          using (FileStream oSourceStream = File.Create(filePath))          {              oSourceStream.Seek(0, SeekOrigin.End);              oSourceStream.Write(oFileContents, 0, oFileContents.Length);          }          return (true);      }      catch (Exception ex)      {          Error = ex.Message;          fileID = string.Empty;      }      return false;  }  /// <summary>  /// Creates the folder.  /// </summary>  /// <param name="parentFolderPath">The parent folder path.</param>  /// <param name="folderName">Name of the folder.</param>  /// <param name="folderID">The folder identifier.</param>  /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>  public bool CreateFolder(string parentFolderPathstring folderNameout string folderID)  {      try      {          parentFolderPath = Path.Combine(parentFolderPath, folderName);          if (!FolderExists(parentFolderPath))          {              parentFolderPath = Path.Combine(Config.Parms["FileRootPath"], parentFolderPath);              Directory.CreateDirectory(parentFolderPath);          }          folderID = parentFolderPath;          return (true);      }      catch (Exception ex)      {          Error = ex.Message;          folderID = string.Empty;      }      return false;  }  /// <summary>  /// Files the exists.  /// </summary>  /// <param name="filePath">The file path.</param>  /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>  public bool FileExists(string filePath)  {      filePath = Path.Combine(Config.Parms["FileRootPath"], filePath);      return File.Exists(filePath);  }  /// <summary>  /// Folders the exists.  /// </summary>  /// <param name="folderPath">The folder path.</param>  /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>  public bool FolderExists(string folderPath)  {      folderPath = Path.Combine(Config.Parms["FileRootPath"], folderPath);      return Directory.Exists(folderPath);  }

/// <summary> /// Gets the file. /// </summary> /// <param name="filepath">The filepath.</param> /// <returns>clsDEFile.</returns> public FWDFSFile GetFile(string filePath) {     FWDFSFile oFileObject = null;     try     {         string fileFullPath = Path.Combine(Config.Parms["FileRootPath"], filePath);         if (File.Exists(fileFullPath))         {             oFileObject = new FWDFSFile(filePath, filePath, this);         }         else         {             Error = "File not found";         }     }     catch (Exception ex)     {         Error = ex.Message;     }     return (oFileObject); } /// <summary> /// Gets the files. /// </summary> /// <param name="folderPath">The folder path.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <returns>List&lt;clsDEFile&gt;.</returns> public List<FWDFSFile> GetFiles(string folderPathbool recursive = false) {     List<FWDFSFile> oFilesObject = new List<FWDFSFile>();     try     {         folderPath = Path.Combine(Config.Parms["FileRootPath"], folderPath);         string[] oFiles = { "" };         if (recursive)         {             oFiles = Directory.GetFiles(folderPath, "*", SearchOption.AllDirectories);         }         else         {             oFiles = Directory.GetFiles(folderPath, "*", SearchOption.TopDirectoryOnly);         }         foreach (var sFilePath in oFiles)         {             string relativeFilePath = sFilePath.Replace(Config.Parms["FileRootPath"] + "\\""");             FWDFSFile oFileObject = new FWDFSFile(relativeFilePath, relativeFilePath, this);             oFilesObject.Add(oFileObject);         }     }     catch (Exception ex)     {         Error = ex.Message;     }     return (oFilesObject); } /// <summary> /// Gets the files using folder identifier. /// </summary> /// <param name="folderID">The folder identifier.</param> /// <returns>List&lt;clsDEFile&gt;.</returns> public List<FWDFSFile> GetFilesUsingFolderID(string folderID) {     return GetFiles(folderID); } /// <summary> /// Gets the file using identifier. /// </summary> /// <param name="fileID">The file identifier.</param> /// <returns>clsDEFile.</returns> public FWDFSFile GetFileUsingID(string fileID) {     return GetFile(fileID); } /// <summary> /// Gets the folder. /// </summary> /// <param name="folderPath">The folder path.</param> /// <returns>clsDEFolder.</returns> public FWDFSFolder GetFolder(string folderPath) {     if (!FolderExists(folderPath)) return (null);     return new FWDFSFolder(folderPath, this); } /// <summary> /// Gets the folder using identifier. /// </summary> /// <param name="folderID">The folder identifier.</param> /// <returns>clsDEFolder.</returns> public FWDFSFolder GetFolderUsingID(string folderID) {     return GetFolder(folderID); } /// <summary> /// Gets the parameters. /// </summary> /// <returns>Dictionary&lt;System.String, System.String&gt;.</returns> public Dictionary<stringstringGetParameters() {     Dictionary<stringstringoMyParams = new Dictionary<stringstring>();     if (Config.Parms == null || Config.Parms.Count == 0)     {         oMyParams.Add("FileRootPath"@"C:\inetpub\wwwroot\cDevWorkflow");     }     else     {         oMyParams = Config.Parms;     }     return (oMyParams); } /// <summary> /// Moves the file. /// </summary> /// <param name="sourceFilePath">The source file path.</param> /// <param name="targetFilePath">The target file path.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> public bool MoveFile(string sourceFilePathstring targetFilePath) {     sourceFilePath = Path.Combine(Config.Parms["FileRootPath"], sourceFilePath);     targetFilePath = Path.Combine(Config.Parms["FileRootPath"], targetFilePath);     if (File.Exists(sourceFilePath))     {         File.Move(sourceFilePath, targetFilePath);         return true;     }     return false; }

/// <summary>  /// Removes the file.  /// </summary>  /// <param name="filePath">The file path.</param>  /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>  public bool RemoveFile(string filePath)  {      filePath = Path.Combine(Config.Parms["FileRootPath"], filePath);      if (File.Exists(filePath))      {          try          {              File.Delete(filePath);              return (true);          }          catch (IOException e)          {              Error = e.Message;          }      }      return (false);  }  /// <summary>  /// Removes the file using identifier.  /// </summary>  /// <param name="fileID">The file identifier.</param>  /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>  public bool RemoveFileUsingID(string fileID)  {      return RemoveFile(fileID);  }  /// <summary>  /// Removes the folder.  /// </summary>  /// <param name="folderPath">The folder path.</param>  /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>  public bool RemoveFolder(string folderPath)  {      if (!FolderExists(folderPath)) return false;      folderPath = Path.Combine(Config.Parms["FileRootPath"], folderPath);      Directory.Delete(folderPath, true);      return true;  }  /// <summary>  /// Removes the folder using identifier.  /// </summary>  /// <param name="folderID">The folder identifier.</param>  /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>  public bool RemoveFolderUsingID(string folderID)  {      return RemoveFolder(folderID);  }  /// <summary>  /// Tests the connector.  /// </summary>  /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>  public bool TestConnector()  {      bool bStatus = false;      try      {          Dictionary<stringstringoFilesystemParms = Config.Parms;          string sFilePath = oFilesystemParms["FileRootPath"].Trim();          bStatus = Directory.Exists(sFilePath);          if (!bStatus)              Error = "File path does not exist";      }      catch (Exception ex)      {          Error = ex.Message;          bStatus = false;      }      return (bStatus);  }  /// <summary>  /// Updates the file.  /// </summary>  /// <param name="filePath">The file path.</param>  /// <param name="oFileContents">The o file contents.</param>  /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>  public bool UpdateFile(string filePathbyte[] oFileContents)  {      try      {          filePath = Path.Combine(Config.Parms["FileRootPath"], filePath);          using (FileStream SourceStream = File.Open(filePath, FileMode.OpenOrCreate))          {              SourceStream.Seek(0, SeekOrigin.End);              SourceStream.Write(oFileContents, 0, oFileContents.Length);          }          return (true);      }      catch (Exception ex)      {          Error = ex.Message;      }      return false;  }  /// <summary>  /// Gets the file contents.  /// </summary>  /// <param name="filePath">The file path.</param>  /// <returns>System.Byte[].</returns>  public byte[] GetFileContents(string filePath)  {      filePath = Path.Combine(Config.Parms["FileRootPath"], filePath);      byte[] oFileData = null;      using (FileStream oFS = File.OpenRead(filePath))      {          using (BinaryReader oBinaryReader = new BinaryReader(oFS))          {              oFileData = oBinaryReader.ReadBytes((int)oFS.Length);          }      }      return oFileData;  }  /// <summary>  /// Updates the file using identifier.  /// </summary>  /// <param name="fileID">The file identifier.</param>  /// <param name="oFileContents">The o file contents.</param>  /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>  public bool UpdateFileUsingID(string fileIDbyte[] oFileContents)  {      return UpdateFile(fileID, oFileContents);  }  /// <summary>  /// Gets the sub folders.  /// </summary>  /// <param name="folderPath">The folder path.</param>  /// <param name="recursive">if set to <c>true</c> [recursive].</param>  /// <returns>List&lt;clsDEFolder&gt;.</returns>  public List<FWDFSFolder> GetSubFolders(string folderPathbool recursive)  {      List<FWDFSFolder> folderList = new List<FWDFSFolder>();      folderPath = Path.Combine(Config.Parms["FileRootPath"], folderPath);      SearchOption oOption = SearchOption.TopDirectoryOnly;      if (recursive)      {          oOption = SearchOption.AllDirectories;      }      Directory.GetDirectories(folderPath, "*", oOption).ToList().ForEach(sPath => folderList.Add(GetFolder(sPath)));      return folderList;  }

 

Copy the DFS connector DLL file to the FlowWright BIN directory:

 “C:\inetpub\wwwroot\cDevWorkflow\bin”.

 

 

Navigate to the FlowWright Configuration Manager and expand the “Distributed File Storage” Menu and select the “DFS Storage Connectors” menu item.

 

Let’s use the auto-detect feature of FlowWright to auto-detect and configure the DFS connector:

 

 

 

 

 

 

Once you click on the “Auto Detect” button on the toolbar, the auto-detect UI should display the custom DFS connector as shown below: 

Now Select the “clsFileSystem” DFS connector and select “ManageConfigure” menu item, the DFS connector will be automatically configured within FlowWright.

Select the DFS connector and edit the “fileRootPath” parameter as what path you want for storage.

Now that the DFS connector is fully configured, let’s use the new DFS connector for FlowWright application storage. 

Go to the status page. Status Distributed-> Storage Connector 

 

Then save the connector and verify the details.

 

 

 

 

 

 

 

 

 

 

Once we have configured the “clsFileSystem” as the default storage connector, we will see how it works.  Let’s create a new workflow definition.

 

Open the Workflow designer and drag and drop a “task” step to the workflow definition.

 

 

 

 

 

Select the task step and fill the properties of the step. Route the task to the current user – to your account. Save Workflow definition and click the “Create Instance” menu item. 

 

Click on the “Generate” button to generate a new name for the Instance. Then click the “Create & Execute” button to create the Workflow instance. Close the Workflow designer and go to the Workflow instances page. The newly created instance will be displayed on the list.

Click Execute->Run Engine. The instance will be in “sleeping” status.

 

 

 

Open the task menu to view the list of tasks.

 

Select the task from the list and render the task.

 

Click on the “Choose Files” button and select any file. Then click the “Upload” button to upload the files for the task.

 

 

Now go to the file system and open the folder: “C:\DFSStorage” and verify that the uploaded files exist or not.

 

Using DFS connectors, user uploaded files can be stored centrally and accessed by multiple distributed servers.  FlowWright also provides a “Storage Explorer”, navigate to the Distributed File System menu and select “Storage Explorer” menu item.

You can also download any given file using the context menu for the file.

There are more helpful articles are available within the FlowWright knowledgebase, you can view the knowledgebase using the following link: