com.facebook.buck.util.network.BlockingHttpEndpoint.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.util.network.BlockingHttpEndpoint.java

Source

/*
 * Copyright 2012-present Facebook, Inc.
 *
 * 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.facebook.buck.util.network;

import com.facebook.buck.log.Logger;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.io.CharStreams;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * HttpEndpoint implementation which only allows a certain number of concurrent requests to be in
 * flight at any given point in time.
 */
public class BlockingHttpEndpoint implements HttpEndpoint {

    private static final Logger LOG = Logger.get(BlockingHttpEndpoint.class);

    public static final int DEFAULT_COMMON_TIMEOUT_MS = 5000;
    private URL url;
    private int timeoutMillis;
    private final ListeningExecutorService requestService;
    private static final ThreadFactory threadFactory = new ThreadFactoryBuilder()
            .setNameFormat(BlockingHttpEndpoint.class.getSimpleName() + "-%d").build();

    public BlockingHttpEndpoint(String url, int maxParallelRequests, int timeoutMillis)
            throws MalformedURLException {
        this.url = new URL(url);
        this.timeoutMillis = timeoutMillis;

        // Create an ExecutorService that blocks after N requests are in flight.  Taken from
        // http://www.springone2gx.com/blog/billy_newport/2011/05/there_s_more_to_configuring_threadpools_than_thread_pool_size
        LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(maxParallelRequests);
        ExecutorService executor = new ThreadPoolExecutor(maxParallelRequests, maxParallelRequests, 2L,
                TimeUnit.MINUTES, workQueue, threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
        requestService = MoreExecutors.listeningDecorator(executor);
    }

    @Override
    public ListenableFuture<HttpResponse> post(final String content) {
        return requestService.submit(() -> {
            try {
                HttpURLConnection connection = buildConnection("POST");
                connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                return send(connection, content);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }

    @VisibleForTesting
    HttpResponse send(final HttpURLConnection connection, final String content) throws IOException {
        try (DataOutputStream out = new DataOutputStream(connection.getOutputStream())) {
            out.writeBytes(content);
            out.flush();
            out.close();
            InputStream inputStream = connection.getInputStream();
            String response = CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8));
            return new HttpResponse(response);
        } finally {
            connection.disconnect();
        }
    }

    private HttpURLConnection buildConnection(String httpMethod) throws IOException {
        HttpURLConnection connection = (HttpURLConnection) this.url.openConnection();
        connection.setUseCaches(false);
        connection.setDoOutput(true);
        connection.setConnectTimeout(timeoutMillis);
        connection.setReadTimeout(timeoutMillis);
        connection.setRequestMethod(httpMethod);
        return connection;
    }

    /**
     * Attempt to complete submitted requests on close so that as much information is recorded as
     * possible. This aids debugging when close is called during exception processing.
     */
    @Override
    public void close() {
        requestService.shutdown();
        try {
            if (!requestService.awaitTermination(timeoutMillis, TimeUnit.MILLISECONDS)) {
                LOG.warn(Joiner.on(System.lineSeparator()).join(
                        "A BlockingHttpEndpoint failed to shut down within the standard timeout.",
                        "Your build might have succeeded, but some requests made to ",
                        this.url + " were probably lost.", "Here's some debugging information:",
                        requestService.toString()));
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}