package org.itsnat.droid.impl.browser; import android.os.AsyncTask; import org.apache.http.params.HttpParams; import org.itsnat.droid.ClientErrorMode; import org.itsnat.droid.GenericHttpClient; import org.itsnat.droid.HttpRequestResult; import org.itsnat.droid.ItsNatDroidException; import org.itsnat.droid.ItsNatDroidServerResponseException; import org.itsnat.droid.OnHttpRequestErrorListener; import org.itsnat.droid.OnHttpRequestListener; import org.itsnat.droid.impl.util.NameValue; import org.itsnat.droid.impl.util.StringUtil; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Created by jmarranz on 9/10/14. */ public class GenericHttpClientImpl extends GenericHttpClientBaseImpl implements GenericHttpClient { protected String userUrl; protected String method = "POST"; // Por defecto protected RequestPropertyMap requestPropertyMap; protected HttpParams httpParams; protected int connectTimeout; protected int readTimeout; protected List<NameValue> paramList = new ArrayList<NameValue>(10); protected String overrideMime; protected int errorMode = ClientErrorMode.SHOW_SERVER_AND_CLIENT_ERRORS; public GenericHttpClientImpl(ItsNatDocImpl itsNatDoc) { super(itsNatDoc); PageImpl page = itsNatDoc.getPageImpl(); this.requestPropertyMap = page.getRequestPropertyMapImpl().copy(); this.httpParams = page.getHttpParams().copy(); this.connectTimeout = page.getConnectTimeout(); this.readTimeout = page.getReadTimeout(); } @Override public GenericHttpClient setClientErrorMode(int errorMode) { setClientErrorModeNotFluid(errorMode); return this; } public int getClientErrorMode() { return errorMode; } public void setClientErrorModeNotFluid(int errorMode) { // if (errorMode == ClientErrorMode.NOT_CATCH_ERRORS) throw new ItsNatDroidException("ClientErrorMode.NOT_CATCH_ERRORS is not supported"); // No tiene mucho sentido porque el objetivo es dejar fallar y si el usuario no ha registrado "error listeners" ItsNat Droid deja siempre fallar lanzando la excepción this.errorMode = errorMode; } @Override public GenericHttpClient setOnHttpRequestListener(OnHttpRequestListener listener) { setOnHttpRequestListenerNotFluid(listener); return this; } @Override public GenericHttpClient setOnHttpRequestErrorListener(OnHttpRequestErrorListener httpErrorListener) { setOnHttpRequestErrorListenerNotFluid(httpErrorListener); return this; } @Override public GenericHttpClient setRequestMethod(String method) { setMethodNotFluid(method); return this; } public void setMethodNotFluid(String method) { this.method = method; } @Override public GenericHttpClient setURL(String url) { setURLNotFluid(url); return this; } private void setURLNotFluid(String url) { this.userUrl = url; } protected String getFinalURL() { // En el ejemplo ItsNatDroidServletNoItsNat se hace un ejemplo de llamada a GenericHttpClient con código custom en donde no se especifica un userUrl por lo que se usa el pageURLBase de contexto // que en ese caso es el path al servlet ItsNatDroidServletNoItsNat, de esa manera nos evitamos formar un URL y las llamadas específicas las hacemos a través de los parámetros // Podría ser también un path a un JSP (a fin de cuentas un JSP es un servlet). return StringUtil.isEmpty(userUrl) ? itsNatDoc.getPageImpl().getPageURLBase() : userUrl; // Como se puede ver seguridad de "single server" ninguna } public void addParamNotFluid(String name,Object value) { // Si se añade el mismo parámetro varias veces, es el caso de multivalor paramList.add(new NameValue(name, value)); } public void clearParamsNotFluid() { paramList.clear(); } @Override public GenericHttpClient addParameter(String name, Object value) { addParamNotFluid(name, value); return this; } @Override public GenericHttpClient clearParameters() { clearParamsNotFluid(); return this; } public RequestPropertyMap getRequestPropertyMap() { return requestPropertyMap; } @Override public GenericHttpClient addRequestProperty(String name, String value) { requestPropertyMap.addProperty(name, value); return this; } @Override public GenericHttpClient setRequestProperty(String name, String value) { requestPropertyMap.setProperty(name, value); return this; } @Override public boolean removeProperty(String name) { return requestPropertyMap.removeProperty(name); } @Override public String getRequestProperty(String name) { return requestPropertyMap.getPropertySingle(name); } @Override public Map<String, List<String>> getRequestProperties() { return requestPropertyMap.getPropertyUnmodifiableMap(); } public HttpParams getHttpParams() { return httpParams; } @Override public GenericHttpClient setConnectTimeout(int timeoutMillis) { this.connectTimeout = timeoutMillis; return this; } @Override public int getConnectTimeout() { return connectTimeout; } @Override public GenericHttpClient setReadTimeout(int timeoutMillis) { this.readTimeout = timeoutMillis; return this; } @Override public int getReadTimeout() { return readTimeout; } public void setOverrideMimeTypeNotFluid(String mime) { this.overrideMime = mime; } @Override public GenericHttpClient setOverrideMimeType(String mime) { setOverrideMimeTypeNotFluid(mime); return this; } public void request(boolean async) // Interno { if (async) requestAsync(); else requestSync(); } @Override public HttpRequestResult requestSync() { String url = getFinalURL(); HttpRequestData httpRequestData = new HttpRequestData(this); List<NameValue> paramList = this.paramList; // No hace falta clonar pues es síncrono HttpRequestResultOKImpl result; try { result = executeInBackground(this,method, url, httpRequestData, paramList, overrideMime); } catch (Exception ex) { int errorMode = getClientErrorMode(); onFinishError(this,ex, httpRequestListener,errorListener,errorMode); return null; // No podemos seguir } onFinishOk(this, result, httpRequestListener, errorListener); return result; } @Override public void requestAsync() { String url = getFinalURL(); HttpRequestData httpRequestData = new HttpRequestData(this); List<NameValue> paramList = new ArrayList<NameValue>(this.paramList); // hace una copia, los NameValue son de sólo lectura por lo que no hay problema de compartirlos en hilos GenericHttpClientAsyncTask task = new GenericHttpClientAsyncTask(this,method,url,httpRequestData, paramList, httpRequestListener,errorListener,overrideMime); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); // Con execute() a secas se ejecuta en un "pool" de un sólo hilo sin verdadero paralelismo } protected static HttpRequestResultOKImpl executeInBackground(GenericHttpClientImpl parent,String method,String url,HttpRequestData httpRequestData,List<NameValue> paramList,String overrideMime) throws Exception { // Multihilo en el caso async return HttpUtil.httpAction(method,url, httpRequestData, paramList, overrideMime); } public static void onFinishOk(GenericHttpClientImpl parent,HttpRequestResultOKImpl result,OnHttpRequestListener listener,OnHttpRequestErrorListener errorListener) { try { parent.processResult(result,listener); } catch(Exception ex) { if (errorListener != null) { HttpRequestResult resultError = (ex instanceof ItsNatDroidServerResponseException) ? ((ItsNatDroidServerResponseException)ex).getHttpRequestResult() : result; errorListener.onError(parent.getPageImpl(), ex, resultError); } else { if (ex instanceof ItsNatDroidException) throw (ItsNatDroidException)ex; else throw new ItsNatDroidException(ex); } } } public static void onFinishError(GenericHttpClientImpl parent,Exception ex,OnHttpRequestListener listener,OnHttpRequestErrorListener errorListener,int errorMode) { HttpRequestResult result = (ex instanceof ItsNatDroidServerResponseException) ? ((ItsNatDroidServerResponseException)ex).getHttpRequestResult() : null; ItsNatDroidException exFinal = GenericHttpClientBaseImpl.convertException(ex); if (errorListener != null) { errorListener.onError(parent.getPageImpl(),exFinal, result); } else { if (errorMode != ClientErrorMode.NOT_CATCH_ERRORS) { // Error del servidor, lo normal es que haya lanzado una excepción ItsNatDocImpl itsNatDoc = parent.getItsNatDocImpl(); itsNatDoc.showErrorMessage(true, result,exFinal, errorMode); } else throw exFinal; } } }