Implementing Metadata Browse Handler

Most databases and LOB applications provide some form of metadata that can be used to define the data that is exchanged between the adapter and the target system. Adapter Developer needs to implement IMetadataBrowseHandler interface provided within [ASDK] for providing Adapter Consumer an ability to explore the available LOB application metadata. For example, a database provides a way to retrieve stored procedures, so a database adapter can provide the browse functionality by showing a hierarchy of available stored procedures. The number and type of stored procedures will vary for each customer implementation.

In this post, I will use an example of creating an adapter for a Dynamic Link Library (DLL) as a sample. In this sample,we will use reflection to obtain themetadata about classes and operations within an available DLL. For the browse interface, I want to return each class as a category with the public methods as operations within each category. For example – I have created a simple Class Library called MyExistingLibrary that has following public classes –

1)  CalcuatorService

a.  double Add(double n1, double n2);

b.  double Subtract(double n1, double n2);

c.  double Multiply(double n1, double n2);

d.  double Divide(double n1, double n2);

1)  EchoService

a.  string EchoString(string input)

b.  string EchoStringNTimes(string input, int count)

2)  EightBallService

a.  public string AskQuestion()

Once the adapter is developed, new classes/methods added in the existing library, will be directly consumed by the Adapter to always return up-to-date metadata information to the consumer.

Note: As a best practice, if you have access to the code base from which the DLL was compiled and you want to WCF-enable the classes, you don’t want to use the [ASDK] to build a WCF-based adapter to it. Just decorate the classes, operations and types with ServiceContract, OperationContract and DataContract attributes.

The following diagram shows the components implemented within this Class Library adapter. The adapter implements two metadata handlers – Browse and Search. In this post, we will only talk about how to implement the Browse Handler.

After the [VSWizard] is used to generate the code required for adapter development, use the following steps to provide a sample implementation of IMetadataBrowseHandler interface.

Step 1: Implement Browse method in Adapter implementation of IMetadataBrowseHandler

This interface has one key method called

public MetadataRetrievalNode[] Browse(string nodeId, int childStartIndex,int maxChildNodes, TimeSpan timeout);

where

nodeId / Each item in the hierarchy of the metadata explorer has a node ID. This item can be a category or an operation. The category can have sub-categories. Ensure a unique node ID is assigned to each item.
childStartIndex / Index of first child to return
maxChildNodes / Maximum number of children to return
MetadataRetrievalNode[] / Returns the child nodes for the supplied node ID.

This method is responsible for supporting the “Browse Metadata” functionality in the adapter. Before implementing this method, Adapter Developer needs to design the anticipated tree to return in the metadata explorer. For DLL adapter, my design is to show the available public classes in the DLL as Categories and public methods within each class as Operations of that particular category. Another alternative could have been to show all the DLLs available in a particular location, where each DLL name will be main category and the classes within it becoming the sub-categories. It is up to the Adapter Developer to provide the best possible representation of the existing application that will make sense to the adapter consumer. In SAP Adapter, for example, the Adapter Developer may categorize using SAP taxonomy such as RFC, BAPI and IDOC.

Here is the sample implementation of my Browse method for DLL adapter.

public MetadataRetrievalNode[] Browse(string nodeId, int childStartIndex, int maxChildNodes, TimeSpan timeout)

{

ListMetadataRetrievalNode> nodes = new ListMetadataRetrievalNode>();

Assembly loadedAssembly = this.Connection.LoadedAssembly;

// start building the tree

if (MetadataRetrievalNode.Root.NodeId.CompareTo(nodeId) == 0)

{

// go through the DLL and retrieve all public classes

Type[] types = loadedAssembly.GetTypes();

foreach (Type type in types)

{

if (type.IsClass )

{

MetadataRetrievalNode node = new MetadataRetrievalNode(type.Namespace + "." + type.Name);

node.Description = type.ToString();

node.DisplayName = type.Name;

node.IsOperation = false;

nodes.Add(node);

}

}

}

else

{

// go through each node and bring out operation

string categoryName = nodeId;

// catch exception here, if can't resolve the type

Type category = loadedAssembly.GetType(categoryName);

if (category != null)

{

nodes.Clear();

MethodInfo[] methods = category.GetMethods();

foreach (MethodInfo method in methods)

{

string declaringType = method.DeclaringType.Name;

// This ID is used by IMetdataResolver-ResolveOperationMetadata implementation to

// resolve input and ouptput parameters for this operation

string uniqueNodeId = category.Namespace + “.” + category.Name + “/” + method.Name;

MetadataRetrievalNode node = new MetadataRetrievalNode(uniqueNodeId);

node.DisplayName = method.Name;

node.Description = method.ToString();

node.IsOperation = true;

if( method.IsPublic & !"Object".Equals(declaringType)) nodes.Add(node);

}

}

}

return nodes.ToArray();

}

The interaction between the Adapter Consumer and Browse function is using WCF. In [ASDK], there is a Metadata Service that implements a contract IMetadataRetrievalContract containing Browse, Search and GetMetadata operations.

Step 2: Test Adapter Browse Handler functionality

1)  Using WCF Channel Programming

This method shows how Adapter’s Browse functionality can be called using WCF channel programming.

class Program

{

static void Main(string[] args)

{

// Create the instance of the binding

MyExistingLibraryAdapterBinding binding = new MyExistingLibraryAdapterBinding();

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

ChannelFactoryIMetadataRetrievalContract> channelFactorytr = new ChannelFactoryIMetadataRetrievalContract>(binding);

channelFactory.Open();

IMetadataRetrievalContract proxy = channelFactory.CreateChannel(ea);

Console.WriteLine("\nOutputting metadata tree:");

MetadataRetrievalNode[] nodes = OutputMetadataTree(proxy, null, 0);

// Generate WSDL from the metadata retrieval nodes

Console.WriteLine("\nGenerating WSDL for " + binding.Name + "...\n");

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

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

Console.WriteLine("WSDL generated!");

// Close channel factory

channelFactory.Close();

}

/// <summary>

/// This method recursively browses through each node and prints its name out on the

/// console.

/// </summary>

private static MetadataRetrievalNode[] OutputMetadataTree(IMetadataRetrievalContract proxy, MetadataRetrievalNode node, int depth)

{

for (int i = 0; i < depth; i++)

{

if (i < depth - 1)

{

Console.Write(" ");

}

else

{

Console.Write("└──");

}

}

Console.WriteLine(node == null ? "Root" : node.NodeId);

MetadataRetrievalNode[] rootNodes = proxy.Browse(node == null ? "/" : node.NodeId, 0, 100);

foreach (MetadataRetrievalNode childNode in rootNodes)

{

OutputMetadataTree(proxy, childNode, depth + 1);

}

return rootNodes;

}

}

Here is the expected output, when this console program is run.

Outputting metadata tree:

Root

└──MyExistingLibrary.EchoService

└──MyExistingLibrary.EchoService/EchoString

└──MyExistingLibrary.EchoService/EchoStringNTimes

└──MyExistingLibrary.CalculatorService

└──MyExistingLibrary.CalculatorService/Add

└──MyExistingLibrary.CalculatorService/Subtract

└──MyExistingLibrary.CalculatorService/Multiply

└──MyExistingLibrary.CalculatorService/Divide

└──MyExistingLibrary.EightBallService

└──MyExistingLibrary.EightBallService/AskQuestion

Generating WSDL for MyExistingLibraryAdapterBinding...

WSDL generated!

2)  Using [AASR]

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.

3)  Using [CAS]

Create a BizTalk Project in Visual Studio for experiencing [CAS]. 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 click on OK after selecting operations generates Schemas instead of a .NET proxy.

Next Post: Resolving Metadata