package gov.samhsa.acs.documentsegmentation;
import gov.samhsa.acs.brms.RuleExecutionServiceImpl;
import gov.samhsa.acs.brms.domain.FactModel;
import gov.samhsa.acs.brms.domain.RuleExecutionContainer;
import gov.samhsa.acs.brms.domain.XacmlResult;
import gov.samhsa.acs.brms.guvnor.GuvnorServiceImpl;
import gov.samhsa.acs.common.tool.DocumentAccessorImpl;
import gov.samhsa.acs.common.tool.DocumentXmlConverterImpl;
import gov.samhsa.acs.common.tool.FileReaderImpl;
import gov.samhsa.acs.common.tool.SimpleMarshallerImpl;
import gov.samhsa.acs.common.tool.XmlTransformer;
import gov.samhsa.acs.common.tool.XmlTransformerImpl;
import gov.samhsa.acs.common.util.EncryptTool;
import gov.samhsa.acs.common.util.FileHelper;
import gov.samhsa.acs.common.validation.exception.XmlDocumentReadFailureException;
import gov.samhsa.acs.documentsegmentation.dto.SegmentDocumentResponse;
import gov.samhsa.acs.documentsegmentation.exception.InvalidOriginalClinicalDocumentException;
import gov.samhsa.acs.documentsegmentation.exception.InvalidSegmentedClinicalDocumentException;
import gov.samhsa.acs.documentsegmentation.tools.AdditionalMetadataGeneratorForSegmentedClinicalDocumentImpl;
import gov.samhsa.acs.documentsegmentation.tools.DocumentEditorImpl;
import gov.samhsa.acs.documentsegmentation.tools.DocumentEncrypterImpl;
import gov.samhsa.acs.documentsegmentation.tools.DocumentFactModelExtractorImpl;
import gov.samhsa.acs.documentsegmentation.tools.DocumentRedactor;
import gov.samhsa.acs.documentsegmentation.tools.DocumentRedactorImpl;
import gov.samhsa.acs.documentsegmentation.tools.DocumentTaggerImpl;
import gov.samhsa.acs.documentsegmentation.tools.EmbeddedClinicalDocumentExtractorImpl;
import gov.samhsa.acs.documentsegmentation.tools.MetadataGeneratorImpl;
import gov.samhsa.acs.documentsegmentation.tools.redact.base.AbstractClinicalFactLevelRedactionHandler;
import gov.samhsa.acs.documentsegmentation.tools.redact.base.AbstractDocumentLevelRedactionHandler;
import gov.samhsa.acs.documentsegmentation.tools.redact.base.AbstractObligationLevelRedactionHandler;
import gov.samhsa.acs.documentsegmentation.tools.redact.base.AbstractPostRedactionLevelRedactionHandler;
import gov.samhsa.acs.documentsegmentation.tools.redact.impl.clinicalfactlevel.Entry;
import gov.samhsa.acs.documentsegmentation.tools.redact.impl.clinicalfactlevel.HumanReadableTextNodeByCode;
import gov.samhsa.acs.documentsegmentation.tools.redact.impl.clinicalfactlevel.HumanReadableTextNodeByDisplayName;
import gov.samhsa.acs.documentsegmentation.tools.redact.impl.documentlevel.UnsupportedHeaderElementHandler;
import gov.samhsa.acs.documentsegmentation.tools.redact.impl.obligationlevel.Section;
import gov.samhsa.acs.documentsegmentation.tools.redact.impl.postredactionlevel.DocumentCleanupForNoEntryAndNoSection;
import gov.samhsa.acs.documentsegmentation.tools.redact.impl.postredactionlevel.RuleExecutionResponseMarkerForRedactedEntries;
import gov.samhsa.acs.documentsegmentation.valueset.ValueSetServiceImplMock;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.Key;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.utils.EncryptionConstants;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import ch.qos.logback.audit.AuditException;
public class DocumentSegmentationImplIT {
public static final Set<String> unsupportedHeaders = new HashSet<String>(
Arrays.asList("realmCode", "custodian"));
private final static String DISCLAIMER_DOCUMENT = "<disclaimerText><text>This is a disclaimer text</text></disclaimerText>";
private static String xacmlResult;
private static String ruleExecutionResponseContainer;
private static String c32Document;
private static XacmlResult xacmlResultObject;
private static String endpointAddressGuvnorService;
private static RuleExecutionServiceImpl ruleExecutionService;
private static SimpleMarshallerImpl marshaller;
private static FileReaderImpl fileReader;
private static DocumentXmlConverterImpl documentXmlConverter;
private static MetadataGeneratorImpl metadataGenerator;
private static DocumentEditorImpl documentEditor;
private static DocumentTaggerImpl documentTagger;
private static DocumentEncrypterImpl documentEncrypter;
private static DocumentRedactor documentRedactor;
private static DocumentFactModelExtractorImpl documentFactModelExtractor;
private static DocumentAccessorImpl documentAccessor;
private static EmbeddedClinicalDocumentExtractorImpl embeddedClinicalDocumentExtractor;
private static AdditionalMetadataGeneratorForSegmentedClinicalDocumentImpl additionalMetadataGeneratorForSegmentedClinicalDocumentImpl;
private static XmlTransformer xmlTransformer;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Before
public void setUp() throws IOException {
fileReader = new FileReaderImpl();
documentXmlConverter = new DocumentXmlConverterImpl();
marshaller = new SimpleMarshallerImpl();
documentEncrypter = new DocumentEncrypterImpl(documentXmlConverter);
documentAccessor = new DocumentAccessorImpl();
xmlTransformer = new XmlTransformerImpl(marshaller);
metadataGenerator = new MetadataGeneratorImpl(xmlTransformer);
documentEditor = new DocumentEditorImpl(metadataGenerator, fileReader,
documentXmlConverter, new DocumentAccessorImpl());
documentTagger = new DocumentTaggerImpl(DISCLAIMER_DOCUMENT,
xmlTransformer);
final Set<AbstractObligationLevelRedactionHandler> obligationLevelChain = new HashSet<AbstractObligationLevelRedactionHandler>();
final Set<AbstractClinicalFactLevelRedactionHandler> clinicalFactLevelChain = new HashSet<AbstractClinicalFactLevelRedactionHandler>();
final Set<AbstractPostRedactionLevelRedactionHandler> postRedactionChain = new HashSet<AbstractPostRedactionLevelRedactionHandler>();
final Set<AbstractDocumentLevelRedactionHandler> documentLevelRedactionHandlers = new HashSet<AbstractDocumentLevelRedactionHandler>();
documentLevelRedactionHandlers.add(new UnsupportedHeaderElementHandler(
documentAccessor, unsupportedHeaders));
obligationLevelChain.add(new Section(documentAccessor));
clinicalFactLevelChain.add(new Entry(documentAccessor));
clinicalFactLevelChain.add(new HumanReadableTextNodeByCode(
documentAccessor));
clinicalFactLevelChain.add(new HumanReadableTextNodeByDisplayName(
documentAccessor));
postRedactionChain.add(new DocumentCleanupForNoEntryAndNoSection(
documentAccessor));
postRedactionChain
.add(new RuleExecutionResponseMarkerForRedactedEntries(
documentAccessor));
documentRedactor = new DocumentRedactorImpl(marshaller,
documentXmlConverter, documentAccessor,
documentLevelRedactionHandlers, obligationLevelChain,
clinicalFactLevelChain, postRedactionChain);
documentFactModelExtractor = new DocumentFactModelExtractorImpl(
xmlTransformer);
additionalMetadataGeneratorForSegmentedClinicalDocumentImpl = new AdditionalMetadataGeneratorForSegmentedClinicalDocumentImpl(
xmlTransformer);
embeddedClinicalDocumentExtractor = new EmbeddedClinicalDocumentExtractorImpl(
documentXmlConverter, documentAccessor);
c32Document = fileReader.readFile("sampleC32/c32.xml");
xacmlResult = "<xacmlResult><pdpDecision>Permit</pdpDecision><purposeOfUse>TREAT</purposeOfUse><messageId>4617a579-1881-4e40-9f98-f85bd81d6502</messageId><homeCommunityId>2.16.840.1.113883.3.467</homeCommunityId><pdpObligation>urn:oasis:names:tc:xspa:2.0:resource:org:us-privacy-law:42CFRPart2</pdpObligation><pdpObligation>urn:oasis:names:tc:xspa:2.0:resource:org:refrain-policy:NORDSLCD</pdpObligation><pdpObligation>urn:oasis:names:tc:xspa:2.0:resource:patient:redact:ETH</pdpObligation><pdpObligation>urn:oasis:names:tc:xspa:2.0:resource:patient:redact:PSY</pdpObligation><pdpObligation>urn:oasis:names:tc:xspa:2.0:resource:patient:mask:HIV</pdpObligation></xacmlResult>";
ruleExecutionResponseContainer = "<ruleExecutionContainer><executionResponseList><executionResponse><c32SectionLoincCode>11450-4</c32SectionLoincCode><c32SectionTitle>Problems</c32SectionTitle><code>66214007</code><codeSystemName>SNOMED CT</codeSystemName><displayName>Substance Abuse Disorder</displayName><documentObligationPolicy>ENCRYPT</documentObligationPolicy><documentRefrainPolicy>NORDSLCD</documentRefrainPolicy><impliedConfSection>R</impliedConfSection><itemAction>REDACT</itemAction><observationId>e11275e7-67ae-11db-bd13-0800200c9a66b827vs52h7</observationId><sensitivity>ETH</sensitivity><USPrivacyLaw>42CFRPart2</USPrivacyLaw></executionResponse><executionResponse><c32SectionLoincCode>11450-4</c32SectionLoincCode><c32SectionTitle>Problems</c32SectionTitle><code>111880001</code><codeSystemName>SNOMED CT</codeSystemName><displayName>Acute HIV</displayName><documentObligationPolicy>ENCRYPT</documentObligationPolicy><documentRefrainPolicy>NORDSLCD</documentRefrainPolicy><impliedConfSection>R</impliedConfSection><itemAction>MASK</itemAction><observationId>d11275e7-67ae-11db-bd13-0800200c9a66</observationId><sensitivity>HIV</sensitivity><USPrivacyLaw>42CFRPart2</USPrivacyLaw></executionResponse></executionResponseList></ruleExecutionContainer>";
endpointAddressGuvnorService = "http://localhost:7070/guvnor-5.5.0.Final-tomcat-6.0/rest/packages/AnnotationRules/source";
ruleExecutionService = new RuleExecutionServiceImpl(
new GuvnorServiceImpl(endpointAddressGuvnorService, "admin",
"admin"), new SimpleMarshallerImpl());
try {
xacmlResultObject = marshaller.unmarshalFromXml(XacmlResult.class,
xacmlResult);
} catch (final JAXBException e) {
logger.error(e.getMessage(), e);
}
}
// Integration test
@Test
public void testSegmentDocument_Decrypt_Document() {
RuleExecutionContainer ruleExecutionContainer = null;
String document = "";
try {
final JAXBContext jaxbContext = JAXBContext
.newInstance(RuleExecutionContainer.class);
final Unmarshaller jaxbUnmarshaller = jaxbContext
.createUnmarshaller();
final ByteArrayInputStream input = new ByteArrayInputStream(
ruleExecutionResponseContainer.getBytes());
ruleExecutionContainer = (RuleExecutionContainer) jaxbUnmarshaller
.unmarshal(input);
final FactModel factModel = new FactModel();
factModel.setXacmlResult(xacmlResultObject);
document = documentRedactor.redactDocument(c32Document,
ruleExecutionContainer, factModel).getRedactedDocument();
Document processedDoc = documentXmlConverter.loadDocument(document);
FileHelper
.writeDocToFile(processedDoc, "unitTest_Redacted_C32.xml");
// commented out for redact-only application
// document = documentSegmentation.maskElement(document,
// ruleExecutionContainer, xacmlResultObject);
processedDoc = documentXmlConverter.loadDocument(document);
FileHelper.writeDocToFile(processedDoc, "unitTest_Masked_C32.xml");
/*
* Generate the key to be used for decrypting the xml data
* encryption key.
*/
final Key desedeEncryptKey = EncryptTool.generateKeyEncryptionKey();
final Key desedeMaskKey = EncryptTool.generateKeyEncryptionKey();
document = documentEncrypter.encryptDocument(desedeEncryptKey,
document, ruleExecutionContainer);
processedDoc = documentXmlConverter.loadDocument(document);
FileHelper.writeDocToFile(processedDoc,
"unitTest_Encrypted_C32.xml");
/*************************************************
* DECRYPT DOCUMENT
*************************************************/
final Element encryptedDataElement = (Element) processedDoc
.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTEDDATA).item(0);
/*
* The key to be used for decrypting xml data would be obtained from
* the keyinfo of the EncrypteData using the kek.
*/
final XMLCipher xmlCipher = XMLCipher.getInstance();
xmlCipher.init(XMLCipher.DECRYPT_MODE, null);
xmlCipher.setKEK(desedeEncryptKey);
/*
* The following doFinal call replaces the encrypted data with
* decrypted contents in the document.
*/
if (encryptedDataElement != null) {
xmlCipher.doFinal(processedDoc, encryptedDataElement);
}
FileHelper.writeDocToFile(processedDoc,
"unitTest_Decrypted_C32.xml");
/*************************************************
* DECRYPT ELEMENTS
*************************************************/
NodeList encryptedDataElements = processedDoc
.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTEDDATA);
while (encryptedDataElements.getLength() > 0) {
/*
* The key to be used for decrypting xml data would be obtained
* from the keyinfo of the EncrypteData using the kek.
*/
final XMLCipher xmlMaskCipher = XMLCipher.getInstance();
xmlMaskCipher.init(XMLCipher.DECRYPT_MODE, null);
xmlMaskCipher.setKEK(desedeMaskKey);
xmlMaskCipher.doFinal(processedDoc,
(Element) encryptedDataElements.item(0));
encryptedDataElements = processedDoc.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTEDDATA);
}
FileHelper.writeDocToFile(processedDoc,
"unitTest_DecryptedUnMasked_C32.xml");
} catch (final Exception e) {
logger.error(e.getMessage(), e);
}
Assert.assertNotSame(document, c32Document);
}
// Integration test
@Test
public void testSegmentDocument_Segment_Document()
throws InvalidOriginalClinicalDocumentException,
InvalidSegmentedClinicalDocumentException,
XmlDocumentReadFailureException, AuditException {
final DocumentSegmentationImpl documentSegmentation = new DocumentSegmentationImpl(
ruleExecutionService, null, documentEditor, marshaller,
documentRedactor, documentTagger, documentFactModelExtractor,
embeddedClinicalDocumentExtractor, new ValueSetServiceImplMock(
fileReader),
additionalMetadataGeneratorForSegmentedClinicalDocumentImpl);
final SegmentDocumentResponse result = documentSegmentation
.segmentDocument(c32Document.toString(), xacmlResult, false,
true, true);
Assert.assertNotNull(result);
}
}