pl.llp.aircasting.util.http.HttpBuilder.java Source code

Java tutorial

Introduction

Here is the source code for pl.llp.aircasting.util.http.HttpBuilder.java

Source

/**
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;
        }
    }
}