org.openhim.mediator.orchestration.RepositoryActor.java Source code

Java tutorial

Introduction

Here is the source code for org.openhim.mediator.orchestration.RepositoryActor.java

Source

/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

package org.openhim.mediator.orchestration;

import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
import org.openhim.mediator.denormalization.CSDRequestActor;
import org.openhim.mediator.denormalization.PIXRequestActor;
import org.openhim.mediator.engine.MediatorConfig;
import org.openhim.mediator.engine.messages.ExceptError;
import org.openhim.mediator.engine.messages.FinishRequest;
import org.openhim.mediator.engine.messages.MediatorHTTPRequest;
import org.openhim.mediator.engine.messages.MediatorHTTPResponse;
import org.openhim.mediator.messages.OrchestrateProvideAndRegisterRequest;
import org.openhim.mediator.messages.OrchestrateProvideAndRegisterRequestResponse;
import org.openhim.mediator.normalization.SOAPWrapper;
import org.openhim.mediator.normalization.XDSbMimeProcessorActor;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class RepositoryActor extends UntypedActor {

    LoggingAdapter log = Logging.getLogger(getContext().system(), this);

    private MediatorConfig config;
    private ActorRef mtomProcessor;

    private MediatorHTTPRequest originalRequest;

    private String action;
    private String messageID;
    private String xForwardedFor;
    private String mimeDocument;
    private String contentType;
    private boolean messageIsMTOM;

    private String messageBuffer;
    private SOAPWrapper soapWrapper;

    public RepositoryActor(MediatorConfig config) {
        this.config = config;
        mtomProcessor = getContext().actorOf(Props.create(XDSbMimeProcessorActor.class),
                "xds-multipart-normalization");
    }

    private void readMessage() {
        contentType = originalRequest.getHeaders().get("Content-Type");

        if (contentType != null
                && (contentType.contains("multipart/related") || contentType.contains("multipart/form-data"))) {
            log.info("Message is multipart. Parsing contents...");
            XDSbMimeProcessorActor.MimeMessage mimeMsg = new XDSbMimeProcessorActor.MimeMessage(
                    originalRequest.getRequestHandler(), getSelf(), originalRequest.getBody(), contentType);
            mtomProcessor.tell(mimeMsg, getSelf());
            messageIsMTOM = true;
        } else {
            messageBuffer = originalRequest.getBody();
            messageIsMTOM = false;
            triggerRepositoryAction();
        }
    }

    private void processMtomProcessorResponse(XDSbMimeProcessorActor.XDSbMimeProcessorResponse msg) {
        if (msg.getOriginalRequest() instanceof XDSbMimeProcessorActor.MimeMessage) {
            log.info("Successfully parsed multipart contents");
            messageBuffer = msg.getResponseObject();

            if (msg.getDocuments() != null && msg.getDocuments().size() > 0) {
                //TODO atm only a single document is handled
                //this is just used for 'autoRegister' and really only so that there is _some_ support for mtom.
                mimeDocument = msg.getDocuments().get(0);
            }

            triggerRepositoryAction();
        } else if (msg.getOriginalRequest() instanceof XDSbMimeProcessorActor.EnrichedMessage) {
            messageBuffer = msg.getResponseObject();
            forwardRequestToRepository();
        } else {
            unhandled(msg);
        }
    }

    private boolean determineSOAPAction() {
        try {
            readSOAPHeader();
            if (action == null || action.isEmpty()) {
                //not in soap header. maybe it's in the content-type?
                action = getSOAPActionFromContentType();

                if (action == null || action.isEmpty()) {
                    FinishRequest fr = new FinishRequest(
                            "Could not determine SOAP Action. Is the correct WS-Adressing header set?",
                            "text/plain", HttpStatus.SC_BAD_REQUEST);
                    originalRequest.getRespondTo().tell(fr, getSelf());
                    return false;
                }
            }

            action = action.trim();
            log.info("Action: " + action);
            return true;
        } catch (ParserConfigurationException | SAXException | XPathExpressionException | IOException ex) {
            originalRequest.getRequestHandler().tell(new ExceptError(ex), getSelf());
            return false;
        }
    }

    private void readSOAPHeader()
            throws ParserConfigurationException, IOException, SAXException, XPathExpressionException {
        DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = builder.parse(IOUtils.toInputStream(messageBuffer));
        XPath xpath = XPathFactory.newInstance().newXPath();
        action = xpath.compile("//Envelope/Header/Action").evaluate(doc);
        messageID = xpath.compile("//Envelope/Header/MessageID").evaluate(doc);
    }

    private String getSOAPActionFromContentType() {
        if (contentType == null) {
            return null;
        }

        int startI = contentType.indexOf("action=") + "action=\"".length();
        if (startI < 0) {
            return null;
        }

        String subStr = contentType.substring(startI);
        int endI = subStr.indexOf("\"");
        if (endI > -1) {
            return subStr.substring(0, endI);
        }
        return subStr;
    }

    private void processProviderAndRegisterAction() {
        ActorRef resolvePatientIDHandler = getContext().actorOf(Props.create(PIXRequestActor.class, config),
                "pix-denormalization");
        ActorRef resolveHealthcareWorkerIDHandler = getContext()
                .actorOf(Props.create(CSDRequestActor.class, config), "csd-denormalization");
        ActorRef resolveFacilityIDHandler = resolveHealthcareWorkerIDHandler;
        ActorRef pnrOrchestrator = getContext()
                .actorOf(
                        Props.create(ProvideAndRegisterOrchestrationActor.class, config, resolvePatientIDHandler,
                                resolveHealthcareWorkerIDHandler, resolveFacilityIDHandler),
                        "xds-pnr-orchestrator");

        try {
            soapWrapper = new SOAPWrapper(messageBuffer);
            OrchestrateProvideAndRegisterRequest msg = new OrchestrateProvideAndRegisterRequest(
                    originalRequest.getRequestHandler(), getSelf(), soapWrapper.getSoapBody(), xForwardedFor,
                    mimeDocument, messageID);
            pnrOrchestrator.tell(msg, getSelf());
        } catch (SOAPWrapper.SOAPParseException ex) {
            FinishRequest fr = new FinishRequest(ex.getMessage(), "text/plain", HttpStatus.SC_BAD_REQUEST);
            originalRequest.getRequestHandler().tell(fr, getSelf());
        }
    }

    private void processProvideAndRegisterResponse(OrchestrateProvideAndRegisterRequestResponse msg) {
        soapWrapper.setSoapBody(msg.getResponseObject());
        messageBuffer = soapWrapper.getFullDocument();

        if (messageIsMTOM) {
            XDSbMimeProcessorActor.EnrichedMessage mimeMsg = new XDSbMimeProcessorActor.EnrichedMessage(
                    originalRequest.getRequestHandler(), getSelf(), messageBuffer);
            mtomProcessor.tell(mimeMsg, getSelf());
        } else {
            forwardRequestToRepository();
        }
    }

    private void triggerRepositoryAction() {
        if (determineSOAPAction()) {
            if ("urn:ihe:iti:2007:ProvideAndRegisterDocumentSet-b".equals(action)) {
                processProviderAndRegisterAction();
            } else {
                messageBuffer = originalRequest.getBody();
                forwardRequestToRepository();
            }
        }
    }

    private void forwardRequestToRepository() {
        log.info("Forwarding request to repository");
        ActorSelection httpConnector = getContext().actorSelection(config.userPathFor("http-connector"));

        // Copy original content type
        String contentType = originalRequest.getHeaders().get("Content-Type");
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", contentType);

        String scheme;
        Integer port;
        if (config.getProperty("xds.repository.secure").equals("true")) {
            scheme = "https";
            port = Integer.parseInt(config.getProperty("xds.repository.securePort"));
        } else {
            scheme = "http";
            port = Integer.parseInt(config.getProperty("xds.repository.port"));
        }

        MediatorHTTPRequest request = new MediatorHTTPRequest(originalRequest.getRespondTo(), getSelf(),
                "XDS.b Repository", "POST", scheme, config.getProperty("xds.repository.host"), port,
                config.getProperty("xds.repository.path"), messageBuffer, headers, null);
        httpConnector.tell(request, getSelf());
    }

    private void finalizeResponse(MediatorHTTPResponse response) {
        originalRequest.getRespondTo().tell(response.toFinishRequest(), getSelf());
    }

    @Override
    public void onReceive(Object msg) throws Exception {
        if (msg instanceof MediatorHTTPRequest) {
            originalRequest = (MediatorHTTPRequest) msg;
            xForwardedFor = ((MediatorHTTPRequest) msg).getHeaders().get("X-Forwarded-For");
            readMessage();
        } else if (msg instanceof XDSbMimeProcessorActor.XDSbMimeProcessorResponse) {
            processMtomProcessorResponse((XDSbMimeProcessorActor.XDSbMimeProcessorResponse) msg);
        } else if (msg instanceof OrchestrateProvideAndRegisterRequestResponse) {
            processProvideAndRegisterResponse((OrchestrateProvideAndRegisterRequestResponse) msg);
        } else if (msg instanceof MediatorHTTPResponse) {
            finalizeResponse((MediatorHTTPResponse) msg);
        } else {
            unhandled(msg);
        }
    }
}