HL7v2 based simulator
From HL7Common-2.2, the structure of the AbstractServer class has changed, some changes need to be done in classes extending this abstract class.
If you are about to implement a new simulator based on the HL7v2 standard, please read carefully those lines, it will help you to use the common component developed by IHE Development team and to avoid wasting time rewriting existing things. Moreover, this will help us to keep a consistency between all HL7v2 simulators.
First of all, in this page, we define a simulator as the emulation of an actor of a given IHE integration profile for a given transaction. Obviously, your application can gather several simulators. Regarding the HL7v2 standard and IHE specifications, we define two different roles:
- Initiator: a simulator acting as a initiator sends a message to a system under test (SUT) and waits for its response. It works as a client.
- Responder: a simulator acting as a responder is a server, listening on a given port and sending a response to all received messages, this response can be an ACK, a NACK or anything else.
We are aware that some actors can play both roles but regarding the implementation of your application, you will have to distinguish each parts since the implementations differ.
Project configuration to use HL7Common maven module
IHE Development team has developed a maven archetype which enable you to build the architecture of your simulator. This archetype is available on Gazelle Maven repository. The values you need to generate the project from the archetype are given below.
- archetypeArtifactId: simulator-archetype
- archetypeCatalog: http://gazelle.ihe.net:9080/nexus/content/groups/public/
- archetypeGroupId: net.ihe.gazelle.simulators
- archetypeVersion: 1.16
If everything works well, the generated project contains three modules: EJB, WAR and EAR.
More over a maven module called HL7Common which contains all the classes and xhtml files you may use to implement your simulator based on HL7v2 standard is available in Gazelle Maven repository. To use it, complete your pom.xml files with the follwing lines.
This documentation is currently based on HL7Common:2.2.
Configure the POM file of your parent project to make the HL7Common module the parent of your new project.
<parent> <groupId>net.ihe.gazelle.simulator.hl7</groupId> <artifactId>HL7Common</artifactId> <version>2.2-SNAPSHOT</version> </parent>
Then, in the pom.xml file of the EAR and EJB modules add
<dependency> <groupId>net.ihe.gazelle.simulators</groupId> <artifactId>simulator-common-ejb</artifactId> <type>ejb</type> </dependency> <dependency> <groupId>net.ihe.gazelle.simulator.hl7</groupId> <artifactId>HL7Common-ejb</artifactId> <type>ejb</type> </dependency>
Also in the pom.xml file of the EAR project add the following lines in the configuration of the maven-ear-plugin
<ejbModule> <groupId>net.ihe.gazelle.simulators</groupId> <artifactId>simulator-common-ejb</artifactId> <uri>simulator-common-ejb.jar</uri> </ejbModule> <ejbModule> <groupId>net.ihe.gazelle.simulator.hl7</groupId> <artifactId>HL7Common-ejb</artifactId> <uri>HL7Common-ejb.jar</uri> </ejbModule>
Then, in the pom.xml file of the WAR module add
<dependency> <groupId>net.ihe.gazelle.simulators</groupId> <artifactId>simulator-common-war</artifactId> <type>war</type> </dependency> <dependency> <groupId>net.ihe.gazelle.simulator.hl7</groupId> <artifactId>HL7Common-war</artifactId> <type>war</type> </dependency>
Charset
If you take a look to the classes defined in this module, you will find out an entity named Charset. This class is used to defined the character set encoding used by the simulator in one hand and by the system under test in another hand. For each charset we give the HL7 code used in MSH-18 segment and the code as understood by the JVM to read and write messages. By making the simulator and the system under test using the same charset, we ensure that the messages are understandable for each part. A set of data is available as an sql script in the parent parent of HL7Common module. You can also find it here.
System Under Test configuration
If your simulator acts as an initiator, it will need to know to "who" (host, port) send messages. A seam entity has been created to enable the simulator to retrieve those information and the user to store his/her system under test configuration into the simulator's database. In this way, he/she can retrieve it easily when he/she need to test his/her system. The XHTML page to manage configurations is stored in the WAR module of HL7Common, please provide a link to this page ("/systemConfigurations.seam") into your GUI. Each configuration will be linked to an actor and a charset.
Simulator configuration
To increase the interoperability between the simulator and the systems under test, we have chosen to send messages with the appropriate charset (when acting as an initiator). In the same way, when the simulator acts as a responder, we open different ports using different character set encodings. The SimulatorResponderConfiguration class has been implemented with this goal in mind. The main attributes of this object are the port number (on which the server listen) and the character set encoding. The README file attached give you some examples of how to fill your database.
HL7 messages and message validation
The application is expected to keep a trace of all transactions initiated by itself or by a system under test. We have chosen to store in the database, the received message along with the sent message. The HL7Message class is dedicated to this task. Consequently, each time the simulator sends or receives a message, it is required to instanciate a new HL7Message object and to store both the request and the response inside.
The HL7Message object is linked to another entity named EVSCValidationResults. Actually, each HL7v2 simulator has to be linked to the HL7InriaValidator in order to provide to the user a way to validate messages against the message profiles. The XHTML pages created in the WAR part of HL7Common provides the button which called the validator by a webservice mechanism. To work properly, you will need to add a preference to your application database, the one is given in the README file attached.Morevoer, the validation service needs to know which message profile to use and this action must be transparent for the user. Consequently, another entity named ValidationParameters is defined. For each tuple (actor, transaction, affinity domain, message type) we give the OID of the message profile to use.
A page gathering all the HL7Message contained in the application's database can be added into your GUI by making a link to "/hl7Messages.seam".
Affinity domain
An application could implement a same actor in different affinity domains. In this case, some distinctions may be required, especially for the message validation. Consequently, an entity named AffinityDomain has been defined and a link is made to it from the HL7Message, the SimulatorResponderConfiguration and the ValidationParameters.
Creating HL7 messages
HL7 defines a set of segments, some of which are common to most of the messages. It is the case for example for the MSH segment. In order to save time and to avoid to build this segment in as much ways as simulators, a class SegmentBuilder has been created in HL7Common and can be extended by each simulator if needed. By now, this class contains methods to fill MSH, EVN, PV1 and MSA segments.
An HL7MessageDecoder class exists in the same package and by now contains only one method which extracts the message type from a message using the HAPI Terser.
Acting as an initiator
If your simulator acts as an initiator, you will need to
- Collect information from the user to build messages
- Collect information about the SUT to "contact"
- Send messages
The first two points require a user interface. In order to keep a consistency between all simulators, a XHTML page ("/configuration/initiatorFrame.xhtml) has been created which has to be included at the top of your page for each actor your application emulates. This page contains a drop-down menu with all the available SUT for the simulated actor, the sequence diagram of the transaction and the configuration of the simulator. Some parameters are expected to be given to the page in order to display properly all the informations.
- actorKeyword: keyword of the actor played by the SUT, so that the offered configurations are filtered
- diagramImg: path to the sequence diagram image to include in the page
- currentConfig: the attribute from the managed bean in which the selected SUT configuration has to be set
- simuFacility: the string reprensenting the sending facility used by the simulator
- simuApplication: the string reprensenting the sending application used by the simulator
- sectionsToReRender: the list of page sections to rerender when the user change the selected configuration.
- simulatorConfigurationName: the name you want to give to the simulator's configuration
See below an example of how to include it into your page and how it is rendered.
<ui:include src="/configuration/initiatorFrame.xhtml"> <ui:param name="actorKeyword" value="PDC" /> <ui:param name="diagramImg" value="/img/diag/ident_diag.png" /> <ui:param name="currentConfig" value="#{creationManager.selectedSystemConfig}" /> <ui:param name="simuFacility" value="#{creationManager.sendingFacility}" /> <ui:param name="simuApplication" value="#{creationManager.sendingApplication}" /> <ui:param name="sectionsToReRender" value="sendMessageButtonDiv" /> <ui:param name="simulatorConfigurationName" value="PAM PDS Simulator"/> </ui:include>
HL7Common uses HAPI to build messages and to send and received them. A class named Initiator has been implemented into HL7Common in order to help you to send message and to instanciate the HL7Message object. The constructor of this class set all the parameters required for the sending of the message and a method sendMessage() sends the message, waits for the response, builds the HL7Message object, stores it in the database and returns an instance of HL7MessageDataModel containing the created message. So that you can displayed this message in the GUI using a rich:dataTable component and the columns defined in HL7Common WAR part ("/hl7MessagesTableColumns"). See below an example of how to use this class.
public void sendMergeMessage() { Initiator hl7Initiator = new Initiator(selectedSystemConfiguration, sendingFacility, sendingApplication, Actor.findActorWithKeyword("PDS"), Transaction.GetTransactionByKeyword("ITI-30"), createMessage(), MESSAGE_TYPE, "IHE"); try{ msgDataModel = hl7Initiator.sendMessage(); }catch(HL7Exception e){ FacesMessages.instance().add(e.getMessage()); } }
Acting as a responder
First of all, note that when your simulator is acting as a responder, this part of the application cannot access the EntityManager using the Component class of seam. You have to declare all the entities in a configuration file and create your entityManager using this file. The squeleton of this file, usually named hibernate.cfg.xml is attached to this page.
If your simulator acts as a responder, you will need to perform the following actions:
- Listen for incoming messages on different ports (one port per character set encoding)
- Process incoming messages and send response
- Display received and sent messages
Concerning the first point, HAPI library offers a mechanism to create a server linked to an handler; the latter is used to process messages (2). Starting the JVM used by Jboss with the proper option enable the developer to specifiy the character set encoding used by HAPI to read and write messages on the socket. To increase interoperability between the SUT and the simulator, we have chosen to let the user choosing the character set encoding to use for sending and receiving. Consequently, some changes have been brought to the basic classes. Using IHE classes (built from HAPI ones) you can easily declare the character set encoding to use. Instead of instanciating a MinLowerLayerProtocol object, create a new IHELowerLayerProtocol instance by using the constructor which need a charset as a parameter.
We use the SimpleServer class from HAPI library to create the server part; the one has to be started at deployment time. This can be easily done using the annotations @Startup and @Create in the managed-bean dedicated to the server.
Concerning the second point, you need to create a class implementing the Application interface from HAPI. This interface contains 3 methods, two of them are very important: processMessage and processException. They are the methods called when a message is caught by the SimpleServer.
In order to save your time, two abstract classes IHEDefaultHandler and AbstractServer have been implemented and both of them have to be extended in your simulator. The first one has to be extended by the handler, so you will have to implement processMessage and processException; for the second one, you only need to implement the abstract method startServers() as shown below. Obviously, the AbstractServer is based on the SimulatorResponderConfiguration that is to say that when you call the startServers(Actor, Transaction, AffinityDomain, IHEDefaultHandler) method, all the configurations matching the 3 first criteria are retrieved and a instance of SimpleServer is created and linked to the IHEDefaultHandler for each item of the list.
@Startup @Name("myServerBean") @Scope(ScopeType.APPLICATION) public class Server extends AbstractServer<MyHandler>{ private static final long serialVersionUID = 1L; @Override public void startServers() { entityManagerFactory = HibernateUtil.buildEntityManagerFactory("META-INF/hibernate.cfg.xml"); entityManager = HibernateUtil.beginTransaction(entityManagerFactory); Actor simulatedActor = Actor.findActorWithKeyword("PDC", entityManager); AffinityDomain affinityDomain = AffinityDomain.getAffinityDomainByKeyword("IHE", entityManager); HibernateUtil.commitTransaction(entityManager); handler = new MyHandler(entityManagerFactory); startServers(simulatedActor, null, affinityDomain); } }
Concerning the last point, an abstract class SimulatorResponderConfigurationDisplay is used to display the list of listening ports for a given (actor, transaction, affinity domain) tuple. This class is also used to display the list of messages which have been received by the simulator. For each responder your application implements, you have to extend this class and implement the two abstract methods. An XHTML file (/configuration/simulatorResponderConfigurationTemplate.xhtml) is related to this class and has to be included into your GUI in this way:
<rich:panel> <f:facet name="header">Patient Demographic Consumer</f:facet> <ui:include src="/configuration/simulatorResponderConfigurationTemplate.xhtml"> <ui:param name="managedBean" value="#{pdcGUIManagerBean}"/> <ui:param name="diagramPath" value="/img/diag/pdcResponder.png"/> </ui:include> </rich:panel>
Managing Simulator Responder Configuration
Has seen above, the responder part of the HL7 simulators is managed using an entity called SimulatorResponderConfiguration. This entity defines several attributes, among them
- the IP address (this one is not used to start the service but to inform the user of the IP address to communicate with.
- the port (on which the simulator will listen)
- the serverBeanName (this attribute must match the @Name value of the AbstractServer which starts it, otherwise you will not be able to start/stop the server from the GUI)
The menu item "Simulator responder configuration", shown when logged as admin, leads you to the page used to configure the responder part of the simulator. From this page, you can
- Create a new configuration that means declare a new port to listen on
- Start/Stop listening on a port
- Start/Stop listenning on all the declared ports
Display of HL7 messages
The part of the HL7v2.x simulator which displays received and sent HL7 messages has been developed in HL7Common module and, in most of the case, you are not expected to bring changes to it. But, as we may have specific needs in some simulators, we tried to develop this part in such a way that it is easy to bring enhancements.
Menu
In order to have uniform applications, the menu items common to all HL7v2 simulators are gathered in a xhtml page /layout/hl7CommonMenuBar.xhtml you have to include in the menu of your application. The HL7 menu bar looks like this
Add the following lines in your file /layout/menu.xhtml
<!-- HL7 menu --> <ui:include src="/layout/hl7CommonMenuBar.xhtml"> <ui:param name="displaySUTMenu" value="true"/> <ui:param name="displaySimulatorMenu" value="true"/> </ui:include>
- displaySUTMenu is used to indicate whether the menu for reaching the SUT configuration part must be displayed or not.
- displaySimulatorMenu is used to indicate whether the menu for reaching the Simulator "responder" configuration part must be displayed or not.
Footer
In order to have uniform applications, it is necessary to use the same template for the "about" modal panel in the footer item.
See the screenshot below to have an example :
Test reports
HL7Common integrates a feature that enables the simulator to provide test logs to other applications using a REST web service. In order to make this functionnality available in your simulator, you need to update your WEB-INF/web.xml file of the war module of your project with the following lines:
<!-- REST web service (see also line 178)--> <context-param> <param-name>resteasy.jndi.resources</param-name> <param-value>${contextRootOfYourSimulator}/HL7v2TestReport/local</param-value> </context-param> <context-param> <param-name>resteasy.servlet.mapping.prefix</param-name> <param-value>/rest</param-value> </context-param> <listener> <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class> </listener> <servlet> <servlet-name>Resteasy</servlet-name> <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class> </servlet> <servlet-mapping> <servlet-name>Resteasy</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping>
!!! Make sure the application_name and application_url entries are present and not empty in your app_configuration table.