Extension HowTo - Using dynamic data during file transfer
In some cases you may have a need to be able to add custom data to be shared
between endpoints and message processing steps. This sample describes how to add
and use custom data during file transfers.
This sample is designed to transfer XML files. While transferring files the destination
directory is configured dynamically instead of using a static destination folder
configured in Cobalt connection point schema.
Create a message processing step for inserting the custom data
First we create a custom message processing step which adds the custom properties
to be used in dictionary which in turn can be used our custom endpoint which will
be created in Step 2.
Note: It is good practice to add only a single unique dictionary entry to the CustomData dictionary for storing the data for a single purpose. This way the chance of conflicts is smaller. A conflict could occur when using custom endpoints and custom message processing steps, which all use the dictionary for different purposes.
For example, if a source endpoint would store data about files under their names in the dictionary, which a message processing step would use, and another message processing step would do the same with some other information, the dictionary entries could conflict.
02 | public class ContextBasedRoutingStep : MessageProcessingStepBase |
04 | public const string DynamicDirectoryKeyName = "DestinationDirectory" ; |
05 | private readonly IDictionary< string , string > _directoryConfigurations = new Dictionary< string , string >(); |
08 | public ContextBasedRoutingStep(MessageProcessingStepConfig config) : base (config) |
10 | XmlDocument xdoc = new XmlDocument(); |
11 | xdoc.LoadXml(config.CustomParameters); |
14 | var directoryConfigs = xdoc.GetElementsByTagName( "directoryConfig" ); |
17 | foreach (XmlElement route in directoryConfigs) |
19 | string rootElementName = route.ChildNodes[0].InnerText; |
20 | string destinationDirectory = route.ChildNodes[1].InnerText; |
22 | _directoryConfigurations.Add(rootElementName, destinationDirectory); |
26 | public override FileInfo Execute(FileInfo inputFile, ExtendedTransferContext context) |
32 | if (context.CustomData.ContainsKey(DynamicDirectoryKeyName)) |
33 | context.CustomData.Remove(DynamicDirectoryKeyName); |
36 | var xdoc = new XmlDocument(); |
37 | xdoc.Load(inputFile.FullName); |
39 | string xmlRootElement = xdoc.DocumentElement.Name; |
42 | if (_directoryConfigurations.ContainsKey(xmlRootElement)) |
43 | context.CustomData.Add(DynamicDirectoryKeyName, _directoryConfigurations[xmlRootElement]); |
47 | Trace.WriteLine(String.Format( "Error occurred while parsing XML file, file will be transferred to the default destination folder. Exception details: {0}" , ex)); |
55 | Trace.WriteLine( "Disposing ContextBasedRoutingStep" ); |
Message processing step receives an XML structure as its constructor parameter which
contains details on how different XML files are handled, i.e to which directory
they will be transferred. Consider the following XML parameter structure:
<destinationConfig xmlns="">
<directoryConfig>
<rootElementName>invoice</rootElementName>
<destination>C:\XML\Invoices</destination>
</directoryConfig>
<directoryConfig>
<rootElementName>order</rootElementName>
<destination>C:\XML\Orders</destination>
</directoryConfig>
</destinationConfig>
Structure instructs that if XML document's root element is invoice
the file is transferred to C:\XML\Invoices
directory. If the document's
root element is order
the file is transferred to C:\XML\Orders
directory.
Parameters could contain any number of directoryConfig
elements.
That adds more dynamics to the Cobalt routine executions as new directives on where
to send XML files, based on their root element name, could be easily added simply
by adding new directoryConfig
elements to the Cobalt connection point
configuration.
During file transfer execution the transferred XML file is parsed in order to check
the whether the root element is order
or invoice
and set
the destination directory accordingly. If error occurs during parsing the XML file,
the destination directory is not set and the file will be transferred to the default
destination folder (Cobalt connection point schema's destination endpoint's Directory
parameter).
Because ContextBasedRoutingStep
implements IMessageProcessingStep
we have to implement the Dispose
method as well, even though there are
no resources to free etc. Ín this case we just log a message of disposing the
MessageProcessingStep.
Create an endpoint consuming the custom data
Next, we need to create an endpoint for using the custom data added by our message
processing step. This section shows only the operations which are needed to perform
in order to get this sample to work. For more in depth view of extending an endpoint,
see: Extension HowTo - Extend Existing
Endpoint or Endpoint from scratch
HowTo - Creating a new endpoint.
01 | public class ContextBasedRoutingFileEndPoint : FileEndPoint |
03 | private readonly IDictionary< string , object > _customData; |
05 | public ContextBasedRoutingFileEndPoint(TransferEndPointConfig endPointConfig, IDictionary< string , object > customData) : base (endPointConfig) |
07 | _customData = customData; |
11 | public override void Put( string sourceFile, string remoteFile) |
14 | if (_customData.ContainsKey(ContextBasedRoutingStep.DynamicDirectoryKeyName)) |
16 | string directory = ( string )_customData[ContextBasedRoutingStep.DynamicDirectoryKeyName]; |
17 | remoteFile = Path.Combine(directory, remoteFile); |
20 | base .Put(sourceFile, remoteFile); |
24 | public override string Rename( string remoteFile, string to) |
28 | if (_customData.ContainsKey(ContextBasedRoutingStep.DynamicDirectoryKeyName)) |
30 | string directory = ( string )_customData[ContextBasedRoutingStep.DynamicDirectoryKeyName]; |
31 | remoteFile = Path.Combine(directory, Path.GetFileName(remoteFile)); |
32 | to = Path.Combine(directory, to); |
35 | return base .Rename(remoteFile, to); |
In this sample we use FileEndpoint as our base class. We need to override Put
method as otherwise the file would be transferred to the default destination folder
configured in Cobalt connection point schema. First we need to check has the DestinationDirectory
been defined, i.e. is the key found in the CustomData
dictionary. If
the destination directory has been set, set the destination folder to point to that
new direction, otherwise transfer the file normally to the default destination directory.
Because the CustomData
dictionary is of type <string,object>
we need to cast the value to string
.
Because Cobalt transfers file with a temporary file name (unless otherwise configured,
see Cobalt general parameters for
more details) we need to override the Rename
method as well. As with
the Put
method we also need to check is the DestinationDirectory
defined. If it is defined we need to set the rename operation to point to that new
directory instead of the default destination directory.
Step 1: Compile and deploy the assembly
In order to use the code with Cobalt you first need to compile it to an assembly
and deploy the assembly somewhere FRENDS Iron can find the code. This means either
the \Program Files\Frends Technology\FRENDS Iron -directory or the GAC
Step 2: Configure Cobalt
Once the assembly is deployed, you need to configure Cobalt to use the new custom
EndPoint and MessageProcessing step. To do this, just create a new Cobalt connection
point as described in the manual and:
- Change the
Destination
endpoint type to Custom
- Under the
Custom
element:
- Set the
AssemblyName
to the assembly name where the custom class resides
in, e.g. Frends.Cobalt.Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
.
- Set the
ClassName
to class name of custom class, e.g.Frends.Cobalt.Extensions.ContextBasedRoutingFileEndPoint
.
- Leave the
Parameters
empty, as our custom endpoint does not need any
parameters.
Next, set the file transfer to use the MessageProcessingStep
we created:
- Select
MessageProcessing
and set the first Step
type to
Custom
.
- Under the
Custom
element:
- Set the
AssemblyName
to the assembly name where the custom class resides
in, e.g. Frends.Cobalt.Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
.
- Set the
ClassName
to class name of custom class, e.g.Frends.Cobalt.Extensions.ContextBasedRoutingStep
.
- Set
Parameters
to <destinationConfig xmlns=""><directoryConfig><rootElementName>invoice</rootElementName><destination>C:\XML\Invoices</destination></directoryConfig><directoryConfig><rootElementName>order</rootElementName><destination>C:\XML\Orders</destination></directoryConfig></destinationConfig>
Step 3: Create files to transfer and required directories
For this sample, create 2 XML files in your Cobalt input directory. Create file
with a name of 'order.xml' and with the following contents:
<?xml version="1.0"?>
<order>
<customer>Global Corporation In</customer>
<address>Central Avenue 5</address>
<item>General big item</item>
<price>50000.00</price>
</order>
Create a second file with a name of 'invoice.xml' and with the following contents:
<?xml version="1.0"?>
<invoice>
<customer>Acme Inc</customer>
<address>Hill Street 3</address>
<sum>8650.00</sum>
</invoice>
Also create the directories:
- C:\XML\Invoices
- C:\XML\Orders
Step 4: Create and execute the file transfer routine
As in the manual create and
execute file transfer routine.
Examine the C:\XML\Orders
and C:\XML\Invoices
directories.
You should have one file transferred to each folder, based on the root element of
the XML file.