BizTalk Server 2004 introduced a mechanism for generating property sheets for the adapter configuration user interface based on an XML Schema Definition (XSD) definition of the configuration properties. The use of this framework introduces a number of significant challenges for the adapter writer. This section covers the various problems this technology introduced and suggests effective workarounds.
Item 1: Custom Property Editors
It is impossible to put significant property validation on top of properties in the property sheet. The framework attempts to use XML Schema Definition (XSD) simpleType restrictions to define the validation rules for the UI. The simple rules for maximum and minimum value are applied but the regular expression restriction for string fields is not supported.
Also, the data is converted into CLR types before the restriction is applied so the user of the UI can sometime get a cryptic type conversion error rather than an out of range error. All in all, the validation logic is very weak.
The solution many adapter writers have adopted is to write custom property editors for the main properties on their property sheet. If there are a number of cross dependent properties (as is often the case), then the custom property editor can mangle the result into a single field and the runtime and then un-mangle it. This is the only way to get the necessary (though still generally trivial) custom code into the interface.
Custom Property editors are relatively straightforward to write and the property in the XSD can be annotated to use them. The annotation references an assembly which exposed the property editor entry point, and it is coded to show a CLR Form. You can now write a regular user interface and use the traditional interface generation tools that Visual Studio has to offer.
The following code shows how to use the XSD to add a custom property editor:
<xs:element name="queueDetails" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:appinfo>
<baf:designer>
<baf:displayname _locID="queueName">Queue Definition</baf:displayname>
<baf:description _locID="receiveQueueDesc">Details of MQSeries Server, Queue Manager and Queue from where you want to receive messages.</baf:description>
<baf:category _locID="mqsCategory">MQSeries</baf:category>
<baf:editor assembly="Microsoft.BizTalk Server.Adapter.MQSAdmin.dll">Microsoft.BizTalk Server.Adapter.MQSAdmin.MQSUITypeEditor</baf:editor>
</baf:designer>
</xs:appinfo>
</xs:annotation>
</xs:element>
Where the code referenced should look like this:
public class MQSUITypeEditor : System.Drawing.Design.UITypeEditor
{
public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle
(System.ComponentModel.ITypeDescriptorContext context)
{
return System.Drawing.Design.UITypeEditorEditStyle.Modal;
}
public override object EditValue (System.ComponentModel.ITypeDescriptorContext context,
System.IServiceProvider provider, object value)
{
Form qdForm = new QueueDefinitionForm(value);
IWindowsFormsEditorService service =
(IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
DialogResult dialogResult = service.ShowDialog(qdForm);
//…
}
}
class QueueDefinitionForm : System.Windows.Forms.Form
{
// traditional UI code goes here!
}
The administration runtime must be able to find the assembly referenced in the XSD, so you should add it to the GAC.
Item 2: Dynamic Send Issues
In the case of a dynamic send port in an orchestration, the message engine invokes an appropriate adapter based on the prefix of the URL string (as set through myDynamicPort(Microsoft.XLANGs.BaseTypes.Address) = "<URL>).
In the orchestration, if the URL string is “SQL://…..”, the SQL adapter is invoked, if the URL is “MSMQ://……”, then the MSMQ adapter is invoked. This prefix is defined in the adm_AdapterAlias table in the BizTalk Configuration database. After the adapter send runtime is invoked, it receives the URL string from the OutboundTransportLocation property of the SystemMessageContext object.
The part of the URL string after the prefix contains information for the adapter runtime to perform the send operation. For example, in the case of the SQL adapter, if the URL string is SQL://MyDatabaseServer/MyDatabase, the SQL adapter send runtime knows the data that is going to the database MyDatabase on the server MyDatabaseServer.
In many cases the simple URL string cannot carry all the information that the send adapter needs. In the case of the SQL adapter, the send adapter needs to also know the message RootElementName and TargetNamespace. For the additional information, there are two solutions here:
-
Put the information into send handler properties. This approach has an obvious limitation: the global availability defeats the dynamic purpose.
-
Implement adapter-specific message context properties.
Item 3: Avoiding Duplicate Properties
You must take care in writing the runtime code because of an issue introduced by XSD-generated property sheets. The runtime is often presented with the exact same property in a number of places: both property sheet Document Object Models (DOMs), the message context and even the location URI. As with any de-normalized model there is a potential for confusion. Adapters should first consider the handler properties, then override them with location properties, and then finally override them with values taken from the BizTalk Server message context and URI.
Item 4: Uniform Resource Identifier (URI)
An adapter must provide a Uniform Resource Identifier (URI) on its location property sheet but should make this URI hidden if possible. You must identify every BizTalk Server endpoint location with a URI and the XML Schema Definition (XSD) for the location should include this URI as a field. However, in general it is best to derive the URI from other fields rather than have the user enter it directly.
There is usually a simple way to do this. For example the FILE and FTP adapters generate URIs like: file://c:/myFolder/*.txt or ftp://myServer:8008/myFolder/*.xml. In these cases, the URI should be hidden in the property sheet. This is done by marking browsable show=false in the XSD.
For example:
<xs:element name=”uri” type=”xs:string”>
<xs:annotation>
<xs:appinfo>
<baf:designer xmlns:baf=”BizTalk ServerAdapterFramework.xsd”>
<baf:browsable show=”false” />
</baf:designer>
</xs:appinfo>
</xs:annotation>
</xs:element>
You should generate the URI and add it to the Document Object Model (DOM). When this is done correctly, the location URI appears in the parent property page. In fact the whole location property sheet is opened as an extension of editing this URI property.
The URI shown in this parent property page is generally not escaped, that is, illegal URI characters, like a space, are not encoded. This is just a convention and not of particular significance. To BizTalk Server these are just strings. Using the Common Language Runtime (CLR) URI class to manipulate URIs in code is generally a good idea, but you should watch for exceptions. For example, in MQSeries, queue manager and queue names may contain a forward slash, which must be explicitly handled.
The point is that URIs have their own syntax and escaping rules and these can matter occasionally in the adapter code but in general does not affect BizTalk Server.
Item 5: Localization Issues
By using the Adapter Framework, adapter developers can implement adapter property pages with XML Schema Definition (XSD) schemas.
If your adapter has no globalization or localization requirement, then you can hardcode the XSD schema string inside the IDynamicAdapterConfig:GetConfigSchema() function.
If your adapter has globalization or localization requirements, you can implement the XSD schema in one of two ways.
-
Use separate XSD files outside the design-time binary. Make the whole text of the schema a manifest resource.
-
Dynamically replace the Property Names and Description from the resource:
-
Add a _locID to each element that you want to localize.
-
Use an xpath to pull back all the nodes in the schema that have a _locID attribute.
-
Look up the resources for a string indexed by the value of the _locID.
-
Replace the node text with the result.
The following is sample code for the second option:
string mySchema = GetSchemaFromResource(“mySchema”);
string myLocalizedSchema = LocalizeSchemaDOM (mySchema, resourceManager);
// where…
protected string GetSchemaFromResource (string name)
{
Assembly assem = this.GetType().Assembly;
Stream stream = assem.GetManifestResourceStream(name);
StreamReader reader = new StreamReader(stream);
string schema = reader.ReadToEnd();
return schema;
}
protected XmlDocument LocalizeSchemaDOM (string schema, ResourceManager resourceManager)
{
XmlDocument document = new XmlDocument();
document.LoadXml(schema);
XmlNodeList nodes = document.SelectNodes
("/descendant::*[@_locID]");
foreach (XmlNode node in nodes)
{
string locID = node.Attributes["_locID"].Value;
node.InnerText = resourceManager.GetString(locID);
}
return document;
}
Item 6: Managing Passwords
Consider using BizTalk Server Enterprise Single Sign-On (SSO) to handle credentials. If you put credentials directly in the properties of an endpoint, the password field will be blanked out when you need to export a binding file. This will require your user to re-key in the password as an administrator. You can avoid this difficulty by using SSO for credentials.
If the adapter endpoint has a Password property, be aware that the actual value is stored in clear text in the SSO Configure Store database despite the fact that it is displayed in the UI as "*". This property is also transferred through the network and a simple script using the BizTalk Server sample ExplorerOM will be able to read it.
Item 7: Handler Properties as Strings
Handler properties should be strings if used as default configurations.
Although it seems attractive to use the properties on the XSD-generated handler property sheet as defaults for their Location properties, there are several issues that make this less useful.
This seems attractive because if the value is not set in the Location the runtime automatically uses the value set in the handler.
The problem comes with knowing whether the value presented to the runtime is to be overridden or not. The typical way of doing this would be to have some notion of NULL defined for values and then a test could be made against that value. The problem when using the XSD based property sheets in BizTalk Server is that NULL is only supported for strings.
Even if you want your adapter to have default settings through the use of this NULL test and are willing to restrict the adapter to string types, it is still exposed to a very odd piece of user interface.
The XSD-generated property sheets only support the setting of a property back to NULL by right clicking on the property at which point a nullify? context menu appears and the property can be set to NULL. There is no visual feedback at all as to whether a property is NULL or not.