Answered by:
Reading Values from SSO into BizTalk Custom XSLT

Question
-
Hi All
I am using BDTF for my application deployment .Now there is a scenario where in I have a map that uses Custom XSLT . There are fields/variables in tht XSLT for which I would like to read the value from SSO , by referring to SSOSettingsFileReader.dll that comes along with BDTF.
I know I can use it in the scripting functoid with external assembly reference option but I want that in the custom XSLT. I have already tried this linkhttp://www.tfabraham.com/BTDFDocs/V5_5/
also if you use any other functoid in the map , the XSL will generate the script at end of the xsl but in case of scripting functoid all you get is
<xsl:variable name="var:v1" select="ScriptNS0:Read(string(TagName/text()))" />and nothing else and the following name space
xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0">
in short no reference to the DLL and the static method inside the dll .Please advise. Thanks in advance
Regards Gaurav
- Edited by Gaurav Sood Thursday, May 4, 2017 8:03 PM edited the link
Thursday, May 4, 2017 8:02 PM
Answers
-
Hi
I was able to make things work by using the code that comes with SSO MMC Snap in and then calling the function inside the custom XSLT using the ExternalAssembly.xml . The process for that is listed here
https://code.msdn.microsoft.com/windowsdesktop/calling-an-external-c74ce8dc
and download MMC Snap in
https://www.microsoft.com/en-in/download/details.aspx?id=14524
But after you use this Snap in there will be issues of NOT able to create the keys , so download the Fix from
https://gallery.technet.microsoft.com/BizTalk-Server-2016-Fix-12784c1f
(Unzip the downloaded file, overwrite the “SSOMMCSnapIn.dll” file to "C:\Program Files (x86)\Microsoft Services\SSO Application Configuration\" folder (or the folder where you have installed the snap-in from the setup file downloaded from Microsoft))
use the function in Custom XLST as
<xsl:variable name="var:YourVariableName" select="ScriptNS0:Read("YourSSOApplicationName" , "YourApplicationKey")" />
The mistake I was doing was in the XSLT was to view the name spaces in one window I changed the xsl:stylesheet tag to the below code
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var s0 userCSharp ScriptNS0" version="1.0" xmlns:ns0="http://www.myNamespace.com/XXX/MsgRequest" xmlns:s0="http://www.MyNameSpace.com/XXX/TagList" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" xmlns:userCSharp="http://schemas.microsoft.com/BizTalk/2003/userCSharp">
INSTEAD of
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var s0 userCSharp ScriptNS0" version="1.0" xmlns:ns0="http://www.myNameSpace.com/XXX/MsgRequest" xmlns:s0="http://www.myNameSpace.com/XXX/TagList" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" xmlns:userCSharp="http://schemas.microsoft.com/BizTalk/2003/userCSharp">
Doing so it introduced new line characters which didnt allow the compiler to read the namespaces . Hence always keep the xsl:stylesheet in one row.
I would like to thank all of you for your time and inputs however.
Regards
Gaurav
Regards Gaurav
- Edited by Gaurav Sood Thursday, May 11, 2017 8:21 AM changed
- Marked as answer by Gaurav Sood Thursday, May 11, 2017 8:22 AM
Thursday, May 11, 2017 8:20 AM
All replies
-
I know I can use it in the scripting functoid with external assembly reference option but I want that in the custom XSLT. I have already tried this link
http://www.tfabraham.com/BTDFDocs/V5_5/
also if you use any other functoid in the map , the XSL will generate the script at end of the xsl but in case of scripting functoid all you get is
Regards Gaurav
Using .net helpers via an external assembly reference is always a better as it prevents memory leaks during map execution.
If you have a custom xslt, its better to have the custom extension and call the helper method rather than doing it inline.
Pi_xel_xar
Blog: My Blog
BizTalkApplicationDeploymentTool: BizTalk Application Deployment Tool/
Friday, May 5, 2017 6:15 AMAnswerer -
Hi
Thank you for your reply. As suggested I have used the custom extension but some how I get the error in that as well. I have implemented exactly the same approach listed at the following link
https://code.msdn.microsoft.com/windowsdesktop/calling-an-external-c74ce8dc
BUT I am NOT using INLINE XSLT within the scripting functiod instead custom XSLT . This is the error I get
sometimes this
Error details: Exception from HRESULT: 0x80131942
OR
Error details: The Messaging Engine failed while executing the inbound map for the message coming from source URL:"C:\Gaurav\BatchIN\*.xml" with the Message Type "http://www.XXXX.com/YYYY/TagList#AllTags". Details:"An error occurred during a call to extension function 'Read'. See InnerException for a complete description of the error."
I am unable to fine any innerException details anywhere . Suggestions please.
Regards
Gaurav
Regards Gaurav
Friday, May 5, 2017 3:16 PM -
Hi Garuav,
In the same link the Author also faced the same issue and he suggested two solution for this issue. Have you tried to implement those solutions ?
you can do one more thing :
first complete your map without using SSO , just assign blank value in respective node and then after this map execution in the orchestration you can use SSO to read the value then assign to respective node.
Here i am assuming that you need to assign some value in any node , not aware about your exact req.
Regards
Abhay Giri
Mark this reply as answered or vote if this help you.Saturday, May 6, 2017 5:44 PM -
Make sure you also apply the Custom Extension XML for the reference to the classes
Any helper classes has to be strong named and added to the GAC
https://msdn.microsoft.com/en-us/library/aa560154.aspxhth /Peter
- Edited by Peter Lykkegaard Sunday, May 7, 2017 5:37 PM Added link to MSDN article
Sunday, May 7, 2017 7:32 AM -
Hi
THanks for your reply . The issue was not related to the workarounds listed on the link since I am using BTS 2016. It was because I did not pass the Key name correctly as a string.
But now there is a new problem. Things WORKED ! BUT ONLY FOR FIRST TIME . Now whatever value I am passing , it only returns the OLD CACHED values . No idea how to solve that .
1. I have deleted the SSO application and tested by passing the correct key name - it fails
2. I have then deleted the valid Key name from the SSO , it fails again
3. I recreated the SSO application with the new updated values to the keys , but Then again , It gave the same OLD value which is no where present in the SSO
4. Thinking that I might have hardcoded something in custom XSLT , I created a new Map and using the scripting functoid and used the external assembly refernce and the fucntion name and passed the parameters , I still get the old values . :(
5. I have tried restarting the host instances as well as the ENTSSO service but to no good .
Please help me on how to resolve this caching issue.
Regards
Gaurav
Regards Gaurav
Monday, May 8, 2017 6:17 AM -
Host instance restart should reflect any changes.
You should restart the instances, your orch is running on if the map is used with orch else the port instances if map runs on port.
Try a Stop and Start aswell.
Pi_xel_xar
Blog: My Blog
BizTalkApplicationDeploymentTool: BizTalk Application Deployment Tool/
Monday, May 8, 2017 7:49 AMAnswerer -
Hi
I am not using the orchestration . Its an inbound map on receive port.
Also I am confused too why restarting the host instances along with the ENTSSO service isnt reflecting the changes in the cache ?
Regards
Gaurav
Regards Gaurav
Monday, May 8, 2017 8:47 AM -
Hi,
1. We need to use the .net code for fetching value from the BTDF from the SSOSettings files. The sso application config code wont work
using System; using System.Collections; using System.Diagnostics; using System.Runtime.InteropServices; using System.IO; using System.Text; using System.Xml; using System.Xml.Serialization; using System.Timers; using Microsoft.BizTalk.SSOClient.Interop; namespace SSOSettingsFileManager { public class SSOSettingsFileReader { private static Hashtable _hashOfHashes = new Hashtable(); public static bool ExceptionOnNull = true; private static DateTime _timeOfLastRefresh; private const int RefreshIntervalSecs = 60; static SSOSettingsFileReader() { _timeOfLastRefresh = DateTime.Now; } /// <summary> /// Will clear all the caches after the refresh interval has expired. /// It is assumed that a lock has been acquired prior to calling this function. /// </summary> private static void ClearAllCachesIfRefreshIntervalExpired() { // First get the elapsed time in seconds. TimeSpan elapsed = DateTime.Now - _timeOfLastRefresh; if (elapsed.TotalSeconds >= RefreshIntervalSecs) { _hashOfHashes.Clear(); _timeOfLastRefresh = DateTime.Now; } } [Obsolete] public static string ReadValue(string affiliateApplication, string valueName) { return ReadString(affiliateApplication,valueName); } public static string ReadString(string affiliateApplication, string valueName) { Hashtable ht = Read(affiliateApplication); string val = (string)ht[valueName]; if(val == null && ExceptionOnNull) { throw(new ArgumentException( string.Format("Requested SSO value {0} is not available within affiliate app {1}", valueName==null?"empty":valueName,affiliateApplication),"valueName")); } return val; } public static int ReadInt32(string affiliateApplication, string valueName) { return System.Convert.ToInt32(ReadString(affiliateApplication, valueName)); } public static void ClearCache(string affiliateApplication) { Hashtable ht = (Hashtable)_hashOfHashes[affiliateApplication]; if(ht != null) { lock (_hashOfHashes) { _hashOfHashes[affiliateApplication] = null; } } } public static Hashtable Read(string affiliateApplication) { Hashtable htToReturn = null; lock(_hashOfHashes) { // Clear all caches if the refresh interval has expired. ClearAllCachesIfRefreshIntervalExpired(); // Check to see if the applicatin is in the table. htToReturn = (Hashtable)_hashOfHashes[affiliateApplication]; if (htToReturn == null) { // Not in table. Read it in from SSO. object propertyValue; ISSOConfigStore configStore = (ISSOConfigStore)new SSOConfigStore(); SSOPropertyBag bag = new SSOPropertyBag(); configStore.GetConfigInfo(affiliateApplication,SSOHelper.InfoIdentifier, SSOFlag.SSO_FLAG_RUNTIME, bag); bag.Read(SSOHelper.PropName, out propertyValue, 0); // Now we have what should be a settings file in hand as a string... // Need to deserialize it... settings appSettings = null; XmlSerializer serializer = new XmlSerializer(typeof(settings)); StringReader stringReader = new StringReader((string)propertyValue); appSettings = (settings)serializer.Deserialize(stringReader); // Get a hashtable from the property collection, cache it, and return it. htToReturn = new Hashtable(); _hashOfHashes[affiliateApplication] = htToReturn; foreach(property prop in appSettings.propertyCollection) { htToReturn[prop.name] = prop.Value; } } } return htToReturn; } public static void Update(string affiliateApplication, Hashtable ht) { settings inSettings = new settings(); foreach(object key in ht.Keys) { SSOSettingsFileManager.property prop = new SSOSettingsFileManager.property(); prop.name = key.ToString(); prop.Value = (string)ht[key]; inSettings.propertyCollection.Add(prop); } StringBuilder sb = new StringBuilder(); StringWriter writer = new StringWriter(sb); XmlSerializer serializer = new XmlSerializer(typeof(settings)); serializer.Serialize(writer,inSettings); SSOHelper helper = new SSOHelper(); SSOPropertyBag bag = new SSOPropertyBag(); object o = sb.ToString(); bag.Write(SSOHelper.PropName,ref o); if(helper.AppExists(affiliateApplication)) { helper.SaveConfigInfo(bag, affiliateApplication); } ClearCache(affiliateApplication); } } /// <summary> /// Helper component for SSO interactions /// </summary> internal class SSOHelper { public static string PropName = "AppConfig"; public static string InfoIdentifier = "{56D74464-67EA-464d-A9D4-3EBBA4090010}"; /// <summary> /// Saves the config info. /// </summary> public void SaveConfigInfo(SSOPropertyBag bag, string affiliateApplication) { ISSOConfigStore configStore = (ISSOConfigStore)new SSOConfigStore(); configStore.SetConfigInfo(affiliateApplication, SSOHelper.InfoIdentifier, bag); } /// <summary> /// Deletes the config info. /// </summary> public void DeleteConfigInfo(string affiliateApplication) { ISSOConfigStore configStore = (ISSOConfigStore)new SSOConfigStore(); configStore.DeleteConfigInfo(affiliateApplication, SSOHelper.InfoIdentifier); } public void DeleteApp(string appName) { if(AppExists(appName)) { ISSOAdmin ssoa = GetAdmin(); ssoa.DeleteApplication(appName); } } // Borrowed largely from Flanders' SSOAppConfigHelper object. public void CreateApp(string appName, string userGroup, string adminGroup) { ISSOAdmin ssoa = GetAdmin(); int fields = 2; ssoa.CreateApplication(appName,appName + " Configuration Data","someone@microsoft.com",userGroup,adminGroup,SSOFlag.SSO_FLAG_APP_CONFIG_STORE|SSOFlag.SSO_FLAG_APP_ALLOW_LOCAL,fields); ssoa.CreateFieldInfo(appName,"someone@microsoft.com",SSOFlag.SSO_FLAG_FIELD_INFO_SYNC); ssoa.CreateFieldInfo(appName,SSOHelper.PropName,SSOFlag.SSO_FLAG_FIELD_INFO_SYNC); ssoa.UpdateApplication(appName,null,null,null,null,SSOFlag.SSO_FLAG_ENABLED,SSOFlag.SSO_FLAG_ENABLED); } // Borrowed from Flanders' SSOAppConfigHelper object. public bool AppExists(string appName) { ISSOAdmin ssoa = GetAdmin(); string description; string contact; string grpName; string grpAdmin; int flags; int fields; bool ret=false; try { ssoa.GetApplicationInfo(appName,out description,out contact,out grpName,out grpAdmin,out flags, out fields); ret=true; } catch{} finally { if(ssoa!=null) Marshal.ReleaseComObject(ssoa); } return ret; } // Borrowed from Flanders' SSOAppConfigHelper object. ISSOAdmin GetAdmin() { SSOAdmin ssoadmin = new SSOAdmin(); ISSOAdmin ssoa = (ISSOAdmin)ssoadmin; return ssoa; } } } /// <summary> /// This class is simply a forwarder (with a few extra convenience methods) that lives outside the /// namespace so as to shorten up the syntax in the orchestration expression editor. /// </summary> public class SSOSettingsFileReader { public static string ReadString(string affiliateApplication, string valueName) { return SSOSettingsFileManager.SSOSettingsFileReader.ReadString(affiliateApplication, valueName); } public static int ReadInt32(string affiliateApplication, string valueName) { return SSOSettingsFileManager.SSOSettingsFileReader.ReadInt32(affiliateApplication, valueName); } public static void ClearCache(string affiliateApplication) { SSOSettingsFileManager.SSOSettingsFileReader.ClearCache(affiliateApplication); } public static Hashtable Read(string affiliateApplication) { return SSOSettingsFileManager.SSOSettingsFileReader.Read(affiliateApplication); } }
2. Whenever u are changing the sso value we need to change the value in the BTDF under your project in c drive and then urProjectPath\Framework\DeployTools\SSOFileImport open that exe and load your application
using SSOSettingsFileReader.ReadValue("Applicationname","Key") you can fetch value from sso.
Regards, Aboorva Raja R Please remember to mark the replies as answers if they help and unmark them if they provide no help.
- Edited by AboorvaRaja Ramar Tuesday, May 9, 2017 8:13 PM
Tuesday, May 9, 2017 8:04 PM -
I am using the helper class that comes along with SSO MMC Snap in released by Microsoft now. BDTF has some issues it seems with caching.
BUT yes some how it hasnt solved my problem , I need to figure out whats causing the issue again .
Regards
Gaurav
Regards Gaurav
Wednesday, May 10, 2017 8:30 AM -
The helper class u need to change as i attached, the helper class comes along with SSO MMC snap in will not work
Regards, Aboorva Raja R Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Wednesday, May 10, 2017 9:12 AM -
The helper class u need to change as i attached, the helper class comes along with SSO MMC snap in will not work
Regards, Aboorva Raja R Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Hi Aboorva
On the contrary SSO Snap in code works fine when I tested it against a console application .
The issue is even after updated the ExternalAssembly.xml file to use the newly built DLL with SSO Snap in helper class , the solution still says that it cannot find the function 'ReadString' which is a function SSOSettingsFileReader . I have checked my xslt code and I am using ScriptNS0:Read which is a function in the helper class that came along with the SSO Snap in .
I am still looking through what is wrong in the code .
PS: all host instances restarted , ENTSSO restarted.
Reagrds
Gaurav
Regards Gaurav
Wednesday, May 10, 2017 10:09 AM -
Hi
I was able to make things work by using the code that comes with SSO MMC Snap in and then calling the function inside the custom XSLT using the ExternalAssembly.xml . The process for that is listed here
https://code.msdn.microsoft.com/windowsdesktop/calling-an-external-c74ce8dc
and download MMC Snap in
https://www.microsoft.com/en-in/download/details.aspx?id=14524
But after you use this Snap in there will be issues of NOT able to create the keys , so download the Fix from
https://gallery.technet.microsoft.com/BizTalk-Server-2016-Fix-12784c1f
(Unzip the downloaded file, overwrite the “SSOMMCSnapIn.dll” file to "C:\Program Files (x86)\Microsoft Services\SSO Application Configuration\" folder (or the folder where you have installed the snap-in from the setup file downloaded from Microsoft))
use the function in Custom XLST as
<xsl:variable name="var:YourVariableName" select="ScriptNS0:Read("YourSSOApplicationName" , "YourApplicationKey")" />
The mistake I was doing was in the XSLT was to view the name spaces in one window I changed the xsl:stylesheet tag to the below code
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var s0 userCSharp ScriptNS0" version="1.0" xmlns:ns0="http://www.myNamespace.com/XXX/MsgRequest" xmlns:s0="http://www.MyNameSpace.com/XXX/TagList" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" xmlns:userCSharp="http://schemas.microsoft.com/BizTalk/2003/userCSharp">
INSTEAD of
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var s0 userCSharp ScriptNS0" version="1.0" xmlns:ns0="http://www.myNameSpace.com/XXX/MsgRequest" xmlns:s0="http://www.myNameSpace.com/XXX/TagList" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" xmlns:userCSharp="http://schemas.microsoft.com/BizTalk/2003/userCSharp">
Doing so it introduced new line characters which didnt allow the compiler to read the namespaces . Hence always keep the xsl:stylesheet in one row.
I would like to thank all of you for your time and inputs however.
Regards
Gaurav
Regards Gaurav
- Edited by Gaurav Sood Thursday, May 11, 2017 8:21 AM changed
- Marked as answer by Gaurav Sood Thursday, May 11, 2017 8:22 AM
Thursday, May 11, 2017 8:20 AM -
Werid with the line breaks between namespaces issue
Done it a few times without problemsThanks for the heads up /Peter
Thursday, May 11, 2017 8:43 AM