HL7 V3 API Overview
Introduction
The new HL7 V3 messaging framework is based on an object-oriented data
model, the RIM, and on defined data types. Because the data model is
well defined, the derived messages are unambiguous and standardized.
Additionally, the V3 messaging framework provides "meta" files that
define how messages are translated from the RIM model to an XML V3
message format and vice versa.
The RIM data model, HL7 data types, and the Meta file message
definitions enable the HL7 V3 API to systematically parse and
build V3 messages without requiring "hard coded" pre-existing knowledge
of message structure. Given the proper Meta file and the actual V3 XML
message, the API can process the message regardless of the message type
and message contents. As new message types and their resultant Meta
files are added to the HL7 V3 domain, the API will be able to parse and
build these messages without any API changes!
As a result of the message parsing process, the API will generate an
in-memory graph of RIM objects that represents the V3 XML message
contents. The developer can then leverage the RIM objects as needed to
extract the pertinent message values. In order to build a V3 XML
message, the API requires an in-memory RIM graph to drive the
transformation process.
Though the design of the API is elegant, the implementation and the
various domain concepts are complicated. Before one can understand the
low level mechanics of the API, there are other concepts that you
should become familiar with:
- HL7 V3 messaging, including the RIM and HL7 Data Types
- HL7 meta information as found in the HMD and MIF meta files
- XML SAX parsing
- XML XSLT transformations
- Java Reflection and Generics
In general, a deep understanding of the API is not necessary to
actually use the API. The API comes with a demo package that contains
examples of how the API can be used to parse and build messages. You
can jump right in by inspecting the demo driver code and see how the
API is used. By inspecting the ANT build file you can ascertain the
typical input parameters required: a V3 message file, a message type, a
meta file (MIF or HMD), and a RIM file.
Specifically, look at org.hl7.demo.DemoDriver. This class first loads
the meta information for a particular message type. It then parses a
message into memory, and optionally persists the in memory object graph
using Hibernate. The class then reverses the process and reads the in
memory RIM graph back out to a new XML V3 message that reflects the
original parsed message.
Other sets of example code will be provided as the API evolves thru
beta and into a production release.
Roll up your sleeves, there is a lot under the hood. See the resources
section at the end of this document for links to online resources that
may aid in becoming familiar with some of the API building block
concepts.
Meta Information and Loader
HL7 provides 2 formats (HMD and MIF) for specifying message meta data
(structure, format, and constraints). They are both XML based. HMD
stands for Hierarchical Message Definition. MIF stands for Model
Interchange Format.
The API supports both format types. The Meta Loader processes the meta
file via a SAX parser. As key meta elements are encountered within the
input meta file, Meta java classes are created that mirror the
attributes
and other meta class associations specified in the file. These meta
classes drive how the RIM object graph is built when messages are
actually parsed.
The current meta loader classes are found in the org.hl7.meta package. This package
may become obsolete as another meta file parsing API from the HL7 tools
SIG may be utilized instead. However, for those interested, see the
MifMessageTypeLoader class under the impl
directory.
RIM Classes
The source files for these classes are automatically generated from the
XSLT transform of definition files. The RIM classes are based on high
level abstractions such as patients, observations, procedures,
roles, acts, etc.
The RIM classes are found in the org.hl7.rim
package. Actual implementations are found one level lower in the impl directory.
Data Types
The source files for the data types are hand crafted and contain many
specialized methods. These classes also make heavy use of Java Generics
(introduced in Java 1.5). Some example data type classes include
abstractions such as coded elements (CE), timestamps (TS),
physical quantities (PQ), etc.
The HL7 data type classes are found in org.hl7.types
package. Actual implementations are found one level lower in the impl directory.
Message Parser
The main parsing classes (contenet handlers) are found in the org.hl7.xml.parser package.
Presenter classes are located one level higher.
The message parser utilizes a SAX parser when parsing the XM nodes of
the input HL7 V3 message. As SAX events fire, data from the SAX event
is used to query Meta classes for meta data. The specific meta data
allows the parser to instantiate a node as a RIM object or HL7 data
type object. The meta data also denotes where to attach the
instantiated object. Relection is used to find the correct “setter”
method to “add” the object to the growing RIM graph in memory. When an
instance of a RIM class or data type is needed, it is created by a
properties based factory.
Since there are many flavors of objects (RIM classes, data types, CMETS
or Common Message Element Types) we need specialized content handlers.
The specialized content handlers can be dynamically "switched out"
during message parsing. See the DynamicContentHandler class.
Specialized handlers (Presenters) are included for each RIM and data
type class. Presenters are called by intermediate content handlers.
Examples of these intermediate content handlers include:
TreeContentHandler, DataTypeContentHandler, and
SimpleTypeContentHandler, and others as well.
As Sax events fire, they are handled by MessageElementContentHandler
which in turn can dynamically “suspendWith” to temporarily trade places
with a specialized “presenter” class. The final object is handed back
down the call stack to a “resultReceiver” with a call to
“notifyResult”. Eventually, the returned partial result is added to the
growing RIM graph by using reflection to find the correct “setter”
method on the parent object.
Once the entire message is parsed, the resulting object graph is the
entire in memory representation of the RIM Graph.
Message Builder
The message building classes are mostly found in the org.hl7.xml.builder package. The
message builder’s design resembles the parser in that it relies on
small specialized content handlers (which in this case are called
“builders” instead of “presenters”).
If you look inside a presenter class you will find an inner class of
type “builder”. This design choice was made so that once a presenter is
written, it is able to handle both message parsing and message building.
The message builder uses a clever “trick” to generate the XML
output. Instead of writing many print statements, another
approach was taken utilizing the "Identify transform" feature of XSLT
transforms.
The “Identitiy transform” is a special case where the incoming XML
message is sent through the XSLT transform machinery, but no actual
transform is done. The newly generated output is exactly the same as
the XML input: but the output isn't simply copied. The input generated
SAX events and these events were used to create the XML output. Since
no transform is performed, the resulting message is the same as the
input message.
If you could read or parse the in memory RIM graph and pretend to be a
SAX XMLReader, essentially generating the same events as if you were
reading an XML stream, then you could use that reader in an identity
transform to create XML output. That is the approach that was taken in
the message builder (credit: Gunther Schadow).
The mechanism that masquerades as a SAX XML reader is made up of the
classes XMLSpeaker and XMLRimGraphSpeaker. As the SAX events are
emitted by “parsing” the in memory RIM graph, they are handled by
“MessageBuilders”. The message builders are then dynamically switched
for one another as appropriate. Finally one of the builders emits a SAX
event to an internal member of type “org.xml.sax.ContentHandler”. This
member happens to be the "_contentHandler" member from the abstract
class XMLSpeaker. Once this event fires, the XSLT transform takes over
and the XML for that particular node is created. Pretty nifty.
Persistence
Persitence is accomplished by Hibernate (see source forge). Details of
the implementation can not be described yet as this is a work in
progress.
Validation
TBD.
Constraint Checking
TBD
Resources
Information on XML is widely available in books and on the web. Some
websites, such as w3schools
offer online tutorials on XML and XSLT.
Information on HL7 version 3 is harder to come by. There is at least
one introductory book for sale by HL7. There are some documentation
materials on the HL7 web site. For more information on data types, see HL7
data types.
It is valuable to attend an HL7 meeting and take a tutorial on Version
3 and the RIM (Reference Information Model). This information and
understanding is the biggest hurdle a newcomer will have to overcome
before they can understand and use this API.
Information about Java Reflection can be found in the Java
Reflection Tutorial. Information about Java Generics can be found
in the Java
Generics Tutorial.
Bored? Check out the SAX Project
for more information on SAX parsing.