Dynamic FTP Folder pipeline

689 Views Asked by At

I'm trying to set Dynamically the output folder of an FTP location. Assignment, for each customer I need to create a separate folder to store an Excel file and / or XML file.

What I've tried

  • Created a Custom Pipeline Component to set all the required Properties into a FTP send port.
  • Tried the same pipeline into a Dynamic Send Port
  • For testing tried the code in an Orchestration.

What I've noticed:

When I send through the FTP Send Port the properties won't be overridden by the custom pipeline properties.

When I send through the Dynamic I always get the following error

A failure was encountered while transmiting the message

Even when I'm trying to set the properties into the Orchestration I get the same error.

Also when I'm trying to send through the Dynamic Send Port I notice that the pipeline component is not touched.

Execute code part of the custom pipeline component

public IBaseMessage Execute(IPipelineContext pipelineContext, IBaseMessage inputMessage)
    {
        Guid callToken = TraceManager.PipelineComponent.TraceIn(CLASSNAME + ".Execute() - Start", pipelineContext.PipelineID, pipelineContext.PipelineName, pipelineContext.StageID);

        if (!this.Active)
        {
            TraceManager.PipelineComponent.TraceOut(callToken, CLASSNAME + ".Execute() - Pipeline component is not active!");
            return inputMessage;
        }

        try
        {
            string completeFTPUri = null;
            string fileName = null;
            string accountNumber = Convert.ToString(inputMessage.Context.Read(PROP_ACCOUNTNUMBER.Name.Name, PROP_ACCOUNTNUMBER.Name.Namespace));

            if (!string.IsNullOrWhiteSpace(accountNumber))
                this.Folder = string.Format("{0}/{1}", this.Folder, accountNumber);

            if (!string.IsNullOrWhiteSpace(this.Folder))
                completeFTPUri = string.Format("ftp://{0}:21/{1}", this.FTPUri, this.Folder);
            else
                completeFTPUri = this.FTPUri;

            if (!UseDefaultFilename)
            {
                string receiveFilename = null;
                receiveFilename = Convert.ToString(inputMessage.Context.Read(FTP_RECEIVED_FILENAME.Name.Name, FTP_RECEIVED_FILENAME.Name.Namespace));

                if (!string.IsNullOrWhiteSpace(receiveFilename))
                    fileName = Path.GetFileName(receiveFilename);
            }

            if (string.IsNullOrWhiteSpace(fileName))
            {
                if (string.IsNullOrWhiteSpace(this.Filename))
                    fileName = DEFAULT_FILENAME;
                else
                    fileName = this.Filename;
            }

            if (fileName.Contains("{0") || fileName.Contains("{1"))
            {
                fileName = string.Format(fileName, DateTime.Now, inputMessage.MessageID);
            }

            if (!string.IsNullOrWhiteSpace(this.Folder))
            {
                //inputMessage.Context.Write(FTP_BEFORE_PUT.Name.Name, FTP_BEFORE_PUT.Name.Namespace, string.Format("MKDIR {0}", string.Format("ftp://{0}:21/{1}", this.FTPUri, this.Folder)));
                inputMessage.Context.Promote(FTP_BEFORE_PUT.Name.Name, FTP_BEFORE_PUT.Name.Namespace, string.Format("MKDIR {0}", completeFTPUri));
            }

            //inputMessage.Context.Write(OUTBOUND_TRANSPORT_LOCATION.Name.Name, OUTBOUND_TRANSPORT_LOCATION.Name.Namespace, completeFTPUri);
            //inputMessage.Context.Write(FILE_RECEIVED_FILENAME.Name.Name, FILE_RECEIVED_FILENAME.Name.Namespace, fileName);
            //inputMessage.Context.Write(FTP_USERNAME.Name.Name, FTP_USERNAME.Name.Namespace, _userName);
            //inputMessage.Context.Write(FTP_PASSWORD.Name.Name, FTP_PASSWORD.Name.Namespace, _password);
            inputMessage.Context.Promote(OUTBOUND_TRANSPORT_LOCATION.Name.Name, OUTBOUND_TRANSPORT_LOCATION.Name.Namespace, completeFTPUri);
            inputMessage.Context.Promote(OUTBOUND_TRANSPORT_TYPE.Name.Name, OUTBOUND_TRANSPORT_TYPE.Name.Namespace, "FTP");
            inputMessage.Context.Promote(FILE_RECEIVED_FILENAME.Name.Name, FILE_RECEIVED_FILENAME.Name.Namespace, fileName);
            inputMessage.Context.Promote(FTP_USERNAME.Name.Name, FTP_USERNAME.Name.Namespace, this.UserName);
            inputMessage.Context.Promote(FTP_PASSWORD.Name.Name, FTP_PASSWORD.Name.Namespace, this.Password);

        }
        catch (Exception ex)
        {
            TraceManager.PipelineComponent.TraceError(ex, false, callToken);
            throw new Exception(CLASSNAME + ".Execute() - Failed to set the filename.", ex);
        }

        TraceManager.PipelineComponent.TraceOut(callToken, CLASSNAME + ".Execute() - Finished.");
        return inputMessage;
    }

EDIT:

After trying a lot this morging an update. When I try to Send Dynamicly through the Static SendPort I keep the same issue. When I try to Send Dynamicly through a Dynamic SendPort I'm getting different error:

Inner exception: The value assigned to property 'Microsoft.XLANGs.BaseTypes.Address' is not valid: 'FTP URI'.

I don't know what the best solution is to resolve this issue. I can also write everything to a helper class en try to send through C# code. But I want to use the force of BizTalk and want to be able to enable en disable ports when necessary. That's the main reason. I'm not afraid to write custom pipeline components or somthing else, so if someone could help. PLEASE

Code of the Message Assign of the Orchestration

MsgPublishArticleMessage = MsgFullArticleMessage;
MsgPublishArticleMessage(*) = MsgFullArticleMessage(*);

MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Domain) = "ArticleMessage";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Service) = "PricatService";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Action) = "PublishPricatXLSX";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Version) = "1.0";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.AccountNumber) = articleMessageRequest.AccountNumber;
MsgPublishArticleMessage(BTS.OutboundTransportLocation) = "ftp://URI:21/Pricat/" + articleMessageRequest.AccountNumber;
MsgPublishArticleMessage(BTS.OutboundTransportType) = "FTP";
MsgPublishArticleMessage(FTP.Password) = "********";
MsgPublishArticleMessage(FTP.UserName) = "UserName";
MsgPublishArticleMessage(FTP.BeforePut) = "MKDIR " + articleMessageRequest.AccountNumber;
MsgPublishArticleMessage(FTP.ReceivedFileName) = Destil.BizTalk.ArticleMessage.Components.OrchestrationHelper.CreateReceivedFileName(articleMessageRequest, ".xlsx");
PublishArticleMessagePort(Microsoft.XLANGs.BaseTypes.Address) = "FTPURI";
PublishArticleMessagePort(Microsoft.XLANGs.BaseTypes.TransportType) = "FTP";
MsgPublishArticleMessage(BTS.IsDynamicSend) = true;

EDIT 2:

When I change the Message Assingment to below code I can send the file to a dynamic folder. The only problem I'm running into now: When the Folder already exist I'm getting a failure.

Does anyone know what FTP command I need to use to create a Folder only if it don't exist? I've try'ed the following commands

MDK -p /Pricat/AccountNumber;
MDK /Pricat/AccountNumber;
if not exist "/Pricat/AccountNumber" MDK /Pricat/AccountNumber

Changed code of message assign in the orchestration

MsgPublishArticleMessage = MsgFullArticleMessage;
MsgPublishArticleMessage(*) = MsgFullArticleMessage(*);

MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Domain) = "ArticleMessage";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Service) = "PricatService";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Action) = "PublishPricatXLSX";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Version) = "1.0";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.AccountNumber) = articleMessageRequest.AccountNumber;
MsgPublishArticleMessage(BTS.OutboundTransportLocation) = "ftp://URI:21/Pricat/" + articleMessageRequest.AccountNumber;
MsgPublishArticleMessage(BTS.OutboundTransportType) = "FTP";
MsgPublishArticleMessage(FTP.Password) = "*********";
MsgPublishArticleMessage(FTP.UserName) = "username";
MsgPublishArticleMessage(FTP.BeforePut) = "MKD Pricat/" + articleMessageRequest.AccountNumber + "; CWD Pricat/" + articleMessageRequest.AccountNumber;
PublishArticleMessagePort(Microsoft.XLANGs.BaseTypes.Address) = "ftp://URI:21/" + DOMAIN.BizTalk.ArticleMessage.Components.OrchestrationHelper.CreateReceivedFileName(articleMessageRequest, ".xlsx");
PublishArticleMessagePort(Microsoft.XLANGs.BaseTypes.TransportType) = "FTP";
MsgPublishArticleMessage(BTS.IsDynamicSend) = true;
2

There are 2 best solutions below

0
On

When overwriting static send port properties you have to give adapter know that it should use message properties instead of port properties.

Set IsDynamicSend property to true

inmsg.Context.Promote("IsDynamicSend", "http://schemas.microsoft.com/BizTalk/2003/system-properties", true);
2
On

From the code snippet you have provided, can you check the below line.

PublishArticleMessagePort(Microsoft.XLANGs.BaseTypes.Address) = "FTPURI";

You have declared FTPURI as a variable and assigning a constant string to the address. This might explain the error -

Inner exception: The value assigned to property 'Microsoft.XLANGs.BaseTypes.Address' is not valid: 'FTP URI'.