Introduction to the Open eHealth Integration Platform
HL7 validation
IPF also adds support for specifying validation rules in a way that is easy to write and simple to understand. It facilitates the definition of custom validation rules by providing a dedicated validation DSL. Like the HL7 DSL, the validation DSL can also be used standalone but also integrates well into the IPF DSL for defining message processing routes. The IPF component that implements HL7 validation is modules-hl7. Validation rules are defined by extending the ValidationContextBuilder class of IPF. The validation rules DSL is provided by the RuleBuilder class. The following example defines a subset of segments from the HL7 version 2.2 specification.
package example
import ca.uhn.hl7v2.validation.ValidationContext
import org.openehealth.ipf.modules.hl7.validation.builder.RuleBuilder
import org.openehealth.ipf.modules.hl7.validation.builder.ValidationContextBuilder
class SampleRulesBuilder extends ValidationContextBuilder {
RuleBuilder forContext(ValidationContext context) {
new RuleBuilder(context)
.forVersion('2.2')
.message('ADT', 'A01').abstractSyntax(
'MSH',
'EVN',
'PID',
[ { 'NK1' } ],
'PV1',
[ { INSURANCE(
'IN1',
[ 'IN2' ] ,
[ 'IN3' ]
)}]
)
}
}
The sequence and cardinality of groups and segments is defined in a syntax that is very closely related to the HL7 Abstract Message Syntax. The message must contain the segments MSH, EVN, PID and PV1; it may contain zero or more NK1 segments and it may contain a repeatable INSURANCE group. In a next step we configure our SampleRulesBuilder in a Spring application context along with a ValidationContextFactoryBean.
<bean id="validationContext"
class="org.openehealth.ipf.modules.hl7.validation.ValidationContextFactoryBean">
</bean>
<bean id="customRules"
class="example.SampleRulesBuilder">
</bean>
The ValidationContextFactoryBean auto-detects any beans of type ValidationContextBuilder and adds their validation rules to a ValidationContext that is used in route definitions. The validate().ghl7() DSL extension, which we have already seen, can be configured with a custom validation profile using the profile() DSL extension. The validation context is looked up from the Spring application context via bean(ValidationContext.class).
import ca.uhn.hl7v2.validation.ValidationContext
import org.apache.camel.spring.SpringRouteBuilder
class SampleRouteBuilder extends SpringRouteBuilder {
void configure() {
from(...)
.unmarshal().ghl7()
.validate().ghl7().profile(bean(ValidationContext.class))
...
.to(...)
}
}
Code mapping
HL7 message processing often involves mapping between code systems i.e. from one set of codes into a corresponding different set of codes. For example, HL7 version 2 and HL7 version 3 use different code systems for most coded values such as message type, gender, clinical encounter type, marital status codes, address and telecommunication use codes, just to mention a few. IPF defines a mapping service that provides the mapping logic. This may be a simple map but it can also be a facade to a remote mapping or terminology service. IPF's default mapping service implementation, the BidiMappingService, supports bidirectional mappings and reads custom mapping definitions from mapping files. An instance of the BidiMappingService can be created with the following bean definition.
<bean id="mappingService" class="org.openehealth.ipf.modules.hl7.mappings.BidiMappingService">
<property name="mappingScript" value="classpath:example.map">
</bean>
The mapping service references a mapping file example.map on the classpath. If there is more than one mapping file, a list can be provided via the mappingScripts property. Here is the content of example.map:
mappings = {
encounterType(['2.16.840.1.113883.12.4','2.16.840.1.113883.5.4'],
E : 'EMER',
I : 'IMP',
O : 'AMB'
)
}The example mapping file is a Groovy script and contains a single mapping with three entries for encounter type codes. Also defined are the ISO Object Identifiers (OIDs) for the key and value code systems. The mapping service can now be accessed either directly or via methods on java.lang.String. The String.map() method maps the codes on the left side to the right side. The identifier for the mapping can either be passed as argument
assert 'E'.map('encounterType') == 'EMER'
assert 'X'.map('encounterType') == null
assert 'X'.map('encounterType', 'DEFAULT') == 'DEFAULT'or as part of a method name.
assert 'E'.mapEncounterType() == 'EMER'
assert 'X'.mapEncounterType() == null
assert 'X'.mapEncounterType('DEFAULT') == 'DEFAULT'
A dynamic dispatch is used to select the mapping definition from the method name. The method names must therefore correspond to the registered mappings. Mapping in the reverse direction is equally possible.
assert 'EMER'.mapReverse('encounterType') == 'E'
assert 'EMER'.mapReverseEncounterType() == 'E'Code systems are often associated with a globally unique identifier, usually in form of an OID. The identifier of both sides of a mapping can be obtained as follows.
assert 'encounterType'.keySystem() == '2.16.840.1.113883.12.4'
assert 'encounterType'.valueSystem() == '2.16.840.1.113883.5.4'
Code mapping methods may also be used in combination with the HL7 DSL.
MessageAdapter message = ...
assert message.PV1.patientClass.value == 'I'
assert message.PV1.patientClass.map('encounterType') == 'IMP'
assert message.PV1.patientClass.mapEncounterType() == 'IMP'
Response messages
HL7 messaging often requires the return of an HL7 response message to the sender. With IPF, positive (ACK) or negative (NAK) acknowledgments to messages can be generated. Acknowledgments are in the same HL7 version as the original message and are populated with arguments to the ack() method.
MessageAdapter message = ...
def ack = message.ack()
def nak1 = message.nak('Reason for failure')
def nak2 = message.nak(new HL7Exception('Reason for failure', 204))
Generating acknowledgments is, however, only one special case of generating a response to an original message. For responses other than acknowledgements, a response message prototype can be created via the respond(eventType, triggerEvent) method. The MSH and MSA segments of the response message are then populated as required by the HL7 specification.
def rsp = msg.respond('RSP','K21') // generates a RSP_K21 message- Login or register to post comments
- 6286 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)









