Overview
The Data Integration Module (DIM) allows Users to create custom Plugins that can directly interface with the Data Integration Service and Projects. This document describes how to setup and use Plugins with the DIM.
DIM Processing Overview
The Data Integration Module executes processing instructions from top to bottom as defined in the XML project file. First, it sends each processor an ‘execute’ message in order. After each processor indicates successful execution, it sends a ‘commit’ message to finalize changes. This sequence establishes a transaction for atomic dispatch processing. However, processors are not required to participate in the transaction and may finalize changes directly during the execute phase.
If any processor fails to execute or commit successfully, the DIM sends a ‘rollback’ message to all processors that completed execution. Because an error may happen during the commit phase, it is possible for a processor to receive a rollback message after it has already received a commit message. Most processors will simply ignore a rollback message received after a commit message.
Plugin Details and Requirements
You can include custom data processing code in DIM execution by writing a .NET 2.0 class implementing the IPlugin2 and IDispatchProcessor interfaces from TransformerSdk.dll. Add your plugin to the processing sequence using a processPlugin element in the project XML file. Your plugin assembly and any other libs it depends on should be copied into the DIM Program Files folder. The default location for the DIM is one of following:
- 32 Bit OS: C:\Program Files\F2B Data Integration Module
- 64 Bit OS: C:\Program Files (x86)\F2B Data Integration Module
The processPlugin element must give the assembly-qualified name of your plugin class using a class attribute.
IPlugin2 defines an Initialize method that your plugin should implement to do any startup tasks when the DIM loads the project file. The plugin receives the processPlugin XML element from the project XML file, which can be used for instance configuration. For example, a plugin that creates files may specify the location for new files as a child of processPlugin. The plugin also receives an IPluginHost containing a WaitHandle that gets signaled when the DIM service is shutting down.
IDispatchProcessor defines methods Execute, Commit, and Rollback. Implement these methods to execute your custom processing code at each stage of processing.
The Execute method receives dispatch data using the IDispatch interface. This interface contains some information such as the dispatch reference number and sender’s username. It also lists the rendered files included in the dispatch and contains a list of ISentFormData instances providing access to sent form data. Most dispatches will simply contain one sent form.
DIM Interface Definitions
The following section outlines the definitions for the Integration and Transformer SDK’s of the DIM Plugin API.
Field2Base.Integration.Sdk Namespace
IDispatch Interface
Accesses dispatch data
Properties
int ReferenceNumber { get; }
| Get the reference number assigned to this dispatch by F2B
|
string SenderUsername { get; }
| Get the username of the user who sent this dispatch
|
IList<ISentFormData> Forms { get; }
| Get form data included in this dispatch
|
IList<FileInfo> ContentFiles { get; }
| Get a list of files included in the dispatch
|
ISentFormData Interface
Accesses form data
Properties
XmlDocument Data { get; }
| Direct access to the XML data file. Null if the data file is missing.
|
Methods
string GetRegionValue(string pRegionFullName);
| Helper to get a region value
Returns: Serialized string region value; null if the region doesn't exist or was left blank
|
string GetHeaderValue(string pHeaderKey);
| Helper to get a header value
Returns: Header value; null if the value doesn't exist
|
byte[] GetPageImage(int pPageIndex);
| Get image data for a rendered form page using its 0-based index in the form
Returns: Page image; null if the page doesn't exist or page image data isn't available
|
IVariableBindings Interface
Carries execution parameters and variables during form processing. i.e. The min and max date values for restricting processing to a date range.
Methods
object Lookup(string pIdentifier);
| Get the value bound to an identifier
Returns: The value bound to an identifier; null if the identifier is not bound.
|
Field2Base.Transformer.Sdk Namespace
IPluginHost interface
Provides an environment for plugins and form processing in general
Properties
WaitHandle StopSignal { get; }
| Signal set when the host is trying to shut down
|
IPlugin2 interface
Generic interface for extensions
Methods
void Initialize(IPluginHost pHost, XmlNodepInstanceData, ILogWriter pLog);
| Initialize a plugin instance by giving it the environment and any instance data specified where the plugin was referenced. Plugins should have a public default constructor so the host can activate them and do the majority of initialzation in this method.
|
ProcessResult enum
Result codes that may be returned from a processor
Values
Success
| The operation succeeded
|
Failed
| The operation failed and should not be tried again
|
TransientError
| The operation did not succeed but may be tried again
|
ILogWriter interface
Interface for log object.
Methods
void Log(string pMessage, LogLevel pSeverity);
| Write a message to the DIM log.
|
LogLevel enum
Severity levels for log messages
Values
Trace
| Messages that aid development or provide verbose processing information
|
Info
| Messages that are of interest during normal processing
|
Error
| Messages that indicate failure or unexpected conditions during processing
|
IDispatchProcessor interface
implements IDisposable
Processes a dispatch
Methods
ProcessResult Execute(IDispatch pData,IVariableBindings pEnvironment, ILogWriter pLog);
| Process a dispatch
Returns: Success if processing should continue; Failed if processing failed and should not be retried; TransientError if processing failed but may be retried later.
|
ProcessResult Commit(ILogWriter pLog);
| Finalize results generated during the execute phase.
Implementors may choose to commit results immediately in Execute rather than using a separate commit phase.
Returns: Success if processing should continue; Failed if processing failed and should not be retried; TransientError if processing failed but may be retried later.
|
void Rollback(ILogWriter pLog);
| Undo unfinalized results generated during the execute phase.
Rollback can be called after Commit has already been called if a later processor fails to commit. In this case it's not expected that committed results will be undone, though implementors may undo them anyway if possible.
Rollback methods should avoid throwing exceptions since this will exit the rollback phase early. If that happens then the processors that have not been called yet will never have their rollback methods invoked.
|
Sample Plugin Files
The following files are sample and definition files for use with the DIM. They can be downloaded in a single zip file from this location:
Here is a breakdown of the files that are included in the zip file which are then broken down further in the sections below:
- LoggingPlugin.cs
- SqlCeWriterPlugin.cs
- Sample DIM Project File
- Field2Base Sample Form.xml
Sample File: LoggingPlugin.cs
Here is a sample DIM Plugin that writes what gets processed by the DIM to a log file.
- using System;
- using System.Xml;
- using Field2Base.Integration.Sdk;
- using Field2Base.Transformer.Sdk;
-
- namespace Field2Base.Transformer.Samples
- {
- /// <summary>
- /// LoggingPlugin is a minimal plugin implementation that writes to the DIM log
- /// as a dispatch passes through each stage of processing.
- /// </summary>
- public class LoggingPlugin : IPlugin2, IDispatchProcessor
- {
- /// <summary>
- /// Plugins require a public parameterless constructor. The DIM will call this
- /// constructor to create a new plugin instance each time it is referenced by
- /// a DIM project.
- /// </summary>
- public LoggingPlugin()
- {
- }
-
- /// <summary>
- /// The IDispatchProcessor interface inherits IDisposable. Implement a Dispose
- /// method to release resources as necessary and do any cleanup at shutdown.
- /// </summary>
- public void Dispose()
- {
- }
-
- /// <summary>
- /// Perform one-time initialization at startup and load configuration from the
- /// project XML file.
- /// </summary>
- public void Initialize(IPluginHost pHost, XmlNode pNode, ILogWriter pLog)
- {
- pLog.Log("LoggingPlugin initialized.", LogLevel.Info);
- }
-
- /// <summary>
- /// Process dispatch data.
- /// </summary>
- public ProcessResult Execute(IDispatch pData, IVariableBindings pEnvironment, ILogWriter pLog)
- {
- pLog.Log(String.Format("LoggingPlugin received dispatch {0} with ref# {1}", pEnvironment.Lookup("internalFormId"), pData.ReferenceNumber), LogLevel.Info);
- return ProcessResult.Success;
- }
-
- /// <summary>
- /// If the plugin wants to participate in the transaction that coordinates multiple
- /// processing steps, it should defer finalization of any steps taken in Execute
- /// until this method gets called.
- ///
- /// Plugins are not requried to work in the transaction and may simply finalize all
- /// work in the Execute phase.
- /// </summary>
- public ProcessResult Commit(ILogWriter pLog)
- {
- pLog.Log("LoggingPlugin commit", LogLevel.Info);
- return ProcessResult.Success;
- }
-
- /// <summary>
- /// If the plugin works transactionally and some later processing step encounters
- /// an error, the rollback phase allows it to discard any changes prepared during
- /// the execute phase.
- ///
- /// If the error occurs during commit of a later processing step, the plugin's
- /// Rollback method may be called after the Commit method has already been called.
- /// </summary>
- public void Rollback(ILogWriter pLog)
- {
- pLog.Log("LoggingPlugin rollback", LogLevel.Info);
- }
- }
- }
Sample File: SqlCeWriterPlugin.cs
Here is a sample plugin that connects to a SQL Compact Edition database and writes region values to the database. NOTE: This plugin requires that Microsoft SQL Compact Edition 3.5 be installed where the DIM is running.
- using System;
- using System.IO;
- using System.Xml;
- using System.Data.SqlServerCe;
- using Field2Base.Integration.Sdk;
- using Field2Base.Transformer.Sdk;
-
- namespace Field2Base.Transformer.Samples
- {
- /// <summary>
- /// SqlCeWriterPlugin is a plugin implementation that writes form data to a SQL CE database.
- /// </summary>
- public class SqlCeWriterPlugin : IPlugin2, IDispatchProcessor
- {
- public SqlCeWriterPlugin()
- {
- }
-
- private string _dbFilePath; // path to SQL CE database that will hold form data
- private SqlCeConnection _conn; // db connection while processing a form
- private SqlCeTransaction _tran; // db transaction while processing a form
-
- private string DbConnectionString
- {
- get { return String.Format("Data Source = {0}", _dbFilePath); }
- }
-
- public void Dispose()
- {
- DisposeDbObjects();
- }
-
- /// <summary>
- /// Initialize this plugin instance.
- /// </summary>
- public void Initialize(IPluginHost pHost, XmlNode pInstanceData, ILogWriter pLogWriter)
- {
- // make sure we have required configuration
- if (pInstanceData["dbFilePath"] == null)
- throw new ApplicationException("SqlCeWriterPlugin instance data is missing the 'dbFilePath' element.");
-
- // load configuration
- _dbFilePath = pInstanceData["dbFilePath"].InnerText;
-
- // create the db that will receive form data if it doesn't already exist
- CreateDatabaseIfNecessary();
- }
-
- /// <summary>
- /// Process dispatch data.
- /// </summary>
- public ProcessResult Execute(IDispatch pDispatchData, IVariableBindings pVariables, ILogWriter pLog)
- {
- // open the db, create a transaction, and populate for this form
- try
- {
- _conn = new SqlCeConnection(DbConnectionString);
- _conn.Open();
- _tran = _conn.BeginTransaction();
-
- foreach (ISentFormData formData in pDispatchData.Forms)
- {
- // the DIM project can be configured to skip form data, so make sure we have a data document
- if (formData.Data == null) continue;
-
- // get id assigned by the client when the form was sent
- string formDataIdStr = formData.GetHeaderValue("FormDataId");
-
- // insert a row for the form
- using (SqlCeCommand cmd = new SqlCeCommand())
- {
- cmd.Connection = _conn;
- cmd.Transaction = _tran;
- cmd.CommandText = "INSERT INTO Form ( FormDataId ) VALUES ( @formDataId )";
- cmd.Parameters.AddWithValue("@formDataId", formDataIdStr);
- cmd.ExecuteNonQuery();
- }
-
- // get the identity value for the form
- int formId;
- using (SqlCeCommand cmd = new SqlCeCommand())
- {
- cmd.Connection = _conn;
- cmd.Transaction = _tran;
- cmd.CommandText = "SELECT @@IDENTITY";
- formId = Convert.ToInt32(cmd.ExecuteScalar());
- }
-
- // insert region values
- foreach (XmlNode regionNode in formData.Data.SelectNodes("/FormData/Values/Page/Region"))
- {
- int userPageNumber = Int32.Parse(regionNode.ParentNode.Attributes["UserPageNumber"].Value);
- string regionName = regionNode.Attributes["Name"].Value;
- string regionFullName = String.Format("Page{0}@{1}", userPageNumber, regionName);
-
- string regionValue = formData.GetRegionValue(regionFullName);
-
- using (SqlCeCommand cmd = new SqlCeCommand())
- {
- cmd.Connection = _conn;
- cmd.Transaction = _tran;
- cmd.CommandText = "INSERT INTO FormData ( ID, UserPageNumber, RegionName, RegionValue ) VALUES ( @id, @userPageNumber, @regionName, @regionValue )";
- cmd.Parameters.AddWithValue("@id", formId);
- cmd.Parameters.AddWithValue("@userPageNumber", userPageNumber);
- cmd.Parameters.AddWithValue("@regionName", regionName);
- cmd.Parameters.AddWithValue("@regionValue", regionValue);
- cmd.ExecuteNonQuery();
- }
- }
- }
- }
- catch
- {
- // must clean up db objects in case of error because we won't get a later Rollback or Commit
- // if we don't return success from this method
- DisposeDbObjects();
- throw;
- }
-
- return ProcessResult.Success;
- }
-
- /// <summary>
- /// Commit the transaction once all processing steps are complete.
- /// </summary>
- public ProcessResult Commit(ILogWriter pLog)
- {
- // try to commit the transaction
- try
- {
- _tran.Commit();
- pLog.Log("SQL CE transaction has been committed.", LogLevel.Trace);
- return ProcessResult.Success;
- }
- catch (SqlCeException ex)
- {
- pLog.Log(String.Format("Could not commit SQL CE transaction. Caught {0}: {1}", ex.GetType().Name, ex.Message), LogLevel.Error);
- return ProcessResult.Failed;
- }
- finally
- {
- DisposeDbObjects();
- }
- }
-
- /// <summary>
- /// Roll back the transaction if a later processing step failed.
- /// </summary>
- public void Rollback(ILogWriter pLog)
- {
- // try to roll back the transaction
- try
- {
- // must check for null transaction because Rollback will be called after Commit
- // if a later processing step fails during its Commit phase
- if (_tran != null)
- {
- _tran.Rollback();
- pLog.Log("SQL CE transaction has been rolled back.", LogLevel.Trace);
- }
- else
- {
- pLog.Log("SQL CE transaction is already complete and cannot be rolled back.", LogLevel.Info);
- }
- }
- finally
- {
- DisposeDbObjects();
- }
- }
- private void DisposeDbObjects()
- {
- if (_tran != null)
- {
- _tran.Dispose();
- _tran = null;
- }
- if (_conn != null)
- {
- _conn.Dispose();
- _conn = null;
- }
- }
- private void CreateDatabaseIfNecessary()
- {
- // nothing to do if the file has already been created
- if (File.Exists(_dbFilePath))
- return;
-
- // create a new db file
- using (SqlCeEngine engine = new SqlCeEngine(DbConnectionString))
- {
- engine.CreateDatabase();
- }
-
- // create the data tables
- try
- {
- using (SqlCeConnection conn = new SqlCeConnection(DbConnectionString))
- {
- conn.Open();
-
- using (SqlCeCommand cmd = new SqlCeCommand())
- {
- cmd.Connection = conn;
- cmd.CommandText = @"
- CREATE TABLE Form (
- ID int identity(1, 1),
- FormDataId uniqueidentifier
- )";
- cmd.ExecuteNonQuery();
- }
- using (SqlCeCommand cmd = new SqlCeCommand())
- {
- cmd.Connection = conn;
- cmd.CommandText = @"
- CREATE TABLE FormData (
- ID int,
- UserPageNumber int,
- RegionName ntext,
- RegionValue ntext
- )";
- cmd.ExecuteNonQuery();
- }
- }
- }
- catch
- {
- // don't leave a half-created db file
- File.Delete(_dbFilePath);
- throw;
- }
- }
- }
- }
Here is a Sample DIM Project file that is using both the LoggingPlugin and SqlCeWriterPlugin plugins.
- <?xml version="1.0" encoding="utf-8"?>
- <processForms>
- <systemName>Field2Base Sample Form</systemName>
- <friendlyName>Field2Base Sample Form</friendlyName>
- <formFileName>Field2Base Sample Form.eform</formFileName>
- <enabled>True</enabled>
- <rfiSvc>
- <name>Field2Base Sample Form</name>
- <uri>https://fieldconnect.field2base.com/V2/F2BRFIWebService/GetRfi.asmx</uri>
- <username>RfiSvcClient</username>
- <password>password</password>
- <companyId>101</companyId>
- <formTemplateId>80d54d03-cec0-472a-9400-b6c8f17582ac</formTemplateId>
- </rfiSvc>
- <processPlugin class="Field2Base.Transformer.Samples.LoggingPlugin,DataIntegrationSamples" />
- <processPlugin class="Field2Base.Transformer.Samples.SqlCeWriterPlugin,DataIntegrationSamples">
- <dbFilePath>c:\FormData.sdf</dbFilePath>
- </processPlugin>
- </processForms>