Back in November I wrote a post about encoding messages as base64 strings in a BizTalk map. I never added the explicit implementation of how to decode the message though. However, I was asked to provide an example of it, so here is part 2: how to decode a base64 string from an XML message and output it in BizTalk.
Consider the scenario to be the opposite of what we did in part 1.
We have a message on the input side that has two fields, one with an id as a string, and another string element that holds the entire base64 encoded string.
<ns0:Root xmlns:ns0="http://BizTalk_Server_Project6.Schema1">
<SomeId>1</SomeId>
<EncodedData>PG5zMDpSb290IHhtbG5zOm5zMD0iaHR0cDovL0JpelRhbGtfU2VydmVyX1Byb2plY3Q2LlNjaGVtYTIiPg0KICA8RGF0YT5EYXRhXzA8L0RhdGE+IA0KICA8RGF0YTI+RGF0YTJfMDwvRGF0YTI+IA0KPC9uczA6Um9vdD4=</EncodedData>
</ns0:Root>
On the output side, we want to have a message that conforms to the schema we have defined. The message is the entire contents of the base64 encoded string in the input.
If we parse the encoded string, we get:
<ns0:Root xmlns:ns0="http://BizTalk_Server_Project6.Schema2">
<Data>Data_0</Data>
<Data2>Data2_0</Data2>
</ns0:Root>
Which conforms fully to the schema defined.
So, the task is to extract the contents from the EncodedData element, decode them, and use the full string as the output message. All in a single BizTalk map.
The map will look like this to begin with, with the two schemas chosen:
Similar to when we encoded, we add first a scripting functoid that has no connections, and paste the code for the decode function in it:
public string Base64DecodeData(string param1)
{
byte[] decodedBytes = Convert.FromBase64String(param1);
return System.Text.UTF8Encoding.UTF8.GetString(decodedBytes);
}
We simply take a string as an input parameter, decode this and return the decoded string back.
Then we create a new scripting functoid that we set to inline Xsl and paste this code into it:
<xsl:variable name="data">
<xsl:value-of select="//EncodedData" />
</xsl:variable>
<xsl:value-of select="userCSharp:Base64DecodeData($data)" disable-output-escaping="yes" />
This functoid is connected to the output root node giving us a map that looks like this:
When executing this map with the input message above, we will get the output message properly formatted.
The tricks used here is two. First off, we use the same pattern as before with calling a predefined function we have in another scripting functoid in order to call a simple C# function from our XSL code. Then in the XSL, we first extract the string from our input message and store it in a variable. This is then passed into our C# function and we get a string back. However, if we do not specify the disable-output-escaping="yes" in our value-of select, we would get the string entity encoded. With this extra property set, the string will be output as it is and the way we want it.
The same technique can of course easily be used to just output part of a message by simply connecting the scripting functoid to the node you want to populate (if for instance you have a schema that has a node defined as xs:any that you want to use).
Ramblings, thoughts and experiences from the life as a BizTalk architect (as well as everything else I catch sight of).
Showing posts with label code. Show all posts
Showing posts with label code. Show all posts
Tuesday, June 24, 2014
Tuesday, November 19, 2013
Base64 encode/decode part of messages in map
This post is to show how to base64 encode or decode a part of a message (or the entire input message for that matter) and output the resulting string in an element in the output message.
The technique used allows for execution of inline C# (or VB) code from another script functoid using inline XSLT / XSLT Template.
The input and output schemas looks like this:
In the example, our entire input message should be base64 encoded and the resulting string output to the EncodedData element in the output message. This element is of the type xs:string.
First, we create a script functoid to hold our inline C# code for base64 encoding of the input node. When building such a method, one might first try to use string as the datatype for the input which will result in just the inner xml being transferred to the method. This is since the data from our XSLT call will be of the type MS.Internal.Xml.Cache.XPathDocumentNavigator. We therefore declare the input variable as XPathNavigator instead to get access to the full XML node. The base64 encoding is done using the System.Text.Encoding class together with the System.Convert class.
public string Base64EncodeData(XPathNavigator param1)
{
byte[] bytesToEncode =
System.Text.Encoding.UTF8.GetBytes(param1.OuterXml);
string encodedText = Convert.ToBase64String(bytesToEncode);
return encodedText;
}
This functoid will not be connected to any node, but just left to "hang" in the map since we call it manually from another script functoid. Another solution to have it a bit cleaner would be to place this code in an external assembly and call that, but sometimes it is nice to have everything in the map.
Next we create a script functoid and connect it to the destination element. We set the following Inline XSLT code:
<xsl:variable name="data">
<xsl:copy-of select="/" />
</xsl:variable>
<xsl:element name="EncodedData">
<xsl:value-of select="userCSharp:Base64EncodeData($data)" />
</xsl:element>
We first declare a variable that holds a copy of our source node which we simply specify using xpath.
Then we call the previously configured method doing the encoding, and send the resulting data to our created destination element.
The map will now look like this:
The result from sending in a test XML is an XML with the entire original contents base64 encoded in an element.
The technique used allows for execution of inline C# (or VB) code from another script functoid using inline XSLT / XSLT Template.
The input and output schemas looks like this:
In the example, our entire input message should be base64 encoded and the resulting string output to the EncodedData element in the output message. This element is of the type xs:string.
First, we create a script functoid to hold our inline C# code for base64 encoding of the input node. When building such a method, one might first try to use string as the datatype for the input which will result in just the inner xml being transferred to the method. This is since the data from our XSLT call will be of the type MS.Internal.Xml.Cache.XPathDocumentNavigator. We therefore declare the input variable as XPathNavigator instead to get access to the full XML node. The base64 encoding is done using the System.Text.Encoding class together with the System.Convert class.
public string Base64EncodeData(XPathNavigator param1)
{
byte[] bytesToEncode =
System.Text.Encoding.UTF8.GetBytes(param1.OuterXml);
string encodedText = Convert.ToBase64String(bytesToEncode);
return encodedText;
}
This functoid will not be connected to any node, but just left to "hang" in the map since we call it manually from another script functoid. Another solution to have it a bit cleaner would be to place this code in an external assembly and call that, but sometimes it is nice to have everything in the map.
Next we create a script functoid and connect it to the destination element. We set the following Inline XSLT code:
<xsl:variable name="data">
<xsl:copy-of select="/" />
</xsl:variable>
<xsl:element name="EncodedData">
<xsl:value-of select="userCSharp:Base64EncodeData($data)" />
</xsl:element>
We first declare a variable that holds a copy of our source node which we simply specify using xpath.
Then we call the previously configured method doing the encoding, and send the resulting data to our created destination element.
The map will now look like this:
The result from sending in a test XML is an XML with the entire original contents base64 encoded in an element.
Monday, December 10, 2012
"Clever" uninstall of msi packages/applications using PowerShell
When I created my own PowerShell script library for BizTalk deployment automation I ran across the need to uninstall applications, both BizTalk applications and non-BizTalk ones, by only knowing their name.
At first, I solved it using a WMI query like so:
This is not a recommended way of doing it though. It is both very slow and also causes a bit of spamming in the Windows Eventlog since the query in fact does a reconfigure of ALL applications installed. This reconfiguration can also cause a bit of other issues in some cases.
I then created another way of trying to uninstall applications in a more failsafe and secure way by using msiexec with the uninstall flag. The tricky part was to find a way to get the product key in order to be able to use msiexec since it requires this for uninstalling an application. The result can be found below.
The script function will take the application name as argument. It will then via the registry (note that this is configured for x64, so change the path if you are running x86) look up the application settings. If the application can be found via name (it should), we extract the product key. Then this is used as a parameter to msiexec.
If the product key cannot be found (it happens), we will instead try to read the uninstall string that is set when installing the application. Windows will run this string when you choose to uninstall an application, so why do not we use it? If found, we extract the product key and do our msiexec call.
If all fail, we throw an exception to be caught in the real part of the script.
This is the full script function:
Function Uninstall-Program([string]$name)
{
$success = $false
# Read installation information from the registry
$registryLocation = Get-ChildItem "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"
foreach ($registryItem in $registryLocation)
{
# If we get a match on the application name
if ((Get-itemproperty $registryItem.PSPath).DisplayName -eq $name)
{
# Get the product code if possible
$productCode = (Get-itemproperty $registryItem.PSPath).ProductCode
# If a product code is available, uninstall using it
if ([string]::IsNullOrEmpty($productCode) -eq $false)
{
Write-Host "Uninstalling $name, ProductCode:$code"
$args="/uninstall $code"
[diagnostics.process]::start("msiexec", $args).WaitForExit()
$success = $true
}
# If there is no product code, try to read the uninstall string
else
{
$uninstallString = (Get-itemproperty $registryItem.PSPath).UninstallString
if ([string]::IsNullOrEmpty($uninstallString) -eq $false)
{
# Grab the product key and create an argument string
$match = [RegEx]::Match($uninstallString, "{.*?}")
$args = "/x $($match.Value) /qb"
[diagnostics.process]::start("msiexec", $args).WaitForExit()
$success = $true
}
else { throw "Unable to uninstall $name" }
}
}
}
if ($success -eq $false)
{ throw "Unable to find application $name" }
}
At first, I solved it using a WMI query like so:
$name = "application name"
$product = Get-WmiObject -Class Win32_Product -Filter "name='$name'" -ComputerName "localhost"
[void]$product.Uninstall()
This is not a recommended way of doing it though. It is both very slow and also causes a bit of spamming in the Windows Eventlog since the query in fact does a reconfigure of ALL applications installed. This reconfiguration can also cause a bit of other issues in some cases.
I then created another way of trying to uninstall applications in a more failsafe and secure way by using msiexec with the uninstall flag. The tricky part was to find a way to get the product key in order to be able to use msiexec since it requires this for uninstalling an application. The result can be found below.
The script function will take the application name as argument. It will then via the registry (note that this is configured for x64, so change the path if you are running x86) look up the application settings. If the application can be found via name (it should), we extract the product key. Then this is used as a parameter to msiexec.
If the product key cannot be found (it happens), we will instead try to read the uninstall string that is set when installing the application. Windows will run this string when you choose to uninstall an application, so why do not we use it? If found, we extract the product key and do our msiexec call.
If all fail, we throw an exception to be caught in the real part of the script.
This is the full script function:
Function Uninstall-Program([string]$name)
{
$success = $false
# Read installation information from the registry
$registryLocation = Get-ChildItem "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"
foreach ($registryItem in $registryLocation)
{
# If we get a match on the application name
if ((Get-itemproperty $registryItem.PSPath).DisplayName -eq $name)
{
# Get the product code if possible
$productCode = (Get-itemproperty $registryItem.PSPath).ProductCode
# If a product code is available, uninstall using it
if ([string]::IsNullOrEmpty($productCode) -eq $false)
{
Write-Host "Uninstalling $name, ProductCode:$code"
$args="/uninstall $code"
[diagnostics.process]::start("msiexec", $args).WaitForExit()
$success = $true
}
# If there is no product code, try to read the uninstall string
else
{
$uninstallString = (Get-itemproperty $registryItem.PSPath).UninstallString
if ([string]::IsNullOrEmpty($uninstallString) -eq $false)
{
# Grab the product key and create an argument string
$match = [RegEx]::Match($uninstallString, "{.*?}")
$args = "/x $($match.Value) /qb"
[diagnostics.process]::start("msiexec", $args).WaitForExit()
$success = $true
}
else { throw "Unable to uninstall $name" }
}
}
}
if ($success -eq $false)
{ throw "Unable to find application $name" }
}
Wednesday, August 15, 2012
BizTalk Map Viewer: Graphical interface for the BizTalk Map Documenter
Quite a while ago during an assignment we needed a way to display the logic in BizTalk maps for developers that didn't have BizTalk installed on their workstations. We quickly turned to the BizTalk Map Documenter which simply is an XSLT file built to take a standard BizTalk map file (.btm) and output it in a human readable format as an HTML page.
In order to use the XSLT file you need to execute it, preferably with the utility msxsl.exe which can take the .btm file and XSLT file as arguments and then output the result to be viewed by the user. However, this caused a bit too much command line hacking in our case and I took an evening in the hotel bar to quickly whip together a GUI for it and thus, the BizTalk Map Viewer was born.
The application is very simple and in it's basic form simply allows you to open a BizTalk map file which then will be parsed by the BizTalk Map Documenter XSLT file and the result will be shown in the application window. I added functionality to save, print and copy the result. I also added functionality to register the application as a valid right-click option for .btm files. If enabled, you will get the option to "Open with BizTalk Map Viewer" when right-clicking on BizTalk map files which then will open the selected file in the application. Just be sure to have placed the application executable in a good folder before registering it since the current path will be used.
Feel free to download the code and executable.
Use it as you please.
In order to use the XSLT file you need to execute it, preferably with the utility msxsl.exe which can take the .btm file and XSLT file as arguments and then output the result to be viewed by the user. However, this caused a bit too much command line hacking in our case and I took an evening in the hotel bar to quickly whip together a GUI for it and thus, the BizTalk Map Viewer was born.
The application is very simple and in it's basic form simply allows you to open a BizTalk map file which then will be parsed by the BizTalk Map Documenter XSLT file and the result will be shown in the application window. I added functionality to save, print and copy the result. I also added functionality to register the application as a valid right-click option for .btm files. If enabled, you will get the option to "Open with BizTalk Map Viewer" when right-clicking on BizTalk map files which then will open the selected file in the application. Just be sure to have placed the application executable in a good folder before registering it since the current path will be used.
Feel free to download the code and executable.
Use it as you please.
Subscribe to:
Posts (Atom)






