/*
* Copyright 2010 Nabeel Mukhtar
*
* 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.github.api.v2.services.impl;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import com.github.api.v2.services.GitHubException;
import com.github.api.v2.services.GitHubCommunicator.HttpMethod;
import com.github.api.v2.services.auth.Authentication;
import com.github.api.v2.services.auth.HeaderBasedAuthentication;
import com.github.api.v2.services.auth.ParameterBasedAuthentication;
import com.github.api.v2.services.constant.ApplicationConstants;
/**
* The Class GitHubApiGateway.
*/
public abstract class GitHubApiGateway {
/** The logger. */
protected final Logger logger = Logger.getLogger(getClass().getCanonicalName());
/** The Constant GZIP_ENCODING. */
private static final String GZIP_ENCODING = "gzip";
/** The Constant REFERRER. */
private static final String REFERRER = "Referer";
/** The Constant UTF_8_CHAR_SET. */
private static final Charset UTF_8_CHAR_SET = Charset.forName(ApplicationConstants.CONTENT_ENCODING);
/** The request headers. */
protected Map<String, String> requestHeaders = new HashMap<String, String>();
/** The request parameters. */
protected Map<String, String> requestParameters = new HashMap<String, String>();
/** The user ip address. */
protected String userIpAddress;
/** The api version. */
protected String apiVersion = ApplicationConstants.DEFAULT_API_VERSION;
/** The max rate limit. */
private int maxRateLimit = -1;
/** The current rate limit. */
private int currentRateLimit = -1;
/**
* Gets the max rate limit.
*
* @return the max rate limit
*/
public int getMaxRateLimit() {
return maxRateLimit;
}
/**
* Gets the current rate limit.
*
* @return the current rate limit
*/
public int getCurrentRateLimit() {
return currentRateLimit;
}
/**
* Gets the api version.
*
* @return the api version
*/
public String getApiVersion() {
return apiVersion;
}
/**
* Sets the api version.
*
* @param apiVersion
* the new api version
*/
public void setApiVersion(String apiVersion) {
this.apiVersion = apiVersion;
}
/**
* Sets the request headers.
*
* @param requestHeaders
* the request headers
*/
public void setRequestHeaders(Map<String, String> requestHeaders) {
this.requestHeaders = requestHeaders;
}
/**
* Gets the request headers.
*
* @return the request headers
*/
public Map<String, String> getRequestHeaders() {
return requestHeaders;
}
/**
* Adds the request header.
*
* @param headerName
* the header name
* @param headerValue
* the header value
*/
public void addRequestHeader(String headerName, String headerValue) {
requestHeaders.put(headerName, headerValue);
}
/**
* Removes the request header.
*
* @param headerName
* the header name
*/
public void removeRequestHeader(String headerName) {
requestHeaders.remove(headerName);
}
/**
* Sets the referrer.
*
* @param referrer
* the new referrer
*/
public void setReferrer(String referrer) {
requestHeaders.put(REFERRER, referrer);
}
/**
* Sets the user ip address.
*
* @param userIpAddress
* the new user ip address
*/
public void setUserIpAddress(String userIpAddress) {
this.userIpAddress = userIpAddress;
}
/**
* Sets the authentication.
*
* @param authentication
* the new authentication
*/
public void setAuthentication(Authentication authentication) {
if (authentication != null) {
if (authentication instanceof ParameterBasedAuthentication) {
requestParameters.putAll(((ParameterBasedAuthentication) authentication).getParameters());
} else if (authentication instanceof HeaderBasedAuthentication) {
requestHeaders.putAll(((HeaderBasedAuthentication) authentication).getHeaders());
}
}
}
/**
* Convert stream to string.
*
* @param is
* the is
*
* @return the string
*/
protected static String convertStreamToString(InputStream is) {
/*
* To convert the InputStream to String we use the BufferedReader.readLine()
* method. We iterate until the BufferedReader return null which means
* there's no more data to read. Each line will appended to a StringBuilder
* and returned as String.
*/
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
/**
* Call api get.
*
* @param apiUrl
* the api url
*
* @return the input stream
*/
protected InputStream callApiGet(String apiUrl) {
return callApiGet(apiUrl, HttpURLConnection.HTTP_OK);
}
/**
* Call api get.
*
* @param apiUrl
* the api url
* @param expected
* the expected
*
* @return the input stream
*/
protected InputStream callApiGet(String apiUrl, int expected) {
try {
URL url = new URL(apiUrl);
if (!requestParameters.isEmpty()) {
if (url.getQuery() == null) {
url = new URL(apiUrl + "?" + getParametersString(requestParameters));
} else {
url = new URL(apiUrl + "&" + getParametersString(requestParameters));
}
}
HttpURLConnection request = (HttpURLConnection) url.openConnection();
if (ApplicationConstants.CONNECT_TIMEOUT > -1) {
request.setConnectTimeout(ApplicationConstants.CONNECT_TIMEOUT);
}
if (ApplicationConstants.READ_TIMEOUT > -1) {
request.setReadTimeout(ApplicationConstants.READ_TIMEOUT);
}
for (String headerName : requestHeaders.keySet()) {
request.setRequestProperty(headerName, requestHeaders.get(headerName));
}
request.connect();
maxRateLimit = request.getHeaderFieldInt(ApplicationConstants.MAX_RATE_LIMIT_HEADER, -1);
currentRateLimit = request.getHeaderFieldInt(ApplicationConstants.CURRENT_RATE_LIMIT_HEADER, -1);
if (request.getResponseCode() != expected) {
throw new GitHubException(convertStreamToString(getWrappedInputStream(request.getErrorStream(),
GZIP_ENCODING.equalsIgnoreCase(request.getContentEncoding()))));
} else {
return getWrappedInputStream(request.getInputStream(),
GZIP_ENCODING.equalsIgnoreCase(request.getContentEncoding()));
}
} catch (IOException e) {
throw new GitHubException(e);
}
}
/**
* Call api post.
*
* @param apiUrl
* the api url
* @param parameters
* the parameters
*
* @return the input stream
*/
protected InputStream callApiPost(String apiUrl, Map<String, String> parameters) {
return callApiPost(apiUrl, parameters, HttpURLConnection.HTTP_OK);
}
/**
* Call api post.
*
* @param apiUrl
* the api url
* @param parameters
* the parameters
* @param expected
* the expected
*
* @return the input stream
*/
protected InputStream callApiPost(String apiUrl, Map<String, String> parameters, int expected) {
try {
URL url = new URL(apiUrl);
HttpURLConnection request = (HttpURLConnection) url.openConnection();
if (ApplicationConstants.CONNECT_TIMEOUT > -1) {
request.setConnectTimeout(ApplicationConstants.CONNECT_TIMEOUT);
}
if (ApplicationConstants.READ_TIMEOUT > -1) {
request.setReadTimeout(ApplicationConstants.READ_TIMEOUT);
}
for (String headerName : requestHeaders.keySet()) {
request.setRequestProperty(headerName, requestHeaders.get(headerName));
}
parameters.putAll(requestParameters);
request.setRequestMethod(HttpMethod.POST.name());
request.setDoOutput(true);
PrintStream out = new PrintStream(new BufferedOutputStream(request.getOutputStream()));
out.print(getParametersString(parameters));
out.flush();
out.close();
request.connect();
maxRateLimit = request.getHeaderFieldInt(ApplicationConstants.MAX_RATE_LIMIT_HEADER, -1);
currentRateLimit = request.getHeaderFieldInt(ApplicationConstants.CURRENT_RATE_LIMIT_HEADER, -1);
if (request.getResponseCode() != expected) {
throw new GitHubException(convertStreamToString(getWrappedInputStream(request.getErrorStream(),
GZIP_ENCODING.equalsIgnoreCase(request.getContentEncoding()))));
} else {
return getWrappedInputStream(request.getInputStream(),
GZIP_ENCODING.equalsIgnoreCase(request.getContentEncoding()));
}
} catch (IOException e) {
throw new GitHubException(e);
} finally {
}
}
/**
* Call api delete.
*
* @param apiUrl
* the api url
*
* @return the input stream
*/
protected InputStream callApiDelete(String apiUrl) {
return callApiDelete(apiUrl, HttpURLConnection.HTTP_OK);
}
/**
* Call api delete.
*
* @param apiUrl
* the api url
* @param expected
* the expected
*
* @return the input stream
*/
protected InputStream callApiDelete(String apiUrl, int expected) {
try {
URL url = new URL(apiUrl);
if (!requestParameters.isEmpty()) {
if (url.getQuery() == null) {
url = new URL(apiUrl + "?" + getParametersString(requestParameters));
} else {
url = new URL(apiUrl + "&" + getParametersString(requestParameters));
}
}
HttpURLConnection request = (HttpURLConnection) url.openConnection();
if (ApplicationConstants.CONNECT_TIMEOUT > -1) {
request.setConnectTimeout(ApplicationConstants.CONNECT_TIMEOUT);
}
if (ApplicationConstants.READ_TIMEOUT > -1) {
request.setReadTimeout(ApplicationConstants.READ_TIMEOUT);
}
for (String headerName : requestHeaders.keySet()) {
request.setRequestProperty(headerName, requestHeaders.get(headerName));
}
request.setRequestMethod(HttpMethod.DELETE.name());
request.connect();
maxRateLimit = request.getHeaderFieldInt(ApplicationConstants.MAX_RATE_LIMIT_HEADER, -1);
currentRateLimit = request.getHeaderFieldInt(ApplicationConstants.CURRENT_RATE_LIMIT_HEADER, -1);
if (request.getResponseCode() != expected) {
throw new GitHubException(convertStreamToString(getWrappedInputStream(request.getErrorStream(),
GZIP_ENCODING.equalsIgnoreCase(request.getContentEncoding()))));
} else {
return getWrappedInputStream(request.getInputStream(),
GZIP_ENCODING.equalsIgnoreCase(request.getContentEncoding()));
}
} catch (IOException e) {
throw new GitHubException(e);
}
}
/**
* Gets the parameters string.
*
* @param parameters
* the parameters
*
* @return the parameters string
*/
protected String getParametersString(Map<String, String> parameters) {
StringBuilder builder = new StringBuilder();
for (Iterator<Map.Entry<String, String>> iterator = parameters.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<String, String> entry = iterator.next();
builder.append(entry.getKey());
builder.append("=");
builder.append(encodeUrl(entry.getValue()));
if (iterator.hasNext()) {
builder.append("&");
}
}
return builder.toString();
}
/**
* Call api method.
*
* @param apiUrl
* the api url
* @param xmlContent
* the xml content
* @param contentType
* the content type
* @param method
* the method
* @param expected
* the expected
*
* @return the input stream
*/
protected InputStream callApiMethod(String apiUrl, String xmlContent, String contentType,
HttpMethod method, int expected) {
try {
URL url = new URL(apiUrl);
if (!requestParameters.isEmpty()) {
if (url.getQuery() == null) {
url = new URL(apiUrl + "?" + getParametersString(requestParameters));
} else {
url = new URL(apiUrl + "&" + getParametersString(requestParameters));
}
}
HttpURLConnection request = (HttpURLConnection) url.openConnection();
if (ApplicationConstants.CONNECT_TIMEOUT > -1) {
request.setConnectTimeout(ApplicationConstants.CONNECT_TIMEOUT);
}
if (ApplicationConstants.READ_TIMEOUT > -1) {
request.setReadTimeout(ApplicationConstants.READ_TIMEOUT);
}
for (String headerName : requestHeaders.keySet()) {
request.setRequestProperty(headerName, requestHeaders.get(headerName));
}
request.setRequestMethod(method.name());
request.setDoOutput(true);
if (contentType != null) {
request.setRequestProperty("Content-Type", contentType);
}
if (xmlContent != null) {
PrintWriter out = new PrintWriter(new OutputStreamWriter(request.getOutputStream(), UTF_8_CHAR_SET));
out.print(xmlContent);
out.flush();
out.close();
}
request.connect();
maxRateLimit = request.getHeaderFieldInt(ApplicationConstants.MAX_RATE_LIMIT_HEADER, -1);
currentRateLimit = request.getHeaderFieldInt(ApplicationConstants.CURRENT_RATE_LIMIT_HEADER, -1);
if (request.getResponseCode() != expected) {
throw new GitHubException(convertStreamToString(getWrappedInputStream(request.getErrorStream(),
GZIP_ENCODING.equalsIgnoreCase(request.getContentEncoding()))));
} else {
return getWrappedInputStream(request.getInputStream(),
GZIP_ENCODING.equalsIgnoreCase(request.getContentEncoding()));
}
} catch (IOException e) {
throw new GitHubException(e);
}
}
/**
* Close stream.
*
* @param is
* the is
*/
protected void closeStream(InputStream is) {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
logger.log(Level.SEVERE, "An error occurred while closing stream.", e);
}
}
/**
* Close connection.
*
* @param connection
* the connection
*/
protected void closeConnection(HttpURLConnection connection) {
try {
if (connection != null) {
connection.disconnect();
}
} catch (Exception e) {
logger.log(Level.SEVERE, "An error occurred while disconnecting connection.", e);
}
}
/**
* Gets the wrapped input stream.
*
* @param is
* the is
* @param gzip
* the gzip
*
* @return the wrapped input stream
*
* @throws IOException
* Signals that an I/O exception has occurred.
*/
protected InputStream getWrappedInputStream(InputStream is, boolean gzip)
throws IOException {
if (gzip) {
return new BufferedInputStream(new GZIPInputStream(is));
} else {
return new BufferedInputStream(is);
}
}
/**
* Encode url.
*
* @param original
* the original
*
* @return the string
*/
private static String encodeUrl(String original) {
if (original == null) {
return "";
} else {
try {
return URLEncoder.encode(original, ApplicationConstants.CONTENT_ENCODING);
} catch (UnsupportedEncodingException e) {
// should never be here..
return original;
}
}
}
}