/*******************************************************************************
* Copyright (c) 2010-2015 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.usage.internal.http;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import org.jboss.tools.usage.tracker.internal.UsagePluginLogger;
import org.jboss.tools.usage.util.HttpEncodingUtils;
/**
* Base class that holds a map that subclasses may get. The values in the map
* are fetched and parsed from a document that is fetched on a url that the
* subclass provides
*
* @author Andre Dietisheim
*/
public class HttpRemotePropertiesProvider implements IPropertiesProvider {
static final String GET_METHOD_NAME = "GET"; //$NON-NLS-1$
private Map<Object, Object> valuesMap;
private String url;
private UsagePluginLogger logger;
public HttpRemotePropertiesProvider(String url, UsagePluginLogger loggingAdapter) {
this.url = url;
this.logger = loggingAdapter;
}
/* (non-Javadoc)
* @see org.jboss.tools.usage.internal.http.IMapProvider#getValueMap()
*/
@Override
public synchronized Map<Object, Object> getMap() throws IOException {
if (valuesMap == null) {
HttpURLConnection urlConnection = createURLConnection(url);
InputStreamReader reader = request(urlConnection);
if(reader != null) {
try {
Properties pr = new Properties();
pr.load(reader);
valuesMap = pr;
} finally {
reader.close();
}
}
if(valuesMap == null) {
valuesMap = Collections.emptyMap();
}
}
return valuesMap;
}
/**
* Sends a http GET request to the given URL. Returns the response string or
* <tt>null</tt> if an error occurred. The errors catched are Exceptions or
* HTTP error codes.
*
* @param url
* the url to send the GET request to
* @return the response or <tt>null</tt> if an error occured.
* @throws UnsupportedEncodingException
*
* @see HttpURLConnection
*/
protected InputStreamReader request(HttpURLConnection urlConnection) throws IOException {
InputStreamReader responseReader = null;
try {
urlConnection.connect();
int responseCode = getResponseCode(urlConnection);
if (responseCode == HttpURLConnection.HTTP_OK) { // OK
logger.debug(MessageFormat.format(HttpMessages.HttpResourceMap_Info_HttpQuery, url));
responseReader = getInputStreamReader(urlConnection.getInputStream(), urlConnection.getContentType());
} else if(responseCode >= 300 && responseCode < 400) { // Redirect
// URLConnection does not redirect automatically if the protocols are different. HTTP -> HTTPS for example.
// So we have to do it manually.
// See https://issues.jboss.org/browse/JBDS-3159
String redirectLocation = urlConnection.getHeaderField("location");
if(redirectLocation!=null && !urlConnection.getURL().toString().equalsIgnoreCase(redirectLocation)) { // Ignore responses with empty redirect locations or with the same redirect URL
urlConnection = createURLConnection(redirectLocation);
return request(urlConnection);
} else {
logStatusCode(urlConnection);
}
} else {
logStatusCode(urlConnection);
}
return responseReader;
} catch (IOException e) {
logger.debug(MessageFormat.format(HttpMessages.HttpGetMethod_Error_Io, url, e.toString()));
return null;
}
}
private void logStatusCode(HttpURLConnection urlConnection) throws IOException {
String responseMessage = urlConnection.getResponseMessage();
StringBuilder message = new StringBuilder(MessageFormat.format(HttpMessages.HttpGetMethod_Error_Http, url, urlConnection.getResponseCode()));
if(responseMessage!=null) {
message.append("; Response message: ").append(responseMessage);
}
logger.error(message.toString(), false);
}
private InputStreamReader getInputStreamReader(InputStream inputStream, String contentType)
throws UnsupportedEncodingException, IOException {
String contentTypeCharset = HttpEncodingUtils.getContentTypeCharset(contentType);
if (contentTypeCharset != null && contentTypeCharset.length() > 0) {
return new InputStreamReader(new BufferedInputStream(inputStream),
contentTypeCharset);
} else {
return new InputStreamReader(new BufferedInputStream(inputStream));
}
}
/**
* Creates a new url connection.
*
* @param urlString
* the url string
* @return the http url connection
* @throws IOException
* @throws IOException
* Signals that an I/O exception has occurred.
*/
protected HttpURLConnection createURLConnection(String urlString) throws IOException {
URL url = new URL(urlString);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(true);
urlConnection.setRequestMethod(GET_METHOD_NAME);
return urlConnection;
}
/**
* Returns the return code from the given {@link HttpURLConnection}.
* Provided to be called by test cases so that they can retrieve the return
* code.
*
* @param urlConnection
* to get the response code from
* @return the return code the HttpUrlConnection received
* @throws IOException
* Signals that an I/O exception has occurred.
*/
protected int getResponseCode(HttpURLConnection urlConnection) throws IOException {
return urlConnection.getResponseCode();
}
}