Java tutorial
/** AirCasting - Share your Air! Copyright (C) 2011-2012 HabitatMap, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. You can contact the authors by email at <info@habitatmap.org> */ package pl.llp.aircasting.util.http; import android.util.Log; import org.apache.http.client.methods.HttpDelete; import pl.llp.aircasting.android.Logger; import pl.llp.aircasting.helper.SettingsHelper; import pl.llp.aircasting.util.Constants; import com.google.common.base.Joiner; import com.google.common.io.CharStreams; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.google.inject.Inject; import org.apache.http.HttpEntity; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthState; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; 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.protocol.ClientContext; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.ByteArrayBody; import org.apache.http.entity.mime.content.ContentBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.HttpContext; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.lang.reflect.Type; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.Map; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.io.Closeables.closeQuietly; import static java.net.URLEncoder.encode; public class HttpBuilder implements ChooseMethod, ChoosePath, PerformRequest { @Inject static Gson gson; @Inject static SettingsHelper settingsHelper; private String login; private String password; private boolean useLogin; private List<Parameter> parameters = newArrayList(); private String address; private Method method; private enum Method { GET, POST, DELETE; } public static ChooseMethod http() { return new HttpBuilder(); } public static <T> HttpResult<T> error() { HttpResult<T> httpResult = new HttpResult<T>(); httpResult.setStatus(Status.ERROR); return httpResult; } @Override public PerformRequest from(String path) { return to(path); } @Override public PerformRequest to(String address) { this.address = address; return this; } @Override public PerformRequest with(String key, String value) { Parameter parameter = new StringParameter(key, value); parameters.add(parameter); return this; } @Override public PerformRequest upload(String key, Uploadable thing) { Parameter parameter = new FileParameter(key, thing); parameters.add(parameter); return this; } @Override public PerformRequest authenticate(String login, String password) { this.login = login; this.password = password; this.useLogin = true; return this; } @Override public ChoosePath post() { method = Method.POST; return this; } @Override public ChoosePath get() { this.method = Method.GET; return this; } @Override public ChoosePath delete() { this.method = Method.DELETE; return this; } public HttpResult<Void> execute() { return into(Void.class); } public <T> HttpResult<T> into(Type target) { if (method == Method.POST) { return doPost(target); } else if (method == Method.GET) { return doGet(target); } else { return doDelete(target); } } private URI createPath(String path) throws URISyntaxException { return createPath(path, null); } private URI createPath(String path, String query) throws URISyntaxException { return new URI("http", null, settingsHelper.getBackendURL(), settingsHelper.getBackendPort(), path, query, null); } private String query() { StringBuilder query = new StringBuilder(); for (Parameter parameter : parameters) { if (parameter.supportsGet()) { String name = parameter.key; String value = encode(parameter.getValue()); query.append(name).append("=").append(value).append("&"); } } return query.toString(); } private <T> HttpResult<T> doGet(Type target) { try { URI path = createPath(address, query()); HttpGet get = new HttpGet(path); return doRequest(get, target); } catch (URISyntaxException e) { Logger.e("Couldn't create path", e); return error(); } } private <T> HttpResult<T> doDelete(Type target) { try { URI path = createPath(address, query()); HttpDelete delete = new HttpDelete(path); return doRequest(delete, target); } catch (URISyntaxException e) { e.printStackTrace(); return error(); } } private <T> HttpResult<T> doPost(Type target) { try { URI path = createPath(address); HttpPost post = new HttpPost(path); HttpEntity entity = prepareMultipart(); post.setEntity(entity); return doRequest(post, target); } catch (UnsupportedEncodingException e) { Logger.e("Couldn't process parameters", e); return error(); } catch (URISyntaxException e) { Logger.e("Couldn't create path", e); return error(); } } private HttpEntity prepareMultipart() throws UnsupportedEncodingException { MultipartEntity result = new MultipartEntity(); for (Parameter parameter : parameters) { result.addPart(parameter.getKey(), parameter.toBody()); } return result; } private <T> HttpResult<T> doRequest(HttpUriRequest request, Type target) { DefaultHttpClient client = new DefaultHttpClient(); HttpResult<T> result = new HttpResult<T>(); Reader reader = null; InputStream content = null; // scoped so high for debugging connectivity in devMode String fullJson = null; try { client.addRequestInterceptor(preemptiveAuth(), 0); HttpResponse response = client.execute(request); if (response.getStatusLine().getStatusCode() == 204) { result.setStatus(Status.SUCCESS); return result; } content = response.getEntity().getContent(); reader = new InputStreamReader(content); if (Constants.isDevMode()) { List<String> strings = CharStreams.readLines(reader); fullJson = Joiner.on("\n").join(strings); reader = new StringReader(fullJson); Logger.i("Full http json: " + fullJson); } if (response.getStatusLine().getStatusCode() < 300) { result.setStatus(Status.SUCCESS); T output = gson.fromJson(reader, target); result.setContent(output); } else { result.setStatus(Status.FAILURE); Type type = new TypeToken<Map<String, String[]>>() { }.getType(); Map<String, String[]> output = gson.fromJson(reader, type); result.setErrors(output); } } catch (Exception e) { Logger.e("Http request failed", e); result.setStatus(Status.ERROR); return result; } finally { closeQuietly(content); closeQuietly(reader); client.getConnectionManager().shutdown(); } return result; } private HttpRequestInterceptor preemptiveAuth() { return new HttpRequestInterceptor() { @Override public void process(HttpRequest httpRequest, HttpContext httpContext) throws HttpException, IOException { AuthState authState = (AuthState) httpContext.getAttribute(ClientContext.TARGET_AUTH_STATE); Credentials credentials; if (useLogin) { credentials = new UsernamePasswordCredentials(login, password); } else { credentials = new UsernamePasswordCredentials(settingsHelper.getAuthToken(), "X"); } authState.setAuthScope(AuthScope.ANY); authState.setAuthScheme(new BasicScheme()); authState.setCredentials(credentials); } }; } private abstract static class Parameter { private String key; public abstract ContentBody toBody() throws UnsupportedEncodingException; public Parameter(String key) { this.key = key; } public String getKey() { return key; } public abstract boolean supportsGet(); public abstract String getValue(); } private static class StringParameter extends Parameter { public String value; private StringParameter(String key, String value) { super(key); this.value = value; } @Override public ContentBody toBody() throws UnsupportedEncodingException { return new StringBody(value); } @Override public boolean supportsGet() { return true; } @Override public String getValue() { return value; } } private static class FileParameter extends Parameter { private Uploadable uploadable; public FileParameter(String key, Uploadable uploadable) { super(key); this.uploadable = uploadable; } @Override public ContentBody toBody() throws UnsupportedEncodingException { return new ByteArrayBody(uploadable.getData(), uploadable.getFilename()); } @Override public boolean supportsGet() { return false; } @Override public String getValue() { return null; } } }