Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0

...

HL7

...

Component

...

The

...

hl7

...

component

...

is

...

used

...

for

...

working

...

with

...

the

...

HL7

...

MLLP

...

protocol

...

and

...

HL7

...

v2

...

messages

...

using

...

the

...

HAPI

...

library

...

.

This component supports the following:

  • HL7 MLLP codec for Mina
  • Agnostic data format using either plain String objects or HAPI HL7 model objects.
  • Type Converter from/to HAPI and String
  • HL7 DataFormat using HAPI library
  • Even more ease-of-use

...

  • as

...

  • it's

...

  • integrated

...

  • well

...

  • with

...

  • the

...

...

  • (

...

  • Camel

...

  • 2.11

...

  • :

...

...

  • )

...

  • component.

...

Maven

...

users

...

will

...

need

...

to

...

add

...

the

...

following

...

dependency

...

to

...

their

...

pom.xml

...

for

...

this

...

component:

Code Block
xml
xml

{code:xml}
<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-hl7</artifactId>
    <version>x.x.x</version>
    <!-- use the same version as your Camel core version -->
</dependency>
{code}

h3. HL7 MLLP protocol
HL7 is often used with the HL7 MLLP protocol that is a text based TCP socket based protocol. This component ships with a Mina Codec that conforms to the MLLP protocol so you can easily expose a HL7 listener that accepts HL7 requests over the TCP transport. 

To expose a HL7 listener service we reuse the existing mina/mina2 component where we just use the {{HL7MLLPCodec}} as codec.

The HL7 MLLP codec has the following options:

HL7 MLLP protocol

HL7 is often used with the HL7 MLLP protocol that is a text based TCP socket based protocol. This component ships with a Mina Codec that conforms to the MLLP protocol so you can easily expose a HL7 listener that accepts HL7 requests over the TCP transport.

To expose a HL7 listener service we reuse the existing mina/mina2 component where we just use the HL7MLLPCodec as codec.

The HL7 MLLP codec has the following options:

Wiki Markup
{div:class=confluenceTableSmall}
|| Name || Default Value || Description ||
| {{startByte}} | {{0x0b}} | The start byte spanning the HL7 payload. |
| {{endByte1}} | {{0x1c}} | The first end byte spanning the HL7 payload.|
| {{endByte2}} | {{0x0d}} | The 2nd end byte spanning the HL7 payload. |
| {{charset}} | JVM Default | The encoding (is a [charset name|http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html]) to use for the codec. If not provided, Camel will use the [JVM default Charset|http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html#defaultCharset()]. |
| {{convertLFtoCR}} | {{true}} (*Camel 2.11*:{{false}}) | Will convert {{\n}} to {{\r}} ({{0x0d}}, 13 decimal) as HL7 stipulates {{\r}} as segment terminators. The HAPI library requires the use of {{\r}}. |
| {{validate}} | {{true}} | Whether HAPI Parser should validate or not. |
| {{parser}} | {{ca.uhn.hl7v2.parser.PipeParser}} | *Camel 2.11:* To use a custom parser. Must be of type {{ca.uhn.hl7v2.parser.Parser}}. |
{div}

h4. 

Exposing

...

a

...

HL7

...

listener

...

In

...

our

...

Spring

...

XML

...

file,

...

we

...

configure

...

an

...

endpoint

...

to

...

listen

...

for

...

HL7

...

requests

...

using

...

TCP:

Code Block
xml
xml

{code:xml}
        <endpoint id="hl7listener" uri="mina:tcp://localhost:8888?sync=true&amp;codec=#hl7codec"/>
        <!-- Camel 2.11: uri="mina2:tcp... -->
{code}

Notice

...

that

...

we

...

use

...

TCP

...

on

...

localhost

...

on

...

port

...

8888

...

.

...

We

...

use

...

sync=true

...

to

...

indicate

...

that

...

this

...

listener

...

is

...

synchronous

...

and

...

therefore

...

will

...

return

...

a

...

HL7

...

response

...

to

...

the

...

caller.

...

Then

...

we

...

setup

...

mina

...

to

...

use

...

our

...

HL7

...

codec

...

with

...

codec=#hl7codec

...

.

...

Notice

...

that

...

hl7codec

...

is

...

just

...

a

...

Spring

...

bean

...

ID,

...

so

...

we

...

could

...

have

...

named

...

it

...

mygreatcodecforhl7

...

or

...

whatever.

...

The

...

codec

...

is

...

also

...

set

...

up

...

in

...

the

...

Spring

...

XML

...

file:

Code Block
xml
xml

{code:xml}
    <bean id="hl7codec" class="org.apache.camel.component.hl7.HL7MLLPCodec">
        <property name="charset" value="iso-8859-1"/>
    </bean>
{code}

Above

...

we

...

also

...

configure

...

the

...

charset

...

encoding

...

to

...

use

...

(

...

iso-8859-1

...

).

...

The

...

endpoint

...

hl7listener

...

can

...

then

...

be

...

used

...

in

...

a

...

route

...

as

...

a

...

consumer,

...

as

...

this

...

Java

...

DSL

...

example

...

illustrates:

Code Block
java
java

{code:java}
    from("hl7listener").to("patientLookupService");
{code}

This

...

is

...

a

...

very

...

simple

...

route

...

that

...

will

...

listen

...

for

...

HL7

...

and

...

route

...

it

...

to

...

a

...

service

...

named

...

patientLookupService

...

that

...

is

...

also

...

a

...

Spring

...

bean

...

ID

...

we

...

have

...

configured

...

in

...

the

...

Spring

...

XML

...

as:

Code Block
xml
xml

{code:xml}
    <bean id="patientLookupService" class="com.mycompany.healthcare.service.PatientLookupService"/>
{code}

Another

...

powerful

...

feature

...

of

...

Camel

...

is

...

that

...

we

...

can

...

have

...

our

...

business

...

logic

...

in

...

POJO

...

classes

...

that

...

is

...

not

...

tied

...

to

...

Camel

...

as

...

shown

...

here:

Code Block
java
java

{code:java}

import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.model.v24.segment.QRD;

public class PatientLookupService {
    public Message lookupPatient(Message input) throws HL7Exception {
        QRD qrd = (QRD)input.get("QRD");
        String patientId = qrd.getWhoSubjectFilter(0).getIDNumber().getValue();

        // find patient data based on the patient id and create a HL7 model object with the response
        Message response = ... create and set response data
        return response
    }
{code}

Notice

...

that

...

this

...

class

...

uses

...

just

...

imports

...

from

...

the

...

HAPI

...

library

...

and

...

not

...

from

...

Camel.

...

HL7

...

Model

...

using

...

java.lang.String

...

The

...

HL7MLLP

...

codec

...

uses

...

plain

...

String

...

as

...

its

...

data

...

format.

...

Camel

...

uses

...

its

...

Type

...

Converter

...

to

...

convert

...

to/from

...

strings

...

to

...

the

...

HAPI

...

HL7

...

model

...

objects.

...

However,

...

you

...

can

...

use

...

plain

...

String

...

objects

...

if

...

you

...

prefer,

...

for

...

instance

...

if

...

you

...

wish

...

to

...

parse

...

the

...

data

...

yourself.

...

See

...

samples

...

for

...

such

...

an

...

example.

...

HL7v2

...

Model

...

using

...

HAPI

...

The

...

HL7v2

...

model

...

uses

...

Java

...

objects

...

from

...

the

...

HAPI

...

library.

...

Using

...

this

...

library,

...

we

...

can

...

encode

...

and

...

decode

...

from

...

the

...

EDI

...

format

...

(ER7)

...

that

...

is

...

mostly

...

used

...

with

...

HL7v2.

...


With

...

this

...

model

...

you

...

can

...

code

...

with

...

Java

...

objects

...

instead

...

of

...

the

...

EDI

...

based

...

HL7

...

format

...

that

...

can

...

be

...

hard

...

for

...

humans

...

to

...

read

...

and

...

understand.

...

The

...

sample

...

below

...

is

...

a

...

request

...

to

...

lookup

...

a

...

patient

...

with

...

the

...

patient

...

ID

...

0101701234

...

.

{
Code Block
}
MSH|^~\\&|MYSENDER|MYRECEIVER|MYAPPLICATION||200612211200||QRY^A19|1234|P|2.4
QRD|200612211200|R|I|GetPatient|||1^RD|0101701234|DEM||
{code}

Using

...

the

...

HL7

...

model

...

we

...

can

...

work

...

with

...

the

...

data

...

as

...

a

...

ca.uhn.hl7v2.model.Message

...

object.

...


To

...

retrieve

...

the

...

patient

...

ID

...

in

...

the

...

message

...

above,

...

you

...

can

...

do

...

this

...

in

...

Java

...

code:

Code Block
java
java

{code:java}
Message msg = exchange.getIn().getBody(Message.class);
QRD qrd = (QRD)msg.get("QRD");
String patientId = qrd.getWhoSubjectFilter(0).getIDNumber().getValue();
{code}

If

...

you

...

know

...

the

...

message

...

type

...

in

...

advance,

...

you

...

can

...

be

...

more

...

type-safe:

Code Block
java
java

{code:java}
QRY_A19 msg = exchange.getIn().getBody(QRY_A19.class);
String patientId = msg.getQRD().getWhoSubjectFilter(0).getIDNumber().getValue();
{code}

Camel

...

has

...

built-in

...

type

...

converters,

...

so

...

when

...

this

...

operation

...

is

...

invoked:

Code Block
java
java

{code:java}
Message msg = exchange.getIn().getBody(Message.class);
{code}

Camel

...

will

...

convert

...

the

...

received

...

HL7

...

data from String to Message. This is powerful when combined with the HL7 listener, then you as the end-user don't have to work with byte[], String or any other simple object formats. You can just use the HAPI HL7v2 model objects.

Include Page
HL7 DataFormat
HL7 DataFormat

Message Headers

The unmarshal operation adds these MSH fields as headers on the Camel message:

Wiki Markup
 from {{String}} to {{Message}}. This is powerful when combined with the HL7 listener, then you as the end-user don't have to work with {{byte[]}}, {{String}} or any other simple object formats. You can just use the HAPI HL7v2 model objects.

{include:HL7 DataFormat}

h3. Message Headers
The *unmarshal* operation adds these MSH fields as headers on the Camel message:

{div:class=confluenceTableSmall}
|| Key || MSH field || Example ||
| {{CamelHL7SendingApplication}} | {{MSH-3}} | {{MYSERVER}} |
| {{CamelHL7SendingFacility}} | {{MSH-4}} | {{MYSERVERAPP}} |
| {{CamelHL7ReceivingApplication}} | {{MSH-5}} | {{MYCLIENT}} |
| {{CamelHL7ReceivingFacility}} | {{MSH-6}} | {{MYCLIENTAPP}} |
| {{CamelHL7Timestamp}} | {{MSH-7}} | {{20071231235900}} |
| {{CamelHL7Security}} | {{MSH-8}} | {{null}} |
| {{CamelHL7MessageType}} | {{MSH-9-1}} | {{ADT}} |
| {{CamelHL7TriggerEvent}} | {{MSH-9-2}} | {{A01}} |
| {{CamelHL7MessageControl}} | {{MSH-10}} | {{1234}} |
| {{CamelHL7ProcessingId}} | {{MSH-11}} | {{P}} |
| {{CamelHL7VersionId}} | {{MSH-12}} | {{2.4}} |
{div}

All

...

headers

...

are

...

String

...

types.

...

If

...

a

...

header

...

value

...

is

...

missing,

...

its

...

value

...

is

...

null.

Options

The HL7 Data Format supports the following options:

Wiki Markup
{}}.

h3. Options
The HL7 Data Format supports the following options:
{div:class=confluenceTableSmall}
|| Option || Default || Description ||
| {{validate}} | true | Whether the HAPI Parser should validate using the default validation rules. *Camel 2.11:* better use the {{parser}} option and initialize the parser with the desired HAPI {{ValidationContext}} |
| {{parser}} | {{ca.uhn.hl7v2.parser.GenericParser}} | *Camel 2.11:* To use a custom parser. Must be of type {{ca.uhn.hl7v2.parser.Parser}}. Note that {{GenericParser}} also allows to parse XML-encoded HL7v2 messages. |

{div}

h3. Dependencies

To use HL7 in your Camel routes 

Dependencies

To use HL7 in your Camel routes you'll

...

need

...

to

...

add

...

a

...

dependency

...

on

...

camel-hl7

...

listed

...

above,

...

which

...

implements

...

this

...

data

...

format.

...

The

...

HAPI

...

library

...

since

...

Version

...

0.6

...

has

...

been

...

split

...

into

...

a

...

base library and several structure libraries, one for each HL7v2 message version:

By default camel-hl7 only references the HAPI base library. Applications are responsible for including structure libraries themselves. For example, if a application works with HL7v2 message versions 2.4 and 2.5 then the following dependencies must be added:

Code Block
xml
xml

<dependency>
    <groupId>ca.uhn.hapi</groupId>
    <artifactId>hapi-structures-v24</artifactId>
    <version>1.2</version>
    <!-- use the same version as your hapi-base version -->
</dependency> library|http://repo1.maven.org/maven2/ca/uhn/hapi/hapi-base] and several structure libraries, one for each HL7v2 message version:

* [v2.1 structures library|http://repo1.maven.org/maven2/ca/uhn/hapi/hapi-structures-v21]
* [v2.2 structures library|http://repo1.maven.org/maven2/ca/uhn/hapi/hapi-structures-v22]
* [v2.3 structures library|http://repo1.maven.org/maven2/ca/uhn/hapi/hapi-structures-v23]
* [v2.3.1 structures library|http://repo1.maven.org/maven2/ca/uhn/hapi/hapi-structures-v231]
* [v2.4 structures library|http://repo1.maven.org/maven2/ca/uhn/hapi/hapi-structures-v24]
* [v2.5 structures library|http://repo1.maven.org/maven2/ca/uhn/hapi/hapi-structures-v25]
* [v2.5.1 structures library|http://repo1.maven.org/maven2/ca/uhn/hapi/hapi-structures-v251]
* [v2.6 structures library|http://repo1.maven.org/maven2/ca/uhn/hapi/hapi-structures-v26]

By default {{camel-hl7}} only references the HAPI [base library|http://repo1.maven.org/maven2/ca/uhn/hapi/hapi-base]. Applications are responsible for including structure libraries themselves. For example, if a application works with HL7v2 message versions 2.4 and 2.5 then the following dependencies must be added:

{code:xml}
<dependency>
    <groupId>ca.uhn.hapi</groupId>
    <artifactId>hapi-structures-v24</artifactId>
    <version>1.2</version>
    <!-- use the same version as your hapi-base version -->
</dependency>
<dependency>
    <groupId>ca.uhn.hapi</groupId>
    <artifactId>hapi-structures-v25</artifactId>
    <version>1.2</version>
    <!-- use the same version as your hapi-base version -->
</dependency>
{code}

Alternatively, an OSGi bundle containing the base library, all structures libraries and required dependencies (on the bundle classpath) can be downloaded from the [central Maven repository|http://repo1.maven.org/maven2/ca/uhn/hapi/hapi-osgi-base]. 

{code:xml}
<dependency>
    <groupId>ca.uhn.hapi</groupId>
    <artifactId>hapi-osgistructures-base<v25</artifactId>
    <version>1.2</version>
    <!-- use the same version as your hapi-base version -->
</dependency>

Alternatively, an OSGi bundle containing the base library, all structures libraries and required dependencies (on the bundle classpath) can be downloaded from the central Maven repository.

Code Block
xml
xml

<dependency>
    <groupId>ca.uhn.hapi</groupId>
    <artifactId>hapi-osgi-base</artifactId>
    <version>1.2</version>
</dependency>

Terser language (Camel 2.11)

HAPI provides a Terser class that provides access to fields using a commonly used terse location specification syntax. The Terser language allows to use this syntax to extract values from messages and to use them as expressions and predicates for filtering, content-based routing etc.

Sample:

Code Block
java
java

</dependency>
{code}


h3. Terser language (*Camel 2.11*)

[HAPI|http://hl7api.sourceforge.net] provides a [Terser|http://hl7api.sourceforge.net/base/apidocs/ca/uhn/hl7v2/util/Terser.html] class that provides access to fields using a commonly used terse location specification syntax. The Terser language allows to use this syntax to extract values from messages and to use them as expressions and predicates for filtering, content-based routing etc.

Sample:

{code:java}
import static org.apache.camel.component.hl7.HL7.terser;
...

   // extract patient ID from field QRD-8 in the QRY_A19 message above and put into message header
   from("direct:test1")
      .setHeader("PATIENT_ID",terser("QRD-8(0)-1"))
      .to("mock:test1");
   // continue processing if extracted field equals a message header
   from("direct:test2")
      .filter(terser("QRD-8(0)-1")
      .isEqualTo(header("PATIENT_ID"))
      .to("mock:test2");

{code}


h3. HL7 Validation predicate (*Camel 

HL7 Validation predicate (Camel 2.11

...

)

...

Often

...

it

...

is

...

preferable to parse a HL7v2 message and validate it against a HAPI ValidationContext in a separate step afterwards.

Sample:

Code Block
java
java
 to parse a HL7v2 message and validate it against a HAPI [ValidationContext|http://hl7api.sourceforge.net/base/apidocs/ca/uhn/hl7v2/validation/ValidationContext.html] in a separate step afterwards.

Sample:

{code:java}

import static org.apache.camel.component.hl7.HL7.messageConformsTo;
import ca.uhn.hl7v2.validation.impl.DefaultValidation;
...

  // Use standard or define your own validation rules
   ValidationContext defaultContext = new DefaultValidation(); 

   // Throws PredicateValidationException if message does not validate
   from("direct:test1").validate(messageConformsTo(defaultContext)).to("mock:test1");
{code}


h3. HL7 Acknowledgement expression (*Camel 

HL7 Acknowledgement expression (Camel 2.11

...

)

...

A

...

common

...

task

...

in

...

HL7v2

...

processing

...

is

...

to

...

generate

...

an

...

acknowledgement

...

message

...

as

...

response

...

to

...

an

...

incoming

...

HL7v2

...

message,

...

e.g.

...

based

...

on

...

a

...

validation

...

result.

...

The

...

ack

...

expression

...

lets

...

us

...

accomplish

...

this

...

very

...

elegantly:

Code Block
java
java


{code:java}

import static org.apache.camel.component.hl7.HL7.messageConformsTo;
import static org.apache.camel.component.hl7.HL7.ack;
import ca.uhn.hl7v2.validation.impl.DefaultValidation;
...

  // Use standard or define your own validation rules
   ValidationContext defaultContext = new DefaultValidation(); 

   from("direct:test1")
      .onException(Exception.class)
         .handled(true)
         .transform(ack()) // auto-generates negative ack because of exception in Exchange
         .end()
      .validate(messageConformsTo(defaultContext))
      // do something meaningful here
      ...
      // acknowledgement
      .transform(ack())

More Samples

In the following example we send a HL7 request to a HL7 listener and retrieves a response. We use plain String types in this example:

Wiki Markup
{code}


h3. More Samples
In the following example we send a HL7 request to a HL7 listener and retrieves a response. We use plain {{String}} types in this example:
{snippet:id=e2|lang=java|url=camel/trunk/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7MLLPCodecTest.java}

In

...

the

...

next

...

sample,

...

we

...

want

...

to

...

route

...

HL7

...

requests

...

from

...

our

...

HL7

...

listener

...

to

...

our

...

business

...

logic.

...

We

...

have

...

our

...

business

...

logic

...

in

...

a

...

plain

...

POJO

...

that

...

we

...

have

...

registered

...

in

...

the

...

registry

...

as

...

hl7service

...

=

...

for

...

instance

...

using

...

Spring

...

and

...

letting

...

the

...

bean

...

id

...

=

...

hl7service

...

.

...

Our

...

business

...

logic

...

is

...

a

...

plain

...

POJO

...

only

...

using

...

the

...

HAPI

...

library

...

so

...

we

...

have

...

these

...

operations

...

defined:

Wiki Markup

{snippet:id=e2|lang=java|url=camel/trunk/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7RouteTest.java}

Then

...

we

...

set

...

up

...

the

...

Camel

...

routes

...

using

...

the

...

RouteBuilder

...

as

...

follows:

Wiki Markup

{snippet:id=e1|lang=java|url=camel/trunk/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7RouteTest.java}

Notice

...

that

...

we

...

use

...

the

...

HL7

...

DataFormat

...

to

...

enrich

...

our

...

Camel

...

Message

...

with

...

the

...

MSH

...

fields

...

preconfigured

...

on

...

the

...

Camel

...

Message.

...

This

...

lets

...

us

...

much

...

more

...

easily

...

define

...

our

...

routes

...

using

...

the

...

fluent

...

builders.

...


If

...

we

...

do

...

not

...

use

...

the

...

HL7

...

DataFormat,

...

then

...

we

...

do

...

not

...

gains

...

these

...

headers

...

and

...

we

...

must

...

resort

...

to

...

a

...

different

...

technique

...

for

...

computing

...

the

...

MSH

...

trigger

...

event

...

(=

...

what

...

kind

...

of

...

HL7

...

message

...

it

...

is).

...

This

...

is

...

a

...

big

...

advantage

...

of

...

the

...

HL7

...

DataFormat

...

over

...

the

...

plain

...

HL7

...

type

...

converters.

...

Sample

...

using

...

plain

...

String

...

objects

...

In

...

this

...

sample

...

we

...

use

...

plain

...

String

...

objects

...

as

...

the

...

data

...

format,

...

that

...

we

...

send,

...

process

...

and

...

receive.

...

As

...

the

...

sample

...

is

...

part

...

of

...

a

...

unit

...

test,

...

there

...

is

...

some

...

code

...

for

...

assertions,

...

but

...

you

...

should

...

be

...

able

...

to

...

understand

...

what

...

happens.

...

First

...

we

...

send

...

the

...

plain

...

string,

...

Hello

...

World

...

,

...

to

...

the

...

HL7MLLPCodec

...

and

...

receive

...

the

...

response

...

as

...

a

...

plain

...

string,

...

Bye

...

World.

Wiki Markup
}}.
{snippet:id=e1|lang=java|url=camel/trunk/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7MLLPCodecPlainStringTest.java}

Here

...

we

...

process

...

the

...

incoming

...

data

...

as

...

plain

...

String

...

and

...

send

...

the

...

response

...

also

...

as

...

plain

...

String:

Wiki Markup

{snippet:id=e2|lang=java|url=camel/trunk/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7MLLPCodecPlainStringTest.java}

{include:Endpoint See Also}
Include Page
Endpoint See Also
Endpoint See Also