How to create a Camel HL7 Listener

This is an example of an HL7 listener. This application listens for Hl7 messages in a specific IP and port and returns an HL7 ACK message.

I am going to explain step by step how to develop an HL7 listener using camel and HAPI. These are the steps that I have followed:

  1. Define a pom.xml
  2. Create an Application Context file
  3. Create a Route Builder
  4. Create a Bean to process the message
  5. Run the application
  6. Optional: Create a Unit Test and check that everything works fine

  • Define a pom.xml

This project has been developed using Maven, so first of all your need to create you pom.xml with the camel and HAPI dependencies. Also you will need to add the Camel Maven plugin. The Camel Maven Plugin allows you to run your Enterprise Integration Patterns using Spring for Dependency Injection.

By default, the Camel Maven Plugin gets from META-INF/spring/*.xml target any application config file. If you would like to set a different location you will need to add the applicationContextUri tag:

<plugin>

<groupId>org.apache.camel</groupId>

<artifactId>camel-maven-plugin</artifactId>

<configuration>

<applicationContextUri>NEW LOCATION</applicationContextUri>

</configuration>

</plugin>

  • Create an Application Context file

In the Applicatoin Contex file, I have defined:

– The auto-detecting components property. This way Spring will register all the beans in the base-package: hl7Integration.camel. Including the Route Builder where I have set my camel route.

–  Properties file which includes the listener IP and port. This properties file is optional but it is handy if you don’t want to have thins information hard coded.

– The Camel Context. The Camel context includes the listener endpoint (hl7listener) and also the contextScan tag which will look for Route Builder instances . Note that I have added two properties: sync= true because every time that we receive a message we send back and ACK and the other property is the HL7 codec.
[xml]




[/xml]

  • Create a Route Builder

Create a route in the Route Builder file with the same name that you have previously defined in the Application Context. In this case, all the messages received in “hl7listener” will be send to the ACKResponse bean.

[java]
@Component
public class InboundRouteBuilder extends SpringRouteBuilder
{
private static final Logger log = LoggerFactory.getLogger(InboundRouteBuilder.class);

@Override
public void configure() throws Exception {

from(“hl7listener”)
.routeId(“route_hl7listener”)
.startupOrder(997)
.unmarshal()
.hl7(false)
.to(“bean:respondACK?method=process”)
.end();
}
}
[/java]

  • Create a Bean to process the message

Implement a bean where we are going to process the message.
[java]
@Component
public class RespondACK {

public Message process(Message in) throws Exception {
System.out.println(in.toString());
Message out = in.generateACK();
System.out.println(out.toString());
return out;
}
}
[/java]

  • Run the application

To run this application you only need to try: mvn camel:run

  • Create a unit Test (optional)

If you would like to test that everything works fine, create a unit test like this:
[java]
public class TestListener extends CamelTestSupport {

/**
* This method sends an HL7 message to the Listener and
* receives and ACK confirmation message
* @throws Exception
*/
@Test
public void testHl7Codec() throws Exception {

String inMessage = “MSH|^~\\&|hl7Integration|hl7Integration|||||ADT^A01|||2.5|\r” +
“EVN|A01|20130617154644\r” +
“PID|1|465 306 5961||407623|Wood^Patrick^^^MR||19700101|1|||High Street^^Oxford^^Ox1 4DP~George St^^Oxford^^Ox1 5AP|||||||”;

String out = (String) template.requestBody(“mina:tcp://localhost:4444?sync=true&codec=#hl7codec”, inMessage);

System.out.println(out);

}

protected JndiRegistry createRegistry() throws Exception {
JndiRegistry jndi = super.createRegistry();
jndi.bind(“hl7codec”, new HL7MLLPCodec());
return jndi;
}
}
[/java]

  • Get the code

You can find all the sources code in github.

You may also like...

15 Responses

  1. Marcus Young says:

    Just interested in your thoughts regarding multiple message types being received and processed by a single channel. To explain I have two applications; one processes ADT the other ORU. It occurred to me that these could be combined into a single application that listens on a single port. Is this reasonable or is there an advantage keeping the processing separate and hence running in (in my case) different threads? In fact I am currently loading the messages into a FIFO buffer to manage any bursting, so I have two FIFO buffers; one for ORU and one for ADT

    • Ignacio Suay says:

      If I were you I would use one single application. If you have more than one application that will be more difficult maintain. In this case, I would use Apache Camel + ActimeMQ. Using Camel you could have different business logic for different messages, I have written a couple of post about camel, but I strongly recommend you read “Camel in Action” it is a very good book, and very easy to follow. On the other hand, you could use ActiveMQ to manage your queues, it is very useful to manage any bursting and provides reliable messaging.
      I hope it helps.

  2. Marcus Young says:

    Thank you for your advice. I am using Camel, but I will have a look at ActiveMQ.

  3. R Sen says:

    I am able to code a listener following your approach. However, although my listener works when tested from the localhost it fails when I have the test running on a machine and sending HL7 message to the listener on a different machine. The error message I get is ‘connection refused’ – However, when run a basic TCP client it can connect to that machine/port. I am not sure what is the problem. Any insight might help.

    • Ignacio Suay says:

      Please could you show me how do you specify the ip destination in your test and in your camel config file?
      Try using 0.0.0.0 instead of localhost, I think that should fix your problem.

  4. Deepa says:

    I used ur complete code, but my listener doesn’t work. I am using my localhost for IP destination. I tried using 0.0.0.0 as well as my ip of my machine but it doesn’t work. I am getting the exception:
    Caused by: org.apache.mina.common.RuntimeIOException: Failed to get the session.
    at org.apache.mina.common.support.DefaultConnectFuture.getSession(DefaultConnectFuture.java:63)
    at org.apache.camel.component.mina.MinaProducer.openConnection(MinaProducer.java:222)
    at org.apache.camel.component.mina.MinaProducer.doProcess(MinaProducer.java:89)
    at org.apache.camel.component.mina.MinaProducer.process(MinaProducer.java:77)
    at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
    at org.apache.camel.processor.UnitOfWorkProcessor.processAsync(UnitOfWorkProcessor.java:150)
    at org.apache.camel.processor.UnitOfWorkProcessor.process(UnitOfWorkProcessor.java:117)
    at org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:99)
    at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:86)
    at org.apache.camel.processor.UnitOfWorkProducer.process(UnitOfWorkProducer.java:63)
    at org.apache.camel.impl.ProducerCache$2.doInProducer(ProducerCache.java:366)
    at org.apache.camel.impl.ProducerCache$2.doInProducer(ProducerCache.java:337)
    at org.apache.camel.impl.ProducerCache.doInProducer(ProducerCache.java:233)
    at org.apache.camel.impl.ProducerCache.sendExchange(ProducerCache.java:337)
    at org.apache.camel.impl.ProducerCache.send(ProducerCache.java:192)
    at org.apache.camel.impl.DefaultProducerTemplate.send(DefaultProducerTemplate.java:115)
    at org.apache.camel.impl.DefaultProducerTemplate.sendBody(DefaultProducerTemplate.java:119)
    … 31 more
    Caused by: java.net.ConnectException: Connection refused: no further information

    • Ignacio Suay says:

      Hi Deepa,

      You will need first to run: “mvn camel:run” in a different terminal to initialize the camel and spring context. The camel:run goal of the Camel Maven Plugin is used to run your Camel Spring configurations in a forked JVM from Maven.

      So you will need to keep the session open, and then you will be able to run the tests.

      Please try this approach and let me know how did it go.

  5. Ganesh says:

    I use your complete code, I am getting the below error while running the mvn camel:run command.
    [ERROR] Failed to execute goal org.apache.camel:camel-maven-plugin:2.10.6:run (default-cli) on project Camel-Hl7-listener: null: MojoExecutionException: InvocationTarget
    Exception: Could not resolve bean definition resource pattern [META-INF/spring/*.xml]; nested exception is java.io.FileNotFoundException: class path resource [META-INF/s
    pring/] cannot be resolved to URL because it does not exist -> [Help 1]
    [ERROR]
    [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
    [ERROR] Re-run Maven using the -X switch to enable full debug logging.
    [ERROR]
    [ERROR] For more information about the errors and possible solutions, please read the following articles:
    [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

    • Ignacio Suay says:

      Hi Ganesh,

      I have just tried to clone the project again in a clean machine and I am not getting any error when I run “mvn camel:run”. Could you please check that the code is exactly the same as in the project please?

  6. Marcus says:

    Just curious – in your example above, how would I change the RespondAck ID Generator:
    ctx.getParserConfiguration().setIdGenerator(new FileBasedHiLoGenerator())

    but where would I get the HapiContext ctx – or for that matter set it?
    or should it be like this:

    @Component
    public class RespondACK {
    public Message process(Message in) throws Exception {

    HapiContext ctx = new DefaultHapiContext();
    ctx.getParserConfiguration().setIdGenerator(new FileBasedHiLoGenerator());

    System.out.println(in.toString());
    Message out = in.generateACK();
    System.out.println(out.toString());
    return out;
    }
    }

  7. Chidambaram says:

    This may not be relevant to the post, but thought someone would have an idea here. Is there Camel component available for DICOM protocol. I don’t see any in official website. If anyone knows about it please let me know.

  8. Dhamotharan Balasubramanian says:

    Could you provide steps to step the same in spring boot

  9. wuhui says:

    Always return automatically processed information

    How do I customize the information for the response

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.