tech.sirwellington.alchemy.http.AlchemyHttpBuilder.java Source code

Java tutorial

Introduction

Here is the source code for tech.sirwellington.alchemy.http.AlchemyHttpBuilder.java

Source

/*
 * Copyright 2015 SirWellington Tech.
 *
 * 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 tech.sirwellington.alchemy.http;

import com.google.common.collect.Maps;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.HttpClientBuilder;
import tech.sirwellington.alchemy.annotations.arguments.NonEmpty;
import tech.sirwellington.alchemy.annotations.arguments.Optional;
import tech.sirwellington.alchemy.annotations.arguments.Positive;
import tech.sirwellington.alchemy.annotations.arguments.Required;
import tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern;

import static java.util.concurrent.TimeUnit.SECONDS;
import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.BUILDER;
import static tech.sirwellington.alchemy.arguments.Arguments.checkThat;
import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull;
import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.positiveInteger;
import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString;
import static tech.sirwellington.alchemy.http.Constants.DEFAULT_HEADERS;

/**
 *
 * @author SirWellington
 */
@BuilderPattern(role = BUILDER)
public final class AlchemyHttpBuilder {
    private static final HttpClient DEFAULT_APACHE_CLIENT = createDefaultApacheClient();

    private HttpClient apacheHttpClient = DEFAULT_APACHE_CLIENT;
    private ExecutorService executor = MoreExecutors.newDirectExecutorService();

    //Copy from DEFAULT HEADERS
    private final Map<String, String> defaultHeaders = Maps.newHashMap(DEFAULT_HEADERS);

    static AlchemyHttpBuilder newInstance() {
        return new AlchemyHttpBuilder();
    }

    AlchemyHttpBuilder() {
    }

    public AlchemyHttpBuilder usingApacheHttpClient(@Required HttpClient apacheHttpClient)
            throws IllegalArgumentException {
        checkThat(apacheHttpClient).is(notNull());

        this.apacheHttpClient = apacheHttpClient;
        return this;
    }

    /**
     * Directly sets the Executor Service to use for Asynchronous Requests. Asynchronous requests only happen when the
     * {@linkplain  AlchemyRequest.Step4#onSuccess(tech.sirwellington.alchemy.http.AlchemyRequest.OnSuccess) Callback}
     * is set on the Request.
     *
     * @param executor
     * @return
     * @throws IllegalArgumentException
     */
    public AlchemyHttpBuilder usingExecutorService(@Required ExecutorService executor)
            throws IllegalArgumentException {
        checkThat(executor).is(notNull());

        this.executor = executor;
        return this;
    }

    /**
     * Sets the Timeout to be used for each request.
     * 
     * Note that this overrides any previously set 
     * {@linkplain #usingApacheHttpClient(org.apache.http.client.HttpClient) Apache Http Clients}.
     * <p>
     * If you wish to use a custom HTTP Client and a default timeout, then use a {@link RequestConfig} and set it
     * on your custom {@linkplain HttpClient Client}.
     * 
     * @param timeout Must be positive.
     * @param timeUnit The Unit of Time to use for the timeout.
     * 
     * @return 
     */
    public AlchemyHttpBuilder usingTimeout(@Positive int timeout, TimeUnit timeUnit) {
        checkThat(timeout).usingMessage("timeout must be > 0").is(positiveInteger());

        checkThat(timeUnit).usingMessage("missing timeunit").is(notNull());

        this.apacheHttpClient = createDefaultApacheClient(timeout, timeUnit);

        return this;
    }

    public AlchemyHttpBuilder enableAsyncCallbacks() {
        return usingExecutorService(Executors.newSingleThreadExecutor());
    }

    public AlchemyHttpBuilder disableAsyncCallbacks() {
        return usingExecutorService(MoreExecutors.newDirectExecutorService());
    }

    public AlchemyHttpBuilder usingDefaultHeaders(@Required Map<String, String> defaultHeaders)
            throws IllegalArgumentException {
        checkThat(defaultHeaders).is(notNull());

        this.defaultHeaders.clear();
        this.defaultHeaders.putAll(defaultHeaders);
        return this;
    }

    public AlchemyHttpBuilder usingDefaultHeader(@NonEmpty String key, @Optional String value)
            throws IllegalArgumentException {
        checkThat(key).usingMessage("missing key").is(nonEmptyString());

        this.defaultHeaders.put(key, value);
        return this;
    }

    public AlchemyHttp build() throws IllegalStateException {
        checkThat(apacheHttpClient).throwing(ex -> new IllegalStateException("missing apache HTTP Client"))
                .is(notNull());

        checkThat(executor).throwing(ex -> new IllegalStateException("missing Executor Service")).is(notNull());

        AlchemyHttpStateMachine stateMachine = buildTheStateMachine();

        return new AlchemyHttpImpl(defaultHeaders, stateMachine);
    }

    private AlchemyHttpStateMachine buildTheStateMachine() {
        return AlchemyHttpStateMachine.Builder.newInstance().usingApacheHttpClient(apacheHttpClient)
                .usingExecutorService(executor).build();
    }

    private static HttpClient createDefaultApacheClient() {
        return createDefaultApacheClient(45, SECONDS);
    }

    private static HttpClient createDefaultApacheClient(int timeout, TimeUnit timeUnit) {
        int actualTimeout = (int) TimeUnit.SECONDS.toMillis(timeout);

        //I really hate that they don't specify the expected Unit for the Timeout.
        RequestConfig config = RequestConfig.custom().setSocketTimeout(actualTimeout)
                .setConnectTimeout(actualTimeout).setConnectionRequestTimeout(actualTimeout).build();

        return HttpClientBuilder.create().setDefaultRequestConfig(config).build();
    }

}