1       Introduction

1.1          Purpose

DDS Registry allows document source systems to register metadata concerning documents according to the Integrating the Healthcare Enterprise (IHE) Cross-Enterprise Document Sharing (XDS.b) transactions ITI-42 and ITI-61.

This document describes the usage of the web service provided by the DDS Registry for this purpose.

The document introduces the general web service interface in addition to an example of how it can be used by a document source system.

The document metadata to be registered must adhere to a certain metadata definition, that is, be based on defined value sets, formats, encoding systems and so forth. The DDS Registry may be used with one of many metadata definitions, but currently, it is used with the one defined in [Metadata Profile].

1.2          Reading Guide

This document is intended for developers and architects that will be using DDS Registry for registering document metadata. Comprehension of this document is facilitated considerably, when the reader is familiar with IHE ITI-42 and ITI-61, described in [ITI TF-2b].

It is assumed that the reader is familiar with Web services in addition to usage of Den Gode Webservice (DGWS) and Security Token Service (STS).

It has been prioritized to keep the code examples short, possibly at the expense of ordinary division of responsibilities.

1.3          Document History

Version

Date

Responsible

Description

0.7

11.1.2013

Systematic

Initial version

0.9

27.2.2013

Systematic

Prepared for testing in the demo project.

0.9a

19.04.2013

Systematic

Version for Release Candidate 1

1.0

19.06.2013

Systematic

Quality assured.

1.1

28.11.2014

Systematic

National Patient Index (NPI) replaced by  Document Sharing Service (DDS)

1.2

05.05.2015

Systematic

Code references has been updated due to name change from NPI to DDS

1.3

26.01.2016

Systematic

Added reference to Danish metadata profile.

1.4          Definitions and References

Definition

Description

DDS

Document Sharing Service

IHE

Integrating the Healthcare Enterprise

NSI

National eHealth Authority

NSP

National Service Platform (within health care)

SOR

Health Service Organization Register

STS

Security Token Service

XDS.b

Cross-Enterprise Document Sharing

Reference

Description

DGWS 1.0

Den Gode Webservice 1.0

DGWS 1.0.1

Den Gode Webservice 1.0.1

ITI TF-2b

IHE IT Infrastructure Technical Framework, Volume 2b (linked from http://ihe.net/Technical_Frameworks/#IT and accessible as http://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf)

Metadata Profile

XDS Metadata for Document Sharing. Danish Profile.

National eHealth Authority, Draft profile for Trial Use, version 0.90b January 30, 2015

http://svn.medcom.dk/svn/drafts/Standarder/IHE/DK_profil_metadata/Metadata-v090.docx

DDS Registry Querying User’s Guide

DDS Registry Querying User’s Guide (SSE/11734/PHB/0037)

DDS Registry Querying Interface

DDS Registry Querying Interface Description (SSE/11734/IFS/0016)

DDS Registry Registering Interface

DDS Registry Registering Interface Description (SSE/11734/IFS/0015)

2       Usage of DDS Registry

DDS Registry provides operations for registration of document metadata in document sharing service through a web service interface, described in [DDS Registry Registering Interface].

Registration of document metadata is performed using the standard IHE Cross-Enterprise Document Sharing (XDS), based on ebXML Registry Services (RS) and ebXML Registry Information Model (RIM).

The DDS Registry provides registration operations based on IHE XDS.b transaction 42, RegisterDocumentSet-b, and transaction 61, RegisterOnDemandDocumentEntry.

With the goal to base the operations RegisterDocumentSet-b and RegisterOnDemandDocumentEntry on the IHE profiles to the greatest extent possible, changes to the IHE XDS standard’s WSDL are minimized. Accordingly, the payload for the operations is unchanged.

As described in [DDS Registry Registering Interface], the DDS Registry expects that RegisterDocumentSet-B and RegisterOnDemandDocumentEntry calls contain additional headers consisting of:

  • A Security-header
  • A Medcom-header

These originate from Den Gode Webservice described in [DGWS 1.0] and [DGWS 1.0.1].

The headers are added as implicit headers, i.e. by being described in the WSDL’s bindings without being added to the message structure of RegisterDocumentSet-b and RegisterOnDemandDocumentEntry.

2.1          Preparation for Invoking of the DDS Registry

Before invoking of the DDS Registry, it is the source system’s responsibility:

  • to obtain an ID card of at least security level 3 issued by STS

Note that the Central Business Register (CVR) number of the organization using the source system must be present in DDS Registry’s whitelist. Addition to the whitelist must be agreed with the DDS Registry system owner.

2.2          Invoking the DDS Registry

2.2.1      Invoking DocumentRegistry_RegisterDocumentSet-b

On invoking of the operation DocumentRegistry_RegisterDocumentSet-b on DDS Registry, the following must be completed:

  1. A SubmitObjectsRequest (i.e. payload of DocumentRegistry_RegistryStoredQuery) is created
  2. Security header must be populated with the ID card from STS
  3. Medcom-header is created, including:
    1. A unique message ID must be assigned
    2. The Medcom-header’s flowID element must be assigned a unique session ID

2.2.2      Invoking DocumentRegistry_RegisterOnDemandDocumentEntry

On invoking of the operation DocumentRegistry_RegisterOnDemandDocumentEntry on DDS Registry, the same steps as on invoking of DocumentRegistry_RegisterDocumentSet-b are completed.

3       Examples of DDS Registry Invocation

This section provides an example on how this service can be invoked. At present, no .NET-example code for calls to DDS Registry exists, but WSDL’s are available, from which a Web service consumer can be created using Visual Studio’s Web service wizard combined with Seal.NET (see below).

3.1     Source System Obtains DGWS ID Card from STS

Below is outlined how a Java-based source system can use Seal.Java to obtain a STS-signed ID card.

Seal is an open source framework that assists "Den Gode Webservice", including integration with a central identity service (IdP/STS), handling of federation certificates etc.

Versions exist for Java and .Net.


An STS-signed ID card is obtained by using a company certificate as outlined below:

Properties properties = SignatureUtil.setupCryptoProviderForJVM();

// A new SOSITestFederation(properties), can be used here if the test-environment is used
Federation federation = new SOSIFederation(properties);

File keystoreFile = … // read in the keystore file
String keystorePassword = … // obtain the password to the keystore file
CredentialVault vault = new FileBasedCredentialVault(
  properties,
  keystoreFile,
  keystorePassword);

SOSIFactory factory = new SOSIFactory(federation, vault, properties);

String flowId = … // A unique flow ID is entered in the ID card

SystemIDCard systemIDCard = factory.createNewSystemIDCard(
 …); // Various ID card content parameters are provided here

SecurityTokenRequest securityTokenRequest = factory.createNewSecurityTokenRequest();
securityTokenRequest.setIDCard(systemIDCard);
SecurityTokenResponse response = … // Send securityTokenRequest to the STS’en and deserialize the                                                   
								   // response with factory.deserializeSecurityTokenResponse                                           
…
IDCard signedIDCard = response.getIDCard();

Accordingly, an ID card signed by STS is obtained. In the examples below, it is assumed that fresh requests to calls of the respective Web service operations are made; that the signed ID card is copied to the fresh request; that additional DGWS-parameters are set. Below this is described as a helper function, getValidDGWSHeaders that the user is expected to create.

DGWSHeaderWrapper signedHeaders = getValidDGWSHeaders(flowId);

3.2          Source System Creates SubmitObjectsRequest

A helper class SubmitObjectsRequestHelper, described in section 3.4, is used below to create SubmitObjectsRequest. The SubmitObjectsRequest contains metadata for an on-demand document that the source system can make available.

Note that the provided code that shows the principles for registration do not necessarily entail registration of all required metadata-information. Which metadata that may or must figure in registration must be agreed upon between the document source and NSI. The provided code is just an example of one of the ways in which the IHE standard makes it possible to register metadata.

3.2.1      Creation of SubmitObjectsRequest

SubmitObjectsRequestHelper requestHelper = new SubmitObjectsRequestHelper();
SubmitObjectsRequest request = requestHelper.create();

3.2.2      Creation and addition of XDSDocumentEntry for on-demand document

// Unique identification of the document that must be replaced by an actual value
String documentId = "urn:myorg:mydoc:doc:12345";
String repositoryUniqueId = "1.3.6.1.4.1.21367.2010.1.2.300.1";


ExtrinsicObjectType metadata = requestHelper.addOnDemandDocumentMetadataEntry(
          request
          documentId,
          repositoryUniqueId);

Below it is registered which citizen (patient) the document concerns in addition to which institution/organization that created the document. In this case, the organization id 486651000016002 is provided as a SOR code and the patient is identified with patient id 1122334455 given as a social security number. Both values are coded/formatted as described in [Metadata Profile].

String authorInstitution = "OUH Fælles Akut Amb (Svendborg)^^^^^& 1.2.208.176.1&ISO^^^^486651000016002";
String patientId = "1122334455^^^&2.16.840.1.113883.3.4208.100.2&ISO";
requestHelper.addDocumentMetadataAttributes(metadata, authorInstitution, patientId);

// Add additional metadata-information in the same manner


3.2.3      Creation and addition of XDSSubmissionSet

As part of the SubmitObjectsRequest, a description of the complete addition in the form of an XDSSubmissionSet must be provided. This is created and added with:

RegistryPackageType submissionSet = requestHelper.addSubmissionSet(request);
requestHelper.addSubmissionSetAttributes(submissionSet, patientId);

// Add additional metadata-information in the same manner


The added XDSDocumentEntry figure in the created XDSSubmissionSet, which is why an association between XDSSubmissionSet and XDSDocumentEntry must be added to the SubmitObjectsRequest. This is done with:

requestHelper.addAssociation(request, metadata, submissionSet);


3.2.4      Possible creation and addition of replacement-associations

If the (new) document metadata described above replaces pre-existing document metadata, then an association as shown below must be added to the SubmitObjectsRequest:

oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory objFactory =
                   new oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory();
AssociationType1 association = objFactory.createAssociationType1();
association.setId(UUID.randomUUID().toString());
association.setAssociationType("urn:ihe:iti:2007:AssociationType:RPLC");
association.setObjectType(
   "urn:oasis:names:tc:ebxml-regrep:ObjectType:RegistryObject:Association");
association.setSourceObject(metadata.getId()); // Id for new metadata
association.setTargetObject(toReplaceId); // Id for metadata to replace


// Add replacement-association to the request
request.getRegistryObjectList().getIdentifiable().add(objFactory.createAssociation(association));

Replacement-association must be created for each individual pre-existing document metadata- object to replace.

Pre-existing document metadata-objects for a particular document ID can be found by use of search in the response from a DDS Registry query. DDS Registry querying is described in [DDS Registry Querying Interface] and [DDS Registry Querying User’s Guide].

3.3          Source System Calls RegisterOnDemandDocumentEntry

Below the ID card that has been obtained as described in section 3.1 is used to call DDS Registry’s operation RegisterOnDemandDocumentEntry. Those metadata, that are registered, are created as described in section 3.2.

Creation of service and port:

URL wsdl = … // DDS Registry’s.WSDL is named
DocumentRegistryService service = new DocumentRegistryService(wsdl, 
          new QName("urn:ihe:iti:xds-b:2007", "DocumentRegistry_Service"));
DocumentRegistryPortType port = service.getDocumentRegistryPortSoap();

Subsequently, the method on the port can be called:

SubmitObjectsRequestHelper requestHelper = new SubmitObjectsRequestHelper();
SubmitObjectsRequest request = requestHelper.create();
… // request-structure is populated as described above

String flowId = XMLUtil.createNonce(); // A unique flow ID that figures in the ID card is created                                                               
									   // using SEAL
DGWSHeaderWrapper signedHeaders = getValidDGWSHeaders(flowId); // see above

RegistryResponseType response =
  port.documentRegistryRegisterOnDemandDocumentEntry(
                   request,
                    signedHeaders.getSecurityHeader(),
                    new Holder<Header>(signedHeaders.getMedcomHeader()));

3.4          Helper Class for Creation of SubmitObjectsRequest

package dk.nsi.ddsregistry.ws;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import oasis.names.tc.ebxml_regrep.xsd.lcm._3.ObjectFactory;
import oasis.names.tc.ebxml_regrep.xsd.lcm._3.SubmitObjectsRequest;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.AssociationType1;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.ClassificationType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.ExternalIdentifierType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.ExtrinsicObjectType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.LocalizedStringType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.RegistryPackageType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.SlotType1;

public class SubmitObjectsRequestHelper {

     public SubmitObjectsRequest create() {
          ObjectFactory objFactory = new ObjectFactory();
          return objFactory.createSubmitObjectsRequest();
     }

     public ExtrinsicObjectType addOnDemandDocumentMetadataEntry(
              SubmitObjectsRequest request,
              String documentId,
              String repositoryUniqueId) {
          ExtrinsicObjectType onDemandDocumentMetadata =
                   createOnDemandDocumentMetadataEntry(
                                   UUID.randomUUID().toString(),
                                   repositoryUniqueId);
          oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory objFactory =
                   new oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory();
          if (request.getRegistryObjectList() == null) {
              request.setRegistryObjectList(objFactory.createRegistryObjectListType());
          }
          request.getRegistryObjectList().getIdentifiable().add(
                   objFactory.createExtrinsicObject(onDemandDocumentMetadata));


          ExternalIdentifierType uniqueIdIdentifier =
                   createUniqueIdExternalIdentifierType(documentId, onDemandDocumentMetadata);
          onDemandDocumentMetadata.getExternalIdentifier().add(uniqueIdIdentifier);

          return onDemandDocumentMetadata;
     }


     public void addDocumentMetadataAttributes(ExtrinsicObjectType documentMetadataEntry,
              String authorInstitution, String patientId) {
          oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory objFactory =
                   new oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory();
          ClassificationType classification = objFactory .createClassificationType();
          classification.setId(UUID.randomUUID().toString());
          classification.setClassificationScheme("urn:uuid:93606bcf-9494-43ec-9b4e-a7748d1a838d");
          classification.setClassifiedObject(documentMetadataEntry.getId());
          SlotType1 authorInstitutionSlot = objFactory.createSlotType1();
          authorInstitutionSlot.setName("authorInstitution");
          authorInstitutionSlot.setValueList(objFactory.createValueListType());
          authorInstitutionSlot.getValueList().getValue().add(authorInstitution);
          classification.getSlot().add(authorInstitutionSlot);
          documentMetadataEntry.getClassification().add(classification);


          ExternalIdentifierType patientIdIdentifier = objFactory.createExternalIdentifierType();
          patientIdIdentifier.setId(UUID.randomUUID().toString());
          patientIdIdentifier.setIdentificationScheme("urn:uuid:58a6f841-87b3-4a3e-92fd-a8ffeff98427");
          patientIdIdentifier.setRegistryObject(documentMetadataEntry.getId());
          patientIdIdentifier.setValue(patientId);
          documentMetadataEntry.getExternalIdentifier().add(patientIdIdentifier);
     }


     public RegistryPackageType addSubmissionSet(SubmitObjectsRequest request) {
          oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory objFactory =
                   new oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory();
          RegistryPackageType submissionSetEntry = objFactory.createRegistryPackageType();
          submissionSetEntry.setId(UUID.randomUUID().toString());
          submissionSetEntry.setObjectType("urn:oasis:names:tc:ebxml-regrep:ObjectType:RegistryObject:RegistryPackage");
          request.getRegistryObjectList().getIdentifiable().add(objFactory.createRegistryPackage(submissionSetEntry));


          // Classify registry package as XDSSubmissionSet (outside the RegistryPackage)
          ClassificationType classification = objFactory.createClassificationType();
          classification.setClassificationNode("urn:uuid:a54d6aa5-d40d-43f9-88c5-b4633d873bdd"); // XDSSubmissionSet node value
          classification.setId(UUID.randomUUID().toString());
          classification.setObjectType("urn:oasis:names:tc:ebxml-regrep:ObjectType:RegistryObject:Classification");
          classification.setClassifiedObject(submissionSetEntry.getId());
          request.getRegistryObjectList().getIdentifiable().add(objFactory.createClassification(classification));


          return submissionSetEntry;
     }


     public void addSubmissionSetAttributes(RegistryPackageType submissionSetEntry, String patientId) {
          oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory objFactory =
                   new oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory();
          ExternalIdentifierType patientIdIdentifier = objFactory.createExternalIdentifierType();
          patientIdIdentifier.setId(UUID.randomUUID().toString());
          patientIdIdentifier.setObjectType("urn:oasis:names:tc:ebxml-regrep:ObjectType:RegistryObject:ExternalIdentifier");
          patientIdIdentifier.setIdentificationScheme("urn:uuid:6b5aea1a-874d-4603-a4bc-96a0a7b38446");
          patientIdIdentifier.setRegistryObject(submissionSetEntry.getId());
          patientIdIdentifier.setValue(patientId);
          submissionSetEntry.getExternalIdentifier().add(patientIdIdentifier);
     }


     public AssociationType1 addAssociation(SubmitObjectsRequest request,
              ExtrinsicObjectType documentMetadataEntry,
              RegistryPackageType submissionSetEntry) {
          oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory objFactory =
                   new oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory();
          AssociationType1 association = objFactory.createAssociationType1();
          association.setId(UUID.randomUUID().toString());
          association.setAssociationType("urn:oasis:names:tc:ebxml-regrep:AssociationType:HasMember");
          association.setObjectType("urn:oasis:names:tc:ebxml-regrep:ObjectType:RegistryObject:Association");
          association.setSourceObject(submissionSetEntry.getId());
          association.setTargetObject(documentMetadataEntry.getId());
          request.getRegistryObjectList().getIdentifiable().add(objFactory.createAssociation(association));
          return association;
     }


     public List<AssociationType1> addAssociationsForReplacements(
              SubmitObjectsRequest request,
              ExtrinsicObjectType documentMetadataEntry,
              List<String> extrinsicObjectIdsReplaced) {
          List<AssociationType1> result = new ArrayList<AssociationType1>();
          oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory objFactory =
                   new oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory();
          for (String extrinsicObjectId : extrinsicObjectIdsReplaced) {
              AssociationType1 association = objFactory.createAssociationType1();
              association.setId(UUID.randomUUID().toString());
              association.setAssociationType("urn:ihe:iti:2007:AssociationType:RPLC");
              association.setObjectType("urn:oasis:names:tc:ebxml-regrep:ObjectType:RegistryObject:Association");
              association.setSourceObject(documentMetadataEntry.getId());
              association.setTargetObject(extrinsicObjectId);
              request.getRegistryObjectList().getIdentifiable().add(objFactory.createAssociation(association));
              result.add(association);
          }
          return result;
     }

     private ExternalIdentifierType createUniqueIdExternalIdentifierType(
              String documentId, ExtrinsicObjectType metadataObjId) {
          oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory objFactory =
                   new oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory();
          ExternalIdentifierType uniqueIdIdentifier = objFactory.createExternalIdentifierType();
          uniqueIdIdentifier.setId(UUID.randomUUID().toString());
          uniqueIdIdentifier.setIdentificationScheme(XDSConstantsHelper.XDS_EXTERNAL_IDENTIFIER_UNIQUE_ID_IDENTIFICATION_SCHEME);
          uniqueIdIdentifier.setRegistryObject(metadataObjId.getId());
          uniqueIdIdentifier.setName(objFactory.createInternationalStringType());
          LocalizedStringType name = objFactory.createLocalizedStringType();
          name.setValue("XDSDocumentEntry.uniqueId");
          uniqueIdIdentifier.getName().getLocalizedString().add(name);
          uniqueIdIdentifier.setValue(documentId);
          return uniqueIdIdentifier;
     }


     private ExtrinsicObjectType createOnDemandDocumentMetadataEntry(
              String documentId,
              String repositoryUniqueId) {
          return createDocumentMetadataEntry(
                   documentId,
                   XDSConstantsHelper.XDS_ONDEMANDDOCUMENT_OBJ_TYPE,
                   repositoryUniqueId);
     }

     private ExtrinsicObjectType createStableDocumentMetadataEntry(
              String documentId,
              String repositoryUniqueId) {
          return createDocumentMetadataEntry(
                   documentId,
                   XDSConstantsHelper.XDS_STABLEDOCUMENT_OBJ_TYPE,
                   repositoryUniqueId);
     }


     private ExtrinsicObjectType createDocumentMetadataEntry(
              String documentId,
              String documentObjType,
              String repositoryUniqueId) {
          oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory objFactory =
                   new oasis.names.tc.ebxml_regrep.xsd.rim._3.ObjectFactory();
          ExtrinsicObjectType extrinsicObjectType = objFactory.createExtrinsicObjectType();
          extrinsicObjectType.setId(documentId);
          extrinsicObjectTfype.setMimeType("text/xml");
          extrinsicObjectType.setObjectType(documentObjType);


          SlotType1 repositoryUniqueIdSlot = objFactory.createSlotType1();
          repositoryUniqueIdSlot.setName("repositoryUniqueId");
          repositoryUniqueIdSlot.setValueList(objFactory.createValueListType());
          repositoryUniqueIdSlot.getValueList().getValue().add(repositoryUniqueId);
          extrinsicObjectType.getSlot().add(repositoryUniqueIdSlot );
          return extrinsicObjectType;
     }
}


3.5          Client Generation from WSDL

A Web service client can be generated based on the DDS Registry WSDL-file, see [DDS Registry Registering Interface]. It is not given that the Web service client’s proxy classes can be generated based on a deployed service (using ?wsdl-extension on the service), as such calls are not necessarily supported.

  • No labels