retrovolley.request.RequestBuilder.java Source code

Java tutorial

Introduction

Here is the source code for retrovolley.request.RequestBuilder.java

Source

/*
 * Copyright (C) 2015 Serghei (Serj) Lotutovici
 * Copyright (C) 2015 Konstantin Tarasenko
 *
 * 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 retrovolley.request;

import android.util.Pair;

import com.android.volley.DefaultRetryPolicy;
import com.android.volley.RetryPolicy;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
import retrovolley.rest.Hateoasles;
import retrovolley.rest.RestCall;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * An abstract request builder, that should be used as facade layer
 * for different request constructions.
 *
 * @author Serghei Lotutovici
 */
public class RequestBuilder<T> {

    /**
     * Debug tag for RequestBuilder
     */
    private static final String TAG = RequestBuilder.class.getName();

    /**
     * Default request cache time (8 hours)
     */
    private static final long DEFAULT_REQUEST_CACHE_TIME = 8 * 60 * 60 * 1000;

    /**
     * Request parameters
     */
    private List<Pair<String, String>> mParams;

    /**
     * Additional headers specified by the request builder
     */
    private Map<String, String> mHeaders;

    /**
     * Url parameters map
     */
    private Map<String, String> mRestParams;

    /**
     * The request body, disables the parameter usage
     */
    private String mBody;

    /**
     * Request flow listener
     */
    private RequestListener<T> mRequestListener;

    /**
     * Should cache flag
     */
    private boolean mShouldCache;

    /**
     * Request tag object
     */
    private Object mTag;

    /**
     * Annotations processor object that is initialized from a restCall object instance
     */
    private final RequestInfo mRequestInfo;

    /**
     * Initial timeout for request
     */
    private int mTimeout = DefaultRetryPolicy.DEFAULT_TIMEOUT_MS;

    /**
     * Number of retries
     */
    private int mNumberOfRetries = DefaultRetryPolicy.DEFAULT_MAX_RETRIES;

    /**
     * used to increase request timeout for
     * next attempts multiplying current timeout value by this amount
     */
    private float mBackOffMultiplier = DefaultRetryPolicy.DEFAULT_BACKOFF_MULT;

    /**
     * Creates an instance of the request builder
     */
    public RequestBuilder(final RestCall restCall) {
        this(new RequestInfo(restCall));
    }

    /**
     * Create an instance of the request builder from a hateoas link
     *
     * @param restCall The rest call
     * @param link     The hateoas link to use
     */
    public RequestBuilder(final RestCall restCall, final Hateoasles link) {
        this(new HateoasRequestInfo(restCall, link));
    }

    public RequestBuilder(final RestCall restCall, final String url) {
        this(new DynamicRequestInfo(restCall, url));
    }

    private RequestBuilder(RequestInfo requestInfo) {
        super();
        mBody = null;
        mParams = new ArrayList<Pair<String, String>>();
        mHeaders = new HashMap<String, String>();
        mRestParams = new HashMap<String, String>();
        mShouldCache = false;
        mRequestInfo = requestInfo;
        if (requestInfo.getMaxNumRetries() > -1) {
            mNumberOfRetries = requestInfo.getMaxNumRetries();
        }
    }

    protected List<Pair<String, String>> getParams() {
        return mParams;
    }

    protected Map<String, String> getHeaders() {
        return mHeaders;
    }

    protected String getBody() {
        return mBody;
    }

    protected boolean getShouldCache() {
        return mShouldCache;
    }

    protected RetryPolicy getRetryPolicy() {
        return new DefaultRetryPolicy(mTimeout, mNumberOfRetries, mBackOffMultiplier);
    }

    /**
     * Add a request parameter for the request. <br>
     * <b>Note: </b> If {@link RequestBuilder#setBody(java.lang.String)}
     * is called, all parameters will be ignored.
     *
     * @param key   The parameter key
     * @param value The parameter value
     * @return The same builder instance
     */
    public RequestBuilder addParam(String key, String value) {
        mParams.add(Pair.create(key, value));
        return this;
    }

    /**
     * Add an array parameter for the request. <br>
     * <b>Note: </b> If {@link RequestBuilder#setBody(java.lang.String)}
     * is called, all parameters will be ignored.
     *
     * @param key   The array parameter key
     * @param values The array parameter values
     * @return The same builder instance
     */
    public RequestBuilder addParams(String key, Iterable<String> values) {
        for (String value : values) {
            mParams.add(Pair.create(key, value));
        }
        return this;
    }

    /**
     * Add additional request header.
     *
     * @param key   The header key
     * @param value The header value
     * @return The same builder instance
     */
    public RequestBuilder addHeader(String key, String value) {
        mHeaders.put(key, value);
        return this;
    }

    /**
     * Add rest url parameters that will be replaced on runtyme
     *
     * @param key   The param key that matches the one set in RestCall object
     * @param value The param value that needs to be replaced
     * @return The same builder instance
     */
    public RequestBuilder addRestParam(String key, String value) {
        RequestInfo.validateParameterName(mRequestInfo, key);
        mRestParams.put(key, value);
        return this;
    }

    /**
     * Set a request body to the request.<br>
     * <b>Note: </b> Calling this method with a non null parameter will ignore the usage of
     * request parameters.
     *
     * @param body The string body to set.
     * @return Same builder instance.
     */
    public RequestBuilder setBody(String body) {
        mBody = body;
        return this;
    }

    /**
     * Set a request body to the request.<br>
     * <b>Note: </b> Calling this method with a non null parameter will ignore the usage of
     * request parameters.
     *
     * @param body The body object to set
     * @param <E>  The body's Java type.
     * @return Same builder instance.
     */
    public <E> RequestBuilder setBody(E body) {
        return setBody(mRequestInfo.getEndpoint().getConverter().toBody(body));
    }

    /**
     * Set a generic smart request listener, which will result in a more convient way of error
     * handling
     *
     * @param mRequestListener An instance of the smart request listener
     * @return Same builder instance
     */
    public RequestBuilder setRequestListener(RequestListener<T> mRequestListener) {
        this.mRequestListener = mRequestListener;
        return this;
    }

    public RequestBuilder shouldCache(boolean shouldCache) {
        mShouldCache = shouldCache;
        return this;
    }

    public RequestBuilder setTag(Object tag) {
        mTag = tag;
        return this;
    }

    /**
     * Sets initial socket timeout for requests
     *
     * @param timeoutMs in ms
     */
    public void setTimeout(int timeoutMs) {
        mTimeout = timeoutMs;
    }

    /**
     * Sets number of retries
     *
     * @param numberOfRetries
     */
    public void setNumberOfRetries(int numberOfRetries) {
        mNumberOfRetries = numberOfRetries;
    }

    /**
     * used to increase request timeout for consecutive attempts
     * multiplying current timeout value by this amount
     *
     * @param backOffMultiplier float value of multiplier
     */
    public void setBackOffMultiplier(float backOffMultiplier) {
        mBackOffMultiplier = backOffMultiplier;
    }

    /**
     * Build a basic POJO request. As a result there will be the specified Java object.
     *
     * @return A new instance of PojoRequest.
     */
    public PojoRequest<T> build() {

        /* Create request */
        PojoRequest<T> request = new PojoRequest<T>(mRequestInfo.getMethod(), buildUrl(), mRequestListener,
                getHeaders(), getParams(), getShouldCache(), DEFAULT_REQUEST_CACHE_TIME,
                mRequestInfo.getResponseType(), getRetryPolicy(), mRequestInfo.getEndpoint());

        /* Append body if set */
        if (getBody() != null) {
            request.setJsonBody(getBody());
        }

        /* Add tag to request tag */
        if (mTag != null) {
            request.setTag(mTag);
        }

        /* Return constructed request */
        return request;
    }

    /**
     * Build the POJO request and execute
     */
    public void execute() {
        build().execute();
    }

    /**
     * Build the url for request execution. Add additional parameters if necessary.
     *
     * @return The request url
     */
    private String buildUrl() {
        /* By default the url is stored in the request info object */
        String url = mRequestInfo.getUrl();

        /* Append get parameters if request method is GET */
        if (mRequestInfo.getMethod() == RequestMethod.GET.method) {
            url = buildGETUrl(url, getParams());
        }

        /* Insert rest params to the request path if needed */
        if (mRequestInfo.getRestParams() != null) {
            /* Check that we have the same number of parameters set */
            if (mRequestInfo.getRestParams().size() != mRestParams.size()) {
                throw new IllegalArgumentException(String.format(
                        "The number of url parameters: %s doesn't match the number of injected parameters: %s",
                        mRequestInfo.getRestParams().toString(), mRestParams.toString()));
            }

            /* Replace by key every parameter */
            for (Map.Entry<String, String> entry : mRestParams.entrySet()) {
                url = url.replace("{" + entry.getKey() + "}", entry.getValue());
            }
        }

        return url;
    }

    /**
     * Prepare the GET request URL and append required parameters
     *
     * @param url    The url string value
     * @param params The parameters map
     * @return A new url with appended parameters
     */
    public static String buildGETUrl(String url, List<Pair<String, String>> params) {
        final StringBuilder urlBuilder = new StringBuilder(url);

        /* Simple null check */
        if (params != null && !params.isEmpty()) {
            /* Add question mark is not present */
            if (!url.endsWith("?")) {
                urlBuilder.append("?");
            }

            /* Create GET parameters */
            List<NameValuePair> getParams = new LinkedList<NameValuePair>();
            for (Pair<String, String> param : params) {
                getParams.add(new BasicNameValuePair(param.first, param.second));
            }

            urlBuilder.append(URLEncodedUtils.format(getParams, "utf-8"));
        }

        return urlBuilder.toString();
    }
}