FRENDS Technology logo

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.

01     
02public class ContextBasedRoutingStep : MessageProcessingStepBase
03{
04    public const string DynamicDirectoryKeyName = "DestinationDirectory";
05    private readonly IDictionary<string, string> _directoryConfigurations = new Dictionary<string, string>();
06 
07    //implementing MessageProcessingStep requires a step to have a constructor with a single MessageProcessingStepConfig parameter
08    public ContextBasedRoutingStep(MessageProcessingStepConfig config) : base(config)
09    {
10        XmlDocument xdoc = new XmlDocument();
11        xdoc.LoadXml(config.CustomParameters);
12         
13        //select all destination directory configuration elements from parameter XML string
14        var directoryConfigs = xdoc.GetElementsByTagName("directoryConfig");
15         
16        //loop through all configurations and add them to dictionary be used during execution
17        foreach (XmlElement route in directoryConfigs)
18        {
19            string rootElementName = route.ChildNodes[0].InnerText;        //first child contains root element name
20            string destinationDirectory = route.ChildNodes[1].InnerText;   //second child contains destination directory for file
21             
22            _directoryConfigurations.Add(rootElementName, destinationDirectory);
23        }
24    }
25 
26    public override FileInfo Execute(FileInfo inputFile, ExtendedTransferContext context)
27    {
28        try
29        {
30            //if key already exists, a destination directory for previous file transfer is stored in dictionary
31            //remove the previous value before proceeding
32            if (context.CustomData.ContainsKey(DynamicDirectoryKeyName))
33                context.CustomData.Remove(DynamicDirectoryKeyName);
34 
35            //load XML file
36            var xdoc = new XmlDocument();
37            xdoc.Load(inputFile.FullName);
38 
39            string xmlRootElement = xdoc.DocumentElement.Name;
40            //is the XML documents root element found in dictionary created by constructor
41            //if so, set the destination directory accordingly
42            if (_directoryConfigurations.ContainsKey(xmlRootElement))
43                context.CustomData.Add(DynamicDirectoryKeyName, _directoryConfigurations[xmlRootElement]);
44        }
45        catch (Exception ex)
46        {
47            Trace.WriteLine(String.Format("Error occurred while parsing XML file, file will be transferred to the default destination folder. Exception details: {0}", ex));
48        }
49 
50        return inputFile;
51    }
52 
53    public void Dispose()
54    {
55        Trace.WriteLine("Disposing ContextBasedRoutingStep");
56    }
57}    

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.

01public class ContextBasedRoutingFileEndPoint : FileEndPoint
02{
03    private readonly IDictionary<string, object> _customData;
04 
05    public ContextBasedRoutingFileEndPoint(TransferEndPointConfig endPointConfig, IDictionary<string, object> customData) : base(endPointConfig)
06    {
07        _customData = customData;
08    }
09 
10    //override Put to transfer the file to dynamically configured destination folder
11    public override void Put(string sourceFile, string remoteFile)
12    {
13        //if custom destination folder has been defined, set 'remoteFile' i.e. where the file will be copied to, to point to new  directory
14        if (_customData.ContainsKey(ContextBasedRoutingStep.DynamicDirectoryKeyName))
15        {
16            string directory = (string)_customData[ContextBasedRoutingStep.DynamicDirectoryKeyName];
17            remoteFile = Path.Combine(directory, remoteFile);
18        }
19 
20        base.Put(sourceFile, remoteFile);
21    }
22 
23    //override Rename in order to be able to rename the destination file in correct directory
24    public override string Rename(string remoteFile, string to)
25    {
26        //if custom destination folder has been defined, set 'remoteFile' and 'to' to point to new  directory
27        //in order to be able to perform renaming
28        if (_customData.ContainsKey(ContextBasedRoutingStep.DynamicDirectoryKeyName))
29        {
30            string directory = (string)_customData[ContextBasedRoutingStep.DynamicDirectoryKeyName];
31            remoteFile = Path.Combine(directory, Path.GetFileName(remoteFile));
32            to = Path.Combine(directory, to);
33        }
34 
35        return base.Rename(remoteFile, to);
36    }
37}

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:

Extensions: Adding Custom data - EndPoint

Next, set the file transfer to use the MessageProcessingStep we created:

Extensions: Adding Custom data - MessageProcessingStep

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:

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.