On Software

 
 

Before we get started on this tutorial, have a quick look at my earlier article titled “A Very Short Introduction to the HL7 2.x Standard” for information on the HL7 standard. Please note that this tutorial assumes you know Java or any equivalent object-oriented language. A basic understanding of network as well as thread programming will be useful, but is not necessary.


Introduction


Building custom applications using the HL7 standard can be a very daunting task for a beginner. I have built a number of custom HL7 server applications over the years, and couldn’t have done it without the help of others in the field through my many discussions with them over the Internet. As I got more proficient, I also received a number of emails from other developers/customers asking the same questions that I did when I was getting started. Therefore, in the spirit of sharing and giving back to the software development community, I decided to put together an example/tutorial illustrating all the basics  any programmer dealing with the HL7 standard should know.


Although the HL7 standard itself does not recommend any specific protocol to use for message communication, most HL7 systems communicate using the Minimum Lower Layer Protocol. So, this tutorial will show you how to build custom HL7 servers in Java using this protocol. However, advanced topics such as robust error handling, error logging and notifications, message translation as well as data persistence are not covered in this tutorial. However, I do plan to write another tutorial on those topics in the near future.


What is Minimum Lower Layer Protocol?


Minimum Lower Layer Protocol (often shortened to “MLLP”) is the most popular protocol used for transmitting HL7 messages using TCP/IP. Because TCP/IP transmits information as a continuous stream of bytes, a wrapping protocol is required for communications code to be able to recognize the start and the end of each message (i.e. headers and trailers). MLLP is often used to address this requirement typically using non-printable characters to serve as wrapping characters around the core HL7 message information.


What are Sockets?


You can think of sockets as an abstraction of “terminals” of a connection between two machines transmitting information through either TCP or the UDP protocols. Most modern languages such as Java, C#, VB.NET and Ruby provide socket-programming support to ease the burden of the application programmers having to deal with lower layers of the OSI (Open Systems Interconnect) model of the ISO (International Standards Organization) standard.  When using sockets, the programmer is working with the “Session” layer, and therefore shielded from having to deal with real-time data communication services provided by the lower layer of the OSI stack. Java provides excellent support for socket programming (through the java.net package). We will be using the “Socket” and the “ServerSocket” classes for the examples to be illustrated below.


Tools you need


JDK 1.4 SDK or higher

Eclipse or any other Java IDE (or even a text editor)


Steps (We will take “baby” steps so all the core things are covered)


1. Build a simple TCP/IP client capable of transmission and reception of information

  1. 2.Build a simple TCP/IP server capable of reception and echo-ing of received information

  2. 3.Build simple threading support to handle many concurrent connections from clients

  3. 4.Add MLLP and message acknowledgement features into the server

  4. 5.Write a small TCP/IP client that will transmit a dummy MLLP-wrapped HL7 message

6. Modify the server class to build MLLP-based parsing functionality

7. Modify the server class to build a simple acknowledgment functionality

8. Put it all together


Step 1 of 8 - Build a simple TCP/IP client capable of transmission and reception of information


Here is a really simple TCP client that we will use for understanding how sockets work. We will use this client to primarily test our server application through the various steps.


As you can see from the code below, the “Socket” class is used to establish a connection with another listener application running locally, listening on port 1080. Once a connection is established, one can read and write through this connection by using the input and output streams. When we are done, we simply close the socket.


Tip: In industrial strength applications, if one cannot establish a connection right away, or if an existing connection is dropped for unforeseen reasons (if the server application crashes, or is shut down accidently), client applications should retry a certain number of times before giving up. I will not be illustrating this functionality as it is not in the scope of this tutorial, however, it is a very useful feature to include in your HL7 application.




Step 2 of 8 - Build a simple TCP/IP server capable of reception and echo-ing of received information


Now that we have seen a TCP client example in the previous step, here is a really simple TCP server that can accept a connection from the client through a specified port, receive any information transmitted by it, and simply echo it back. It will continue running forever in a loop. We typically build server applications in Java using the “ServerSocket” class. This version of our server is capable of accepting and processing only one connection at a time. Once a client connection is accepted by the server, we can read and write through the input and output streams provided by this connection.


Tip: In real 24/7 HL7 server applications, you will have to ensure that the server does not crash by implementing rugged error handling as well as “auto-restart” feature in case the server does crash. Also, you will want to send error notifications through emails in case manual intervention is required quickly, or write to event logs of the operating system.




Step 3 of 8 - Build simple threading support to handle many concurrent connections from clients


The client and server examples illustrated so far are extremely simplified examples to help you understand the basics of socket communications. The server we have implemented is capable of handling only one connection at a time.  In order to handle multiple connections, you will have to use threads. Threads are very easy to implement in Java. But, they are also very easy to misuse. When implementing threads in Java, you will have to ensure that the various threads don’t interfere with one another’s work. This is because threads share the same memory.  In this modified version of the server, we instantiate a new thread, and pass every incoming connection that is accepted by the server to its own  ConnectionHandler class. This will ensure that the server is quickly ready to accept the next client waiting to connect to the server without any noticeable delay.


Tip: In many HL7 systems in use around the world, multiple clients may attempt to connect to a server at the same time (either on different ports, or on the same port). The server application must be able to accept all these connections without too much delay, and then receive, process and acknowledge any information received from each client.




Step 4 of 8 - Add MLLP and message acknowledgement features into the server


Even though the examples illustrated so far allow the client and server to transmit simple text messages back and forth, this will not be enough to communicate with HL7 systems. Communication with HL7 systems will require the use of MLLP protocol and message acknowledgment features. In this step, we will take our existing server and add code to enable MLLP support.  We will also implement functionality in the server to create and transmit a simple acknowledgment message back to the client application.


More on MLLP


At the start of this tutorial, I described the MLLP protocol as a “wrapping” protocol where the core HL7 message is enveloped in special characters to signal the start and end of transmission of message information to the receiving application. When dealing with MLLP, there are essentially three character groups that you will configure and use. They are “Start Block (<SB>)”, “End Block (<EB)>” and “Segment Separator (<CR>)”. HL7 messages transmitted using the MLLP protocol are prefixed with a <SB> character, message segments are terminated with a <CR>, and the messages are terminated with two characters: <EB><CR>.


Most often, the character typically used to signify <SB> is a VT (vertical tab), which is ASCII 11. The FS (file separator), ASCII 28, is used to signify <EB>. And the CR (carriage return), which is ASCII 13, is used to signify <CR>. However, the choice of what character to use is entirely up to you.


Here is an example of an observation request HL7 message “enveloped” for transmission using the MLLP-related wrapping characters:


<SB>MSH^~\&199809091533ORU^R01856369D2.2<CR>

PID139385000343456<CR>

ORC123112312RAD19980909-0005<CR><EB><CR>



Step 5 of 8 - Write a small TCP/IP client that will transmit a dummy MLLP-wrapped HL7 message


Here is the original TCP client modified to transmit a sample HL7 message. Notice how the test message is wrapped in the special characters as explained earlier (highlighted in bold).




Step 6 of 8 - Modify the server class to build MLLP-based parsing functionality


In this step, we will add a method to the server (inside the Connection Handler Class) to handle the parsing of the stream of characters received from the client using the socket stream. The gist of this parsing routine is that it will expect MLLP wrapping characters in the correct order/sequence. If not, it will thrown an exception and close the connection with the client.




Step 7 of 8 - Modify the server class to build a simple acknowledgment functionality


Next, we will add two other methods to the server (again, inside the Connection Handler Class) to build simple HL7 message acknowledgements to transmit back to the client. The first method is to help build a basic message acknowledgement functionality. The method shown below is a verify simplified example to aid in the understanding how acknowledgement messages can be constructed. In reality, you will retransmit other fields in the MSH and MSA segments such as sending facility, sending application, version of the application, and other useful information.





The second supporting method is to help parse the message control id of the received message that will need to be re-transmitted in the acknowledgement message. Some portions of this second routine below may seem like an overkill, but I do this deliberately to illustrate how fields inside a HL7 message can be parsed using the field delimiter (once you can parse one field, you can apply the same concept to do the others). You may also want to raise an exception when the message control id is not found.





Step 8 of 8 - Put it all together


Click here to see the entire code listing for our new Server class.


Conclusion


We are done. We have essentially seen all the bits and pieces needed to assemble a custom HL7 application from scratch if you needed to. Often, instead of re-inventing the wheel, a HL7 programmer will use either freeware or commercial toolkits to build his/her HL7 messaging application. However, even when dealing with toolkits, a basic understanding of sockets, the MLLP protocol as well as message acknowledgement fundamentals that were covered in this tutorial is required.


Building robust HL7 systems capable of running 24/7 and unattended is not easy. For most programmers, once they get past the initial socket-programming hurdles, the bulk of the issues in HL7 2.x-related programming will be around supporting any parsing, translating, formatting and storage requirements. With HL7 3.0 standard starting to come into use increasingly, these issues should go slowly away. However, HL7 2.x standard isn’t going anywhere for now. We should expect it to stay at least for 10 more years before they are fully retired. Until then, we will have to continue on our quest to build more elegant and sturdier solutions for HL7 messaging.


Tip: You will want to design the system so that the MLLP character groupings are configurable. This way, the customer can change them to whatever they require them to be. You should also permit some configuration around the message acknowledgment functionality such as information that is transmitted about the processing system and its location (goes in the “MSH” segment). Also, a configuration to indicate whether the system is in “test” mode versus “production mode”. In the past, things that I have made configurable also include message control id starting number or number pattern, email addresses and error log locations (for error notifications), as well as connection retry attempts.

 

HL7 Programming using Java A Short Tutorial

2009-02-21

 
 

Next Entry

Previous Entry