| /* |
| * 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; |
| |
| import com.google.common.io.ByteStreams; |
| 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.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 { |
| |
| public static final int DEFAULT_COMMON_TIMEOUT_MS = 5000; |
| |
| private URL url; |
| private int timeout = DEFAULT_COMMON_TIMEOUT_MS; |
| private final ListeningExecutorService requestService; |
| private static final ThreadFactory threadFactory = |
| new ThreadFactoryBuilder().setNameFormat(BlockingHttpEndpoint.class.getSimpleName() + "-%d") |
| .build(); |
| |
| public BlockingHttpEndpoint(String url, int maxParallelRequests) throws MalformedURLException { |
| this.url = new URL(url); |
| |
| // 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<?> post(String content) throws IOException { |
| HttpURLConnection connection = buildConnection("POST"); |
| connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); |
| return send(connection, content); |
| } |
| |
| private ListenableFuture<?> send(final HttpURLConnection connection, final String content) { |
| return requestService.submit(new Runnable() { |
| @Override |
| public void run() { |
| try (DataOutputStream out = new DataOutputStream(connection.getOutputStream())) { |
| out.writeBytes(content); |
| out.flush(); |
| out.close(); |
| try (InputStream response = connection.getInputStream()) { |
| ByteStreams.copy(response, ByteStreams.nullOutputStream()); |
| } |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| }); |
| } |
| |
| private HttpURLConnection buildConnection(String httpMethod) throws IOException { |
| HttpURLConnection connection = (HttpURLConnection) this.url.openConnection(); |
| connection.setUseCaches(false); |
| connection.setDoOutput(true); |
| connection.setConnectTimeout(timeout); |
| connection.setReadTimeout(timeout); |
| connection.setRequestMethod(httpMethod); |
| return connection; |
| } |
| } |