jL7: Coverage report

Here is the current unit test coverage report for jL7. This time I’ve been implementing most of the test as I was writing the code (and for all last changes before writing the code) but as you see, much work is still needed in order to reach the 100% code coverage (whether that is a sign of software quality or not…).

OVERALL COVERAGE SUMMARY

name class, % method, % block, % line, %
all classes 68%  (32/47) 64%  (302/469) 73%  (6247/8614) 70%  (1356.8/1949)

OVERALL STATS SUMMARY

total packages: 8
total executable files: 45
total classes: 47
total methods: 469
total executable lines: 1949

COVERAGE BREAKDOWN BY PACKAGE

name class, % method, % block, % line, %
org.jl7.comm 0%   (0/2) 0%   (0/14) 0%   (0/155) 0%   (0/39)
org.jl7.samples 0%   (0/2) 0%   (0/6) 0%   (0/110) 0%   (0/30)
org.jl7.dsl 20%  (2/10) 8%   (6/80) 7%   (74/1122) 8%   (20/265)
org.jl7.hl7proc 50%  (2/4) 33%  (12/36) 43%  (452/1041) 40%  (87/218)
org.jl7.mllp 100% (3/3) 89%  (16/18) 82%  (294/359) 77%  (82/107)
org.jl7.hl7 100% (9/9) 82%  (102/125) 90%  (1940/2146) 89%  (374/420)
org.jl7.test 94%  (15/16) 87%  (157/181) 94%  (3300/3494) 91%  (752.8/829)
org.jl7.textutils 100% (1/1) 100% (9/9) 100% (187/187) 100% (41/41)

jL7: How to create a Sender

package org.jl7.samples;

import java.io.IOException;

import org.jl7.hl7.HL7Message;
import org.jl7.hl7.HL7Parser;
import org.jl7.mllp.MLLPMetaData;
import org.jl7.mllp.MLLPTransport;
import org.jl7.mllp.MLLPTransportable;

public class Sender {
    private static final String MESSAGE = "MSH|^~\\&||ABCHS||AUSDHSV|20070103112951||ADT^A08^ADT_A01|12334456778893|P|2.5|||NE|NE|AU|ASCII\rEVN|A08|20060705000000\rPID|1||0000112234^^^100^A||XXXXXXXXXX^^^^^^S||10131113|1||4|^^RICHMOND^^3121||||1201||||||||1100|||||||||AAA\rPV1|1|O|^^^^^1|||||||2|||||1||||654345509^^^100^A|1|||||||||||||||||||||||||200607050000||||||V\rPV2|||||||1||||||||||||||||^^^^^^^^^103\rROL|1|AD|SAHCP|XXXXXXXXXX^^^^^^S|||||6|1\rPR1|1||1||20060705|1\rGT1|1||||||||||||||||||||NOT APPLICABLE";
    private static final String HOST = "localhost";
    private static int PORT = 9991;

    /**
     * @param args
     */
    public static void main(String[] args) {
        HL7Message msg = HL7Parser.parseMessage(MESSAGE, true);
        MLLPTransportable transportable = new MLLPTransportable();
        transportable.message = msg;
        transportable.metaData = new MLLPMetaData(HOST, PORT);
        MLLPTransport transport = new MLLPTransport();
        for (int i = 0; i < 10; i++) {
            try {
                MLLPTransportable tr = transport.sendMessage(transportable, true);
                System.out.println(tr.message);
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

jL7: How to create a listener

package org.jl7.samples;

import java.io.IOException;

import org.jl7.hl7.HL7Message;
import org.jl7.hl7.HL7Parser;
import org.jl7.mllp.MLLPMetaData;
import org.jl7.mllp.MLLPTransport;
import org.jl7.mllp.MLLPTransportable;

public class Listener {
    private static final String HOST = "localhost";
    private static int PORT = 9991;
    private static MLLPMetaData metaData = new MLLPMetaData(HOST, PORT);

    /**
     * @param args
     */
    public static void main(String[] args) {
        MLLPTransport transport = new MLLPTransport();
        MLLPTransportable transportable = null;
        for (int i = 0; i < 10; i++) {
            try {
                transportable = transport.receiveMessageReconnectOnError(metaData);
                HL7Message msg = transportable.message;
                System.out.println(msg);
                HL7Message ack = HL7Parser.parseMessage("MSH|^~\\&|GC APP|GC FAC|ACME APP|ACME FAC|20071016055244||ACK^A01|20071016055244131|P|2.3.1|\r\n" + "MSA|AA|13463136|MSG Received Successfully|", true);
                MLLPTransportable response = new MLLPTransportable();
                response.message = ack;
                response.metaData = new MLLPMetaData(HOST, PORT);
                transport.sendMessage(response);
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

jL7: How to receive an HL7 message

Let’s assume we have a remote system which sends us HL7 messages on port xxx. We first need to create an MLLPMetaData object which will contain the information concerning host and port we’ll be listening to:

        private MLLPMetaData metaData = new MLLPMetaData(host, port);

The receiving of a message is done using an MLLPTransport object and invoking it’s receiveMessage method:

                MLLPTransport transport = new MLLPTransport();
                MLLPTransportable transportable = null;
                try {
                        transportable = transport.receiveMessage(metaData);
                } catch (IOException e) {
                        fail("IOException: " + e);
                }
                HL7Message msg = transportable.message;

The receiveMessage method returns an MLLPTransportable object which contains a reveived message in its Message member variable.

Since we usually do not want to close the listening socket after every message, the receiveMessage keeps an open connection which needs to be closed:

                try {
                        transport.disconnect();
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }

That’s it !

jL7: How to send an HL7 message

We’ll now see how we can send an HL7 message using jL7.
Let’s assume we already have an HL7 message object:

                HL7Message msg = HL7Parser.parseMessage("...", true);

Now we need to create an MLLPTransportable object. This object basically encapsulates the information need to send the message: the message itself the hostname and port number where it should be sent to

                MLLPTransportable transportable = new MLLPTransportable();
                transportable.message = msg;
                transportable.metaData = new MLLPMetaData(host, port);

Now we use an MLLPTransport object in order to actually send this newly created transportable object:

                MLLPTransport transport = new MLLPTransport();
                try {
                        transport.sendMessage(transportable);
                } catch (IOException e) {
                        fail("IOException: " + e);
                }

Since the transport object retains the connection to the remote host after sending the message (we do not want to create x connections in order to send x messages in a row), we should close the connection when we’re done with the transport object:

                try {
                        transport.disconnect();
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }

That’s it !

jL7: How to use the jl7 domain specific language

Introduction

The jl7 DSL (Domain Specific Language) is a scripting language which can be used to process and convert HL7 messages. It allows accessing HL7 entities (like message, segments, fields…) with a more natural language than using the Java API.

The DSL is implemented in the package org.jl7.dsl. The entry point to the DSL capabilities is the HL7DSL class. It provides two static methods to work with HL7 messages:

  • processMessage
  • convertMessage

The only difference between these two methods is that convertMessage additionally creates an output message which is returned after the processing.

In the processMessage method, the provided message is to be referred to as “message”. In the convertMessage method, the input message is called “message_in” and the output message is called “message_out”.

General syntax

The DSL is based on Groovy (see http://groovy.codehaus.org for more information). All Groovy and Java libraries can be used.

Here are a few examples of how to use the DSL.

Accessing message content

The following example obtains the PID segment:

println "message.PID:\n"+message.PID

If the message contains multiple PID segments, it will return the first one. In order to get the second PID segment the following syntax shall be used:

println "message.PID[2]:\n"+message.PID[2]

Note: The first segment has the index number 1 (not 0).

To obtain the PID-5 field from a message we can write:

println "message.PID(5):\n"+message.PID(5)

If we have a repetition of this field, the second instance of the field can be accessed by using:

println "message.PID(5)[2]:\n"+message.PID(5)[2]

This is actually the same syntax as for segments.

If the field contains multiple components we access the second component with:

println "message.PID(5)(2):\n"+message.PID(5)(2)

The same kind of syntax can be used to access subcomponents:

println "message.PID(5)[2](3)(2):\n"+message.PID(5)[2](3)(2)

So in the DSL a message has the following structure:

  1. Message
  2. Segment
  3. Field
  4. Component
  5. Subcomponent

To go from one level to the next one, round brackets are used: xxx(n).
To access a repetition of a segment or a field, square brackets are used: xxx[n].
In both cases, the first index is 1.

Modifying message content

A message can be assigned the value of another message using the operator: <<.

The following example copies the content of message_in to message_out.

message_out << message_in

A segment of one message can be added to another message. The following example copies the MSH segment from message_in to message_out.

message_out << message_in.MSH

The operator = can be used instead of >> in order to replace a segment rather than add it.

message_out.PV1 = "PV1|1|O"

The example above replaces the first PV1 segment in message_out by the new segment.

message_out.PV1 = message_in.PV1

Note: If there is no PV1 segment already available in message_out, the two operators = and << have the same effect.

Message groups

The jl7 DSL also supports segments groups. These are groups of segment semantically related e.g. the PD1, ARV, ROL, NK1, NTE segments following a PID segment belong together with the PID segment to a PATIENT group.

The following groups exist:

  • PATIENT: PID or MRG, PD1, ARV, ROL, NK1, NTE
  • VISIT: PV1, PV2, ARV, ROL, DB1
  • ORDER: ORC, OBR, NTE
  • PROCEDURE: PR1, ROL
  • INSURANCE: IN1, IN2, IN3, ROL

Segment groups can be extracted from a message:

message_in.PATIENTS

This returns a list of all PATIENT segment groups. In order to get one of these group, the following can be used:

message_in.PATIENTS(1)

The first group in the list is indexed with 1.

Segments group can be added to a message just like segments.

message_out.MSH = message_in.MSH
message_out.EVN = message_in.EVN
message_out << message_in.PATIENTS(1)
message_out << message_in.VISITS(1)

Mapping message content

In order to map values in a HL7 message, a mapping table can be define.

//Define mapping table
def map = ["s":"I", "a":"O"]
//Change the visit type
message_out.PV1(2) << map[message_in.PV1(2)]

A default value can also be defined for the map.

def map = ['s':'I', 'a':'O'].withDefault{ 'U' }

This means that if the value of attribute being mapped is not ‘s’ or ‘a’, ‘U’ will be used as mapped value.

Note: jL7 can be downloaded here.