Using ESB 2.1 with HL7 messages
I assume at this point you have your HL7 messages and its Message header and other schemas deployed in BizTalk. The scenario discussed here is where you receive a HL7 message and you return an ACK back in response to sender of message and send the message to MSMQ.
Using MLLP adapter Or ESB On-Ramp Services
In both cases, we are going to use Request-Response port because that is most typical HL7 message interchange scenario. As part of the sample I created an itinerary which will route the message to MSMQ based on the type of message. Also I have used BRE to resolve itinerary. The following steps I followed to make it work:
Create a new Receive pipeline
This most important step is to create a Receive Pipeline using ESB pipelines and HL7 Disassembler pipeline because none of the off the shelf ESB pipeline uses HL7 Disassembler component. Design the receive pipeline as shown in diagram below:
There are two other components used in addition to HL7 Disassembler component, ESB Itinerary Select, and ESB Dispatcher. These two components are required for ESB to select Itinerary and then execute the itinerary.
Create a new Send pipeline
Similar to receive pipeline, create a send pipeline which we will use on Receive port response side. Notice the two ESB pipeline components ESB and ESB Itinerary Cache which are in addition to HL7 Assembler component.
Create an Itinerary Select Rule
Create Itinerary Select Rules which is used by ESB Itinerary Select component to load the itinerary in message context. Please see a sample of Rule. You can use ESB Vocabulary to check for message type and set the required itinerary name and version.
Please note the OR condition is required otherwise your condition will not work. The reason for this is HL7 Disassembler component generates two messages if you have configured ACK and this rule will be executed for each of those messages and if you don’t have condition for ACK message then itinerary selection will fail and you will get an error like:
Reason: Procedure or function 'Itinerary_getitinerary' expects parameter '@name', which was not supplied.
You might now question why do we have Itinerary Select Pipeline component in receive pipeline after HL7 Disassembler component and create our rule based on some other properties rather than message type?
The question is valid and ideally that’s where we should have it unless you want to use message context properties to create rules, however, this approach with HL7 Disassemble component does not work. The problem is when it generates ACK message it does not populate all the original message context properties and as a result your itinerary selection fails.
Create an End Point Set Rule
In this policy, we set the End point of message based on its message type. Since we want to send it to MSMQ we will set the end point accordingly, see the rule below:
Please note, there is no off-the-shelf MSMQ adapter provider, you can follow this article to create this, it’s very simple or you can use some other end point:
Setup Receive Location and Dynamic Send Port
MLLP Recieve Port
For MLLP adapter, setup a request response MLLP port and location and use your Receive pipeline on Receive pipeline properties as follows:
Note you use Itinerary Select Policy here for ESB load itinerary dynamically
There is no specific configuration required for send side.
WCF Generic Receive Port
For WCF Generic On-Ramp, use the ESB.ItineraryServices.Generic.Response.WCF receive location under Microsoft.Practices.ESB application and change it’s receive and send pipeline to your newly created pipelines and configure the receive side as shown above for MLLP.
Dynamic Send Port
Create the Dynamic send port as required by Itinerary offramp, setting filters for servicename, servicestate and servicetype, isrequestresponse.
Create Itinerary
Now the last part, Create your itinerary as it’s a simple scenario to route messages, I created a simple itinerary which takes message using On-Ramp one of your receive port (MLLP or OnRamp.Itinerary.Response) resolves the endpoint using our BRE policy using BRE resolver and routes the message accordingly. Now export and deploy your itinerary to Itinerary Db. Make sure you keep the name same as you have it in your Itinerary select rule.
Now with all artifacts deployed, you can test your scenario using mllpsend utility for MLLP adapter or for WCF Generic, you can create a unit test project and add a service reference and invoke the service.
Before you run it, do not forget to configure your HL7 party to configure ACK messages.
This scenario returns ACK in response as mentioned in the beginning.
The issues you may encounter during this test run
1. Reason: Procedure or
function 'Itinerary_getitinerary' expects parameter '@name', which was not
supplied.
Check the Itinerary Select rule,
most probably either you have not specified correct policy on Receive Pipeline
properties or your condition is not being met.
2. Reason: Value cannot
be null
Check Receive pipeline config
properties on receive location, most likely you have not specified Itinerary
select rule. Make sure ResolverConnectionString is set to BRI:\\policy=;version=;useMsg=
(version and useMsg are optional)
3. Reason: Error
135008: The itinerary was not found in the repository.
Check the Receive pipeline config
properties on receive location, make sure you have set ItineraryFactKey to Resolver.Itinerary
Using WCF On Ramp services to execute this scenario, you may
encounter following issues:
4. There was a failure
executing the receive pipeline: "Rcv_ESB, Pipelines, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=c947d4e52faa0f65" Source: "Unknown
" Receive Port: "OnRamp.Itinerary.Response" URI:
"/ESB.ItineraryServices.Generic.Response.WCF/ProcessItinerary.svc"
Reason: Could not load file or assembly 'file:///C:\Program Files
(x86)\Microsoft BizTalk Server 2010\Pipeline
Components\Microsoft.Solutions.BTAHL7.HL72fDasm.dll' or one of its
dependencies. An attempt was made to load a program with an incorrect format.
In order to fix this issue, change
IIS application pool CoreEsbWcfAppPool to Enable 32-bit Application to True.
This is required because HL7 Disassembler component can be loaded in 32 bit
process only.
5. There was a failure
executing the receive pipeline: “ESB, Pipelines, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=c947d4e52faa0f65" Source: "BTAHL7 2.X
Disassembler" Receive Port: "OnRamp.Itinerary.Response" URI:
"/ESB.ItineraryServices.Generic.Response.WCF/ProcessItinerary.svc" Reason:
Unable to configure the logstore.
Give access to IIS app pool
“CoreEsbWcfAppPool” user to BTAHL7 database, db_datawriter, db_datareader
6. The formatter threw
an exception while trying to deserialize the message: There was an error while
trying to deserialize parameter :part. The InnerException message was 'Element
part from namespace cannot have child
contents to be deserialized as an object. Please use XmlNode[] to deserialize
this pattern of XML.'. Please see
InnerException for more details.
{"Element part from namespace cannot have child contents to be deserialized
as an object. Please use XmlNode[] to deserialize this pattern of XML."}
{"End element 'part' from namespace ''
expected. Found text 'MSH|^~\\'. Line 1, position 234."}
at
System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader,
String res, String arg1, String arg2, String arg3)
at
System.Xml.XmlExceptionHelper.ThrowEndElementExpected(XmlDictionaryReader
reader, String localName, String ns)
at
System.Xml.XmlBaseReader.ReadEndElement()
at
System.Runtime.Serialization.ObjectDataContract.ReadXmlValue(XmlReaderDelegator
reader, XmlObjectSerializerReadContext context)
The above error occurs due to ObjectDataContract
Deserializer. Due to the & in Message header f HL7 message, the
deserializer fails to deserialize the response received from service. This is a
WCF service consumer issue and a possible bug in System.Xml.XmlSerialization
assembly to deal with such xml. The Add service reference generates the proxy
with following method because of any message type in our service wsdl http://localhost
/ESB.ItineraryServices.Generic.Response.WCF/ProcessItinerary.svc?wsdl of
WCF:
public void SubmitRequestResponse(ref
object part)
As a work around of this issue, I
changed the client proxy method as follows:
public void SubmitRequestResponse(ref
string part)
With this change and other
respective change to proxy class, the issue got resolved.
I also tested this interface using
SoapUI (a web service testing tool) and it worked successfully. Since my
version of soapui does not support all WS* standard which are used by ESB WCF
service for message security, I exposed a basicHttpbinding service.
No comments:
Post a Comment