Admit Discharge Transfer (ADT) Query Patient (XML)
In this scenario a client application queries a remote Master Patient Index (MPI) asynchronously using an XML version of an ADT HL7v2 message.
ADT Admit Patient – XML format – Asynchronous
Overview
In this scenario a client application queries a remote Master Patient Index (MPI) asynchronously using an XML version of an ADT HL7v2 message. From a business perspective this is typically used by a departmental system to retrieve demographics from the hospital PAS.
The following describes the logic required to invoke the ITK urn:nhs-itk:services:201005:getPatientDetailsByNHSNumber-v1-0 service.
From a black-box perspective this asynchronous interaction is still fairly straightforward, though obviously more complicated than the synchronous interactions. The service requestor sends the appropriate ITK request message and receives a synchronous acknowledgement. Any exceptions encountered in accepting the request will be returned as synchronous Simple Object Access Protocol (SOAP) Faults.Sometime later, the query response is returned by the service provider (PAS) asynchronously to a callback handler specified by the service requestor. Any exceptions encountered in processing the request will be returned as asynchronous SOAP Faults.
Sender view
The Sender builds a SimpleMessage using a String containing the XML ADT message. Configuration of the service tells the R.I. that this message is XML and does not requires base64 encoding.
Receiver view
The Receiver consumers the SOAP message. This validates the SOAP headers and then extracts the payload. The SOAP payload is then split further into Distribution Envelope and business payload. The Distribution Envelope is validated and extracted into MessageProperties. The R.I. then looks up the service properties from the configuration and – in this case – as the payload is simple XML it is passed onto the Application Message Handler.
Request message
[code lang=”xml”] <soap:Envelope xmlns:itk="urn:nhs-itk:ns:201005" … > <soap:Header> … </soap:Header> <soap:Body> <itk:DistributionEnvelope> <itk:header service="urn:nhs-itk:services:201005:queryPatientDemographics-v1-0" …> <itk:manifest count="1"> <itk:manifestitem mimetype="text/xml" base64="false" compressed="false" id="… " </itk:manifest> </itk:header> <itk:payloads count="1"> <itk:payload id="uuid_E808A967-49B2-498B-AD75-1D7A0F1262D7"> <hl7v2:QBP_Q21 xmlns:hl7v2="urn:hl7-org:v2xml"><hl7v2:MSH><hl7v2:MSH.1>|</hl7v2:MSH> … … [/code]
Response message
[code lang=”xml”] <itk:DistributionEnvelope xmlns:itk="urn:nhs-itk:ns:201005" … <itk:header service="urn:nhs-itk:services:201005:queryPatientDemographics-v1-0Response" … <itk:manifest count="1"> <itk:manifestitem mimetype="text/xml" base64="false" … </itk:manifest> </itk:header> <itk:payloads count="1"> <itk:payload id="uuid_E808A967-49B2-498B-AD75-1D7A0F1262D7"> <hl7v2:RSP_K21 xmlns:hl7v2="urn:hl7-org:v2xml"> <hl7v2:MSH> <hl7v2:MSH.1>|</hl7v2:MSH.1> [/code]
How it all happens - step 1
The sending application builds a SimpleMessage and populates the business payload with the ADT message. The application then sets a number of message properties before creating a message sender and sending the message.
In this case the application uses the sendAsync call of the ITK API because the call is to be asynchronous. Normally this will be known at design time, but as the example shows this decision can be made at run-time provided the application is sophisticated enough to handle the two paradigms.
[code lang=”java”]
// Create the message
ITKMessage msg = new SimpleMessage();
msg.setBusinessPayload(docText);
…
// Set the message properties
mp.setServiceId("urn:nhs-itk:services:201005:queryPatientDemographics-v1-0");
…
// Create the sender
ITKMessageSender sender = new ITKMessageSenderImpl();
…
// Send this message Asynchronously – return is void.
sender.sendAsync(msg);
[/code]
Step 2
The first layer of the ITK Reference Implementation (R.I.) is the sendAsync method of the ITKMessageSenderImpl. This component is responsible for checking service properties, establishing the physical transport route and adding the ITK Distribution Envelope, as described in earlier scenarios.
For a simple asynchronous message the service configuration is of particular interest. ADT messages can be sent synchronously or asynchronously, but in the later case the return endpoint needs to be defined. The R.I. does this through the directory.properties configuration file, which manages all configurable aspects of message routing.
The config on the right is taken from the Samples Project and shows where callback calls are routed to.
[code lang=”java”] public void sendAsync(ITKMessage request) throws ITKMessagingException { String serviceId = request.getMessageProperties().getServiceId(); // Get the ITKService from the DirectoryOfService ITKService service = dos.getService(serviceId); // validate the service configuration is valid for an async call … ITKTransportRoute route = getRoute(request); ITKSender sender = getSender(route); ITKMessage message = buildMessage(request, route, service); sender.sendAysnc(route, message); [/code] [code lang=”java”] urn\:nhs-itk\:services\:201005\:queryPatientDemographics-v1-0.urn\:nhs-uk\:addressing\:ods\:TESTORGS\:ADTLOCAL.channelid=ADTLOCAL # Configure callback for async calls ADTLOCAL.ReplyTo=https://developer.nhs.uk:8080/itk-samples/adt/callback ADTLOCAL.ExceptionTo=https://developer.nhs.uk:8080/itk-samples/adt/callback [/code]
Step 3
The next layer of the ITK Reference Implementation (R.I.) is the sendAsync method of the ITKSenderWSImpl.
This component is responsible for managing the WebServices layer – building the SOAP wrappers, physically sending the message, then interpreting the response.
The sendAsync code tells the SOAP handler (WSSOAPMessageImpl) that the message is an Async request which, together with the configuration properties, enables the R.I. to build the correct SOAP Headers.
[code lang=”java”] public void sendAysnc(ITKTransportRoute dest, ITKMessage req) … // Add the soap wrappers WSSOAPMessageImpl message = new WSSOAPMessageImpl(dest, req, WSSOAPMessageImpl.ASYNCREQ); [/code] [code lang=”xml”] <soap:Header> <wsa:MessageID>D16AF8FB- … -0A7858587D49</wsa:MessageID> <wsa:Action>urn:nhs … :queryPatientDemographics-v1-0</wsa:Action> <wsa:To>https://developer.nhs.uk:8080/itk-samples/adt/emulator</wsa:To> <wsa:From> … </wsa:From> <wsa:ReplyTo> <wsa:Address>https://developer.nhs.uk:8080/itk-samples/adt/callback </wsa:Address> </wsa:ReplyTo> <wsa:FaultTo> <wsa:Address>https://developer.nhs.uk:8080/itk-samples/adt/callback </wsa:Address> </wsa:FaultTo> … </soap:Header> [/code]
Step 4
The Reference Implementation provides the AbstractSimpleMessageServlet as an abstract implementation of an ITK receiving application. This is realised by an application provider producing an extension of this class providing concrete implementations of the abstract methods.
This abstract class extracts the incoming transport properties from the SOAP headers and attaches them to the ITKMessage which is built to represent the inbound message, providing a mechanism for the reply to parameters to live with the message.
Importantly the replyTo parameter is used by the R.I. to determine whether to invoke the application handler synchronously or asynchronously.
See the full source code for the AbstractSimpleMessageServlet.
[code lang=”java”]
// 2) PROCESS THE SOAP HEADERS
//Extract the transport properties
itkTransportProperties = ITKTransportPropertiesImpl.buildFromSoap(doc);
…
//Attach the associated transport properties so that they
// are available for asynchronous invocations
itkMessageProperties.setInboundTransportProperties(itkTransportProperties);
…
//Determine which method to call on application
if (StringUtils.hasValue(itkTransportProperties.getTransportReplyTo())) {
//ReplyTo is present – must want asynchronous response
this.messageConsumer.onMessage(requestMsg);
} else {
//ReplyTo is not present assume synchronous application
response = this.messageConsumer.onSyncMessage(requestMsg);
}
[/code]
Step 5
In the Async ADT Query in the Samples project the SimpleApplicationEmulator is used to illustrate this. This emulator creates the response message using the message properties from the request, thereby telling the R.I. that this is a response to that message.
In this scenario the onMessage method of the application handler wass called which provides no response. Once control is received from the handler the R.I. will simply return an HTTP 202 acknowledgement on the open channel.
[code lang=”java”]
private ITKMessage turnaroundViaFile(String responseProfileId,
ITKMessage request) throws ITKMessagingException {
//Create a response using the message properties from the request
SimpleMessage msg = new SimpleMessage(request.getMessageProperties(),
this.auditIdentity, responseProfileId, true);
…
// Read the response from a pre-defined file
String responseString;
try {
responseString = readFile(fileName);
} catch (IOException e) {
…
}
msg.setBusinessPayload(responseString);
return msg;
[/code]
Step 6
The response message is sent through the normal send API. However. because the ITKMessage was built from the incoming message properties (including the ReplyTo), the message already knows the route back to the callback endpoint and therefore skips the routing lookup.
The ITKMessageSenderImpl allows for this pre-determination of route.
[code lang=”java”]
private ITKTransportRoute getRoute(ITKMessage request) …
String serviceId = request.getMessageProperties().getServiceId();
// Has the transport route already been resolved?
ITKTransportRoute route = request.getPreresolvedRoute();
…
// If the route has not been pre-resolved look it up
if (route == null) {
route = dos.resolveDestination(serviceId, toAddress);
} else {
Logger.debug("Pre-resolved transport route on object " …
}
[/code]
Step 7
The Reference Implementation provides the AbstractCallbackListenerServlet as an abstract implementation of an ITK callback application. This is realised by an application provider producing an extension of this class which acts as a factory providing an instance of the callback handler ITKCallbackHandler.
The abstract class extracts the incoming transport properties from the SOAP headers and attaches them to the ITKMessage which is built to represent the inbound message. The message is passed to the processITKMessage layer which hands off processing to the appropriate application handler.
See the full source code for the AbstractCallbackListenerServlet.
[code lang=”java”]
public interface ITKCallbackHandler {
public abstract void onMessage(ITKMessage response);
public abstract void onAck(ITKAckDetails ack);
public abstract void onNack(ITKAckDetails ack);
}
[/code]
Step 8
Finally, the application call handler processes the message as per the local requirements of the system
The sample callback hander simply prints a message.
[code lang=”java”]
public class SimpleMessageCallbackHandler
extends AbstractCallbackListenerServlet
implements ITKCallbackHandler {
public void onMessage(ITKMessage request) {
Logger.trace("This is my example callback handler:onMessage()");
}
[/code]
Last edited: 14 January 2022 4:18 pm