An example of an adaptor and relevant configuration...
To help you get started with ESB.NET, both from the point of view of verifying the deployment as well as to help you get started adding your own services, there's a test adaptor that ships with ESB.NET.
Steps required to implement a service
After defining our service, there are 3 main items we need to do to setup our service with ESB.NET
- Prepare a Request to send to the service. This is done using any text editor or XSD tool such as Turbo XML, XMLSpy, BizTalk XML Editor etc.
- Create an adaptor and handler - or just add a new handler to an existing adaptor. This is done by creating an assembly implementing a specific interface (IProcessMsg). This can be done by either:
- writing code - where you code up the logic in VB.NET or C# etc.
- configuring in one of the WWF Adaptor handlers and implementing the functionality using WWF assemblies - by using WWF to build the functionality and deploy the
- Configure the handler to handle a particular request (map request to handler). This is done using the InteropPipelineCfg.xml config file.
The ESB.NET Test Adaptor
The test adaptor is a basic one, and is written directly in code using VB.NET. It's purpose is to do nothing much other than get any old message to the service bus, have it routed to a handler and get a response back. It is configured in a few ways to test various basic functions of the ESB.NET service bus. As such, the message is minimalist in nature.
1. Request Message
Navigate to:
...\5.0\5.0.1.0\Source\ESB\Base\Solutions\Main\Management\ESB.Management.Portal\Testing\XMLRequests\ESBCore
and view the file called:
ESBTest1_Sync.xml
It looks somethng like this:
- <xml version="1.0" encoding="utf-8" ?>
-
<ESBEnvelope xmlns="urn:keystroke-com-au:ESB:Envelope:ESBEnvelope:Version:1-0-3">
-
<header> <delivery><senderMessageID /><receiverMessageID /><sent /> <callSynchronizationType>0<< SPAN>callSynchronizationType> <delivery>
-
<manifest>
<document>
<name>BuildCmd2<< SPAN>name>
<description>Test Document<< SPAN>description>
<documentNamespace>urn:schemas-biztalk-org:biztalk:BuildCmd2:BuildCmd2<< SPAN>documentNamespace>
<< SPAN>document>
<< SPAN>manifest>
-
<systems> <system><adminID /><systemID>Some System ID<< SPAN>systemID> <service><name /> <version /> <context /> <description>Test Service< SPAN>< SPAN>description> < SPAN>service>
-
-
-
<BuildCmd2 xmlns="urn:schemas-biztalk-org:biztalk:BuildCmd2:BuildCmd2">
<BuildCmdString>0<< SPAN>BuildCmdString>
<< SPAN>BuildCmd2>
<< SPAN>documentBody>
<< SPAN>body>
< < SPAN>ESBEnvelope>
Not all fields are mandatory. In the XML Fragment above, you can see the ESBEnvelope and various header fields.
The most interesting parts are the manifest and the body. The manifest lists the contents of the message, inline XML documents, and any attachments that are transported outside of the ESB Envelope - such as DIME or MTOM etc. or may just be URL's.
The body section contains the core service request data. A similar ESBEnvelope will be used for the response.
In the above case, the document is called: BuildCmd2 (remnants from an remotely operated & automated build process... :) ). It has only one parameter, being the BuildCmdString. This example does not contain much service data as it's for testing core functionality. In general, the body will contain XML data conforming to an XSD, and may be a few hundred lines long. Bigger amounts of XML data, eg, MBytes or hundreds of KBytes, are best included as attachments, so as to minimise handling of large data. For example, handling employee contributions for a superannuation fund, which may contain say, 10MBytes of XML data, is best treated as an attachment, with an inline document specifying any general information about the attachment, as pertaining to the business function. This improves system performance, and also makes it clear as to the business service that is being executed.
2. Adaptor and Handler
Navigate to the following directory in the ESB build.
...\5.0\5.0.1.0\Source\ESB\Base\Solutions\Main\Testing\ESB.Testing.TestTxn
Here you'll find a Service Adaptor (ESB.Testing.TestTxn) containing 2 handlers (TestTxn1.vb and ReturnSpecifiedFileHandler.vb).
These 2 handlers show how to manipulate the ESBEnvelope header properties and how to return an ErrorXML document in the response. They also show how to invoke XSLT functionality. XSLT, much like the Service Handlers, is configured in a config file that contains the path to the XSLT files. An XSLT operation is performed in the Service Handling code by invoking a defined XSLT Service for the particular XSLT operation. This Service is defined in another configuration file - InteropDocumentTransformCfg.xml. Note that in the XSLT configuration, you can chain XSLT operations together. Once the service is setup, the XSLT calling code does not need to change if you change file names or if you need to do multiple XSLT operations etc.. Lines 1-10 show how to call the XSLT Service in code.
If oEnv.GetCustomParam("ESBCore", "XsltTest") = "1" Then
Try
'do some xslt testing...
'Dim oContext As ESB.Core.Interfaces.ServerContext.IServerContext
Dim sResolveName As String = "TestResolveName"
Dim sContext As String = "TestContext"
Dim sXmlToTransform As String = oEnv.GetDocument(0).GetAsString()
Dim sTransformResult As String
'CType(ESB.Core.Services.ContextManager.getContextManager(True).GetExistingContext, IServerContext).Transform(sResolveName, sContext, sXmlToTransform, sTransformResult)
GetContext.Transform(sResolveName, sContext, sXmlToTransform, sTransformResult)
If ShouldLog(LogDefs.eLogLevel.eLOG_DATAXFER) Then : LogMsg(eLogLevel.eLOG_DATAXFER, "sTransformResult=" & sTransformResult) : End If
oEnvRs.AddDocument(New InlineDocument(sTransformResult))
Catch ex As Exception
ExceptionManager.Publish(ex)
LogError(eLogLevel.eLOG_ERROR, ex.tostring)
End Try
End If
If oEnv.GetCustomParam("ESBCore", "ConfigTest") = "1" Then
Try
'do some Config Application Block testing...
'Dim oContext As ESB.Core.Interfaces.ServerContext.IServerContext
Dim sTmp As String
GetConfigData()
sTmp = m_oCfgData.ContextManager.ClientContextManagerObjectImplementation.AssemblyPath()
Dim oWarningEntry As New ErrorXML.WarningEntry
With oWarningEntry
.Code = "testcode"
.Description = "if you get this message then the test txn worked."
.Description = .Description & "ConfigTest Value=" & sTmp
.Source = System.Reflection.Assembly.GetExecutingAssembly.ToString
End With
oErr.WarningEntry.Add(oWarningEntry)
If ShouldLog(LogDefs.eLogLevel.eLOG_DATAXFER) Then : LogMsg(eLogLevel.eLOG_DATAXFER, "sTmp=" & sTmp) : End If
Catch ex As Exception
ExceptionManager.Publish(ex)
LogError(eLogLevel.eLOG_ERROR, ex.tostring)
End Try
End If
If oEnv.Context = "TestAddToPipelineTransitionData" Then
Dim sPTD As String = oEnv.GetCustomParam("ESBCore", "TestAddToPipelineTransitionData")
Try
Dim oWarningEntry As New ErrorXML.WarningEntry
With oWarningEntry
.Code = sPTD
.Description = "PipelineTransitionData instance " & sPTD
.Source = System.Reflection.Assembly.GetExecutingAssembly.ToString
End With
oErr.WarningEntry.Add(oWarningEntry)
Catch ex As Exception
ExceptionManager.Publish(ex)
LogError(eLogLevel.eLOG_ERROR, ex.tostring)
End Try
The code below is an excerpt from the SAP Adaptor. It shows how you would get a business document out of the ESBEnvelope (Line 2).
Excerpt from SAP Adaptor - EnqPOSummaryDetails service handler
- m_oPlgRs =
New PurchaseOrderSummaryRs.PurchaseOrderSummaryRs
m_oPlgRq = oEnv.GetDocumentAsObject(GetType(PurchaseOrderSummaryRq.PurchaseOrderSummaryRq))
m_oPlgRs.PONumber = m_oPlgRq.PONumber
GetPOItemsFromSAP()
'Create a response envelope using the request envelope as a starting point. This preserves routing information etc.
'but strips out the original documents, attachments & relevant manifest entries.
oEnvRs = oEnv.CreateResponse()
'Add the response document to the response envelope
oEnvRs.AddDocument(New InlineDocument(m_oPlgRs))
Return oEnvRs
3. Configuration
As you can see above, the services request and the service handler are defined separately. This step is where we tie in steps 1 & 2. i.e. marry the Service Request to the Service Handler(s) that implement the service. The Request and Handlers are decoupled in this way. Because of ths decoupling, a service can change implementation without the client having to change. The client will only need to change if the Request or Response require changes in the data being transferred.
You can swap out adaptors/handlers implementing a particular service without restarting the service bus.
The generic interface of the service bus makes it a simple task for a BPM tool to call into any ESB.NET based service, and then re-map the service calls within the tool, as only the business data for a particular request needs to be considered on the BPM tool side.
To view the configuration entries, navigate to the following directory in the ESB build.
..\5.0\5.0.1.0\Source\ESB\Base\Solutions\Main\Configuration\XMLConfigFiles
Open up InteropPipelineCfg.xml
This file contains the mappings between service request and implementation.
A mapping entry consists of the addition of a PipelineEntry into this file. An example of a simple PipelineEntry is shown below.
The Context and ResolveName are the keys into the service handler.
The Context is a concatenation of the ESBEnvelope Service fields. In our example, it's left blank.
The Context is derived as follows for the ESBEnvelope:
[SystemType][SystemVersion].[Context].[ServiceType][ServiceVersion]
Note the periods between some of the fields.
The reason for this is that ESB.NET can support custom envelopes. Out of the box comes support for the BizTalk Jumpstart Kit envelope - the JSKEnvelope. It has a different mapping to the ESBEnvelope as it has a different set of fields in the envelope header.
This scheme can be fulfilled by custom envelope implementations and the one PipelineEntry configuration can then be used to handle all envelopes. This is addressed by the Envelope's IEnvelopeProvider implementation. Each envelope has it's own scheme for this mapping. Fortunately, there is seldom need to implement your own EnvelopeProvider, so this is less of a problem in real life Note that an EnvelopeProvider model is the preferred method of supporting multiple envelopes as opposed to XSLT, as it encapsulates all the issues related to different envelopes.
The ResolveName is the name of the primary document in the ESBEnvelope.
The MultiLevelOverrides node is used to define a handler for this PipelineEntry. In the example below, the ESB.Testing.TestTxns.TestTxn1 class is the handler specified to handle the request for this entry. You can have multiple PipelineEntry nodes with the same key fields. This is how you specify multpile handlers being executed for a particular request. There are also some parameters to specify whether you want to chain the handlers together using the original request or the outpur of the previous handler etc., whether to execute a particular handler asynchronously, and which queue(s) you want responses to go to.
There are also properties to keep a list of handler inputs/outputs in case you want to create more cohesive handlers.
- <
PipelineEntry>
<ResolveNameEntry>
<ContextEntry>
<Context>< FONT>Context>
<ResolveName>BuildCmd2< FONT>ResolveName>
<PipelineID>1< FONT>PipelineID>
<ProcessInParallel>0< FONT>ProcessInParallel>
<ResponseIsRequestForNextPipelineItem>1< FONT>ResponseIsRequestForNextPipelineItem>
<MultiLevelOverrides>
<RequestQ />
<TransactionComponent>ESB.Testing.TestTxns.TestTxn1< FONT>TransactionComponent>
<UseObjectBroker>0< FONT>UseObjectBroker>
<ObjectBroker>< FONT>ObjectBroker>
<ObjectBrokerServer />
< FONT>MultiLevelOverrides>
< FONT>ContextEntry>
< FONT>ResolveNameEntry>
< FONT>PipelineEntry>
Below is a more complex example. It consists of 4 Handlers executed sequentially to implement a service.
The first writes some data to a database, the second does a generic XSLT operation, the third kicks off a BPM process and the fourth updates a database.