Links

Pages

HAPI ACK/NAK Message Helper

The source code below relates to this blog posting which discussed generating HL7 ACK and NAK messages using the HL7 API (HAPI) library.

I have put the complete NetBeans project for the Apache Camel / HL7 / JMS sample in the public Subversion repository on the Assembla site. This link will take you to the project's SVN page on the Assembla site.

/*
 *
 * Copyright 2009 Roger Searjeant.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package camelhl7jmspersist;

import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.app.DefaultApplication;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.model.Segment;
import ca.uhn.hl7v2.sourcegen.SourceGenerator;
import ca.uhn.hl7v2.util.Terser;
import java.io.IOException;

/**
 * Trivial bean to handle HL7 messages.
 * @author Roger Searjeant
 */
public class HL7MessageHandler
{
  public Message badMessage(Message originalMsg) throws HL7Exception, IOException
  {
    // Use the custom makeACK method to create a NAK:
    Message ack = this.makeACK(originalMsg, "AR");
    return ack;
  }

  /**
   * Convenience overload for makeACK which accepts a Message.
   *
   * @param inboundMessage
   * @param ackCode
   * @return
   * @throws ca.uhn.hl7v2.HL7Exception
   * @throws java.io.IOException
   */
  public Message makeACK(Message inboundMessage, String ackCode) throws HL7Exception, IOException
  {
    Terser t = new Terser(inboundMessage);
    Segment inboundHeader = null;
    try
    {
      inboundHeader = t.getSegment("MSH");
    }
    catch (HL7Exception ex)
    {
      throw new HL7Exception("Need an MSH segment to create a response ACK");
    }
    return this.makeACK(inboundHeader, ackCode);
  }

  /**
   * Create and return an ACK message of the same HL7 version as the inboundHeader.
   * The MSH of the ACK is correctly populated. The acknowledgement code (MSA-1)
   * value can be set using the ackCode parameter. If ackCode is null, makeACK
   * will insert "AA" (application accept).
   *
   * This code was lifted from ca.uhn.hl7v2.app.DefaultApplication and modified
   * slightly to suit my needs.
   *
   * @param inboundHeader The MSH segment from the inbound message being ACKnowledged.
   * @param ackCode The MSA-1 value you want the ACK to carry. If null, MSA-1 will contain "AA".
   * @return Correctly constructed ACK message.
   * @throws ca.uhn.hl7v2.HL7Exception
   * @throws java.io.IOException
   */
  public Message makeACK(Segment inboundHeader, String ackCode) throws HL7Exception, IOException
  {
    if (!inboundHeader.getName().equals("MSH"))
      throw new HL7Exception(
          "Need an MSH segment to create a response ACK (got " + inboundHeader.getName() + ")");

    // Find the HL7 version of the inbound message:
    //
    String version = null;
    try
    {
      version = Terser.get(inboundHeader, 12, 0, 1, 1);
    }
    catch (HL7Exception e)
    {
      // I'm not happy to proceed if we can't identify the inbound
      // message version.
      throw new HL7Exception("Failed to get valid HL7 version from inbound MSH-12-1");
    }

    // Make an ACK of the same version as the inbound message:
    //
    String ackClassName = SourceGenerator.getVersionPackageName(version) + "message.ACK";
    Message out = null;
    try
    {
      Class ackClass = Class.forName(ackClassName);
      out = (Message)ackClass.newInstance();
    }
    catch (Exception e)
    {
      throw new HL7Exception("Can't instantiate ACK of class " + ackClassName + ": " + e.getClass().getName());
    }

    // Create a Terser instance for the outbound message (the ACK).
    Terser terser = new Terser(out);

    // Populate outbound MSH fields using data from inbound message
    Segment outHeader = (Segment)out.get("MSH");
    DefaultApplication.fillResponseHeader(inboundHeader, outHeader);

    // Now set the message type, HL7 version number, acknowledgement code
    // and message control ID fields:
    terser.set("/MSH-9", "ACK");
    terser.set("/MSH-12", version);
    terser.set("/MSA-1", ackCode == null ? "AA" : ackCode);
    terser.set("/MSA-2", terser.get(inboundHeader, 10, 0, 1, 1));

    return out;
  }

  public Message handleORM(Message msg) throws HL7Exception, IOException
  {
    // If the Camel route predicates have done their job, we should be able to guarantee
    // that msg is an ORM^O01.
   
    // Custom logic for processing ORM^O01 goes in here.

    // Return a positive ACKnowledgement.
    return this.makeACK(msg, null);
  }

}