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

Java tutorial

Introduction

Here is the source code for org.openhim.mediator.engine.connectors.HTTPConnector.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.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.apache.http.Header;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.openhim.mediator.engine.CoreResponse;
import org.openhim.mediator.engine.MediatorRequestHandler;
import org.openhim.mediator.engine.messages.*;
import scala.concurrent.ExecutionContext;
import scala.concurrent.Future;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Callable;

import static akka.dispatch.Futures.future;

/**
 * An actor that provides functionality for connecting to HTTP services.
 * <br/><br/>
 * Supports the following messages:
 * <ul>
 * <li>MediatorHTTPRequest - responds with MediatorHTTPResponse</li>
 * <li>SetupHTTPSCertificate - no response</li>
 * </ul>
 */
public class HTTPConnector extends UntypedActor {

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

    private SSLContext sslContext;

    private void copyHeaders(MediatorHTTPRequest src, HttpUriRequest dst) {
        if (src.getHeaders() != null) {
            Iterator<String> iter = src.getHeaders().keySet().iterator();
            while (iter.hasNext()) {
                String header = iter.next();
                dst.addHeader(header, src.getHeaders().get(header));
            }
        }
    }

    private URI buildURI(MediatorHTTPRequest req) throws URISyntaxException {
        URIBuilder builder = new URIBuilder().setScheme(req.getScheme()).setHost(req.getHost())
                .setPort(req.getPort()).setPath(req.getPath());
        if (req.getParams() != null) {
            Iterator<String> iter = req.getParams().keySet().iterator();
            while (iter.hasNext()) {
                String param = iter.next();
                builder.addParameter(param, req.getParams().get(param));
            }
        }
        return builder.build();
    }

    private HttpUriRequest buildApacheHttpRequest(MediatorHTTPRequest req)
            throws URISyntaxException, UnsupportedEncodingException {
        HttpUriRequest uriReq;

        switch (req.getMethod()) {
        case "GET":
            uriReq = new HttpGet(buildURI(req));
            break;
        case "POST":
            uriReq = new HttpPost(buildURI(req));
            StringEntity entity = new StringEntity(req.getBody());
            ((HttpPost) uriReq).setEntity(entity);
            break;
        case "PUT":
            uriReq = new HttpPut(buildURI(req));
            StringEntity putEntity = new StringEntity(req.getBody());
            ((HttpPut) uriReq).setEntity(putEntity);
            break;
        case "DELETE":
            uriReq = new HttpDelete(buildURI(req));
            break;
        default:
            throw new UnsupportedOperationException(req.getMethod() + " requests not supported");
        }

        copyHeaders(req, uriReq);
        return uriReq;
    }

    private MediatorHTTPResponse buildResponseFromOpenHIMJSONContent(MediatorHTTPRequest req,
            CloseableHttpResponse apacheResponse) throws IOException, CoreResponse.ParseException {
        String content = IOUtils.toString(apacheResponse.getEntity().getContent());
        CoreResponse parsedContent = CoreResponse.parse(content);
        if (parsedContent.getResponse() == null) {
            throw new CoreResponse.ParseException(
                    new Exception("No response object found in application/json+openhim content"));
        }

        int status = apacheResponse.getStatusLine().getStatusCode();
        if (parsedContent.getResponse().getStatus() != null) {
            status = parsedContent.getResponse().getStatus();
        }

        Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
        for (Header hdr : apacheResponse.getAllHeaders()) {
            headers.put(hdr.getName(), hdr.getValue());
        }

        if (parsedContent.getResponse().getHeaders() != null) {
            for (String hdr : parsedContent.getResponse().getHeaders().keySet()) {
                headers.put(hdr, parsedContent.getResponse().getHeaders().get(hdr));
            }
        }

        if (parsedContent.getOrchestrations() != null) {
            for (CoreResponse.Orchestration orch : parsedContent.getOrchestrations()) {
                req.getRequestHandler().tell(new AddOrchestrationToCoreResponse(orch), getSelf());
            }
        }

        if (parsedContent.getProperties() != null) {
            for (String prop : parsedContent.getProperties().keySet()) {
                req.getRequestHandler().tell(
                        new PutPropertyInCoreResponse(prop, parsedContent.getProperties().get(prop)), getSelf());
            }
        }

        return new MediatorHTTPResponse(req, parsedContent.getResponse().getBody(), status, headers);
    }

    private MediatorHTTPResponse buildResponseFromContent(MediatorHTTPRequest req,
            CloseableHttpResponse apacheResponse) throws IOException {
        String content = null;
        if (apacheResponse.getEntity() != null && apacheResponse.getEntity().getContent() != null) {
            content = IOUtils.toString(apacheResponse.getEntity().getContent());
        }

        int status = apacheResponse.getStatusLine().getStatusCode();

        Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
        for (Header hdr : apacheResponse.getAllHeaders()) {
            headers.put(hdr.getName(), hdr.getValue());
        }

        return new MediatorHTTPResponse(req, content, status, headers);
    }

    private String getContentType(CloseableHttpResponse response) {
        if (response.getAllHeaders() == null) {
            return null;
        }

        for (Header hdr : response.getAllHeaders()) {
            if ("Content-Type".equalsIgnoreCase(hdr.getName())) {
                return hdr.getValue();
            }
        }

        return null;
    }

    private MediatorHTTPResponse buildResponse(MediatorHTTPRequest req, CloseableHttpResponse apacheResponse)
            throws IOException, CoreResponse.ParseException {
        String contentType = getContentType(apacheResponse);

        if (contentType != null && contentType.contains(MediatorRequestHandler.OPENHIM_MIME_TYPE)) {
            return buildResponseFromOpenHIMJSONContent(req, apacheResponse);
        } else {
            return buildResponseFromContent(req, apacheResponse);
        }
    }

    private CoreResponse.Orchestration buildHTTPOrchestration(MediatorHTTPRequest req, MediatorHTTPResponse resp) {
        CoreResponse.Orchestration orch = new CoreResponse.Orchestration();
        orch.setName(req.getOrchestration());

        CoreResponse.Request orchReq = new CoreResponse.Request();
        orchReq.setBody(req.getBody());
        orchReq.setPath(req.getPath());
        orchReq.setMethod(req.getMethod());
        orchReq.setHeaders(req.getHeaders());
        orch.setRequest(orchReq);

        CoreResponse.Response orchResp = new CoreResponse.Response();
        orchResp.setBody(resp.getBody());
        orchResp.setStatus(resp.getStatusCode());
        orchResp.setHeaders(resp.getHeaders());
        orch.setResponse(orchResp);

        return orch;
    }

    private CloseableHttpClient getHttpClient(final MediatorHTTPRequest req) {
        if (sslContext != null && "https".equalsIgnoreCase(req.getScheme())) {
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } else {
            return HttpClients.createDefault();
        }
    }

    private void sendRequest(final MediatorHTTPRequest req) {
        try {
            final CloseableHttpClient client = getHttpClient(req);
            final HttpUriRequest apacheRequest = buildApacheHttpRequest(req);

            final ExecutionContext ec = getContext().dispatcher();
            Future<CloseableHttpResponse> f = future(new Callable<CloseableHttpResponse>() {
                public CloseableHttpResponse call() throws IOException {
                    return client.execute(apacheRequest);
                }
            }, ec);
            f.onComplete(new OnComplete<CloseableHttpResponse>() {
                @Override
                public void onComplete(Throwable throwable, CloseableHttpResponse result) throws Throwable {
                    try {
                        if (throwable != null) {
                            throw throwable;
                        }

                        //send response
                        MediatorHTTPResponse response = buildResponse(req, result);
                        req.getRespondTo().tell(response, getSelf());

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

    private void setupHTTPSCertificate(SetupHTTPSCertificate msg) {
        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            FileInputStream ksIn = new FileInputStream(new File(msg.getKeyStoreName()));
            ks.load(ksIn, msg.getKeyStorePassword().toCharArray());
            IOUtils.closeQuietly(ksIn);

            KeyStore ts = KeyStore.getInstance("JKS");
            FileInputStream tsIn = new FileInputStream(new File(msg.getTrustStoreName()));
            ts.load(tsIn, null);
            IOUtils.closeQuietly(tsIn);

            TrustStrategy strat = null;
            if (msg.getTrustSelfSigned()) {
                strat = new TrustSelfSignedStrategy();
            }

            sslContext = SSLContexts.custom().loadKeyMaterial(ks, msg.getKeyStorePassword().toCharArray())
                    .loadTrustMaterial(ts, strat).build();
        } catch (NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException | KeyStoreException
                | IOException | CertificateException ex) {
            log.error(ex, "Exception during processing of SetupHTTPSCertificate message");
            sslContext = null;
        }
    }

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