Implementing Metadata Resolver Handler

The Adapter Browse Handler and Adapter Search Handler implementations return an array of objects of type MetadataRetrievalNode. This object structure doesn’t contain the actual information about the input and output types of each operation. The input parameters and output result for each operation are depicted using the messaging format of WSDL/XML Schema. So how does the Adapter Consumer get the contract in this format from the Adapter? This is where the Adapter Metadata Resolver Handler comes in. Once the Adapter Consumer is ready to obtain the Service Contract (WSDL) for the final set of operations they have selected, the Adapter uses its WSDL Builder component to return the WSDL and XML Schema from the passed in operations. An entire category containing operations can also be passed into the WSDL Builder, which recursively finds all the leaf operations available within that category and uses them to generate the resulting service description contract.

The Adapter Consumer can get the metadata (WSDL and/or .NET proxy) using

1) Standard WCF request message

2) WS-Transfer Request

3) Svcutil.Exe

4) ASDK VS-Plug In Tools

a. Add Adapter Service Reference VS Plug-In

b. Consume Adapter Service BizTalk Project VS Plug-In

The WSDL Builder uses the in-memory Metadata Cache to get the Adapter SDK Metadata Object Model for each selected operation. If the operation is not present in the cache, it uses the Adapter Metadata Resolver Handler class to resolve the operation and type metadata. Adapter Developers are responsible for executing calls to their target system and converting target system-specific metadata formats into ASDK Metadata Object Model that is used by the Adapter SDK WSDL Builder to generate the standard contract. If the target system’s operation metadata is already expressed in XML Schema (XSD) format, the ASDK Metadata Object Model can be provided that XSD instead of having to convert to this object model from another format (for example – COBOL interface definition for CICS applications).

See the figure below to understand the flow between various components of Adapter SDK that play a part in returning the metadata in the WSDL / XML Schema format to the caller.

Figure 1: Resolve Metadata Flow Diagram

Adapter SDK Metadata Object Model

The Adapter SDK provides an encapsulated object model to capture the target application’s metadata. This model is used by the WSDL Builder to build a WCF Service Contract.

Class Name

Derives From

Is Abstract?

Description

OperationMetadata

ICachedMetadata

Yes

It is an abstract class that represents the metadata for an adapter operation. The Adapter Developer can extend this class to define adapter specific operation metadata. This class is cached in the Metadata Cache by the Adapter SDK. WSDL Builder component uses this class to generate the Service Description for the client.

ParameterizedOperationMetadata

OperationMetadata

No

This class derives from OperationMetadata and serves as a default implementation for specifying metadata for a business function. The caller of this class can pass in parameters of type OperationParameter and return the result of type OperationResult.

TypeMetadata

ICachedMetadata

Yes

It is an abstract class that represents a rich data structure to be used within the adapter’s operation metadata.

BasicTypeMetadata

TypeMetadata

No

Allows setting W3C schema list, restriction and union element within this class. The behavior is set in the property content of type System.Xml.Schema.XmlSchemaSimpleTypeContent.

EnumTypeMetadata

BasicTypeMetadata

No

Sets the content by creating a type called System.Xml.Schema.XmlSchemaSimpleTypeRestriction for representing enumerations.

CharTypeMetadata

TypeMetadata

No

Class to represent a specific character type for serialization.

DurationTypeMetadata

TypeMetadata

No

Class to represent a specific duration type for serialization.

GuidTypeMetadata

TypeMetadata

No

Class to represent a specific guid type for serialization.

StructuredTypeMetadata

TypeMetadata

No

Class to define a data structure containing various members of type TypeMember. For example, for an object Employee with fields Name, Salary and HireDate. The Employee will be represented using class StructuredTypeMetadata. For each of Employee fields, the class TypeMember will be created and added to the collection property Members.

QualifiedType

System.Object

Yes

It is an abstract helper class that contains static methods to create instances of SimpleQualifiedType.

SimpleQualifiedType

QualifiedType

No

This class encapsulates the W3C Xml Schema Definition simple type using enumeration System.Xml.Schema.XmlTypeCode. For example – W3C Xml Schema xs:double, xs:string, xs:date, etc. W3C facets can also be defined for a simple type within this class.

ComplexQualifiedType

QualifiedType

No

This class defines the adapter specific complex type equivalent of W3C Xml Schema Definition complex type. It is constructed for each TypeMetadata using TypeMetadata unique ID as an input.

QualifiedTypeContainer

System.Object

No

It is a wrapper class for a SimpleQualifiedType or ComplexQualifiedType.

OperationParameter

QualifiedTypeContainer

No

OperationParameter class is used to describe a parameter signature for an operation. For example, if the parameter is of a simple type such as a string, double or int, it will contain a SImpleType. If the parameter is a rich data structure, it uses a ComplexQualifiedType class.

OperationResult

QualifiedTypeContainer

No

This class is used to describe the result for an operation. The result can be of a simple or complex qualified type.

TypeMember

QualifiedTypeContainer

No

This class is used within a StructuredTypeMetadata to provide details about various members of a rich data structure. The type member can be of a simple or complex qualified type.

OperationParameterDirection

System.Object

No

It defines the direction of an operation parameter. The valid values are:

· In

· Out

· InOut

TypeMetadataCollection

System.Object

No

It is a custom collection containing objects of type TypeMetadata.

Let’s continue with the DLL adapter example introduced in the previous post to extend our sample to now implement metadata resolver handler.

Step 1: Implement ResolveOperationMetadata and IsOperationMetadataValid methods in Adapter implementation of IMetadataResolverHandler interface

Here is a sample code snippet for a simple operation with signature string SayHelloWorld( int count ) before we continue with implementing it for the DLL adapter example.

public OperationMetadata ResolveOperationMetadata(string operationId, TimeSpan timeout, out TypeMetadataCollection extraTypeMetadataResolved)

{

bool useCustomSchema = false;

extraTypeMetadataResolved = null;

if ("Hello/SayHelloWorld".Equals(operationId))

{

// USE CUSTOM SCHEMA

if (useCustomSchema)

{

SayHelloWorldOperationMetadata om = new SayHelloWorldOperationMetadata("Hello/SayHelloWorld", "SayHelloWorld");

return om;

}

// USE LOB ADAPTER SDK METADATA OBJECT MODEL

else

{

ParameterizedOperationMetadata om = new ParameterizedOperationMetadata("Hello/SayHelloWorld", "SayHelloWorld");

om.OperationNamespace = HelloWorldAdapter.SERVICENAMESPACE;

// TODO - the node ID is not set

// syntax: String SayHelloWorld( String aName );

OperationParameter parm1 = new OperationParameter("aName", OperationParameterDirection.In, new SimpleQualifiedType(XmlTypeCode.String), false);

parm1.Description = "Hello World is said by this name.";

// Expected result: aName says Hello World

OperationResult result = new OperationResult(new SimpleQualifiedType(XmlTypeCode.String), false);

om.Parameters = new List<OperationParameter>();

om.Parameters.Add(parm1);

om.OperationResult = result;

return om;

}

}

return null;

}

Step 2: Implement ResolveTypeMetadata and IsTypeMetadataValid methods in Adapter implementation of IMetadataResolverHandler interface

Here is a sample code snippet –

public TypeMetadata ResolveTypeMetadata(string typeId, TimeSpan timeout, out TypeMetadataCollection extraTypeMetadataResolved)

{

extraTypeMetadataResolved = null;

// set the loaded assembly

Assembly myLoadedAssembly = this.Connection.LoadedAssembly;

// load the classes from assembly

Type myType = myLoadedAssembly.GetType(typeId);

if (myType != null)

{

StructuredTypeMetadata customType = new StructuredTypeMetadata(typeId, myType.Name);

customType.TypeNamespace = MyExistingLibraryAdapter.SERVICENAMESPACE;

FieldInfo[] fields = myType.GetFields();

foreach ( FieldInfo fieldInfo in fields)

{

TypeMember typeMember = new TypeMember(fieldInfo.Name, MyExistingLibraryOperationMetadata.ResolveType(fieldInfo.FieldType), false);

customType.Members.Add(typeMember);

}

customType.NeverExpires = true;

return customType;

}

return null;

}

Step 5: Test the Adapter Resolver Functionality

1) Using WCF Channel Programming

This method shows how Adapter’s Resolver functionality can be called using WCF channel programming. We will first get all the nodes using metadata browse and then we will pass these nodes to the metadata service, so that it can resolve the input and output types for each operation.

class Program

{

static void Main(string[] args)

{

// Create the instance of the binding

MyExistingLibraryAdapterBinding binding = new MyExistingLibraryAdapterBinding();

// Create endpoint address

EndpointAddress address = new EndpointAddress("reflect://@c:/temp/MyExistingLibrary.dll");

// Create channel factory

ChannelFactory<IMetadataRetrievalContract> channelFactory = new ChannelFactory<IMetadataRetrievalContract>(binding);

// Open the channel factory

channelFactory.Open();

// Create a new channel to invoke metadata service in the adapter

IMetadataRetrievalContract proxy = channelFactory.CreateChannel(address);

// Invoke Browse method starting from the root node

MetadataRetrievalNode[] rootNodes = proxy.Browse("/", 0, Int32.MaxValue);

// Invoke GetMetadata method

System.Web.Services.Description.ServiceDescription sd = proxy.GetMetadata(rootNodes);

// Write the WSDL in the file system

sd.Write("c:\\temp\\CodeLibrary.wsdl");

Console.WriteLine("WSDL generated!");

// Close channel factory

channelFactory.Close();

}

}

Here is the expected output, when this console program is run. Open the generated WSDL file from this program. This WSDL file should contain all the operations (as we selected the root node) as the GetWsdl method of the WSDL Builder recursively obtains all the available operations from a given category node. Check the input parameters and output result of each operation

WSDL generated!

2) Using MetadataExchangeClient

This will use the WS-Transfer protocol to connect to the adapter and get metadata for the passed in operations.

The following snippet shows you how to accomplish this –

class Program

{

static void Main(string[] args)

{

// Declare an EndpointAddress and initialize it with the URI for MEX address

EndpointAddress mexAddress = new EndpointAddress("reflect://@c:/temp/MyExistingLibrary.dll?wsdl&op=MyExistingLibrary.CalculatorService/Subtract");

// Create a metadata exchange client that will retrieve metadata according to the WS-MEX standard

MetadataExchangeClient mexClient = new MetadataExchangeClient("reflect");

// Obtain the metadata

MetadataSet metadataSet = mexClient.GetMetadata(mexAddress);

// For each metadata section, do something - in this case just print the section dialect

int i = 0;

string wsdlFileName;

foreach (MetadataSection section in metadataSet.MetadataSections)

{

// Retrieve the first service description found

System.Web.Services.Description.ServiceDescription wsdl =

(System.Web.Services.Description.ServiceDescription) section.Metadata;

Console.WriteLine("Writing WSDL for: " + section.Identifier);

// Write the WSDL in the file system

wsdlFileName = "c:\\temp\\reflect" + i++ + ".wsdl";

wsdl.Write(wsdlFileName);

Console.WriteLine("WSDL " + wsdlFileName + " generated!");

}

}

}

Here is the expected output. The resulting WSDL file should only contain the metadata definition for one selected operation “MyExistingLibrary.CalculatorService/Subtract” as provided in the endpoint address.

Writing WSDL for: reflect://My.Existing.Library.Adapter

WSDL c:\temp\reflect0.wsdl generated!

3) Using Add Adapter Service Reference VS Plug-In

Right-click on a non-BizTalk VS Project and select “Add Adapter Service Reference…”. This will bring up the Add Adapter Service Reference UI. Once the adapter binding is registered with the WCF configuration system, the adapter binding will show up in the Select a binding drop down box. Select your adapter binding, enter Connection Uri and click on Connect. If the connection is successful, you should be able to see the browse results in left-hand tree pane labeled as Select a category. Choose operations and/or categories from Available Categories and Operations and click on OK. This will generate a client proxy file in the selected project. It uses the binding name in the name of the generated file. For example, unless overridden, the client for HelloWorldAdapterBinding will generate a proxy file called HelloWorldAdapterBindingClient.{ext}, where {ext} is "cs" or "vb" depending on the type of selected VS project.

4) Using Consume Adapter Service BizTalk Project VS Plug-In

Create a BizTalk Project in Visual Studio for experiencing Consume Adapter Service BizTalk Project VS Plug-In. Right-click on this project, select “Add Generated Items > Consume Adapter Service …”. This will bring up the Consume Adapter Service UI. This UI has same browse behavior as the Add Adapter Service Reference UI with the exception that clicking on OK after selecting operations generates XML Schemas instead of a .NET proxy.

5) Using Svcutil.Exe

You can also use WCF Service Metadata Utility Tool Svcutil.Exe to obtain the metadata.

· Generating the contract with all the operations in the adapter

o For generating proxy file: Svcutil reflect://localhost/c:/temp/MyExistingLibrary.dll

o For generating WSDL file: Svcutil /t:metadata reflect://localhost/c:/temp/MyExistingLibrary.dll

· Generating the contract with selected operations from the adapter

o For generating proxy file: svcutil reflect://localhost/c:/temp/MyExistingLibrary.dll?op=MyExistingLibrary.CalculatorService/Subtract

o For generating WSDL file: svcutil /t:metadata reflect://localhost/c:/temp/MyExistingLibrary.dll?op=MyExistingLibrary.CalculatorService/Subtract

Next Post: Message Exchange Patterns

implementing Metadata Resolver Handler.docx