Java tutorial
/* * Copyright 2014 Scott J. Johnson (http://scottjjohnson.com) * * 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.scottjjohnson.finance.analysis; import java.io.IOException; import java.util.Properties; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig.Builder; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.scottjjohnson.util.ParseUtils; /** * Contains methods for building and sending HTTP requests */ public class HttpClient { private static final Logger LOGGER = LoggerFactory.getLogger(HttpClient.class); private static final String CONNECTION_CONFIG_PROPERTIES_FILE = "connection.properties"; private static final int DEFAULT_SOCKET_TIMEOUT = 5000; private static final int DEFAULT_CONNECTION_TIMEOUT = 5000; private Properties connectionProperties = null; private ResponseHandler<String> responseHandler = null; private RequestConfig requestConfig = null; /** * Constructor that uses configuration defaults */ public HttpClient() { this(getDefaultConnectionProperties(), getDefaultResponseHandler()); } /** * Constructor to allow overriding client configuration * * @param connectionProperties key-value pairs describing connection properties * @param responseHandler HttpClient response handler that returns a String object */ public HttpClient(Properties connectionProperties, ResponseHandler<String> responseHandler) { if (connectionProperties == null) connectionProperties = getDefaultConnectionProperties(); if (responseHandler == null) responseHandler = getDefaultResponseHandler(); this.connectionProperties = connectionProperties; this.responseHandler = responseHandler; this.requestConfig = getRequestConfig(); } /** * Builds a request configuration based on application connection properties. * * @return request configuration */ private RequestConfig getRequestConfig() { int socketTimeout = ParseUtils.safeParseInt(connectionProperties.getProperty("SOCKET_TIMEOUT"), DEFAULT_SOCKET_TIMEOUT); int connectionTimeout = ParseUtils.safeParseInt(connectionProperties.getProperty("CONNECTION_TIMEOUT"), DEFAULT_CONNECTION_TIMEOUT); String proxyHost = connectionProperties.getProperty("PROXY_HOST"); int proxyPort = ParseUtils.safeParseInt(connectionProperties.getProperty("PROXY_PORT"), 0); Builder config = RequestConfig.custom().setSocketTimeout(socketTimeout) .setConnectTimeout(connectionTimeout); if (proxyHost != null && proxyPort > 0) { config.setProxy(new HttpHost(proxyHost, proxyPort)); } return config.build(); } /** * Extracts http connection properties from the configuration file and returns them as a Properties object. * * @returns map of cookie key-value pairs (never null) */ public static Properties getDefaultConnectionProperties() { Properties connectionProps = new Properties(); try { ClassLoader loader = HttpClient.class.getClassLoader(); connectionProps.load(loader.getResourceAsStream(CONNECTION_CONFIG_PROPERTIES_FILE)); LOGGER.debug("Connection configuration properties: {}.", connectionProps); } catch (IOException e) { LOGGER.warn("Unable to load connection properties from {}.", CONNECTION_CONFIG_PROPERTIES_FILE); } return connectionProps; } /** * Builds a simple HTTP response handler that checks for status of 2XX. * * @return response handler */ public static ResponseHandler<String> getDefaultResponseHandler() { // Create a custom response handler -- from httpclient docs return new ResponseHandler<String>() { @Override public String handleResponse(final HttpResponse response) throws ClientProtocolException, IOException { int status = response.getStatusLine().getStatusCode(); if (status >= 200 && status < 300) { HttpEntity entity = response.getEntity(); return entity != null ? EntityUtils.toString(entity) : null; } else { throw new ClientProtocolException("Unexpected response status: " + status); } } }; } /** * Executes a HTTP GET request. * * @param url * URL with optional query parameters * @return response body * @throws IOException * if the HTTP request fails */ public String sendGetRequest(String url) throws IOException { // I'm not using try-with-resources because I want to handle an exception on close() differently than other // exceptions. Other exceptions are allowed to bubble up. CloseableHttpClient apacheHttpClient = HttpClients.createDefault(); String responseBody = null; LOGGER.debug("URL = {}", url); try { HttpGet httpGet = new HttpGet(url); httpGet.setConfig(requestConfig); responseBody = apacheHttpClient.execute(httpGet, responseHandler); } finally { try { apacheHttpClient.close(); } catch (IOException e) { LOGGER.debug("Non-fatal exception: Caught IOException when calling ClosableHttpClient.close().", e); } } LOGGER.debug("HTTP Response body = {}", responseBody); return responseBody; } }