All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sdl.odata.client.caller.BasicEndpointCaller Maven / Gradle / Ivy

There is a newer version: 2.13.0
Show newest version
/*
 * Copyright (c) 2014-2023 All Rights Reserved by the RWS Group for and on behalf of its affiliates and subsidiaries.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sdl.odata.client.caller;

import com.sdl.odata.api.service.MediaType;
import com.sdl.odata.api.service.ODataRequest;
import com.sdl.odata.client.api.caller.EndpointCaller;
import com.sdl.odata.client.api.exception.ODataClientException;
import com.sdl.odata.client.api.exception.ODataClientRuntimeException;
import com.sdl.odata.client.api.exception.ODataClientSocketException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.sdl.odata.api.service.HeaderNames.ODATA_CHUNKED_ERROR_MESSAGE_PROPERTY;
import static com.sdl.odata.api.service.HeaderNames.TRANSFER_ENCODING;
import static com.sdl.odata.api.service.MediaType.ATOM_XML;
import static com.sdl.odata.api.service.MediaType.XML;
import static com.sdl.odata.client.ODataClientConstants.DefaultValues.CLIENT_PROXY_PORT_DEFAULT;
import static com.sdl.odata.client.ODataClientConstants.DefaultValues.CLIENT_TIMEOUT_DEFAULT;
import static com.sdl.odata.client.ODataClientConstants.WebService.CLIENT_CONNECTION_TIMEOUT;
import static com.sdl.odata.client.ODataClientConstants.WebService.CLIENT_SERVICE_PROXY_HOST_NAME;
import static com.sdl.odata.client.ODataClientConstants.WebService.CLIENT_SERVICE_PROXY_PORT;
import static com.sdl.odata.client.property.PropertyUtils.getIntegerProperty;
import static com.sdl.odata.client.property.PropertyUtils.getStringProperty;
import static com.sdl.odata.client.util.ODataClientUtils.buildException;
import static com.sdl.odata.client.util.ODataClientUtils.closeIfNecessary;
import static com.sdl.odata.client.util.ODataClientUtils.populateRequestProperties;

/**
 * The basic implementation of Endpoint Caller.
 */
public class BasicEndpointCaller implements EndpointCaller {

    private static final Logger LOG = LoggerFactory.getLogger(BasicEndpointCaller.class);

    private static final Pattern ODATA_CHUNKED_ERROR_MESSAGE_PATTERN = Pattern
            .compile(ODATA_CHUNKED_ERROR_MESSAGE_PROPERTY + ":(.*)");

    private Integer timeout;
    private int proxyServerPort;
    private String proxyServerHostName;

    public BasicEndpointCaller(Properties properties) {
        LOG.trace("Starting to inject client with properties");

        proxyServerHostName = getStringProperty(properties, CLIENT_SERVICE_PROXY_HOST_NAME);
        timeout = getIntegerProperty(properties, CLIENT_CONNECTION_TIMEOUT, CLIENT_TIMEOUT_DEFAULT);
        Integer proxyPort = getIntegerProperty(properties, CLIENT_SERVICE_PROXY_PORT);
        proxyServerPort = proxyPort == null ? CLIENT_PROXY_PORT_DEFAULT : proxyPort;

        logConfiguration();
    }

    @Override
    public String callEndpoint(Map requestProperties, URL urlToCall) throws ODataClientException {
        LOG.debug("Preparing the call endpoint for given url: {}", urlToCall);
        HttpURLConnection conn = getConnection(populateRequestProperties(requestProperties, -1, null, XML), urlToCall);
        return getResponse(conn);
    }

    @Override
    public InputStream getInputStream(Map requestProperties, URL url)
            throws ODataClientRuntimeException {
        HttpURLConnection conn = getConnection(requestProperties, url);
        try {
            return conn.getInputStream();
        } catch (IOException e) {
            conn.disconnect();
            throw new ODataClientRuntimeException("Unable to get connection input stream for url: " + url, e);
        }
    }

    @Override
    public String doPostEntity(Map requestProperties, URL urlToCall, String body,
                               MediaType contentType, MediaType acceptType)
            throws ODataClientException {
        return sendRequest(populateRequestProperties(requestProperties, body.length(), contentType, acceptType),
                urlToCall, body, ODataRequest.Method.POST.name());
    }

    @Override
    public String doPutEntity(Map requestProperties, URL urlToCall, String body,
                              MediaType type) throws ODataClientException {
        return sendRequest(populateRequestProperties(requestProperties, body.length(), type, type),
                urlToCall, body, ODataRequest.Method.PUT.name());
    }

    @Override
    public void doDeleteEntity(Map requestProperties, URL urlToCall) throws ODataClientException {
        sendRequest(populateRequestProperties(requestProperties, 0, ATOM_XML, ATOM_XML), urlToCall, "",
                ODataRequest.Method.DELETE.name());
    }

    private String sendRequest(Map properties, URL urlToCall, String body, String requestMethod)
            throws ODataClientException {
        HttpURLConnection httpConnection = getConnection(properties, urlToCall);
        httpConnection.setDoOutput(true);

        DataOutputStream dataOutputStream = null;
        BufferedWriter writer = null;
        try {
            httpConnection.setRequestMethod(requestMethod);
            // Sending request
            dataOutputStream = new DataOutputStream(httpConnection.getOutputStream());
            writer = new BufferedWriter(new OutputStreamWriter(dataOutputStream, "UTF-8"));
            writer.write(body);

            writer.flush();
            dataOutputStream.flush();
        } catch (IOException e) {
            throw new ODataClientException("Could not perform " + requestMethod + " request.", e);
        } finally {
            closeIfNecessary(writer);
            closeIfNecessary(dataOutputStream);
        }

        return getResponse(httpConnection);
    }

    private HttpURLConnection getConnection(Map requestProperties, URL url)
            throws ODataClientRuntimeException {
        HttpURLConnection urlConnection;
        try {
            if (proxyServerHostName == null) {
                urlConnection = (HttpURLConnection) url.openConnection();
            } else {
                InetSocketAddress proxySocketAddress =
                        new InetSocketAddress(proxyServerHostName, proxyServerPort);
                Proxy proxy = new Proxy(Proxy.Type.HTTP, proxySocketAddress);
                urlConnection = (HttpURLConnection) url.openConnection(proxy);
            }
        } catch (IOException e) {
            LOG.error("Could not open connection to the service endpoint.", e);
            if (proxyServerHostName != null) {
                LOG.info("Using proxy host='{}' port='{}'", proxyServerHostName, proxyServerPort);
            }
            throw new ODataClientRuntimeException("Could not open connection to the service endpoint.", e);
        }

        if (timeout != null) {
            urlConnection.setConnectTimeout(timeout);
            urlConnection.setReadTimeout(timeout);
        }

        if (requestProperties != null && !requestProperties.isEmpty()) {
            requestProperties.entrySet().stream()
                    .forEach(entry -> urlConnection.setRequestProperty(entry.getKey(), entry.getValue()));
        }
        return urlConnection;
    }

    private String getResponse(HttpURLConnection httpConnection) throws ODataClientException {
        BufferedReader bufferedReader = null;
        try {
            int responseCode = httpConnection.getResponseCode();
            boolean isError = responseCode >= HttpURLConnection.HTTP_BAD_REQUEST;
            InputStream inputStream = isError
                    ? httpConnection.getErrorStream()
                    : httpConnection.getInputStream();
            LOG.debug("Request ended with {} status code.", responseCode);

            StringBuilder response = new StringBuilder(isError ? "Unable to get response from OData service: " : "");

            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                String inputLine;
                while ((inputLine = bufferedReader.readLine()) != null) {
                    response.append(inputLine).append(System.lineSeparator());
                }
            } else {
                response.append("No Response.");
            }
            String resultResponse = response.toString();
            if ("chunked".equals(httpConnection.getHeaderField(TRANSFER_ENCODING))) {
                String[] responseLines = resultResponse.split(System.lineSeparator());
                Matcher matcher = ODATA_CHUNKED_ERROR_MESSAGE_PATTERN.matcher(responseLines[responseLines.length - 1]);
                if (matcher.matches()) {
                    String errorMessage = matcher.group(1);
                    resultResponse = "Unable to get response from OData service: " + errorMessage;
                    isError = true;
                }
            }

            if (isError) {
                throw buildException(resultResponse, responseCode);
            }

            return resultResponse;
        } catch (SocketException e) {
            throw new ODataClientSocketException("Could not initiate connection to the endpoint.", e);
        } catch (IOException e) {
            throw new ODataClientException("Unable to process response from OData service.", e);
        } finally {
            closeIfNecessary(bufferedReader);
            httpConnection.disconnect();
        }
    }

    private void logConfiguration() {
        if (LOG.isDebugEnabled()) {
            StringBuilder configLog = new StringBuilder("Client is initialized with following parameters: timeout = ")
                    .append(timeout);
            if (proxyServerHostName != null) {
                configLog.append(", proxyServerHostName = '").append(proxyServerHostName)
                        .append("', proxyServerPort = ").append(proxyServerPort);
            }
            LOG.debug(configLog.toString());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy