ET | EN
- DHX Java library usage guide
- 1. Introduction
- 2. Base platform and external dependencies
- 3. Building
- 4. Known issues (dependency conflicts)
- 5. Loading setup on modern web containers (with annotations)
- 6. Loading setup on old Web containers (with web.xml)
- 7. Configuration properties (dhx-application.properties)
- 8. General principles
- 9. Address book creation and renewal interface
- 10. Document sending (synchronous)
- 11. Document sending (asynchronous)
- 12. Document receiving interface
DHX Java library implements DHX protocol functionality for sending documents, receiving documents and generating local address book.
This guide is intended for software developers (DHX implementors), who wish to use DHX protocol in their document management system (DMS).
Source code of DHX Java library is located at https://github.com/e-gov/DHX-adapter
It contains three sub-packages
- dhx-adapter-core – contains classes for creating and parsing XML objects (Capsule and SOAP), exception classes and some general utility classes
- dhx-adapter-ws – contains classes for sending document (SOAP client), for generating address book (SOAP client) and for receiving documents (SOAP Service endpoint)
- dhx-adapter-server – a separately used adapter server, that caches received documents in local database and offers SOAP interface similar to old DVK interface
The two first packages dhx-adapter-core and dhx-adapter-ws are for direct (java) interfacing.
dhx-adapter-server is intended for them, who cannot use direct (java) interfacing, but plan to deploy separate mediator server, in order to continue to use old style DVK SOAP interface. View more in DHX adapterserver usage guide (estonian).
DHX Java library depends on the components shown below.
Java SE 1.7 (or newer) version is needed for compiling and running the DHX Java library.
As DHX Java library package provides web services (is not only web client), it depends on J2EE Java Servlet API, by using Spring Web Services.
Java Architecture for XML Binding - JAXB API is used for XML marshalling (is part of Java SE 7).
DHX Java library is based on Spring Framework architecture by using extensively its sub-modules:
- For configuration and initializing (Spring AOP, Spring Context, etc)
- For making HTTP SOAP client request (Spring WS Client, Apache HttpClient)
- For providing HTTP SOAP web service (Spring WS Server Endpoint, Java Servlet API)
Direct and indirect external references (dependencies) of DHX Java library are following
Group | Package | Version | Notes |
---|---|---|---|
org.springframework | spring-core | 4.3.3.RELEASE | Spring Core |
org.springframework | spring-aop | 4.2.7.RELEASE | Spring AOP |
org.springframework | spring-beans | 4.2.7.RELEASE | Spring Beans |
org.springframework | spring-context | 4.2.7.RELEASE | Spring Context |
org.springframework | spring-expression | 4.2.7.RELEASE | Spring Expression Language (SpEL) |
org.springframework | spring-oxm | 4.2.7.RELEASE | Spring Object/XML Marshalling |
org.springframework | spring-web | 4.2.7.RELEASE | Spring Web |
org.springframework | spring-webmvc | 4.2.7.RELEASE | Spring Web MVC |
org.springframework.ws | spring-ws-core | 2.4.0.RELEASE | Spring WS Core |
org.springframework.ws | spring-xml | 2.4.0.RELEASE | Spring XML |
org.springframework.boot | spring-boot-starter-log4j2 | 1.3.5.RELEASE | Spring Boot starter Log4j2 |
commons-logging | commons-logging | 1.1.3, 1.2 | Apache commons login pakett logimiseks |
commons-codec | commons-codec | 1.9 | Apache Commons Codec |
aopalliance | aopalliance | 1.0 | AOP alliance |
org.apache.httpcomponents | httpclient | 4.5.2 | Apache HttpClient |
org.apache.httpcomponents | httpcore | 4.4.4 | Apache HttpCore |
org.slf4j | jcl-over-slf4j | 1.7.21 | JCL 1.1.1 implemented over SLF4J |
org.slf4j | jul-to-slf4j | 1.7.21 | JUL to SLF4J bridge |
org.apache.logging.log4j | log4j-api | 2.4.1 | Apache Log4j API |
org.apache.logging.log4j | log4j-core | 2.4.1 | Apache Log4j Core |
org.apache.logging.log4j | log4j-slf4j-impl | 2.4.1 | Apache Log4j SLF4J Binding |
org.projectlombok | lombok | 1.16.6 | Project Lombok |
org.slf4j | slf4j-api | 1.7.12 | SLF4J API Module |
wsdl4j | wsdl4j | 1.6.3 | WSDL4J |
javax.activation | activation | 1.1 | JavaBeans Activation Framework (JAF) |
javax.servlet | javax.servlet-api | 3.0.1 | Java Servlet API |
javax.validation | validation-api | 1.1.0.Final | Bean Validation API |
org.aspectj | aspectjrt | 1.8.2 | AspectJ runtime |
com.jcabi | jcabi-aspects | 0.22.5 | jcabi-aspects |
xerces | xercesImpl | 2.8.1 | xerces XML api |
com.jcabi | jcabi-log | 0.15 | jcabi-log |
junit | junit | 4.12 | JUnit |
The following example is given on how to involve the DHX Java library in existing (or new) software project, by using Apache Maven.
The above external dependencies are downloaded automatically by Maven.
The following rows should be appended into Maven pom.xml
<repositories>
<repository>
<id>dhx-mvn-repo</id>
<name>DHX Maven Repository</name>
<url>https://raw.github.com/e-gov/DHX-adapter/mvn-repo/</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
The following dependencies should be appended into Maven pom.xml
<dependency>
<groupId>ee.ria.dhx</groupId>
<artifactId>dhx-adapter-core</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ee.ria.dhx</groupId>
<artifactId>dhx-adapter-ws</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
In case axiom-dom or axis2-saaj are in Java classpath, the XML marshalling/unmarshalling will not work properly (attachments remain empty). It is known JAXB issue.
It is recommended to remove these libraries from Java classpath.
With Maven, if third party library (for axample axis2-codegen) depends on these, it could be removed like this:
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-codegen</artifactId>
<version>1.4</version>
<exclusions>
<exclusion>
<groupId>org.apache.ws.commons.axiom</groupId>
<artifactId>axiom-dom</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-saaj</artifactId>
</exclusion>
</exclusions>
</dependency>
The simplest way is to use DHX Java library inside Web (Servlet) Container (Tomcat, Jetty, etc).
Inside Servers, that support Java Servlet 3.0 or newer specification, the Spring Framework configuration could be specified with annotations as follows.
import ee.ria.dhx.ws.config.endpoint.DhxEndpointConfig;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import java.util.HashMap;
import java.util.Map;
import javax.xml.soap.SOAPMessage;
@Configuration
public class DhxServerWebServiceConfig {
@Bean(name = "dhxServlet")
public ServletRegistrationBean dhxMessageDispatcherServlet(
ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
AnnotationConfigWebApplicationContext applicationAnnotationContext
= new AnnotationConfigWebApplicationContext();
applicationAnnotationContext.setParent(applicationContext);
applicationAnnotationContext.register(DhxEndpointConfig.class);
servlet.setApplicationContext(applicationAnnotationContext);
servlet.setTransformWsdlLocations(true);
servlet.setMessageFactoryBeanName("messageFactory");
ServletRegistrationBean servletBean = new ServletRegistrationBean(servlet, "/" + "ws" + "/*");
servletBean.setName("dhx");
return servletBean;
}
@Bean(name = "messageFactory")
public SaajSoapMessageFactory messageFactory() {
SaajSoapMessageFactory smf = new SaajSoapMessageFactory();
Map<String, String> props = new HashMap<String, String>();
props.put(SOAPMessage.WRITE_XML_DECLARATION, Boolean.toString(true));
smf.setMessageProperties(props);
return smf;
}
}
View more in SpringFramework ServletRegistrationBean and javax.servlet.ServletContext.
On older Web (Servlet) Containers the loading is done by using Spring Framework classes ContextLoaderListener and MessageDispatcherServlet.
The web.xml must be supplemented with sections:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>spring-ws-dhx</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>ee.ria.dhx.ws.beanconfig.DhxEndpointConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws-dhx</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
Also /WEB-INF/applicationContext.xml
must be placed inside WAR.
It should look like the following:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="ee.ria.dhx.*" />
<task:annotation-driven scheduler="myScheduler" />
<task:scheduler id="myScheduler" pool-size="3" />
<context:property-placeholder location="WEB-INF/classes/dhx-application.properties" />
</beans>
On servlet initialization, the file named dhx-application.properties
is searched from Servlet classpath.
Building processs should include it into WAR /WEB-INF/classes
directory.
Example is in file dhx-application.properties
Example content:
soap.security-server=http://10.0.13.198
soap.xroad-instance=ee-dev
soap.member-class=GOV
soap.member-code=40000001
document-resend-template=30,120,1200
address-renew-timeout=*/20 * * * * ?
Possible dhx-application.properties
property names are described in table below.
The parameters with default value do not have to be added into properties file (if you do not want to change the value).
Parameter | Default value | Example value | Description |
---|---|---|---|
soap.security-server | http://10.0.13.198 | X-road security server network address | |
soap.xroad-instance | ee | Country code. ee-dev in develepment, ee in production. Assigned to X-road header Header/client/xRoadInstance value in SOAP request. |
|
soap.member-class | GOV | Organization X-road member class (COM or GOV ). Assigned to X-road header Header/client/memberClass value in SOAP request. |
|
soap.member-code | 40000001 | Organization registration code. Assigned to X-road header Header/client/memberCode value in SOAP request. |
|
soap.default-subsystem | DHX | Organization X-road DHX subsystem code. Assigned to X-road header Header/client/subsystemCode value in SOAP request. If organization has multiple DHX subsystems, it could be different (like DHX.adit ) |
|
soap.security-server-appender | /cgi-bin/consumer_proxy | X-road security server URL path | |
soap.targetnamespace | http://dhx.x-road.eu/producer |
SOAP X-road namespace | |
soap.protocol-version | 4.0 | X-road protocol version. Assigned to X-road header Header/protocolVersion value in SOAP request. |
|
soap.global-conf-location | verificationconf | The path prefix, that determines X-road Global configuration download URL. In general it is /verificationconf/ee/shared-params.xml |
|
soap.global-conf-filename | shared-params.xml | The filename suffix, that determines X-road Global configuration download URL. In general it is /verificationconf/ee/shared-params.xml |
|
soap.dhx-representation-group-name | DHX vahendajad | DHX intermediary group name. Used for searching mediators from X-road global configuration. | |
soap.accepted-subsystems | DHX | Specifies organization DHX sub-systems (for document receival). Comma separated list. Example: In case organization has several DHX sub-systems DHX.dvk and DHX.adit (served by single DHX endpoint), it could be assigned as soap.accepted-subsystems=DHX.dvk,DHX.adit |
|
soap.send-document-service-code | sendDocument | Service operation name. DHX protocol requires constant sendDocument . Assigned to X-road header Header/service/serviceCode value in SOAP request. |
|
soap.send-document-service-version | v1 | Assigned to X-road header Header/service/serviceVersion value in SOAP request. |
|
soap.representatives-service-code | representationList | Representee list operation name. Assigned to X-road header Header/service/serviceCode value in SOAP request. |
|
soap.representatives-service-version | v1 | Representee list operation version. Assigned to X-road header Header/service/serviceVersion value in SOAP request. |
|
soap.connection-timeout | 60000 | HTTP connection opening timeout (when making SOAP requests). Milliseconds. Default is 1 minute. | |
soap.read-timeout | 120000 | HTTP response waiting timeout (when making SOAP requests). Milliseconds. Default is 2 minutes. Could be increased in case document files are large. | |
soap.http-timeout | 300 | HTTP connection keep alive duration. Seconds. Default is 5 minutes (300 seconds). | |
soap.dhx-subsystem-prefix | DHX | DHX sub-system prefix. Used for searching DHX addressees from X-road global configuration. DHX protocol requires it to be constant DHX |
|
soap.client-truststore-file | ${JAVA_HOME}/jre/lib/security/cacerts | Location of the Java keystore file containing the collection of CA certificates trusted by this application process (trust store) | |
soap.client-truststore-password | changeit | Password to unlock the keystore file (store password) specified by soap.client-truststore-file | |
soap.client-truststore-type | JKS | Java keystore file format. For Java keystore file format, this property has the value jks (or JKS) | |
soap.client-keystore-file | dhx.jks | Location of the Java keystore file containing the collection of keys (key store) | |
soap.client-keystore-password | changeit | Password to unlock the keystore file (store password) specified by soap.client-keystore-file | |
soap.client-keystore-type | JKS | Java keystore file format. For Java keystore file format, this property has the value jks (or JKS) | |
dhx.capsule-validate | true | Specifies whether to validate the document (both received and sended) Capsule XML against its XSD schema. If document does not validate, then respond with error DHX.Validation to the sender. Capsule 2.1 XSD Schema | |
dhx.parse-capsule | true | Specifies whether to marshall (parse) the incoming document Capsule XML into Java objects. | |
dhx.check-recipient | true | Specifies whether to validate incoming document addressees. Validation checks whether addressee inside Capsule XML is the same as receiver (our own) organization code. If addressee is invalid, then respond with error DHX.InvalidAddressee to the sender. | |
dhx.check-sender | false | Specifies whether to check the sender validity of the incoming document. Validation checks whether the sender inside Capsule XML is the same as sender (client) in X-road header. | |
dhx.check-duplicate | true | Specifies whether to perform duplication checks on incoming documents consignments. If true, the DhxImplementationSpecificService isDuplicatePackage is called. If it is duplicate consignment, then respond with error DHX.Duplicate to the sender. |
|
dhx.document-resend-template | 30,120,1200 | Specifies sending re-attempting count and wait times. Used only when sending asynchronously. This example determines that total 4 sending attepts are made. Re-attempt are made at first after 30 seconds, then after 120 seconds (2 minutes) and finally after 1200 seconds (20 minutes). If final attempt fails, then abort sending thread. | |
dhx.wsdl-file | dhx.wsdl | DHX web service WSDL file name. This wsdl file is searched from Java Classpath on server restart. WSDL file is same for all DHX implementors and needs no changes. | |
dhx.protocol-version | 1.0 | DHX protocol version number. Sended inside sendDocument request as paramater DHXVersion value. |
|
dhx.check-dhx-version | true | Specifies whether to check DHXVersion validity on document arrival. If version is not right, then respond with error DHX.UnsupportedVersion to the sender. | |
dhx.accepted-dhx-protocol-versions | 1.0 | Specifies what DHX protocol versions we accept on document arrival. Comma sparated list. In future it might be 1.0,2.0 . Works together with previous dhx.check-dhx-version parameter. |
|
dhx.marshall-context | ee.ria.dhx. types.ee.riik.schemas. deccontainer.vers_2_1: ee.ria.dhx.types.eu. x_road.dhx.producer: ee.ria.dhx.types.eu. x_road.xsd.identifiers: ee.ria.dhx.types.eu. x_road.xsd. representation: ee.ria.dhx.types.eu. x_road.xsd.xroad |
Specifies Java package names that contain XML type objects (marshalled/unmarshalled by JAXB). If SOAP request or Capsule XML contains additional extension elements from third party namespaces, then new types could be added. Capsule XML contains extenison point inside RecordTypeSpecificMetadata element (<xs:any namespace="##any"> ) |
|
dhx.xsd.capsule-xsd-file21 | jar://Dvk_kapsel_vers_ 2_1_eng_est.xsd | Specifies location to search for Capsule XSD schema file. In general it is inside dhx-adapter-core jar. |
|
dhx.renew-address-list-on-startup | true | Specifies whether to start address book renewal job on server restart. Address book renewal could take long time in special case (if a DHX mediator has its servers down). Therefore it is reasonable to cache address list in local database and implement DhxImplementationSpecificService method getAdresseeList. In this case use dhx.renew-address-list-on-startup=false |
|
address-renew-timeout | 0 */20 * * * ? | Specifies local adress boook renewal frequency. Crontab format: <second> <minute> <hour> <day> <month> <weekday> . Value */20 means at every 20-th unit. Therefore 0 */20 * * * ? means after every 20 minutes. Every day at 7:00 a clock is 0 0 7 * * * |
Main functions of DHX Java library are sending documents, receiving documents and generating local address book.
Main functionality, that are of interest to developer (DHX implementor), are in packages
- ee.ria.dhx.ws.service – Java service interfaces
- ee.ria.dhx.ws.service.impl – Java service implementations
Service interfaces are
Interface | Description |
---|---|
AddressService | Services for generating and renewing the address book. |
DhxPackageService | Services for synchronous sending and receiving |
AsyncDhxPackageService | Services for asynchronous sending |
DhxMarshallerService | Services for marshalling/unmarshalling XML objects (Capsule) |
DhxPackageProviderService | Services for creating document consignments (packages). |
DhxImplementationSpecificService | Implementation specific callback interfaces. |
The most important is DhxImplementationSpecificService, that needs to be implemented by developers.
Example:
package com.example.service.impl;
import ee.ria.dhx.ws.service.DhxImplementationSpecificService;
@Service("dhxImplementationSpecificService")
public class CustomDhxImplementationSpecificService
implements DhxImplementationSpecificService {
. . .
}
Above, the @Service
tag specifies that DHX Java library uses now dhxImplementationSpecificService
custom implementation.
Therefore the document receiving and sending internal functionality uses CustomDhxImplementationSpecificService
as callback interface now.
In DHX addressing, the developer needs to bear in mind that, it is not sufficient to use only the registration code of an organization.
For unique addressing, the combination registrationCode + subsystem
should be used.
For example, if document addressee is Lääne Ringkonnaprokuratuur
, then combination code=70000906 + subsystem=DHX.laane
is sufficient.
If document addressee is Lõuna Ringkonnaprokuratuur
, then combination code=70000906 + subsystem=DHX.louna
is sufficient.
Service AddressService is intended for getting, creating and renewing the address book.
It has three methods
-
Method getAdresseeList returns cached (previously created) local adress book.
-
Method renewAddressList executes address book creation algorithm. It is executed also by timer jobi (
dhx-application.properties
parameteraddress-renew-timeout=0 */20 * * * ?
). -
Method getClientForMemberCode helps to find addressee (recipient) technical data, by using unique combination
registrationCode + sybsystem
as input.
For long-term preservation of address book (in database or in filesystem), the developer may implement DhxImplementationSpecificService
methods saveAddresseeList and getAdresseeList.
If implemented, these methods must store and reload all InternalXroadMember attributes (see table below).
Example
@Service("dhxImplementationSpecificService")
public class CustomDhxImplementationSpecificService
implements DhxImplementationSpecificService {
@Override
public List<InternalXroadMember> getAdresseeList() {
// retrieve from database or filesystem
}
@Override
public void saveAddresseeList(List<InternalXroadMember> members) {
// store to database or filesystem
}
...
}
Addressee/recipient object InternalXroadMember contains following attributes
Attrinute | Sample value | Description |
---|---|---|
xroadInstance | EE | Country EE |
memberClass | GOV | GOV- Goverment, COM - commercial. In case of intermediary, it contains mediator memberClass. |
memberCode | 70000001 | organization registration code. In case of intermediary, it contains mediator's registration code, not mediated organization (representee) registration code. |
subsystemCode | DHX or DHX.adit | Sub-system code. Must start with DHX. In general just DHX . In case of intermediary, it contains mediator's subsystem code, not mediated organization (representee) subsystem code. |
name | Riigi infosüsteemide keskus | Organization or sub-system name. In case of intermediary, it contains mediator's name, not mediated organization (representee) name. |
representee.representeeCode | 70012121 | Mediated organization (representee) registration code. (used in case document is sended through intermediary) |
representee.representeeSystem | DHX.subsystem | Mediated organization (representee) syb-system code. In general it is empty. If mediated organization has several subsystems, then subsystem code. |
representee.representeeName | Lasteaed Pallipõnn | Mediated organization (representee) name or its sub-system name |
Method getAdresseeList returns InternalXroadMember objects array. It contains all DHX addressees (recipients).
There are several types of recipients
- With direct DHX capability (single sub-system DHX)
- With direct DHX capability (several syb-systems
DHX.subsystem1
andDHX.subsystem2
etc) - With DHX capability through intermediary (syb-system is misising/empty)
- With DHX capability through intermediary (several sub-systems
DHX.subsystem1
andDHX.subsystem2
etc)
NB! When sending document and generating consignment with getOutgoingPackage methods, all the InternalXroadMember
instance attributes must be specified.
If recipient (addressee) has subsystemCode
value, then it must be specified on getOutgoingPackage()
mehtod call.
If recipient (addressee) has representeeCode
value, then it must be specified on getOutgoingPackage()
mehtod call.
The surest way is to use getOutgoingPackage variations, that have InternalXroadMember recipient
input parameter.
The required pre-valued InternalXroadMember
object can be obtained by AddressService
method getClientForMemberCode, that finds correct instance from local address book, by using combiantion registration-code
+ sub-system
as input.
For synchronous sending use service ee.ria.dhx.ws.service.DhxPackageService
method sendPackage.
NB! Synchronous method sends document only once, waiting for recipient response (success/fail). In general it is preferable to use asynchronous sender AsyncDhxPackageService, that re-attepts sending on failure.
For creating document consignment (package) use service ee.ria.dhx.ws.service.DhxPackageProviderService
methods getOutgoingPackage.
Example
package com.example.service;
import ee.ria.dhx.ws.service.DhxPackageService;
import ee.ria.dhx.ws.service.AddressService;
import ee.ria.dhx.ws.service.DhxPackageProviderService;
import ee.ria.dhx.types.DhxSendDocumentResult;
import ee.ria.dhx.types.InternalXroadMember;
public class Sender {
@Autowired
DhxPackageService dhxPackageService;
@Autowired
AddressService addressService;
@Autowired
DhxPackageProviderService dhxPackageProviderService;
public void sendExample() throws DhxException {
// find addressee/recipient technical data
InternalXroadMember recipient = addressService.getClientForMemberCode(
"70000001", // registration code
"DHX"); // subsystem, DHX or DHX.sybsystem)
// create consignment
OutgoingDhxPackage dhxPackage = dhxPackageProviderService.getOutgoingPackage(
new File("sended-document-capsule.xml"),
UUID.randomUUID().toString(), // unique self generated consignment id
recipient);
// send document over X-road and wait for response
DhxSendDocumentResult result = dhxPackageService.sendPackage(dhxPackage);
// check error
if (result.occuredException != null
|| result.getResponse().getFault() != null) {
// sending error occurred
}
}
}
If sender wants to send the same document capsule to multiple recipients, then he must send it to every recipient separately. For that use ee.ria.dhx.ws.service.DhxPackageService
method sendMultiplePackages.
Asynchronous sending interface is similar to synchronous interface.
The difference is that the interface AsyncDhxPackageService method sendPackage is executed asynchronously (in separate thread) and the current thread continues immediately (not waiting for response).
In case there occurs an technical error, the Asynchronous execution re-attempts sending (see dhx-application.properties
parameter document-resend-template=30,120,1200
).
After sending success (or final failure) the callback interface DhxImplementationSpecificService method saveSendResult is called. It is developer's responsibility to implement it, by storing response to DMS database, etc.
Callback interface method saveSendResult
implementation example
@Service("dhxImplementationSpecificService")
public class CustomDhxImplementationSpecificService
implements DhxImplementationSpecificService {
@Override
public void saveSendResult(DhxSendDocumentResult finalResult,
List<AsyncDhxSendDocumentResult> retryResults) {
if (finalResult.occuredException != null
|| finalResult.getResponse().getFault() != null) {
// error occurred (all attempts failed)
} else {
// success
String id = finalResult.getResponse().getReceiptId();
}
}
...
}
Asynchronous sending method sendPackage execution example
package com.example.service;
import ee.ria.dhx.ws.service.AsyncDhxPackageService;
import ee.ria.dhx.ws.service.AddressService;
import ee.ria.dhx.ws.service.DhxPackageProviderService;
import ee.ria.dhx.types.InternalXroadMember;
public class Sender {
@Autowired
AsyncDhxPackageService asyncDhxPackageService;
@Autowired
AddressService addressService;
@Autowired
DhxPackageProviderService dhxPackageProviderService;
public void sendExample() throws DhxException {
// find addressee/recipient technical data
InternalXroadMember recipient = addressService.getClientForMemberCode(
"70000001", // registration code
"DHX"); // subsystem, DHX or DHX.sybsystem)
// create consignment
OutgoingDhxPackage dhxPackage = dhxPackageProviderService.getOutgoingPackage(
new File("sended-document-capsule.xml"),
UUID.randomUUID().toString(), // unique self generated consignment id
recipient);
// send document over X-road.
// no immediate response, execution is returned to current thread.
// if sending in async thread has finished then method
// DhxImplementationSpecificService.saveSendResult is called
asyncDhxPackageService.sendPackage(dhxPackage);
}
}
If sender wants to send the same document capsule to multiple recipients, then he must send it to every recipient separately. For that use ee.ria.dhx.ws.service.AsyncDhxPackageService
method sendMultiplePackages.
By using the DhxServerWebServiceConfig
or the web.xml
described above, the web service endpoint is registered and created automatically (in web servlet container).
It's URL is http://<hostname>:<port>/ws/dhx.wsdl
Services provided on that URL should be registered in X-road security server.
For receiving and storing incoming documents, the developer must implement DhxImplementationSpecificService methods isDuplicatePackage and receiveDocument.
On arrival, at first the method isDuplicatePackage is called.
It must check if it is duplicate sending attempt (the same document consignment is sended to our DMS twice or more times).
If it is duplicate, then response with error DHX.Duplicate is generated.
If it's not duplicate then method receiveDocument is called. It should store the document (into DMS's Incoming documents
folder).
Example
@Service("dhxImplementationSpecificService")
public class CustomDhxImplementationSpecificService
implements DhxImplementationSpecificService {
@Override
public boolean isDuplicatePackage(InternalXroadMember from,
String consignmentId) {
// check for duplicate: same consignmentId arrived from same sender (from)
// generate sender unique key
String uniqueKey = from.getXroadInstance + "/" + from.getMemberClass()
+ "/" + from.getMemberCode() + "/" + from.getSubsystemCode();
if (from.getRepresentee() != null) {
uniqueKey = uniqueKey + "/" + from.getRepresentee().getRepresenteeCode()
+ "/" + from.getRepresentee().getRepresenteeSystem();
}
// search database if the combination "uniqueKey + consignmentId" is previously stored
. . .
}
@Override
public String receiveDocument(IncomingDhxPackage document,
MessageContext context) throws DhxException {
// Get document capsule XML
DataHandler kapselHandler = document.getDocumentFile();
InputStream kapselStream = kapselHandler.getInputStream();
// Store document XML to database
...
// generate and return unique receipt id
String receiptId = UUID.randomUUID().toString();
return receiptId;
}
...
}