View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.client5.http.impl.classic;
28  
29  import java.io.Closeable;
30  import java.io.IOException;
31  import java.util.concurrent.ExecutorService;
32  import java.util.concurrent.Executors;
33  import java.util.concurrent.FutureTask;
34  import java.util.concurrent.atomic.AtomicBoolean;
35  
36  import org.apache.hc.client5.http.classic.HttpClient;
37  import org.apache.hc.core5.annotation.Contract;
38  import org.apache.hc.core5.annotation.ThreadingBehavior;
39  import org.apache.hc.core5.concurrent.FutureCallback;
40  import org.apache.hc.core5.http.ClassicHttpRequest;
41  import org.apache.hc.core5.http.io.HttpClientResponseHandler;
42  import org.apache.hc.core5.http.protocol.HttpContext;
43  
44  /**
45   * This class schedules message execution execution and processing
46   * as {@link FutureTask}s with the provided {@link ExecutorService}.
47   */
48  @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
49  public class FutureRequestExecutionService implements Closeable {
50  
51      private final HttpClient httpclient;
52      private final ExecutorService executorService;
53      private final FutureRequestExecutionMetrics metrics = new FutureRequestExecutionMetrics();
54      private final AtomicBoolean closed = new AtomicBoolean(false);
55  
56      /**
57       * Create a new FutureRequestExecutionService.
58       *
59       * @param httpClient
60       *            you should tune your httpclient instance to match your needs. You should
61       *            align the max number of connections in the pool and the number of threads
62       *            in the executor; it doesn't make sense to have more threads than connections
63       *            and if you have less connections than threads, the threads will just end up
64       *            blocking on getting a connection from the pool.
65       * @param executorService
66       *            any executorService will do here. E.g.
67       *            {@link Executors#newFixedThreadPool(int)}
68       */
69      public FutureRequestExecutionService(
70              final HttpClient httpClient,
71              final ExecutorService executorService) {
72          this.httpclient = httpClient;
73          this.executorService = executorService;
74      }
75  
76      /**
77       * Schedule a request for execution.
78       *
79       * @param <T>
80       *
81       * @param request
82       *            request to execute
83       * @param HttpClientResponseHandler
84       *            handler that will process the response.
85       * @return HttpAsyncClientFutureTask for the scheduled request.
86       */
87      public <T> FutureTask<T> execute(
88              final ClassicHttpRequest request,
89              final HttpContext context,
90              final HttpClientResponseHandler<T> HttpClientResponseHandler) {
91          return execute(request, context, HttpClientResponseHandler, null);
92      }
93  
94      /**
95       * Schedule a request for execution.
96       *
97       * @param <T>
98       *
99       * @param request
100      *            request to execute
101      * @param context
102      *            optional context; use null if not needed.
103      * @param HttpClientResponseHandler
104      *            handler that will process the response.
105      * @param callback
106      *            callback handler that will be called when the request is scheduled,
107      *            started, completed, failed, or cancelled.
108      * @return HttpAsyncClientFutureTask for the scheduled request.
109      */
110     public <T> FutureTask<T> execute(
111             final ClassicHttpRequest request,
112             final HttpContext context,
113             final HttpClientResponseHandler<T> HttpClientResponseHandler,
114             final FutureCallback<T> callback) {
115         if (closed.get()) {
116             throw new IllegalStateException("Close has been called on this httpclient instance.");
117         }
118         metrics.getScheduledConnections().incrementAndGet();
119         final HttpRequestTaskCallable<T> callable = new HttpRequestTaskCallable<>(
120                 httpclient, request, context, HttpClientResponseHandler, callback, metrics);
121         final HttpRequestFutureTask<T> httpRequestFutureTask = new HttpRequestFutureTask<>(
122                 request, callable);
123         executorService.execute(httpRequestFutureTask);
124 
125         return httpRequestFutureTask;
126     }
127 
128     /**
129      * @return metrics gathered for this instance.
130      * @see FutureRequestExecutionMetrics
131      */
132     public FutureRequestExecutionMetrics metrics() {
133         return metrics;
134     }
135 
136     @Override
137     public void close() throws IOException {
138         closed.set(true);
139         executorService.shutdownNow();
140         if (httpclient instanceof Closeable) {
141             ((Closeable) httpclient).close();
142         }
143     }
144 }