Back to project page beansight-mobile-android.
The source code is released under:
Apache License
If you think the Android project beansight-mobile-android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* * Copyright (C) 2010 Thomas Dudek//from w w w.java 2 s .co m * * 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.beansight.android.http; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.params.HttpClientParams; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import android.util.Log; import com.beansight.android.api.NotAuthenticatedException; import com.beansight.android.api.ServerErrorException; /** * Fluent builder for the {@linkplain HttpClient} to simplify its usage. * * <p>To execute a request, use {@linkplain #get(String) Http.get("http://w...")} * or {@linkplain #post(String) Http.post("http://w...")}, specify at least the * {@linkplain HttpClient client} to use for this request and execute it with * one of the execution methods. * * <h2>Request execution methods overview:</h2> * <dl> * <dt>{@link HttpRequestBuilder#asResponse() .asResponse()}</dt> * <dd>execute and return the {@linkplain HttpResponse}</dd> * * <dt>{@link HttpRequestBuilder#asString() .asString()}</dt> * <dd>execute and return the content body as a String</dd> * * <dt>{@link HttpRequestBuilder#asFile(File) .asFile(File)}</dt> * <dd>execute and save the stream to a file</dd> * * <dt>{@link HttpRequestBuilder#as(ResponseHandler) .as(ResponseHandler)}</dt> * <dd>execute and process the response with the {@link ResponseHandler}</dd> * * <dt>{@link HttpRequestBuilder#consumeResponse() .consumeResponse()}</dt> * <dd>execute and consume any available content on the stream</dd> * * <dt>{@link HttpRequestBuilder#throwAwayResponse() .throwAwayResponse()}</dt> * <dd>execute and abort immediately</dd> * </dl> * * <h2>Usage examples:</h2> * <p><dl> * <dt>Simple GET-request</dt> * <dd><pre>final String site = * Http.get("http://somesite.com") * .use(client) * .asString();</pre></dd> * * <dt>Extended GET-request using a {@linkplain RequestCustomizer request * customizer} and a custom {@linkplain ResponseHandler response handler}</dt> * <dd><pre>final String ok = Http.get("http://somesite.com") * .use(client) * .header("User-Agent", "HttpClient Wrapper") * .charset("UTF-8") * .followRedirects(true) * .customize(new RequestCustomizer() { * @Override * public void customize(final HttpUriRequest request) { * HttpProtocolParams.useExpectContinue(request.getParams()); * } * }) * .as(new ResponseHandler<String>() { * @Override * public String handleResponse(final HttpResponse response) throws IOException { * final int statusCode = response.getStatusLine().getStatusCode(); * return statusCode == HttpStatus.SC_OK ? "YES" : "NO"; * } * });</pre></dd> * * <dt>Simple POST-request</dt> * <dd><pre>final HttpResponse serachResult = * Http.post("http://somesite.com") * .data("search_name", "cat") * .data("search_gender", "m") * .asResponse();</pre></dd> * </dl></p> * * @author Thomas Dudek (mastacode@gmail.com) * * @version 0.1a * @copyright Copyright ? Thomas Dudek */ public final class Http { /** * May be used to modify the {@linkplain HttpUriRequest request} just * before it is being executed. * * @see {@link HttpRequestBuilder#customize(RequestCustomizer)} */ public static interface RequestCustomizer { /** * Customizes the request before the execution is done. * * @param request the request to customize */ void customize(final HttpUriRequest request); } Http() { } /** * Creates a builder object for a POST-request. Supports data and entity * modifications. * * @param url the URL to use for this request. * @return the builder object for this URL. */ public static HttpRequestBuilder post(final String url) { return new HttpPostRequestBuilder(url); } /** * Creates a builder object for a GET-request. Supports no data nor entity * modifications. * * @param url the URL to use for this request. * @return the builder object for the this URL. */ public static HttpRequestBuilder get(final String url) { return new HttpGetRequestBuilder(url); } /** * Converts a {@linkplain HttpResponse} to a String by calling * {@link EntityUtils#toString(HttpEntity)} on its {@linkplain HttpEntity * entity}. * * @param response the {@linkplain HttpResponse response} to convert. * @param encoding the encoding to use for the conversion. * @throws NullPointerException if the given response was null * @return the response body as a String or {@code null}, if no * response body exists or an error occurred while converting. */ public static String asString(final HttpResponse response, final String encoding) { if (response == null) { throw new NullPointerException(); } final HttpEntity entity = response.getEntity(); if (entity == null) { return null; } try { return EntityUtils.toString(entity, encoding); } catch (final Exception e) { return null; } } /** * Contains a superset of builder methods for all request-type dependent * builders. */ public abstract static class HttpRequestBuilder { protected final String url; protected HttpClient client; protected List<Header> headers; protected List<RequestCustomizer> customizers; protected String charset = null; protected Boolean followRedirects; protected HttpUriRequest request; /** * Creates a new builder object for the given URL. * * @throws NullPointerException if the given URL was null * @param url the URL for the current request. */ protected HttpRequestBuilder(final String url) { if (url == null) { throw new NullPointerException("URL must not be null."); } this.url = url; } /** * Sets the entity to send with this request. * * @param entity the entity to set for this request * @throws UnsupportedOperationException * if this request not supports entity modifications * @return this builder */ public HttpRequestBuilder entity(final HttpEntity entity) { throw new UnsupportedOperationException( "This HTTP-method doesn't support to add an entity."); } /** * Appends data to send with this request. * * @param data the data to append to this request * @throws UnsupportedOperationException * if this request not supports data modifications * @return this builder */ public HttpRequestBuilder data(final NameValuePair ... data) { throw new UnsupportedOperationException( "This HTTP-method doesn't support to add data."); } /** * Appends a new {@link NameValuePair}, specified by the given * {@code name} and {@code value}, to this request. * * @param name the name of the parameter to add to this request * @param value the value of the parameter to add to this request * @throws UnsupportedOperationException * if this request not supports data modifications * @return this builder */ public HttpRequestBuilder data(final String name, final String value) { throw new UnsupportedOperationException( "This HTTP-method doesn't support to add data."); } /** * Appends the String representation of each key-value-pair of the given * map to this request. * * @param data the {@link Map} containing the data to append to this request * @throws UnsupportedOperationException * if this request not supports data modifications * @return this builder */ public HttpRequestBuilder data(final Map<?, ?> data) { throw new UnsupportedOperationException( "This HTTP-method doesn't support to add data."); } /** * Specifies the {@linkplain HttpClient client} to use for this request. * * @param client the client * @throws NullPointerException if the given {@link HttpClient} was null * @return this builder */ public HttpRequestBuilder use(final HttpClient client) { if (client == null) { throw new NullPointerException("HttpClient must not be null."); } this.client = client; return this; } /** * Adds the given {@linkplain RequestCustomizer request customizer} to * this request. All customizers are being applied sequentially just * before the request is being executed. * * @param customizer the customizer to add to this request * @return this builder */ public HttpRequestBuilder customize(final RequestCustomizer customizer) { getCustomizers().add(customizer); return this; } /** * Adds a header with the given {@code name} and {@code value} * to this request. * * @param name * @param value * @return this builder */ public HttpRequestBuilder header(final String name, final String value) { getHeaders().add(new BasicHeader(name, value)); return this; } /** * Adds the given {@linkplain Header header} to this request. * * @param header * @return this builder */ public HttpRequestBuilder header(final Header header) { getHeaders().add(header); return this; } /** * Sets the encoding for this request. * * @param charset * @return this builder */ public HttpRequestBuilder charset(final String charset) { this.charset = charset; return this; } /** * Sets the behavior of redirection following for this request. The * behavior effects for this request only. * * @param follow {@code true}, if redirects should be followed, otherwise * {@code false} * @return this builder */ public HttpRequestBuilder followRedirects(final boolean follow) { followRedirects = follow; return this; } /** * Executes this request and returns the result as a * {@linkplain HttpResponse} object. * * @return the response of this request * @throws IllegalStateException if no {@link HttpClient} was specified * @throws IOException if an error occurs while execution */ public HttpResponse asResponse() throws IOException { if (client == null) { throw new IllegalStateException( "Please specify a HttpClient instance to use for this request."); } request = createFinalRequest(); final HttpResponse response = client.execute(request); return response; } /** * Executes this request and returns the content body of the result as a * String. If no response body exists, this returns {@code null}. * * @return the response body as a String or {@code null} if * no response body exists. * @throws IOException if an error occurs while execution * @throws ServerErrorException */ public String asString() throws IOException, NotAuthenticatedException, ServerErrorException { final HttpResponse response = asResponse(); if (response.getStatusLine().getStatusCode() == 403) { throw new NotAuthenticatedException(response.getStatusLine().toString()); } final HttpEntity entity = response.getEntity(); if (entity == null) { return null; } String result = EntityUtils.toString(entity, charset); // Ugly: if the JSON doesn't contain "response", raise a server error. if(result.indexOf("\"response\"") < 0) { Log.e("Beansight", "Server Error: " + result); throw new ServerErrorException(); } return result; } /** * Executes this request and saves the response stream to a file. The * stream is going to be copied if and only if the response was * successful ({@code 2xx}) and a response body exists. If the response * code was {@code >= 300}, a {@link FileNotFoundException} is thrown. * If no body exists, this returns {@code false} and no exception is * thrown. * * @param target the file in which the stream should be copied. * @return {@code true} if the stream was copied successful to the file, * otherwise {@code false}. * @throws IOException if an error occurs while execution * @throws FileNotFoundException if the response code was {@code >= 300}. */ public boolean asFile(final File target) throws IOException { final HttpResponse response = asResponse(); return new FileResponseHandler(target, url).handleResponse(response); } /** * Executes this request and processes the response using the given * response handler. * * @param handler the response handler * @return the response object generated by the response handler. * @throws IOException if an error occurs while execution */ public <T> T as(final ResponseHandler<? extends T> handler) throws IOException { if (handler == null) { throw new NullPointerException("ResponseHandler must not be null."); } final HttpResponse response = asResponse(); return handler.handleResponse(response); } /** * Executes this request and aborts immediately after execution using * the {@linkplain HttpUriRequest#abort() abort} method of this request. * * @throws IOException if an error occurs while execution */ public void throwAwayResponse() throws IOException { asResponse(); request.abort(); } /** * Executes this request and consumes any available content on the * response stream. * * @throws IOException if an error occurs while execution */ public void consumeResponse() throws IOException { final HttpResponse response = asResponse(); final HttpEntity entity = response.getEntity(); if (entity == null) { return; } entity.consumeContent(); } abstract protected HttpUriRequest createRequest() throws IOException; protected String getUrl() { return url; } protected List<Header> getHeaders() { if (headers == null) { headers = new ArrayList<Header>(); } return headers; } protected List<RequestCustomizer> getCustomizers() { if (customizers == null) { customizers = new ArrayList<RequestCustomizer>(); } return customizers; } protected HttpClient getClient() { return client; } private HttpUriRequest createFinalRequest() throws IOException { final HttpUriRequest request = createRequest(); applyHeaders(request); if (followRedirects != null) { HttpClientParams.setRedirecting(request.getParams(), followRedirects); } applyCustomizers(request); return request; } private void applyHeaders(final HttpRequest request) { if (headers != null) { for (final Header h : headers) { request.setHeader(h); } } } private void applyCustomizers(final HttpUriRequest request) { if (customizers != null) { for (final RequestCustomizer modifier : customizers) { modifier.customize(request); } } } } // Response handlers /** * Saves the stream to a file and returns <code>true</code>, if no error * occurred while saving. * @see {@link HttpRequestBuilder#asFile(File)} */ private static class FileResponseHandler implements ResponseHandler<Boolean> { protected final File file; protected final String url; public FileResponseHandler(final File file, final String url) { this.file = file; this.url = url; } public Boolean handleResponse(final HttpResponse response) throws IOException { final int statusCode = response.getStatusLine().getStatusCode(); if (statusCode >= 300) { throw new FileNotFoundException("Source not found at " + url + ", response code " + statusCode); } final HttpEntity entity = response.getEntity(); if (entity == null) { return false; } return copyStreamToFile(entity.getContent(), file); } protected boolean copyStreamToFile(final InputStream source, final File target) throws IOException { final byte[] buffer = new byte[1024*4]; final OutputStream out = new FileOutputStream(target); int read = 0; while((read = source.read(buffer)) != -1) { out.write(buffer, 0, read); } if (out != null) { try { out.close(); } catch (final IOException e) { } } return true; } } // Request builders /** * GET-request builder. * @see {@link Http#get(String)} */ private static class HttpGetRequestBuilder extends HttpRequestBuilder { Map<String, String> params = new HashMap<String, String>(); protected HttpGetRequestBuilder(final String url) { super(url); } @Override protected HttpUriRequest createRequest() throws IOException { StringBuilder theUrl = new StringBuilder(url); Iterator<Entry<String, String>> paramsIt = params.entrySet().iterator(); if (paramsIt.hasNext()) { Entry<String, String> aParam = paramsIt.next(); theUrl.append(String.format("?%s=%s", aParam.getKey(), aParam.getValue())); } while (paramsIt.hasNext()) { Entry<String, String> aParam = paramsIt.next(); theUrl.append(String.format("&%s=%s", aParam.getKey(), aParam.getValue())); } return new HttpGet(theUrl.toString()); } @Override public HttpRequestBuilder data(String name, String value) { this.params.put(name, value); return this; } } /** * POST-request builder. Supports data and entity modifications. * @see {@link Http#post(String)} */ private static class HttpPostRequestBuilder extends HttpRequestBuilder { protected List<NameValuePair> data; protected HttpEntity entity; protected HttpPostRequestBuilder(final String url) { super(url); } @Override public HttpRequestBuilder entity(final HttpEntity entity) { if (data != null) { throw new IllegalStateException( "You cannot specify the entity after setting POST data."); } this.entity = entity; return this; } @Override public HttpRequestBuilder data(final String name, final String value) { ensureNoEntity(); getData().add(new BasicNameValuePair(name, value)); return this; } @Override public HttpRequestBuilder data(final NameValuePair... data) { ensureNoEntity(); if (data != null) { final List<NameValuePair> dataList = getData(); for (final NameValuePair d : data) { if (d != null) { dataList.add(d); } } } return this; } @Override public HttpRequestBuilder data(final Map<?, ?> data) { ensureNoEntity(); final List<NameValuePair> dataList = getData(); for (Entry<?, ?> entry : data.entrySet()) { final String name = entry.getKey().toString(); final String value = entry.getValue().toString(); dataList.add(new BasicNameValuePair(name, value)); } return this; } @Override protected HttpUriRequest createRequest() throws IOException { final HttpPost request = new HttpPost(url); if (data != null) { entity = new UrlEncodedFormEntity(data, charset); } request.setEntity(entity); return request; } protected List<NameValuePair> getData() { if (data == null) { data = new ArrayList<NameValuePair>(); } return data; } private void ensureNoEntity() { if (entity != null) { throw new IllegalStateException( "You cannot set the data after specifying a custom entity."); } } } }