ADT Query patient (XML)

Black Box View

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 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 p/h ADT message. Configuration of the service tells the R.I. that this messages requires base64 encoding so the RI performs the encoding and builds the DistributionEnvelope with the appropriate tagging.

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 in base64 form it is decoded before being packaged up as a SimpleMessage and passed onto the Application Message Handler.

Request Message (Get example)

<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:admitPatientPH-v1-0" ...>
    ...
   </itk:header>
   <itk:payloads count="1">
    <itk:payload id="...">TVNIfF5+X ... 4MTIzNHwNCg== </itk:payload>
   </itk:payloads>
  </itk:DistributionEnvelope>
 </soap:Body>
</soap:Envelope>

Response Message (Get example)

<itk:manifest count="1">
        <itk:manifestitem base64="true"
            id="uuid_6e9306ba-9a09-4479-a719-fcc23234b9ec"
            mimetype="text/plain" profileid="ITKv1.0"/>
    </itk:manifest>
    <itk:senderAddress uri="urn:nhs-uk:addressing:ods:TESTORGS:ORGA"/>
</itk:header>
<itk:payloads count="1">
   <itk:payload id="uuid_6e9306ba-9a09-4479-a719-fcc23234b9ec">TVNIfF5+XCZ8...
    ....HwNCg==</itk:payload>
</itk:payloads>

Step 1 (Sending Application)

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 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 to handle the two paradigms.

The full source code for the sample ADT Servlet can be seen here.

 // 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);

Step 2 (Sending R.I. - ITK Layer)

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.

The full source code for the ITKMessageSenderImpl can be seen here.

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);
 urn\:nhs-itk\:services\:201005\:queryPatientDemographics-v1-0.urn\:nhs-uk\:addressing\:ods\:TESTORGS\:ADTLOCAL.channelid=ADTLOCAL
 # Configure callback for async calls
 ADTLOCAL.ReplyTo=http://localhost:8080/itk-samples/adt/callback
 ADTLOCAL.ExceptionTo=http://localhost:8080/itk-samples/adt/callback

Step 3 (Sending R.I. - WS Layer)

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.

The full source code for the ITKSenderWSImpl can be seen here.

public void sendAysnc(ITKTransportRoute dest, ITKMessage req) ...
 // Add the soap wrappers
 WSSOAPMessageImpl message 
   = new WSSOAPMessageImpl(dest, req, WSSOAPMessageImpl.ASYNCREQ);
 <soap:Header>
  <wsa:MessageID>D16AF8FB- ... -0A7858587D49</wsa:MessageID>
  <wsa:Action>urn:nhs ... :queryPatientDemographics-v1-0</wsa:Action>
  <wsa:To>http://localhost:8080/itk-samples/adt/emulator</wsa:To>
  <wsa:From> ...  </wsa:From>
  <wsa:ReplyTo>
      <wsa:Address>http://localhost:8080/itk-samples/adt/callback
      </wsa:Address>
  </wsa:ReplyTo>
  <wsa:FaultTo>
      <wsa:Address>http://localhost:8080/itk-samples/adt/callback
      </wsa:Address>
  </wsa:FaultTo>
	...
  </soap:Header>

Step 4 (Receiver R.I.)

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 concete 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.

The full source code for the AbstractSimpleMessageServlet can be seen here.

 // 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);
 }

Step 5 (Receiver Application)

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.

The full source code for the SimpleApplicationEmulator can be seen here.

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;

Step 6 (Receiver R.I.)

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.

The full source code for the ITKMessageSenderImpl can be seen here.

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 " ...
  }

Step 7 (Callback R.I.)

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.

The full source code for the AbstractCallbackListenerServlet can be seen here.

public interface ITKCallbackHandler {

	public abstract void onMessage(ITKMessage response);

	public abstract void onAck(ITKAckDetails ack);

	public abstract void onNack(ITKAckDetails ack);

}

Step 8 (Callback Application)

Finally, the application call handler processes the message as per the local requirements of the system

The sample callback hander simply prints a message.

public class SimpleMessageCallbackHandler 
  extends AbstractCallbackListenerServlet 
  implements ITKCallbackHandler {

  public void onMessage(ITKMessage request) {
    Logger.trace("This is my example callback handler:onMessage()");
  }