10 August, 2010
XSLT namespace fun in scripting functoids
Another thing I frequently forget...
To find the namespace prefix to use in a scripting functoid in a map, right click on the map, select "validate map" and view the resulting xsl file. The opening stylesheet tag should show you which namespace prefixes apply to each input message (eg. s0 / s1).
If you're using aggregate schemas (particularly if you changed an existing map to use an aggregate input schema), you may now need to include the root node name in your XSLT. For example, if you have a message
<?xml:namespace prefix="ns0" /><ns0:request ns0="http://myschema">
<ns0:order>
<ns0:id>1234</ns0:id>
</ns0:order>
</ns0:request>
and you previously referenced the ID element using s0:order/so:id
you may now need to reference it using //s0:Request/s0:order/so:id
To find the namespace prefix to use in a scripting functoid in a map, right click on the map, select "validate map" and view the resulting xsl file. The opening stylesheet tag should show you which namespace prefixes apply to each input message (eg. s0 / s1).
If you're using aggregate schemas (particularly if you changed an existing map to use an aggregate input schema), you may now need to include the root node name in your XSLT. For example, if you have a message
<?xml:namespace prefix="ns0" /><ns0:request ns0="http://myschema">
<ns0:order>
<ns0:id>1234</ns0:id>
</ns0:order>
</ns0:request>
and you previously referenced the ID element using s0:order/so:id
you may now need to reference it using //s0:Request/s0:order/so:id
29 July, 2010
VB IsDate equivalent in C#
Now I'm living in a C# world... and my memory fails me every time I need to do this (which frankly, says worrying things about my memory)... DateTime.TryParse returns a boolean indicating whether the input string can be converted to a datetime or not. So you can write a wee function like the following:
public Boolean checkDate(string inputDate)
{
Boolean retValue = false;
DateTime outputDate;
if (DateTime.TryParse(inputDate, out outputDate))
{
retValue = true;
}
return retValue;
}
public Boolean checkDate(string inputDate)
{
Boolean retValue = false;
DateTime outputDate;
if (DateTime.TryParse(inputDate, out outputDate))
{
retValue = true;
}
return retValue;
}
27 July, 2010
ESB Toolkit and UDDI: Binding a UDDI service to SQL
Maybe I was just blind or stupid when I needed to do this, but it took me a while to find some info on how to set up bindings for SQL in UDDI. So I'm noting these down for next time...
Context: Building a solution using BizTalk 2009 and the ESB Toolkit. I need to call a SQL stored procedure to return price data for a given product. Schemas have already been created using the WCF LOB adapter. Now I want to use a UDDI resolver to configure my off-ramp.
- Browse to the publish tab in UDDI
- Right click on the desired provider and choose "add service"
- Either specify a key or allow the system to generate one
- Edit the service name (eg. GetPriceData)
- Right click on the service (or click on the bindings tab) and add a binding
- Configure the endpoint - instead of an http service, we have an mssql service, so the endpoint will be in this format: mssql://databaseservername//databasename?
- Configure the transport type: Add a category and select the scheme microsoft-com:esb:runtimeresolution:transporttype. Choose a value of WCF Custom Binding.
- Configure the BizTalk application: Add a custom category and search for tModels using %biztalk%. Select microsoft-com:esb:runtimeresolution:biztalkapplication. Type "BizTalk Application" for the key name and the name of the your BizTalk application container (this is in your admin console) for the key value.
- Add instance info (search for tModels containing %esb%) - you need to add the following:
- microsoft-com:esb:runtimeresolution:transporttype (use WCF-Custom, by which I mean set the instance parameter to WCF-Custom)
- microsoft-com:esb:runtimeresolution:targetnamespace (target namespace of your SQL schema - in my case http://schemas.microsoft.com/Sql/2008/05/TypedProcedures/dbo )
- microsoft-com:esb:runtimeresolution:action (the correct action is in your schema, if you created it using the WCF LOB adapter - in my case it was {TypedProcedure/dbo/GetPrice} - note that you need to enclose the action in curly braces)
- microsoft-com:esb:runtimeresolution:messageexchangepattern (use "Two-Way", if you're sending a request and expecting a response)
- microsoft-com:esb:runtimeresolution:jaxrpcresponse (set to "false")
- microsoft-com:esb:runtimeresolution:cachetimeout (set to "-1")
- microsoft-com:esb:runtimeresolution:endpointconfig (set to "BindingType=sqlBinding")
To get the key to use in your resolver, click on the service name. On the details tab, click the "more details" link on the right. This brings up the UDDI keys. Copy the value for the version 3 key into the "service key" property for the UDDI resolver in your itinerary.
26 July, 2010
Document structure validation and routing failed messages
A nice way of validating the document structure of an incoming message is to use a receive pipeline with both a disassembler component (which validates the structure) and a validation component (which apparently validates the message against any restrictions in the schema).
Configure your receive location to use this pipeline and check "enable routing for failed messages" on the receive port.
Now you can set up a send port (or whatever you need to handle invalid messages), which subscribes to messages with an ErrorReport.ReceivePortName = your receive port, ErrorReport.ErrorType = 'FailedMessage' and ErrorReport.FailureCategory = (you may need to do some testing to see what the appropriate categories are - the ErrorReport options are promoted properties on the failed message) and send an email alert or set up an error-handling orchestration etc.
Configure your receive location to use this pipeline and check "enable routing for failed messages" on the receive port.
Now you can set up a send port (or whatever you need to handle invalid messages), which subscribes to messages with an ErrorReport.ReceivePortName = your receive port, ErrorReport.ErrorType = 'FailedMessage' and ErrorReport.FailureCategory = (you may need to do some testing to see what the appropriate categories are - the ErrorReport options are promoted properties on the failed message) and send an email alert or set up an error-handling orchestration etc.
Sending flat file attachments with an HTML email from BizTalk
I got this working in two different ways, both from within orchestrations. In both examples, I only ever needed to attach one file per email. However, I think the second example could be easily modified to work with multiple attachments by adding more parts to the multi-part message type. I haven't yet tried using SMTP.Attachments to add multiple attachments (in the first example), but it could be worth a try.
Example 1:
The requirement here was to email a file to the client upon dispatch of an order batch. To do this, I archived the dispatch file (in my case, this was an Excel file) out to a specific file directory using a FILE send port early in the orchestration (as file archiving was a requirement anyway). I used a dynamic send port, so I could set the file name and use this to reference the file later. Note that in the orchestration, I also set up a loop with a delay shape to check for the existence of the archived file before creating the email (in case the send port had been disabled or something else went wrong). To do this, I used a helper class which returned the boolean value of System.IO.File.Exists(path)- where path is a string variable equal to the archive file directory path and file name.
I created a multi-part message type for the email. It only had one part of type RawString (Microsoft.Samples.BizTalk.XlangCustomFormatters.RawString - sample code for this is in the SDK).
In a message assignment shape, I built the body of the email (usually I'd have HTML tags in the string variable for the body, but it's a pain to publish them here) using a string variable responseText and message ProvDispatch (an instance of the multi-part message type) eg.
responseText = "This is to confirm an order requiring provisioning has been dispatched.";
ProvDispatch.ResponseBody = new Microsoft.Samples.BizTalk.XlangCustomFormatters.RawString(responseText);
ProvDispatch.ResponseBody(Microsoft.XLANGs.BaseTypes.ContentType) = "text/html";
ProvDispatch(SMTP.EmailBodyFileCharset) = "UTF-8";
In the same shape, I then added the Excel file created earlier in the orchestration as an attachment. The file path was stored in a string variable "reportPath" and the file name was stored in a string variable "attachmentName".
ProvDispatch(SMTP.Attachments) = reportPath + attachmentName + ".xlsx";
Then set the subject and email addresses (if required):
ProvDispatch(SMTP.Subject) = "Dispatch Notification";
ProvDispatch(SMTP.From) = emailVariable;
I needed to use dynamic addressing for the outbound email, so I then configured my send port (DispatchResponse_Port) with the email to address, stored earlier in the orchestration in a string variable "provEmail":
DispatchResponse_Port(Microsoft.XLANGs.BaseTypes.Address) = "mailto:" + provEmail;
Your port should be configured to use the PassThru pipeline.
This example produces an email with a formatted HTML body "This is to confirm an order requiring provisioning has been dispatched" with the required file attached in the correct format with the correct name. For example, if the variable "attachmentName" above had a value "TestFile", the attachment on the email would be "TestFile.xlsx".
Example 2:
In this example, the client was sending an order file which needed to undergo some validation checks. If the file failed validation, it needed to be emailed back to the client as an attachment, with a list of validation errors in the body of the email.
The order file was in CSV format, which was being mapped to a common order schema on the receive port. So, if data validation checks failed, I needed to recreate the CSV file the customer sent in order to email it back to them.
To do this, I:
Example 1:
The requirement here was to email a file to the client upon dispatch of an order batch. To do this, I archived the dispatch file (in my case, this was an Excel file) out to a specific file directory using a FILE send port early in the orchestration (as file archiving was a requirement anyway). I used a dynamic send port, so I could set the file name and use this to reference the file later. Note that in the orchestration, I also set up a loop with a delay shape to check for the existence of the archived file before creating the email (in case the send port had been disabled or something else went wrong). To do this, I used a helper class which returned the boolean value of System.IO.File.Exists(path)- where path is a string variable equal to the archive file directory path and file name.
I created a multi-part message type for the email. It only had one part of type RawString (Microsoft.Samples.BizTalk.XlangCustomFormatters.RawString - sample code for this is in the SDK).
In a message assignment shape, I built the body of the email (usually I'd have HTML tags in the string variable for the body, but it's a pain to publish them here) using a string variable responseText and message ProvDispatch (an instance of the multi-part message type) eg.
responseText = "This is to confirm an order requiring provisioning has been dispatched.";
ProvDispatch.ResponseBody = new Microsoft.Samples.BizTalk.XlangCustomFormatters.RawString(responseText);
ProvDispatch.ResponseBody(Microsoft.XLANGs.BaseTypes.ContentType) = "text/html";
ProvDispatch(SMTP.EmailBodyFileCharset) = "UTF-8";
In the same shape, I then added the Excel file created earlier in the orchestration as an attachment. The file path was stored in a string variable "reportPath" and the file name was stored in a string variable "attachmentName".
ProvDispatch(SMTP.Attachments) = reportPath + attachmentName + ".xlsx";
Then set the subject and email addresses (if required):
ProvDispatch(SMTP.Subject) = "Dispatch Notification";
ProvDispatch(SMTP.From) = emailVariable;
I needed to use dynamic addressing for the outbound email, so I then configured my send port (DispatchResponse_Port) with the email to address, stored earlier in the orchestration in a string variable "provEmail":
DispatchResponse_Port(Microsoft.XLANGs.BaseTypes.Address) = "mailto:" + provEmail;
Your port should be configured to use the PassThru pipeline.
This example produces an email with a formatted HTML body "This is to confirm an order requiring provisioning has been dispatched" with the required file attached in the correct format with the correct name. For example, if the variable "attachmentName" above had a value "TestFile", the attachment on the email would be "TestFile.xlsx".
Example 2:
In this example, the client was sending an order file which needed to undergo some validation checks. If the file failed validation, it needed to be emailed back to the client as an attachment, with a list of validation errors in the body of the email.
The order file was in CSV format, which was being mapped to a common order schema on the receive port. So, if data validation checks failed, I needed to recreate the CSV file the customer sent in order to email it back to them.
To do this, I:
- Created a map, which mapped the contents of the common order message to the CSV order message
- Created a send pipeline "MyFF_SPipe", using a Flat File Assembler (document schema = CSV order file schema)
- Created a receive pipeline "MyXDoc_RPipe", with nothing in the disassemble stage of the pipeline. This will be used to receive any document in a message variable of type System.XML.XmlDocument (which preserves the format of the inbound message - ie. does not convert it to XML)
- In the orchestration, created a message FFRequest (type = CSV order file schema). Constructed this message using the map file created in step 1.
- Created a message FFRequestOut (type = CSV order file schema)
- Created an atomic scope and defined a variable "inputMsgs" of type Microsoft.XLANGs.Pipeline.SendPipelineInputMessages (add a reference to Microsoft.XLANGS.Pipeline.dll to the project) and a variable "pipelineMsgs" of type Microsoft.XLANGs.Pipeline.ReceivePipelineOutputMessages
- To create the CSV file, in an atomic scope, I executed the send pipeline created in step 2 from a message assignment shape in a construct block (message constructed = FFRequestOut), using the following code:
FFRequestOut = null; inputMsgs.Add(FFRequest); Microsoft.XLANGs.Pipeline.XLANGPipelineManager.ExecuteSendPipeline (typeof(MySolution.pipelines.MyFF_SPipe) ,inputMsgs ,FFRequestOut); - Called the receive pipeline MyXDoc_RPipe created in step 3 from an expression shape using the following code:
pipelineMsgs = Microsoft.XLANGs.Pipeline.XLANGPipelineManager.ExecuteReceivePipeline(typeof(MySolution.pipelines.MyXDoc_RPipe),FFRequestOut); - In a construct block, assigned the first message (there should only be one) in pipelineMsgs to a message called "Attachment" of type System.Xml.XmlDocument using the following code:
pipelineMsgs.MoveNext();
Attachment = new System.Xml.XmlDocument();
pipelineMsgs.GetCurrent(Attachment); - Created a multi-part message type for the outbound email with two parts - BodyPart of type Microsoft.Samples.BizTalk.XlangCustomFormatters.RawString and Attachment of type System.Xml.XmlDocument
- In a message assignment shape in a construct block (constructing a message "FailResp" with the multi-part message type created in step 10), build the message:
responseText = "File rejected for the following reasons...."; FailResp.BodyPart = new Microsoft.Samples.BizTalk.XlangCustomFormatters.RawString(responseText); FailResp.BodyPart(Microsoft.XLANGs.BaseTypes.ContentType) = "text/html"; FailResp.Attachment = new System.Xml.XmlDocument(); FailResp.Attachment = Attachment; FailResp.Attachment(MIME.FileName) = fileName; FailResp(SMTP.MessagePartsAttachments) = 2; FailResp(SMTP.Subject) = "Order file REJECTED"; FailResp(SMTP.EmailBodyFileCharset) = "UTF-8"; FailResp(SMTP.From) = emailFrom;
* emailFrom is a string variable, as is fileName (which simply sets the attachment name to something sensible eg. RejectedOrderFile.csv) - I'm using a dynamic send port again, so I need to also configure the email to address (see example 1). The send port is also configured to use the PassThru pipeline.
26 May, 2010
Setting up Host Names
Again, this is just one of those things that I feel I should remember, but know I never will:
To set up host names, edit the file C:\WINDOWS\system32\drivers\etc\hosts.
It's a simple text file that maps IP addresses to host server names.
To set up host names, edit the file C:\WINDOWS\system32\drivers\etc\hosts.
It's a simple text file that maps IP addresses to host server names.
12 May, 2010
XML Editor Just Wasted My Day
I can't believe this happened. And yet I can.
Try this:
Copy the following xml snippet into a file and save it with an ".xml" extension:
<rowset>
<row>
<blah>OPN </BLAH>
</ROW>
</ROWSET>
If you open that file in e.g. Notepad++, then you see it as-is.
If you right-click on that file, select "Open With" -> "XML Editor". Now what do you see?
- <rowset>
- <row>
<blah>OPN</BLAH>
</ROW>
</ROWSET>
No padding spaces. 37 missing spaces to be exact.
Now, let's say one is troubleshooting a Biztalk map that it throwing output validation errors because a field exceeds the defined 20 character MaxLength. The input file looks fine (in XML Editor). "Why would Biztalk just pad this thing out to 40 characters?"
Would you say that it would be useful to know that the input file has 37 trailing spaces after the text?
How much time, would you say, one could waste Googling and trying various oddball things trying to get rid of this mysterious series of spaces? My answer to that question is embarrassing.
Not a happy developer today.
Try this:
Copy the following xml snippet into a file and save it with an ".xml" extension:
<rowset>
<row>
<blah>OPN </BLAH>
</ROW>
</ROWSET>
If you open that file in e.g. Notepad++, then you see it as-is.
If you right-click on that file, select "Open With" -> "XML Editor". Now what do you see?
- <rowset>
- <row>
<blah>OPN</BLAH>
</ROW>
</ROWSET>
No padding spaces. 37 missing spaces to be exact.
Now, let's say one is troubleshooting a Biztalk map that it throwing output validation errors because a field exceeds the defined 20 character MaxLength. The input file looks fine (in XML Editor). "Why would Biztalk just pad this thing out to 40 characters?"
Would you say that it would be useful to know that the input file has 37 trailing spaces after the text?
How much time, would you say, one could waste Googling and trying various oddball things trying to get rid of this mysterious series of spaces? My answer to that question is embarrassing.
Not a happy developer today.
Subscribe to:
Posts (Atom)