Java tutorial
/** * Copyright (C) 2012 Cellular GmbH * * 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 de.cellular.lib.lightlib.backend; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.KeyStore; import java.util.List; import org.apache.http.HttpResponse; import org.apache.http.HttpVersion; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; 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 android.content.Context; import android.os.AsyncTask; import android.os.Message; import android.text.TextUtils; import android.webkit.WebView; import de.cellular.lib.lightlib.backend.base.LLAbstractResponse; import de.cellular.lib.lightlib.backend.base.LLRequestResponsibleObject; import de.cellular.lib.lightlib.log.LL; /** * The abortable request object based on HttpClient. <br> * Corresponding with a {@link LLRequestResponsibleObject} object. * * <p> * <strong>Known subclasses are</strong> * <p> * {@link LLRequestFile} * * @version 1.0 * @author Chris Xinyue Zhao <hasszhao@gmail.com> * */ public class LLRequest extends AsyncTask<Object, Object, Exception> { private HttpRequestBase mHttpRequestBase; protected static final int TIME_OUT = 60 * 60 * 1000; protected String mUserAgent; protected LLRequestResponsibleObject mHandler; protected Context mContext; /** * The Method for Request Mode. * * @since 1.0 */ public enum Method { /** * HTTP-GET mode. * * @since 1.0 */ GET, /** * HTTP-POST mode. You can override {@link #onWritePostBody} * * @since 1.0 */ POST, } private Method mMethod; public static final int REQUEST_FAILED = 0x34; public static final int REQUEST_SUCCESSED = 0x35; public static final int REQUEST_ABORTED = 0x36; /** * Instantiates a new {@link LLRequest}. * * @since 1.0 * * @param _context * the Context * @param _handler * the {@link LLRequestResponsibleObject} object that can response to the request. * @param _method * the request {@link Method}. */ protected LLRequest(Context _context, LLRequestResponsibleObject _handler, Method _method) { mUserAgent = new WebView(_context).getSettings().getUserAgentString(); mHandler = _handler; mMethod = _method; mContext = _context; } /** * Creates a {@link DefaultHttpClient} object. * * @since 1.0 * @param _ALLOW_ALL_HOSTNAME_VERIFIER_FOR_SSL * true allow all hostname verifier for ssl. * @return the {@link DefaultHttpClient} object */ public static DefaultHttpClient createHttpClient(boolean _ALLOW_ALL_HOSTNAME_VERIFIER_FOR_SSL) { return createHttpClient(null, _ALLOW_ALL_HOSTNAME_VERIFIER_FOR_SSL); } /** * Creates a {@link DefaultHttpClient} object. * * @since 1.0 * @param _credsProvider * the object contains connect credential info like: User, Pwd, Host etc. * @param _ALLOW_ALL_HOSTNAME_VERIFIER_FOR_SSL * true allow all hostname verifier for ssl. * @return the {@link DefaultHttpClient} object */ public static DefaultHttpClient createHttpClient(CredentialsProvider _credsProvider, boolean _ALLOW_ALL_HOSTNAME_VERIFIER_FOR_SSL) { // ------------------------------------------------------------------- // Example for _credsProvider // // String usr = getUser(); // String pwd = getPassword(); // DefaultHttpClient httpclient = new DefaultHttpClient(conMgr, params); // CredentialsProvider credsProvider = new BasicCredentialsProvider(); // credsProvider.setCredentials(new AuthScope(host, port), new UsernamePasswordCredentials(usr, pwd)); // ------------------------------------------------------------------- HttpParams params = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(params, TIME_OUT); HttpConnectionParams.setSoTimeout(params, TIME_OUT); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET); HttpProtocolParams.setUseExpectContinue(params, true); SchemeRegistry schReg = new SchemeRegistry(); PlainSocketFactory plainSocketFactory = PlainSocketFactory.getSocketFactory(); SSLSocketFactory sslSocketFactory = null; if (_ALLOW_ALL_HOSTNAME_VERIFIER_FOR_SSL) { try { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); sslSocketFactory = new EasySSLSocketFactory(trustStore); } catch (Exception _e) { LL.e(_e.toString()); sslSocketFactory = SSLSocketFactory.getSocketFactory(); } sslSocketFactory .setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); } else { sslSocketFactory = SSLSocketFactory.getSocketFactory(); } schReg.register(new Scheme("http", plainSocketFactory, 80)); schReg.register(new Scheme("https", sslSocketFactory, 443)); ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg); DefaultHttpClient httpclient = new DefaultHttpClient(conMgr, params); if (_credsProvider != null) { httpclient.setCredentialsProvider(_credsProvider); } return httpclient; } /** * Creates the {@link HttpRequestBase} object. * * @param _urlStr * the url of target in {@link String} * @return the {@link HttpRequestBase}, it could be GET oder POST. */ private HttpRequestBase createHttpRequest(String _urlStr) { switch (mMethod) { case GET: return new HttpGet(_urlStr); case POST: return new HttpPost(_urlStr); default: return null; } } @Override protected void onCancelled() { if (mHttpRequestBase != null && !mHttpRequestBase.isAborted()) { mHttpRequestBase.abort(); } if (mHandler != null) { mHandler.sendEmptyMessage(REQUEST_ABORTED); } } @Override protected Exception doInBackground(Object... _params) { Exception ret = null; if (_params == null) { LL.e(":( Request must have a URL."); } else { DefaultHttpClient client = (DefaultHttpClient) _params[0]; if (client != null) { String urlstr = (String) _params[1]; if (!TextUtils.isEmpty(urlstr)) { LL.i(":| Request: " + urlstr); mHttpRequestBase = createHttpRequest(urlstr); // ---------------------------------------- // Set header // ---------------------------------------- onAppendHeaders(mHttpRequestBase); // ---------------------------------------- // Set cookies // ---------------------------------------- StringBuilder cookies = new StringBuilder(); String onCookies = onWriteCookies(); String someCookies = (String) _params[2]; if (!TextUtils.isEmpty(onCookies)) { cookies.append(onCookies); } if (!TextUtils.isEmpty(someCookies)) { cookies.append(someCookies); } String cookiesStr = cookies.toString(); if (!TextUtils.isEmpty(cookiesStr)) { mHttpRequestBase.setHeader("Cookie", cookiesStr); } else { LL.d(":| Empty Cookies on the request."); } // ---------------------------------------- // Set body // ---------------------------------------- if (mHttpRequestBase instanceof HttpPost) { String keyValues = onWritePostBody(); if (keyValues != null) { try { ((HttpPost) mHttpRequestBase).setEntity( new StringEntity(keyValues) /*new UrlEncodedFormEntity( keyValues )*/ ); } catch (UnsupportedEncodingException _e) { LL.d(":| Ignore setting data on HTTP-POST"); } } else { LL.d(":| Empty body on HTTP-POST"); } } // ---------------------------------------- // Do request // ---------------------------------------- try { HttpResponse response = client.execute(mHttpRequestBase); if ((mHttpRequestBase != null && mHttpRequestBase.isAborted()) || mHttpRequestBase == null) { onEmptyResponse(new LLHttpClientBaseResponse(urlstr, client, response)); } else { onResponse(LLHttpClientResponse.createInstance(urlstr, client, response)); } } catch (Exception _e) { // Abort request. Aborted request could also fire an exception. abort(); LL.e(":) The exception has been caught: " + _e.toString()); ret = new LLRequestException(_e, urlstr); } } else { LL.e(":( Unknown URL String for " + getClass().getSimpleName() + "."); } } else { LL.e(":( An HttpClient is required."); } } return ret; } /** * Handler when an empty response comes. The fellow codes show when the handler will be triggered(see {@link #doInBackground(Object... )}). * <p> * if( (mHttpRequestBase != null && mHttpRequestBase.isAborted()) || mHttpRequestBase == null ) * * @param _r * the {@link LLAbstractResponse} or a decorated subclass of it that contains information after requesting. * @throws IOException * Signals that an I/O exception has occurred. */ protected void onEmptyResponse(LLAbstractResponse _r) throws IOException { LL.i(":| " + getClass().getSimpleName() + " empty response:" + _r.toString()); // Free http's thing i.e stream and client. // If error at releasing, the request seems failed as well. _r.release(); } /** * Default handler for responding request. <br> * In the default implementation the {@link #finishResponse(int, LLHttpClientBaseResponse)} is called that means the client can do super.onResponse(_r) when non-special message(just a {@link #REQUEST_SUCCESSED}) will be sent to the client. If a subclass of * {@link LLHttpClientResponse} like {@link LLImageResponse} sends a message specifies to it i.e {@link LLImageResponse}, then the {@link #finishResponse(int, LLHttpClientBaseResponse)} must be called. * * @param _r * the {@link LLAbstractResponse} or a decorated subclass of it that contains information after requesting. * @throws IOException * Signals that an I/O exception has occurred. */ protected void onResponse(LLAbstractResponse _r) throws IOException { finishResponse(REQUEST_SUCCESSED, _r); } /** * Finish response by freeing resource and sending message to the client <br> * This method can(must) be called after overriding {@link #onResponse(LLHttpClientBaseResponse)}. * * @param _msg * the message that will be sent through {@link #mHandler} * @param _r * the {@link LLAbstractResponse} or a decorated subclass of it that contains information after requesting. * @throws IOException * Signals that an I/O exception has occurred. */ protected void finishResponse(int _msg, LLAbstractResponse _r) throws IOException { LL.i(":) " + getClass().getSimpleName() + " is successfully:" + _r.toString()); if (mHandler != null) { Message msg = Message.obtain(mHandler, _msg, _r); msg.sendToTarget(); } // Free http's thing i.e stream and client. // If error at releasing, the request seems failed as well. _r.release(); } @Override protected void onPostExecute(Exception _result) { if (_result != null) { LL.e(":( Faild response."); if (mHandler != null) { Message.obtain(mHandler, REQUEST_FAILED, _result).sendToTarget(); } } } /** * Handler when append to write some cookies in subclass. * * @return the cookie in string. */ protected String onWriteCookies() { return null; } /** * Handler when some headers will be added on to request. The default version should be called to tell the backend server that the client is Android. * * @param _req * the {@link HttpRequestBase} object */ protected void onAppendHeaders(HttpRequestBase _req) { if (!TextUtils.isEmpty(mUserAgent)) { _req.setHeader("User-Agent", mUserAgent); } _req.setHeader("Accept-Encoding", "gzip"); if (mMethod == Method.POST) { _req.setHeader("Content-type", "application/x-www-form-urlencoded"); } } // /** // * Handler when a POST request need body. // * // * @return the {@link List} that are written in request body. // */ // protected List<NameValuePair> onWritePostBody() { // // List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2); // // _nameValuePairs.add(new BasicNameValuePair("id", "12345")); // // _nameValuePairs.add(new BasicNameValuePair("stringdata", "Hi")); // return null; // } /** * Handler when a POST request need body. * * @return the {@link List} that are written in request body. */ protected String onWritePostBody() { // List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2); // _nameValuePairs.add(new BasicNameValuePair("id", "12345")); // _nameValuePairs.add(new BasicNameValuePair("stringdata", "Hi")); return null; } /** * Wrap "new" a {@link LLRequest} object * * @param _context the Context * @param _handler the {@link LLRequestResponsibleObject} object * @param _method the request {@link Method} * @param _url the target url in {@link String} * @param _someCookies the cookies in {@link String} * @return the created {@link LLRequest} object. */ public static LLRequest start(Context _context, LLRequestResponsibleObject _handler, Method _method, String _url, String _someCookies) { LLRequest r = new LLRequest(_context, _handler, _method); r.execute(createHttpClient(false), _url, _someCookies); return r; } /** * Abort the request. */ public void abort() { if (mHttpRequestBase != null && !mHttpRequestBase.isAborted()) { mHttpRequestBase.abort(); } } }