Skip to end of metadata
Go to start of metadata

HAPI Extensions

HAPI extensions is a module that adds methods to the original HAPI library in order to access and manipulate HL7 version 2 messages more conveniently. The library is based on Groovy. It can be used directly with HAPI or together with the HAPI DSL.

Integration with HAPI DSL

HAPI DSL adapters forward all unknown method calls to their underlying HAPI model classes, which are extended by this library. Therefore you can easily access these extensions within the HAPI DSL. The reason for splitting the two libraries is while HAPI DSL focuses on providing a 'fluent' HL7 language, the HAPI Extensions module provides access to operations specific to the HL7 domain, e.g. creating an acknowledgment to a given message. Note that the two libraries do not depend on each other and can therefore be used independently, although they complement each other perfectly.

Getting Started

Maven Setup

For setting up Maven follow the instructions on the IPF development page. If you want to use the the HAPI extensions standalone in your Groovy projects, then you only need to include

pom.xml for standalone use

in your pom.xml file. For using the HAPI DSL inside Camel routes you additionally need to include the following dependency:

pom.xml for use with Camel

where ${ipf-version} must be replaced with the IPF version you want to use. For instructions how to activate the HAPI extensions inside your IPF application refer to the configuration section on the HL7 processing page.

As of IPF 1.7-m3, the underlying HAPI library has been updated. The HL7 version-dependent classes have to be included separately:

pom.xml for using HL7 version 2.5 and 2.5.1

Despite the extra configuration, it reduces the overall size of your project as the other version's HL7 classes are not included any more.

HL7 Parser and ModelClassFactory

In order to instantiate concrete implementations of Message, Group, Segment etc, the HAPI Parsers use a ModelClassFactory member object that looks up classes for these model components. The default implementation provides access to model components as specified in the HL7 specs.
In real world HL7 projects you frequently need to deal with non-standard HL7 "dialects" which are not covered by the specification and causes the parser to fail or generate "generic" model classes when used out-of-the-box. Although it's possible to implement a custom ModelClassFactory, the HAPI parsers can not be configured to use it. The HAPI extension library offers a solution for this limitation.

CustomModelClassFactory

The factory implementation org.openehealth.ipf.modules.hl7.parser.CustomModelClassFactory can be configured to map a HL7 version to a list of package names in which the HAPI model classes are looked up. If it fails to find the requested class, the call is delegated to HAPI's default implementation. Example:

The following subpackages are looked up for the respective model classes:

model interface package
Message X.message
Group X.group
Segment X.segment
Type X.datatype

Note that the value side of the map is always a List. In the example above, the Message classes for version 2.5 are looked up in the following order:

  1. com.mycompany.profile1.hl7def.v25.message
  2. com.mycompany.profile2.hl7def.v25.message
  3. ca.uhn.hl7v2.model.v25.message (the default)

Custom PipeParser

Use the PipeParser implementation provided by this module (org.openehealth.ipf.modules.hl7.parser.PipeParser), which can be configured with a custom model factory

In Camel integration scenarios, use the custom parser instance by adding a parameter. Also refer to the Camel HL7 extension reference

Mapping Service

The org.openehealth.ipf.commons.map.MappingService interface deals with the requirement that processing messages often involves mappings between code systems, i.e. from one set of codes into a corresponding set of codes. For example, HL7 version 2 to HL7 version 3 use different code systems for most coded values like message type, gender, clinical encounter type, marital status codes, address and telecommunication use codes, just to mention a few. MappingService implementations provide the mapping logic, which can be a simple java.util.Map, but can also be a facade for a remote terminology service.
The commons-map component extends the java.lang.String class to map between values. The modules-hl7 component additionally extends HAPI classes to map between values. Please refer to the API Extensions section below for examples.

The HL7 library provides one MappingService implementation (BidiMappingService), which implements

  • bidirectional mapping
  • mapping of arbitrary objects
  • definitions of mappings using external Groovy Scripts

To use BidiMappingService, you have to initialize it with the external Groovy resource, e.g. using Spring:

The mapping example is displayed below:

example.map

This defines three mappings (encounterType, vip, and messageType), having an optional definition for OIDs for the key and value code systems. The encounterType mapping has three entries, while the vip and messageType mappings have only one.

The ELSE entry is called on MappingService.get() request with unknown keys. ELSE can be

  • a Closure, which takes the key as parameter and is then executed
  • any other Object o, which will return o.toString().

In the example above,

  • for the vip mapping the key is returned, so that mappingService.get('vip', 'X') == 'X'
  • for the messageType mapping, an HL7Exception is thrown.

The services also allow mapping in the backward direction, so that mappingService.getKey('vip', 'VIP') == 'Y'.

Ambiguous mappings
In case that a mapping definition maps more than one key to the same value (e.g. A->C and B->C), the backward mapping only contains the last entry, i.e. C->B.

BidiMappingService also can be initialized using a list of mapping files:

Conflicting mappings are overridden by later list entries, i.e. mappings defined in example2.map override existing mappings defined in example1.map.

BidiMappingService also supports default reverse mappings, i.e. you can specify an ELSE mapping also from the reverse direction:

example2.map

The reverseMappingWithClosures mapping also demonstrates how to use a closure in order to return a default key that is already defined as key for a regular mapping.

API Extensions

Strings

The String class has been extended to facilitate usage of the Mapping Service. As an example, we assume the mappings defined as shown in the example.map definition in the Mapping Service subchapter.

String.map() maps the left to the right side. The identifier for the mapping can either be passed as argument:

or as part of the method call. The methods are dynamically added based on the registered mapping identifiers in the defined MappingService instance.

If provided by the MappingService implementation, you can also map in the reverse direction:

Code systems are often associated with a globally unique identifier, usually in form of an ISO Object Identifier (OID). The identifier of both sides of a mapping can be obtained as follows:

Finally, you can check whether a certain key or value is contained in the mapping.

HAPI Message

You can create positive or negative acknowledgments to messages. The acknowledgments are in the same HL7 version as the original message and is populated as specified in the parameters.

In case of parsing errors there's no message object available to derive the negative acknowledgment from. In this case you can reuse the Exception thrown by the parser to create a generic negative acknowledgement of a specific version (2.5 in the following example):

Generating acknowledgments is, however, only a special case of generating a response to an original message. If the response is defined as dedicated HL7 message, as with responses to Query messages, you have to use the respond(eventType, triggerEvent) extension. The MSH and MSA segments of the result message are populated as required by the HL7 specification.

You can check for specific message types

The matches() method can be used e.g. inside closures for filters and routers in HL7 Camel routes like

You can check the three parameters of matches individually, too:

For debugging purposes, it's often useful to know the internal (hierarchical) data structure of a HAPI Message. For complex messages, the returned structure can be pretty extensive, so is possible avoid using this in production environments:

HAPI Structures

All HAPI Structures (i.e. not only Messages, but also arbitrary Groups and Segments) can be printed by calling the encode() extension. Note that a Message is a subclass of Group.

HAPI Types

All HAPI Types (i.e. Primitives, Composites, and Varies) can be printed by calling the encode() extension.

As described above with java.lang.String objects, the mapping extensions can be applied directly on all HAPI types. The encode() extension is called before the mapping is executed.

Collection

A further common mapping scenario is having a collection of keys that map to a value or collection of values.

As an example, we assume the following mapping, which is registered under the name 'test':

key value
A~B~C X~Y~Z
D~E~F A

To comply with the rules for Groovy maps, we better don't directly use Collection as keys; instead we use the tilde "~" to separate the collection elements in a single String. You can either use lists or tilde-encoded strings on the value side.

Collection.map() behaves exactly like String.map() shown above:

Note that with the API you work with lists on the key and value side of the mapping.

Collections may also contains HAPI types, in this case Type.encode() is called on each list element before the mapping is executed.

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.