001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.http.client;
018
019import java.io.Closeable;
020import java.io.IOException;
021import java.net.URI;
022
023import org.apache.http.client.HttpClient;
024import org.apache.http.client.config.RequestConfig;
025import org.apache.http.client.methods.Configurable;
026import org.apache.http.client.methods.HttpUriRequest;
027import org.apache.http.client.protocol.HttpClientContext;
028import org.apache.http.impl.client.CloseableHttpClient;
029import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
030import org.apache.http.impl.nio.client.HttpAsyncClients;
031import org.apache.http.nio.client.HttpAsyncClient;
032import org.apache.http.protocol.HttpContext;
033
034import org.springframework.beans.factory.InitializingBean;
035import org.springframework.http.HttpMethod;
036import org.springframework.util.Assert;
037
038/**
039 * Asynchronous extension of the {@link HttpComponentsClientHttpRequestFactory}. Uses
040 * <a href="https://hc.apache.org/httpcomponents-asyncclient-dev/">Apache HttpComponents
041 * HttpAsyncClient 4.0</a> to create requests.
042 *
043 * @author Arjen Poutsma
044 * @author Stephane Nicoll
045 * @since 4.0
046 * @see HttpAsyncClient
047 */
048public class HttpComponentsAsyncClientHttpRequestFactory extends HttpComponentsClientHttpRequestFactory
049                implements AsyncClientHttpRequestFactory, InitializingBean {
050
051        private HttpAsyncClient asyncClient;
052
053
054        /**
055         * Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
056         * with a default {@link HttpAsyncClient} and {@link HttpClient}.
057         */
058        public HttpComponentsAsyncClientHttpRequestFactory() {
059                super();
060                this.asyncClient = HttpAsyncClients.createSystem();
061        }
062
063        /**
064         * Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
065         * with the given {@link HttpAsyncClient} instance and a default {@link HttpClient}.
066         * @param asyncClient the HttpAsyncClient instance to use for this request factory
067         * @since 4.3.10
068         */
069        public HttpComponentsAsyncClientHttpRequestFactory(HttpAsyncClient asyncClient) {
070                super();
071                setAsyncClient(asyncClient);
072        }
073
074        /**
075         * Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
076         * with the given {@link CloseableHttpAsyncClient} instance and a default {@link HttpClient}.
077         * @param asyncClient the CloseableHttpAsyncClient instance to use for this request factory
078         */
079        public HttpComponentsAsyncClientHttpRequestFactory(CloseableHttpAsyncClient asyncClient) {
080                super();
081                setAsyncClient(asyncClient);
082        }
083
084        /**
085         * Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
086         * with the given {@link HttpClient} and {@link HttpAsyncClient} instances.
087         * @param httpClient the HttpClient instance to use for this request factory
088         * @param asyncClient the HttpAsyncClient instance to use for this request factory
089         * @since 4.3.10
090         */
091        public HttpComponentsAsyncClientHttpRequestFactory(HttpClient httpClient, HttpAsyncClient asyncClient) {
092                super(httpClient);
093                setAsyncClient(asyncClient);
094        }
095
096        /**
097         * Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
098         * with the given {@link CloseableHttpClient} and {@link CloseableHttpAsyncClient} instances.
099         * @param httpClient the CloseableHttpClient instance to use for this request factory
100         * @param asyncClient the CloseableHttpAsyncClient instance to use for this request factory
101         */
102        public HttpComponentsAsyncClientHttpRequestFactory(
103                        CloseableHttpClient httpClient, CloseableHttpAsyncClient asyncClient) {
104
105                super(httpClient);
106                setAsyncClient(asyncClient);
107        }
108
109
110        /**
111         * Set the {@code HttpAsyncClient} used for
112         * {@linkplain #createAsyncRequest(URI, HttpMethod) synchronous execution}.
113         * @since 4.3.10
114         * @see #setHttpClient(HttpClient)
115         */
116        public void setAsyncClient(HttpAsyncClient asyncClient) {
117                Assert.notNull(asyncClient, "HttpAsyncClient must not be null");
118                this.asyncClient = asyncClient;
119        }
120
121        /**
122         * Return the {@code HttpAsyncClient} used for
123         * {@linkplain #createAsyncRequest(URI, HttpMethod) synchronous execution}.
124         * @since 4.3.10
125         * @see #getHttpClient()
126         */
127        public HttpAsyncClient getAsyncClient() {
128                return this.asyncClient;
129        }
130
131        /**
132         * Set the {@code CloseableHttpAsyncClient} used for
133         * {@linkplain #createAsyncRequest(URI, HttpMethod) asynchronous execution}.
134         * @deprecated as of 4.3.10, in favor of {@link #setAsyncClient(HttpAsyncClient)}
135         */
136        @Deprecated
137        public void setHttpAsyncClient(CloseableHttpAsyncClient asyncClient) {
138                this.asyncClient = asyncClient;
139        }
140
141        /**
142         * Return the {@code CloseableHttpAsyncClient} used for
143         * {@linkplain #createAsyncRequest(URI, HttpMethod) asynchronous execution}.
144         * @deprecated as of 4.3.10, in favor of {@link #getAsyncClient()}
145         */
146        @Deprecated
147        public CloseableHttpAsyncClient getHttpAsyncClient() {
148                Assert.state(this.asyncClient == null || this.asyncClient instanceof CloseableHttpAsyncClient,
149                                "No CloseableHttpAsyncClient - use getAsyncClient() instead");
150                return (CloseableHttpAsyncClient) this.asyncClient;
151        }
152
153
154        @Override
155        public void afterPropertiesSet() {
156                startAsyncClient();
157        }
158
159        private void startAsyncClient() {
160        HttpAsyncClient asyncClient = getAsyncClient();
161                if (asyncClient instanceof CloseableHttpAsyncClient) {
162                        CloseableHttpAsyncClient closeableAsyncClient = (CloseableHttpAsyncClient) asyncClient;
163                        if (!closeableAsyncClient.isRunning()) {
164                                closeableAsyncClient.start();
165                        }
166                }
167        }
168
169        @Override
170        public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
171                startAsyncClient();
172
173                HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
174                postProcessHttpRequest(httpRequest);
175        HttpContext context = createHttpContext(httpMethod, uri);
176        if (context == null) {
177            context = HttpClientContext.create();
178        }
179
180                // Request configuration not set in the context
181                if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
182                        // Use request configuration given by the user, when available
183                        RequestConfig config = null;
184                        if (httpRequest instanceof Configurable) {
185                                config = ((Configurable) httpRequest).getConfig();
186                        }
187                        if (config == null) {
188                                config = createRequestConfig(getAsyncClient());
189                        }
190                        if (config != null) {
191                                context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
192                        }
193                }
194
195                return new HttpComponentsAsyncClientHttpRequest(getAsyncClient(), httpRequest, context);
196        }
197
198        @Override
199        public void destroy() throws Exception {
200                try {
201                        super.destroy();
202                }
203                finally {
204                        HttpAsyncClient asyncClient = getAsyncClient();
205                        if (asyncClient instanceof Closeable) {
206                                ((Closeable) asyncClient).close();
207                        }
208                }
209        }
210
211}