org.openhim.mediator.engine.connectors.MLLPConnector.java Source code

Java tutorial

Introduction

Here is the source code for org.openhim.mediator.engine.connectors.MLLPConnector.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/.
 */

/*
 * 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.engine.connectors;

import akka.actor.UntypedActor;
import akka.dispatch.OnComplete;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import org.apache.commons.io.IOUtils;
import org.openhim.mediator.engine.CoreResponse;
import org.openhim.mediator.engine.messages.AddOrchestrationToCoreResponse;
import org.openhim.mediator.engine.messages.ExceptError;
import org.openhim.mediator.engine.messages.MediatorSocketRequest;
import org.openhim.mediator.engine.messages.MediatorSocketResponse;
import scala.concurrent.ExecutionContext;
import scala.concurrent.Future;

import javax.net.ssl.SSLSocketFactory;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.concurrent.Callable;

import static akka.dispatch.Futures.future;

/**
 * An actor that provides functionality for connecting to TCP services using the MLLP protocol.
 * <br/><br/>
 * Supports the following messages:
 * <ul>
 * <li>MediatorSocketRequest - responds with MediatorSocketResponse</li>
 * </ul>
 */
public class MLLPConnector extends UntypedActor {
    public static final char MLLP_HEADER_VT = '\013';
    public static final char MLLP_FOOTER_FS = '\034';
    public static final char MLLP_FOOTER_CR = '\r';

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

    public static String wrapMLLP(String s) {
        return MLLPConnector.MLLP_HEADER_VT + s + MLLPConnector.MLLP_FOOTER_FS + MLLPConnector.MLLP_FOOTER_CR;
    }

    public static boolean isMLLPWrapped(String s) {
        return s != null && s.length() >= 3 && s.charAt(0) == MLLP_HEADER_VT
                && s.charAt(s.length() - 2) == MLLP_FOOTER_FS && s.charAt(s.length() - 1) == MLLP_FOOTER_CR;
    }

    private CoreResponse.Orchestration buildOrchestration(MediatorSocketRequest req, MediatorSocketResponse resp) {
        CoreResponse.Orchestration orch = new CoreResponse.Orchestration();
        orch.setName(req.getOrchestration());

        CoreResponse.Request orchReq = new CoreResponse.Request();
        orchReq.setBody(wrapMLLP(req.getBody()));
        orch.setRequest(orchReq);

        CoreResponse.Response orchResp = new CoreResponse.Response();
        orchResp.setBody(wrapMLLP(resp.getBody()));
        orch.setResponse(orchResp);

        return orch;
    }

    private static String readMLLPStream(InputStream in) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024 * 1024);
        int lastByte = -1;
        int lastLastByte;
        do {
            lastLastByte = lastByte;
            lastByte = in.read();
            if (lastByte != -1) {
                buffer.write(lastByte);
            }
        } while (lastByte != -1 && (lastLastByte != MLLP_FOOTER_FS || lastByte != MLLP_FOOTER_CR));

        return buffer.toString();
    }

    private Socket getSocket(final MediatorSocketRequest req) throws IOException {
        if (req.isSecure()) {
            SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            return factory.createSocket(req.getHost(), req.getPort());
        } else {
            return new Socket(req.getHost(), req.getPort());
        }
    }

    private void sendRequest(final MediatorSocketRequest req) {
        try {
            final Socket socket = getSocket(req);

            ExecutionContext ec = getContext().dispatcher();
            Future<String> f = future(new Callable<String>() {
                public String call() throws IOException {
                    DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                    out.writeBytes(wrapMLLP(req.getBody()));

                    String result = readMLLPStream(socket.getInputStream());
                    if (isMLLPWrapped(result)) {
                        result = result.substring(1).substring(0, result.length() - 3);
                    } else {
                        log.warning("Response from server is not valid MLLP");
                    }

                    return result;
                }
            }, ec);
            f.onComplete(new OnComplete<String>() {
                @Override
                public void onComplete(Throwable throwable, String result) throws Throwable {
                    try {
                        if (throwable != null) {
                            throw throwable;
                        }

                        MediatorSocketResponse response = new MediatorSocketResponse(req, result);
                        req.getRespondTo().tell(response, getSelf());

                        //enrich engine response
                        CoreResponse.Orchestration orch = buildOrchestration(req, response);
                        req.getRequestHandler().tell(new AddOrchestrationToCoreResponse(orch), getSelf());
                    } catch (Exception ex) {
                        req.getRequestHandler().tell(new ExceptError(ex), getSelf());
                    } finally {
                        IOUtils.closeQuietly(socket);
                    }
                }
            }, ec);
        } catch (IOException | UnsupportedOperationException ex) {
            req.getRequestHandler().tell(new ExceptError(ex), getSelf());
        }
    }

    @Override
    public void onReceive(Object msg) throws Exception {
        if (msg instanceof MediatorSocketRequest) {
            sendRequest((MediatorSocketRequest) msg);
        } else {
            unhandled(msg);
        }
    }
}