package com.manuelmaly.hn.server; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.v4.content.LocalBroadcastManager; import org.apache.http.Header; import org.apache.http.HeaderIterator; import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; 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.HttpRequestBase; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.BasicCookieStore; 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 java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * Generic base for HTTP calls via {@link HttpClient}, ideally to be started in * a background thread. When the call has finished, listeners are notified via * an intent sent to {@link LocalBroadcastManager}, i.e. they must first * register (intent name is configurable via notificationBroadcastIntentID). * Response and errors are also sent via the intent. * * @author manuelmaly * * @param <T> * class of response */ public abstract class BaseHTTPCommand<T extends Serializable> implements IAPICommand<T> { private final Map<String, String> mBody; private String mNotificationBroadcastIntentID; private String mUrl; private String mURLQueryParams; private RequestType mType; private int mActualStatusCode; private Context mApplicationContext; private int mErrorCode; private T mResponse; private Object mTag; private int mSocketTimeoutMS; private int mHttpTimeoutMS; private boolean mNotifyFinishedBroadcast; HttpRequestBase mRequest; private CookieStore mCookieStore; public BaseHTTPCommand(final String url, final HashMap<String, String> params, RequestType type, boolean notifyFinishedBroadcast, String notificationBroadcastIntentID, Context applicationContext, int socketTimeoutMS, int httpTimeoutMS, Map<String, String> body) { mUrl = url; mBody = body; if (params != null) { StringBuilder sb = new StringBuilder(); for (String param : params.keySet()) { if (sb.length() > 0) sb.append("&"); sb.append(Uri.encode(param)).append("=").append(Uri.encode(params.get(param))); } mURLQueryParams = sb.toString(); } mType = type; mNotificationBroadcastIntentID = notificationBroadcastIntentID == null ? DEFAULT_BROADCAST_INTENT_ID : notificationBroadcastIntentID; mApplicationContext = applicationContext; mSocketTimeoutMS = socketTimeoutMS; mHttpTimeoutMS = httpTimeoutMS; mNotifyFinishedBroadcast = notifyFinishedBroadcast; } public void setTag(Object tag) { mTag = tag; } public Object getTag() { return mTag; } @Override public void run() { try { mErrorCode = ERROR_UNKNOWN; // Check if Device is currently offline: if (cancelBecauseDeviceOffline()) { onFinished(); return; } // Start request, handle response in separate handler: DefaultHttpClient httpclient = new DefaultHttpClient(getHttpParams()); if (mCookieStore == null) mCookieStore = new BasicCookieStore(); httpclient.setCookieStore(mCookieStore); modifyHttpClient(httpclient); mRequest = createRequest(); httpclient.execute(setRequestData(mRequest), getResponseHandler(httpclient)); } catch (Exception e) { setErrorCode(ERROR_GENERIC_COMMUNICATION_ERROR); onFinished(); } } /** * Override this to make changes to the HTTP client before it executes the * request. * * @param client */ protected void modifyHttpClient(DefaultHttpClient client) { // Override this if you need it. } /** * Notify all registered observers */ protected void onFinished() { if (!mNotifyFinishedBroadcast) return; Intent broadcastIntent = new Intent(mNotificationBroadcastIntentID); broadcastIntent.putExtra(BROADCAST_INTENT_EXTRA_ERROR, mErrorCode); broadcastIntent.putExtra(BROADCAST_INTENT_EXTRA_RESPONSE, mResponse); LocalBroadcastManager.getInstance(mApplicationContext).sendBroadcast(broadcastIntent); } /** * Returns TRUE if OFFLINE. * * @return boolean true if offline, or false if online. */ protected boolean cancelBecauseDeviceOffline() { if (mApplicationContext != null && !ConnectivityUtils.isDeviceOnline(mApplicationContext)) { setErrorCode(ERROR_DEVICE_OFFLINE); return true; } return false; } public void cancel() { if (mRequest != null) mRequest.abort(); } /** * Create a request object according to the request type set. * * @return HttpRequestBase request object. */ protected HttpRequestBase createRequest() { switch (mType) { case GET: return new HttpGet(getUrlWithParams()); case PUT: return new HttpPut(getUrlWithParams()); case DELETE: return new HttpDelete(getUrlWithParams()); default: return new HttpPost(getUrlWithParams()); } } protected String getUrlWithParams() { return mUrl + (mURLQueryParams != null && !mURLQueryParams.equals("") ? "?" + mURLQueryParams : ""); } public void responseHandlingFinished(T parsedResponse, int responseHttpStatus) { mActualStatusCode = responseHttpStatus; mResponse = parsedResponse; if (mActualStatusCode < 200 || mActualStatusCode >= 400) setErrorCode(ERROR_SERVER_RETURNED_ERROR); else if (mResponse == null) setErrorCode(ERROR_RESPONSE_PARSE_ERROR); else setErrorCode(ERROR_NONE); onFinished(); } @Override public T getResponseContent() { return mResponse; } protected void setErrorCode(int errorCode) { mErrorCode = errorCode; } public Map<String, String> getBody() { return mBody; } @Override public int getErrorCode() { return mErrorCode; } public int getActualStatusCode() { return mActualStatusCode; } private HttpParams getHttpParams() { HttpParams httpParameters = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParameters, mHttpTimeoutMS); HttpConnectionParams.setSoTimeout(httpParameters, mSocketTimeoutMS); return httpParameters; } /** * Update the given request before it is sent over the wire. * * @param request */ abstract protected HttpUriRequest setRequestData(HttpUriRequest request); abstract protected ResponseHandler<T> getResponseHandler(HttpClient client); public void setCookieStore(CookieStore cookieStore) { mCookieStore = cookieStore; } }