Introduction
This page describes how the OpenNCP Protocol Terminator common component can be integrated with the National Connector (NC).
Background
Each country has their own implementation of the National Connector and therefore the way that OpenNCP connects with the NC will be different for each country. Therefore it is necessary to “inject” country-specific implementations into the Protocol Terminator.
Implementation
The following diagram shows the relevant OpenNCP classes and the classes that are country-specific.
Injection of the National implementation classes is handled by a ServiceLoader. The ServiceLoader will locate and load a specific implementation (called a Service Provider) of an interface. The ServiceLoader is included in Java SE6 and is already utilized in OpenNCP.
Here is an example that shows the ServiceLoader in action. Here you can see how an implementation of the PatientSearchInterface is loaded.
public class XCPDServiceImpl implements XCPDServiceInterface { private ServiceLoader<PatientSearchInterface> serviceLoader; private PatientSearchInterface patientSearchService; public XCPDServiceImpl() { serviceLoader = ServiceLoader.load(PatientSearchInterface.class); try { logger.info("Loading National implementation of PatientSearchInterface..."); patientSearchService = serviceLoader.iterator().next(); logger.info("Successfully loaded PatientSearchService"); } catch (ServiceConfigurationError e) { logger.fatal("Exception loading PatientSearchService: ", e); } } }
Note that the loader is initialized with an interface type. The loader will search for a provider-configuration file in the resource directory META-INF/services. The file's name is the same as the interface name. The file contains the fully-qualified name of the concrete provider class.
Example of provider-configuration file containing Swedish configuration:
eu.epsos.protocolterminators.ws.server.xcpd.PatientSearchInterface
se.sb.epsos.shelob.ws.server.xcpd.impl.PatientSearchImpl
Mock implementations
Mock implementations of the DocumentSubmitImpl, DocumentSearchImpl and PatientSearchImpl are provided in the project epsos-nc-mock-it (integration test, not Italy!). These classes return dummy responses or exceptions where appropriate. Please refer to the /wiki/spaces/INT/pages/4948035 Wiki pages for more information. The mock implementations are packaged as a jar file epsos-nc-mock-it.jar.
Packaging
Each country must build their own jar file containing their own implementations, dependencies and supporting files.
There are a number of strategies for bundling the local classes
- Build epsos-ws-server.war in a local build environment using maven profile "national-connector-impl" to include the local classes as a dependency. The disadvantage with this approach is that the local build server must download all the source files from Google Code repository plus all dependencies from Joinup.
- Download a released epsos-ws-server.war from Joinup. Use a local build script to unpack the archive, bundle in the local classes and then repackage. This is the preferred approach in Sweden.
- Download epsos-ws-server.war from Joinup. Add local classes to tomcat/lib. This has been briefly tested and did not work due to class loader errors. Furthermore it is never a good idea to deploy application-specific classes into tomcat/lib.
Local build using profile
<profiles> <profile> <!-- This profile bundles a mock National Connector implementation --> <id>national-connector-mock-impl</id> <dependencies> <dependency> <groupId>eu.europa.ec.joinup.ecc.epsos-protocol-terminators.epsos-ncp-server</groupId> <artifactId>epsos-nc-mock-it</artifactId> <version>0.1-SNAPSHOT</version> <scope>runtime</scope> </dependency> </dependencies> </profile> <profile> <!-- This profile bundles the real National Connector implementation --> <id>national-connector-impl</id> <dependencies> <dependency> <groupId>${national-connector-impl.groupId}</groupId> <artifactId>${national-connector-impl.artifactId}</artifactId> <version>${national-connector-impl.version}</version> <scope>runtime</scope> </dependency> </dependencies> </profile> </profiles>
The first profile bundles the mock implementations into the war file. The second profile bundles a National implementation. If no profile is specified then no implementation will be bundled at all. In this case, the jar must be deployed to the tomcat/lib directory.
The National profile needs some properties. These properties are defined in the local build environment:
Per User
- Defined in the Maven-settings (%USER_HOME%/.m2/settings.xml).
Global
- Defined in the global Maven-settings (%M2_HOME%/conf/settings.xml).
Profile descriptor
- a descriptor located in project basedir (profiles.xml) (unsupported in Maven 3.x)
Example settings.xml for Sweden:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <profiles> <profile> <id>national-connector-impl</id> <properties> <national-connector-impl.groupId>se.apotekensservice</national-connector-impl.groupId> <national-connector-impl.artifactId>epsos-shelob</national-connector-impl.artifactId> <national-connector-impl.version>0.0.1-SNAPSHOT</national-connector-impl.version> </properties> </profile> </profiles> </settings>
Here are the Maven commands:
# build epsos-ws-serverwith no implementation mvn clean install # build epsos-ws-server with mock implementations mvn clean install -P national-connector-mock-impl # build epsos-ws-server with real implementations mvn clean install -P national-connector-impl
Manipulation of epsos-ws-server.war
Here is the Maven pom that Sweden uses to download OpenNCP from Joinup Nexus repository and then bundle in the jar containing the National implementations.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>epsos</artifactId> <groupId>se.apotekensservice</groupId> <version>1.1</version> </parent> <artifactId>epsos-ws-server</artifactId> <packaging>war</packaging> <name>OpenNCP Server (Country A)</name> <description>This project downloads the OpenNCP epsos-ws-server.war and bundles in the Swedish functionality (Shelob)</description> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.5.1</version> <executions> <execution> <id>unpack</id> <phase>package</phase> <goals> <goal>unpack</goal> </goals> <configuration> <artifactItems> <artifactItem> <groupId>${project.openNcpGroupId}</groupId> <artifactId>${project.openNcpArtifactId}</artifactId> <version>${project.openNcpVersion}</version> <type>war</type> <overWrite>true</overWrite> <outputDirectory>${project.build.directory}/epsos-ws-server</outputDirectory> <includes></includes> <excludes>**/epsos*-mock-*.jar</excludes> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>${project.shelobGroupId}</groupId> <artifactId>${project.shelobArtifactId}</artifactId> <version>${project.shelobVersion}</version> </dependency> <dependency> <groupId>${project.openNcpGroupId}</groupId> <artifactId>${project.openNcpArtifactId}</artifactId> <version>${project.openNcpVersion}</version> <type>war</type> </dependency> </dependencies> <properties> <!-- OpenNCP war --> <project.openNcpGroupId>eu.europa.ec.joinup.ecc.epsos-protocol-terminators.epsos-ncp-server</project.openNcpGroupId> <project.openNcpArtifactId>epsos-ws-server</project.openNcpArtifactId> <project.openNcpVersion>2.0.0-SNAPSHOT</project.openNcpVersion> <!-- Shelob jar (National implementations for bundling into OpenNCP --> <project.shelobGroupId>se.apotekensservice</project.shelobGroupId> <project.shelobArtifactId>epsos-shelob</project.shelobArtifactId> <project.shelobVersion>1.1</project.shelobVersion> </properties> </project>
XCA DocumentSearchInterface usage
Interfaces
The current implementation of the interface is as follows:
public interface DocumentSearchInterface extends NationalConnectorInterface { public DocumentAssociation<PSDocumentMetaData> getPSDocumentList(SearchCriteria searchCriteria); public List<DocumentAssociation<EPDocumentMetaData>> getEPDocumentList(SearchCriteria searchCriteria); public EPSOSDocument getDocument(SearchCriteria searchCriteria); }
The DocumentAssociation interface
public interface DocumentAssociation<T extends EPSOSDocumentMetaData> { public T getXMLDocumentMetaData(); public T getPDFDocumentMetaData(); public String getDocumentClassCode(String documentId); public String getPatientId(String documentId); }
The DocumentAssociation will store XML and PDF document metadata and has two helper methods for searching document's classCode and patientId.
The EPSOSDocumentMetaData
public interface EPSOSDocumentMetaData { public String getId(); public String getPatientId(); public int getFormat(); public Date getEffectiveTime(); public String getClassCode(); public String getRepositoryId(); public String getTitle(); public String getAuthor(); }
This interface is a parent for EPDocumentMetaData and PSDocumentMetaData that reprecent the EPrescription and PatientSummary metaDatas.
The EPSOSDocument
public interface EPSOSDocument { public String getPatientId(); public String getClassCode(); public org.w3c.dom.Document getDocument(); public boolean matchesCriteria(SearchCriteria sc); }
The EPSOSDocument has the actual DOM document plus a subset of the metadata. The interface also has a helper method for checking that if the SearchCriteria matches the metadata.
The SearchCriteria
public interface SearchCriteria { public enum Criteria { PatientId, RepositoryId, DocumentId } public SearchCriteria add(Criteria c, String value); public String getCriteriaValue(Criteria c); public Iterator<Criteria> getSearchCriteriaKeys(); }
The SearchCriteria interface reprecents the criteria what will be used for searching the metaData and DOM documents from the national side. Currently the possible criteria values are in the Criteria enum.
Implementation
From the national component side
From the national component the usage of this interface, when returning DocumentAssociations should be something like:
EPDocumentMetaData epdXml = DocumentFactory.createEPDocumentXML(documentId, patientId, effectiveDate, repositoryId, title, author); EPDocumentMetaData epdPdf = DocumentFactory.createEPDocumentPDF(documentId, patientId, effectiveDate, repositoryId, title, author); DocumentFactory.createDocumentAssociation(epdXml, epdPdf); PSDocumentMetaData psdPdf = DocumentFactory.createPSDocumentPDF(documentId, patientId, effectiveDate, repositoryId, title, author); PSDocumentMetaData psdXml = DocumentFactory.createPSDocumentXML(documentId, patientId, effectiveDate, repositoryId, title, author); DocumentFactory.createDocumentAssociation(psdPdf, psdXml);
And when returning the (DOM) document itself, should be implemented for example like:
epsosDocument = DocumentFactory.createEPSOSDocument( getPatientIdFromDocumentId(documentId), Constants.EP_CLASSCODE, xmlDocument);
From the XCAService component side
From the XCAService component side the usage of this interface is as following, when fetching PatientSummary document metadatas:
DocumentAssociation<PSDocumentMetaData> daPs = documentSearchService.getPSDocumentList(DocumentFactory.createSearchCriteria().add(Criteria.PatientId, "patientId"));
And when fetching EPrescriptions metadatas:
List<DocumentAssociation<EPDocumentMetaData>> prescriptions = documentSearchService.getEPDocumentList(DocumentFactory.createSearchCriteria().add(Criteria.PatientId, "patientId"));
And when fetching the actual DOM document with metada:
EPSOSDocument epsosDoc = documentSearchService.getDocument(DocumentFactory.createSearchCriteria().add(Criteria.DocumentId, "documentId").add(Criteria.PatientId, "patientId").add(Criteria.RepositoryId, "repositoryId"));
References
ServiceLoader tutorial at java.net