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 }