it.greenvulcano.gvesb.virtual.rest.RestCallOperation.java Source code

Java tutorial

Introduction

Here is the source code for it.greenvulcano.gvesb.virtual.rest.RestCallOperation.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2016 GreenVulcano ESB Open Source Project.
 * All rights reserved.
 *
 * This file is part of GreenVulcano ESB.
 *
 * GreenVulcano ESB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * GreenVulcano ESB is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with GreenVulcano ESB. If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package it.greenvulcano.gvesb.virtual.rest;

import it.greenvulcano.configuration.XMLConfig;
import it.greenvulcano.configuration.XMLConfigException;
import it.greenvulcano.gvesb.buffer.GVBuffer;
import it.greenvulcano.gvesb.internal.data.GVBufferPropertiesHelper;
import it.greenvulcano.gvesb.virtual.CallException;
import it.greenvulcano.gvesb.virtual.CallOperation;
import it.greenvulcano.gvesb.virtual.ConnectionException;
import it.greenvulcano.gvesb.virtual.InitializationException;
import it.greenvulcano.gvesb.virtual.InvalidDataException;
import it.greenvulcano.gvesb.virtual.OperationKey;
import it.greenvulcano.util.metadata.PropertiesHandler;

import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManagerFactory;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 
 * @version 4.0 29/april/2016
 * @author GreenVulcano Developer Team
 */
public class RestCallOperation implements CallOperation {

    private static final String RESPONSE_STATUS = "GVHTTP_RESPONSE_STATUS";
    private static final String RESPONSE_MESSAGE = "GVHTTP_RESPONSE_MESSAGE";
    private static final String RESPONSE_HEADER_PREFIX = "GVHTTP_RESPONSE_HEADER_";
    private static final Logger logger = org.slf4j.LoggerFactory.getLogger(RestCallOperation.class);
    private String name;
    private OperationKey key = null;
    private String url = null;
    private String method = null;

    private int connectionTimeout, readTimeout;

    private String truststorePath = null;
    private String truststorePassword = null;
    private String truststoreAlgorithm = null;

    private Map<String, String> headers = new LinkedHashMap<>();
    private Map<String, String> params = new LinkedHashMap<>();

    private String body;

    private boolean sendGVBufferObject = false;

    @Override
    public void init(Node node) throws InitializationException {
        logger.debug("Init start");
        try {
            name = XMLConfig.get(node, "@name");

            String host = XMLConfig.get(node.getParentNode(), "@endpoint");
            String uri = XMLConfig.get(node, "@request-uri");
            method = XMLConfig.get(node, "@method");
            url = host.concat(uri);
            connectionTimeout = XMLConfig.getInteger(node, "@conn-timeout", 3000);
            readTimeout = XMLConfig.getInteger(node, "@so-timeout", 6000);

            Node trustStore = XMLConfig.getNode(node.getParentNode(), "./truststore");
            if (Objects.nonNull(trustStore)) {
                truststorePath = XMLConfig.get(trustStore, "@path");
                truststorePassword = XMLConfig.getDecrypted(trustStore, "@password", null);
                truststoreAlgorithm = XMLConfig.get(trustStore, "@algorithm", null);
            }

            Node defaults = XMLConfig.getNode(node.getParentNode(), "./rest-call-defaults");
            if (Objects.nonNull(defaults)) {
                readRestCallConfiguration(defaults);
            }

            readRestCallConfiguration(node);

            logger.debug("init - loaded parameters: url= " + url + " - method= " + method);
            logger.debug("Init stop");

        } catch (Exception exc) {
            throw new InitializationException("GV_INIT_SERVICE_ERROR",
                    new String[][] { { "message", exc.getMessage() } }, exc);
        }

    }

    private void readRestCallConfiguration(Node node) throws XMLConfigException {
        if (XMLConfig.exists(node, "./headers")) {
            fillMap(XMLConfig.getNodeList(node, "./headers/header"), headers);
        }

        if (XMLConfig.exists(node, "./parameters")) {
            fillMap(XMLConfig.getNodeList(node, "./parameters/param"), params);
        }

        Node bodyNode = XMLConfig.getNode(node, "./body");
        if (Objects.nonNull(bodyNode)) {

            sendGVBufferObject = Boolean.valueOf(XMLConfig.get(bodyNode, "@gvbuffer-object", "false"));

            body = bodyNode.getTextContent();
        } else {
            body = null;
        }
    }

    private void fillMap(NodeList sourceNodeList, Map<String, String> destinationMap) {

        if (sourceNodeList.getLength() == 0) {
            destinationMap.clear();
        } else {
            IntStream.range(0, sourceNodeList.getLength()).mapToObj(sourceNodeList::item).forEach(node -> {
                try {
                    destinationMap.put(XMLConfig.get(node, "@name"), XMLConfig.get(node, "@value"));
                } catch (Exception e) {
                    logger.error("Fail to read configuration", e);
                }
            });
        }
    }

    @Override
    public GVBuffer perform(GVBuffer gvBuffer) throws ConnectionException, CallException, InvalidDataException {

        try {
            final GVBufferPropertyFormatter formatter = new GVBufferPropertyFormatter(gvBuffer);

            String expandedUrl = formatter.format(url);
            String querystring = "";

            if (!params.isEmpty()) {

                querystring = params.entrySet().stream().map(
                        e -> formatter.formatAndEncode(e.getKey()) + "=" + formatter.formatAndEncode(e.getValue()))
                        .collect(Collectors.joining("&"));

                expandedUrl = expandedUrl.concat("?").concat(querystring);
            }

            StringBuffer callDump = new StringBuffer();
            callDump.append("Performing RestCallOperation " + name).append("\n        ").append("URL: ")
                    .append(expandedUrl);

            URL requestUrl = new URL(expandedUrl);

            HttpURLConnection httpURLConnection;
            if (truststorePath != null && expandedUrl.startsWith("https://")) {
                httpURLConnection = openSecureConnection(requestUrl);
            } else {
                httpURLConnection = (HttpURLConnection) requestUrl.openConnection();
            }
            callDump.append("\n        ").append("Method: " + method);

            callDump.append("\n        ").append("Connection timeout: " + connectionTimeout);
            callDump.append("\n        ").append("Read timeout: " + readTimeout);

            httpURLConnection.setRequestMethod(method);
            httpURLConnection.setConnectTimeout(connectionTimeout);
            httpURLConnection.setReadTimeout(readTimeout);

            for (Entry<String, String> header : headers.entrySet()) {
                String k = formatter.format(header.getKey());
                String v = formatter.format(header.getValue());
                httpURLConnection.setRequestProperty(k, v);
                callDump.append("\n        ").append("Header: " + k + "=" + v);
                if ("content-type".equalsIgnoreCase(k) && "application/x-www-form-urlencoded".equalsIgnoreCase(v)) {
                    body = querystring;
                }

            }

            if (sendGVBufferObject && gvBuffer.getObject() != null) {
                byte[] requestData;
                if (gvBuffer.getObject() instanceof byte[]) {
                    requestData = (byte[]) gvBuffer.getObject();
                } else {
                    requestData = gvBuffer.getObject().toString().getBytes();

                }
                httpURLConnection.setRequestProperty("Content-Length", Integer.toString(requestData.length));
                callDump.append("\n        ").append("Content-Length: " + requestData.length);
                callDump.append("\n        ").append("Request body: binary");
                logger.debug(callDump.toString());

                httpURLConnection.setDoOutput(true);

                DataOutputStream dataOutputStream = new DataOutputStream(httpURLConnection.getOutputStream());
                dataOutputStream.write(requestData);

                dataOutputStream.flush();
                dataOutputStream.close();

            } else if (Objects.nonNull(body) && body.length() > 0) {

                String expandedBody = formatter.format(body);
                callDump.append("\n        ").append("Request body: " + expandedBody);
                logger.debug(callDump.toString());

                httpURLConnection.setDoOutput(true);

                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(httpURLConnection.getOutputStream());
                outputStreamWriter.write(expandedBody);
                outputStreamWriter.flush();
                outputStreamWriter.close();

            }

            httpURLConnection.connect();

            InputStream responseStream = null;

            try {
                httpURLConnection.getResponseCode();
                responseStream = httpURLConnection.getInputStream();
            } catch (IOException connectionFail) {
                responseStream = httpURLConnection.getErrorStream();
            }

            for (Entry<String, List<String>> header : httpURLConnection.getHeaderFields().entrySet()) {
                if (Objects.nonNull(header.getKey()) && Objects.nonNull(header.getValue())) {
                    gvBuffer.setProperty(RESPONSE_HEADER_PREFIX.concat(header.getKey().toUpperCase()),
                            header.getValue().stream().collect(Collectors.joining(";")));
                }
            }

            if (responseStream != null) {

                byte[] responseData = IOUtils.toByteArray(responseStream);
                String responseContentType = Optional
                        .ofNullable(gvBuffer.getProperty(RESPONSE_HEADER_PREFIX.concat("CONTENT-TYPE"))).orElse("");

                if (responseContentType.startsWith("application/json")
                        || responseContentType.startsWith("application/javascript")) {
                    gvBuffer.setObject(new String(responseData, "UTF-8"));
                } else {
                    gvBuffer.setObject(responseData);
                }

            } else { // No content
                gvBuffer.setObject(null);
            }

            gvBuffer.setProperty(RESPONSE_STATUS, String.valueOf(httpURLConnection.getResponseCode()));
            gvBuffer.setProperty(RESPONSE_MESSAGE,
                    Optional.ofNullable(httpURLConnection.getResponseMessage()).orElse("NULL"));

            httpURLConnection.disconnect();

        } catch (Exception exc) {
            throw new CallException("GV_CALL_SERVICE_ERROR",
                    new String[][] { { "service", gvBuffer.getService() }, { "system", gvBuffer.getSystem() },
                            { "tid", gvBuffer.getId().toString() }, { "message", exc.getMessage() } },
                    exc);
        }
        return gvBuffer;
    }

    private class GVBufferPropertyFormatter {

        private final GVBuffer gvBuffer;
        private Map<String, Object> params;

        public GVBufferPropertyFormatter(GVBuffer gvBuffer) {
            this.gvBuffer = gvBuffer;
        }

        String format(String entry) {

            try {
                if (Objects.isNull(params)) {
                    params = GVBufferPropertiesHelper.getPropertiesMapSO(gvBuffer, true);
                }

                return PropertiesHandler.expand(entry, params, gvBuffer.getObject());
            } catch (Exception exception) {
                logger.error("Error formatting value: " + entry, exception);
            }
            return entry;
        }

        String formatAndEncode(String entry) {
            String value = format(entry);
            try {
                return URLEncoder.encode(value, "UTF-8");
            } catch (Exception exception) {
                logger.error("Error encoding value: " + entry, exception);
            }
            return entry;
        }

    }

    private HttpsURLConnection openSecureConnection(URL url) throws Exception {

        InputStream keyStream = new FileInputStream(truststorePath);

        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        keystore.load(keyStream, Optional.ofNullable(truststorePassword).orElse("").toCharArray());

        TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(
                Optional.ofNullable(truststoreAlgorithm).orElseGet(TrustManagerFactory::getDefaultAlgorithm));
        trustFactory.init(keystore);

        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, trustFactory.getTrustManagers(), null);

        HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();

        httpsURLConnection.setSSLSocketFactory(context.getSocketFactory());

        httpsURLConnection.setHostnameVerifier(new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });

        return httpsURLConnection;
    }

    @Override
    public void cleanUp() {
        // do nothing
    }

    @Override
    public void destroy() {
        // do nothing
    }

    @Override
    public String getServiceAlias(GVBuffer gvBuffer) {
        return gvBuffer.getService();
    }

    @Override
    public void setKey(OperationKey key) {
        this.key = key;
    }

    @Override
    public OperationKey getKey() {
        return key;
    }
}