package com.trovebox.android.common.net;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import oauth.signpost.OAuthConsumer;
import org.apache.http.HttpEntity;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import com.trovebox.android.common.CommonConfigurationUtils;
import com.trovebox.android.common.net.ApiRequest.Parameter;
import com.trovebox.android.common.net.HttpEntityWithProgress.ProgressListener;
import com.trovebox.android.common.util.GuiUtils;
import com.trovebox.android.common.util.TrackerUtils;
/**
* ApiBase provides the basic functionality to call RESTful APIs using an
* ApiRequest.
*
* @author Patrick Boos
*/
public class ApiBase {
public final static String TAG = ApiBase.class.getSimpleName();
public static int NetworkConnectionTimeout_ms = 10000;
public ApiBase() {
}
/**
* Execute a request to the API.
*
* @param request request to perform
* @return the response from the API
* @throws ClientProtocolException
* @throws IOException
*/
public ApiResponse execute(ApiRequest request)
throws ClientProtocolException, IOException {
return execute(request, CommonConfigurationUtils.getServer());
}
/**
* Execute a request to the API.
*
* @param request request to perform
* @param baseUrl the base server url
* @return the response from the API
* @throws ClientProtocolException
* @throws IOException
*/
public ApiResponse execute(ApiRequest request, String baseUrl) throws ClientProtocolException,
IOException {
return execute(request, baseUrl, null, NetworkConnectionTimeout_ms);
}
/**
* Execute a request to the API.
*
* @param request request to perform
* @param listener Progress Listener with callback on progress
* @return the response from the API
* @throws ClientProtocolException
* @throws IOException
*/
public ApiResponse execute(ApiRequest request, ProgressListener listener)
throws ClientProtocolException, IOException {
return execute(request, CommonConfigurationUtils.getServer(),
CommonConfigurationUtils.getOAuthConsumer(),
listener);
}
/**
* Execute a request to the API.
*
* @param request request to perform
* @param listener Progress Listener with callback on progress
* @param connectionTimeout the connection and socket timeout
* @return the response from the API
* @throws ClientProtocolException
* @throws IOException
*/
public ApiResponse execute(ApiRequest request, ProgressListener listener, int connectionTimeout)
throws ClientProtocolException, IOException {
return execute(request, CommonConfigurationUtils.getServer(),
listener, connectionTimeout);
}
/**
* Execute a request to the API.
*
* @param request request to perform
* @param baseUrl the base server url
* @param listener Progress Listener with callback on progress
* @param connectionTimeout the connection and socket timeout
* @return the response from the API
* @throws ClientProtocolException
* @throws IOException
*/
public ApiResponse execute(ApiRequest request, String baseUrl, ProgressListener listener, int connectionTimeout)
throws ClientProtocolException, IOException {
return execute(request, baseUrl,
CommonConfigurationUtils.getOAuthConsumer(),
listener, connectionTimeout);
}
/**
* Execute a request to the API.
*
* @param request request to perform
* @param baseUrl the base server url
* @param consumer the oauth consumer key to sign request
* @param listener Progress Listener with callback on progress
* @return the response from the API
* @throws ClientProtocolException
* @throws IOException
*/
public ApiResponse execute(ApiRequest request, String baseUrl, OAuthConsumer consumer,
ProgressListener listener)
throws ClientProtocolException, IOException {
return execute(request, baseUrl, consumer, listener, NetworkConnectionTimeout_ms);
}
/**
* Execute a request to the API.
*
* @param request request to perform
* @param baseUrl the base server url
* @param consumer the oauth consumer key to sign request
* @param listener Progress Listener with callback on progress
* @param connectionTimeout the connection and socket timeout
* @return the response from the API
* @throws ClientProtocolException
* @throws IOException
*/
public ApiResponse execute(ApiRequest request, String baseUrl, OAuthConsumer consumer,
ProgressListener listener, int connectionTimeout)
throws ClientProtocolException, IOException {
// PoolingClientConnectionManager();
HttpParams params = new BasicHttpParams();
// set params for connection...
HttpConnectionParams.setStaleCheckingEnabled(
params, false);
HttpConnectionParams.setConnectionTimeout(params,
connectionTimeout);
HttpConnectionParams.setSoTimeout(params,
connectionTimeout);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
DefaultHttpClient httpClient = new DefaultHttpClient(params);
HttpUriRequest httpRequest = createHttpRequest(request, baseUrl, listener);
httpRequest.getParams().setBooleanParameter(
"http.protocol.expect-continue", false);
httpRequest.setHeader("User-Agent", "android");
httpRequest.setHeader("source", "android");
if (consumer != null) {
try {
consumer.sign(httpRequest);
} catch (Exception e) {
GuiUtils.noAlertError(TAG, "Error signing request", e);
}
} else
{
TrackerUtils.trackBackgroundEvent("not_signed_request", baseUrl + request.getPath());
}
return new ApiResponse(baseUrl + request.getPath(), httpClient.execute(httpRequest));
}
/**
* Create a HttpUriRequest out of a ApiRequest object.
*
* @param request the ApiRequest for which a HttpUriRequest should be
* created
* @param baseUrl the base server url
* @param listener Progress Listener with callback on progress
* @return HttpUriRequest object which will do the request as described in
* ApiRequest
* @throws UnsupportedEncodingException
*/
private HttpUriRequest createHttpRequest(ApiRequest request,
String baseUrl,
ProgressListener listener) throws UnsupportedEncodingException {
HttpUriRequest httpRequest = null;
switch (request.getMethod()) {
case ApiRequest.GET:
httpRequest = new HttpGet(addParamsToUrl(
baseUrl + request.getPath(), request.getParameters()));
break;
case ApiRequest.POST:
httpRequest = new HttpPost(baseUrl + request.getPath());
HttpPost httpPost = ((HttpPost) httpRequest);
if (request.isMime()) {
// TODO use the multipart when possible (currently server
// handles it wrong)
// HttpEntity entity = createMultipartEntity(request);
// TODO remove this when doing correct multipart
httpRequest = new HttpPost(addParamsToUrl(
baseUrl + request.getPath(), request.getParameters()));
httpPost = ((HttpPost) httpRequest);
HttpEntity entity = createFileOnlyMultipartEntity(request);
if (listener != null) {
httpPost.setEntity(new HttpEntityWithProgress(entity,
listener, httpPost));
} else {
httpPost.setEntity(entity);
}
} else {
httpPost.setEntity(new UrlEncodedFormEntity(request
.getParameters(), HTTP.UTF_8));
}
break;
case ApiRequest.PUT:
httpRequest = new HttpPut(addParamsToUrl(
baseUrl + request.getPath(), request.getParameters()));
break;
case ApiRequest.DELETE:
httpRequest = new HttpDelete(addParamsToUrl(
baseUrl + request.getPath(), request.getParameters()));
break;
}
for (NameValuePair pair : request.getHeaders()) {
request.addHeader(pair.getName(), pair.getValue());
}
return httpRequest;
}
private HttpEntity createFileOnlyMultipartEntity(ApiRequest request)
throws UnsupportedEncodingException {
MultipartEntity entity = new MultipartEntity();
for (Parameter<?> parameter : request.getParametersMime()) {
if (parameter.getValue() instanceof File) {
File file = (File) parameter.getValue();
entity.addPart(parameter.getName(), new FileBody(file));
}
}
return entity;
}
/**
* Adds the parameters to the given url.
*
* @param url the url
* @param nameValuePairs the name value pairs to be added to the url
* @return the url with added parameters
* @throws UnsupportedEncodingException
*/
private String addParamsToUrl(String url, List<NameValuePair> nameValuePairs)
throws UnsupportedEncodingException {
if (nameValuePairs == null || nameValuePairs.isEmpty()) {
return url;
}
final StringBuffer newUrl = new StringBuffer(url);
newUrl.append(url.contains("?") ? '&' : '?');
for (NameValuePair param : nameValuePairs) {
newUrl.append(param.getName() + "="
+ URLEncoder.encode(param.getValue(), "UTF-8") + "&");
}
newUrl.deleteCharAt(newUrl.length() - 1);
return newUrl.toString();
}
}