|
38 | 38 | import java.util.Map; |
39 | 39 | import java.util.Objects; |
40 | 40 | import java.util.concurrent.BlockingQueue; |
| 41 | +import java.util.concurrent.CompletableFuture; |
41 | 42 | import java.util.concurrent.ExecutorService; |
42 | 43 | import java.util.concurrent.Future; |
43 | 44 | import java.util.concurrent.LinkedBlockingQueue; |
|
51 | 52 | import org.apache.solr.client.solrj.SolrServerException; |
52 | 53 | import org.apache.solr.client.solrj.request.QueryRequest; |
53 | 54 | import org.apache.solr.client.solrj.request.RequestWriter; |
| 55 | +import org.apache.solr.client.solrj.util.AsyncListener; |
| 56 | +import org.apache.solr.client.solrj.util.Cancellable; |
54 | 57 | import org.apache.solr.client.solrj.util.ClientUtils; |
55 | 58 | import org.apache.solr.common.SolrException; |
56 | 59 | import org.apache.solr.common.params.ModifiableSolrParams; |
@@ -80,7 +83,7 @@ public class HttpJdkSolrClient extends HttpSolrClientBase { |
80 | 83 |
|
81 | 84 | private boolean forceHttp11; |
82 | 85 |
|
83 | | - private boolean shutdownExecutor; |
| 86 | + private final boolean shutdownExecutor; |
84 | 87 |
|
85 | 88 | protected HttpJdkSolrClient(String serverBaseUrl, HttpJdkSolrClient.Builder builder) { |
86 | 89 | super(serverBaseUrl, builder); |
@@ -133,80 +136,133 @@ protected HttpJdkSolrClient(String serverBaseUrl, HttpJdkSolrClient.Builder buil |
133 | 136 | assert ObjectReleaseTracker.track(this); |
134 | 137 | } |
135 | 138 |
|
| 139 | + @Override |
| 140 | + public Cancellable asyncRequest( |
| 141 | + SolrRequest<?> solrRequest, |
| 142 | + String collection, |
| 143 | + AsyncListener<NamedList<Object>> asyncListener) { |
| 144 | + try { |
| 145 | + PreparedRequest pReq = prepareRequest(solrRequest, collection); |
| 146 | + asyncListener.onStart(); |
| 147 | + CompletableFuture<NamedList<Object>> response = |
| 148 | + httpClient |
| 149 | + .sendAsync(pReq.reqb.build(), HttpResponse.BodyHandlers.ofInputStream()) |
| 150 | + .thenApply( |
| 151 | + httpResponse -> { |
| 152 | + try { |
| 153 | + return processErrorsAndResponse( |
| 154 | + solrRequest, pReq.parserToUse, httpResponse, pReq.url); |
| 155 | + } catch (SolrServerException e) { |
| 156 | + throw new RuntimeException(e); |
| 157 | + } |
| 158 | + }) |
| 159 | + .whenComplete( |
| 160 | + (nl, t) -> { |
| 161 | + if (t != null) { |
| 162 | + asyncListener.onFailure(t); |
| 163 | + } else { |
| 164 | + asyncListener.onSuccess(nl); |
| 165 | + } |
| 166 | + }); |
| 167 | + return new HttpSolrClientCancellable(response); |
| 168 | + } catch (Exception e) { |
| 169 | + asyncListener.onFailure(e); |
| 170 | + return () -> {}; |
| 171 | + } |
| 172 | + } |
| 173 | + |
136 | 174 | @Override |
137 | 175 | public NamedList<Object> request(SolrRequest<?> solrRequest, String collection) |
138 | 176 | throws SolrServerException, IOException { |
| 177 | + PreparedRequest pReq = prepareRequest(solrRequest, collection); |
| 178 | + HttpResponse<InputStream> response = null; |
| 179 | + try { |
| 180 | + response = httpClient.send(pReq.reqb.build(), HttpResponse.BodyHandlers.ofInputStream()); |
| 181 | + return processErrorsAndResponse(solrRequest, pReq.parserToUse, response, pReq.url); |
| 182 | + } catch (InterruptedException e) { |
| 183 | + Thread.currentThread().interrupt(); |
| 184 | + throw new RuntimeException(e); |
| 185 | + } catch (HttpTimeoutException e) { |
| 186 | + throw new SolrServerException( |
| 187 | + "Timeout occurred while waiting response from server at: " + pReq.url, e); |
| 188 | + } catch (SolrException se) { |
| 189 | + throw se; |
| 190 | + } catch (RuntimeException re) { |
| 191 | + throw new SolrServerException(re); |
| 192 | + } finally { |
| 193 | + if (pReq.contentWritingFuture != null) { |
| 194 | + pReq.contentWritingFuture.cancel(true); |
| 195 | + } |
| 196 | + |
| 197 | + // See |
| 198 | + // https://docs.oracle.com/en/java/javase/17/docs/api/java.net.http/java/net/http/HttpResponse.BodySubscribers.html#ofInputStream() |
| 199 | + if (!wantStream(pReq.parserToUse)) { |
| 200 | + try { |
| 201 | + response.body().close(); |
| 202 | + } catch (Exception e1) { |
| 203 | + // ignore |
| 204 | + } |
| 205 | + } |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + private PreparedRequest prepareRequest(SolrRequest<?> solrRequest, String collection) |
| 210 | + throws SolrServerException, IOException { |
139 | 211 | checkClosed(); |
140 | 212 | if (ClientUtils.shouldApplyDefaultCollection(collection, solrRequest)) { |
141 | 213 | collection = defaultCollection; |
142 | 214 | } |
143 | 215 | String url = getRequestPath(solrRequest, collection); |
144 | 216 | ResponseParser parserToUse = responseParser(solrRequest); |
145 | 217 | ModifiableSolrParams queryParams = initalizeSolrParams(solrRequest, parserToUse); |
146 | | - HttpResponse<InputStream> resp = null; |
| 218 | + var reqb = HttpRequest.newBuilder(); |
| 219 | + PreparedRequest pReq = null; |
147 | 220 | try { |
148 | | - var reqb = HttpRequest.newBuilder(); |
149 | 221 | switch (solrRequest.getMethod()) { |
150 | 222 | case GET: |
151 | 223 | { |
152 | | - resp = doGet(url, reqb, solrRequest, queryParams); |
| 224 | + pReq = prepareGet(url, reqb, solrRequest, queryParams); |
153 | 225 | break; |
154 | 226 | } |
155 | 227 | case POST: |
156 | 228 | case PUT: |
157 | 229 | { |
158 | | - resp = doPutOrPost(url, solrRequest.getMethod(), reqb, solrRequest, queryParams); |
| 230 | + pReq = preparePutOrPost(url, solrRequest.getMethod(), reqb, solrRequest, queryParams); |
159 | 231 | break; |
160 | 232 | } |
161 | 233 | default: |
162 | 234 | { |
163 | 235 | throw new IllegalStateException("Unsupported method: " + solrRequest.getMethod()); |
164 | 236 | } |
165 | 237 | } |
166 | | - return processErrorsAndResponse(solrRequest, parserToUse, resp, url); |
167 | | - } catch (InterruptedException e) { |
168 | | - Thread.currentThread().interrupt(); |
169 | | - throw new RuntimeException(e); |
170 | | - } catch (HttpTimeoutException e) { |
171 | | - throw new SolrServerException( |
172 | | - "Timeout occurred while waiting response from server at: " + url, e); |
173 | | - } catch (SolrException se) { |
174 | | - throw se; |
175 | 238 | } catch (URISyntaxException | RuntimeException re) { |
176 | 239 | throw new SolrServerException(re); |
177 | | - } finally { |
178 | | - // See |
179 | | - // https://docs.oracle.com/en/java/javase/17/docs/api/java.net.http/java/net/http/HttpResponse.BodySubscribers.html#ofInputStream() |
180 | | - if (!wantStream(parserToUse)) { |
181 | | - try { |
182 | | - resp.body().close(); |
183 | | - } catch (Exception e1) { |
184 | | - // ignore |
185 | | - } |
186 | | - } |
187 | 240 | } |
| 241 | + pReq.parserToUse = parserToUse; |
| 242 | + pReq.url = url; |
| 243 | + return pReq; |
188 | 244 | } |
189 | 245 |
|
190 | | - private HttpResponse<InputStream> doGet( |
| 246 | + private PreparedRequest prepareGet( |
191 | 247 | String url, |
192 | 248 | HttpRequest.Builder reqb, |
193 | 249 | SolrRequest<?> solrRequest, |
194 | 250 | ModifiableSolrParams queryParams) |
195 | | - throws IOException, InterruptedException, URISyntaxException { |
| 251 | + throws IOException, URISyntaxException { |
196 | 252 | validateGetRequest(solrRequest); |
197 | 253 | reqb.GET(); |
198 | 254 | decorateRequest(reqb, solrRequest); |
199 | 255 | reqb.uri(new URI(url + "?" + queryParams)); |
200 | | - return httpClient.send(reqb.build(), HttpResponse.BodyHandlers.ofInputStream()); |
| 256 | + return new PreparedRequest(reqb, null); |
201 | 257 | } |
202 | 258 |
|
203 | | - private HttpResponse<InputStream> doPutOrPost( |
| 259 | + private PreparedRequest preparePutOrPost( |
204 | 260 | String url, |
205 | 261 | SolrRequest.METHOD method, |
206 | 262 | HttpRequest.Builder reqb, |
207 | 263 | SolrRequest<?> solrRequest, |
208 | 264 | ModifiableSolrParams queryParams) |
209 | | - throws IOException, InterruptedException, URISyntaxException { |
| 265 | + throws IOException, URISyntaxException { |
210 | 266 |
|
211 | 267 | final RequestWriter.ContentWriter contentWriter = requestWriter.getContentWriter(solrRequest); |
212 | 268 |
|
@@ -274,15 +330,21 @@ private HttpResponse<InputStream> doPutOrPost( |
274 | 330 | URI uriWithQueryParams = new URI(url + "?" + queryParams); |
275 | 331 | reqb.uri(uriWithQueryParams); |
276 | 332 |
|
277 | | - HttpResponse<InputStream> response; |
278 | | - try { |
279 | | - response = httpClient.send(reqb.build(), HttpResponse.BodyHandlers.ofInputStream()); |
280 | | - } finally { |
281 | | - if (contentWritingFuture != null) { |
282 | | - contentWritingFuture.cancel(true); |
283 | | - } |
| 333 | + return new PreparedRequest(reqb, contentWritingFuture); |
| 334 | + } |
| 335 | + |
| 336 | + private static class PreparedRequest { |
| 337 | + Future<?> contentWritingFuture; |
| 338 | + HttpRequest.Builder reqb; |
| 339 | + |
| 340 | + ResponseParser parserToUse; |
| 341 | + |
| 342 | + String url; |
| 343 | + |
| 344 | + PreparedRequest(HttpRequest.Builder reqb, Future<?> contentWritingFuture) { |
| 345 | + this.reqb = reqb; |
| 346 | + this.contentWritingFuture = contentWritingFuture; |
284 | 347 | } |
285 | | - return response; |
286 | 348 | } |
287 | 349 |
|
288 | 350 | /** |
@@ -469,6 +531,23 @@ protected String allProcessorSupportedContentTypesCommaDelimited( |
469 | 531 | .collect(Collectors.joining(", ")); |
470 | 532 | } |
471 | 533 |
|
| 534 | + protected static class HttpSolrClientCancellable implements Cancellable { |
| 535 | + private final CompletableFuture<NamedList<Object>> response; |
| 536 | + |
| 537 | + protected HttpSolrClientCancellable(CompletableFuture<NamedList<Object>> response) { |
| 538 | + this.response = response; |
| 539 | + } |
| 540 | + |
| 541 | + @Override |
| 542 | + public void cancel() { |
| 543 | + response.cancel(true); |
| 544 | + } |
| 545 | + |
| 546 | + protected CompletableFuture<NamedList<Object>> getResponse() { |
| 547 | + return response; |
| 548 | + } |
| 549 | + } |
| 550 | + |
472 | 551 | public static class Builder |
473 | 552 | extends HttpSolrClientBuilderBase<HttpJdkSolrClient.Builder, HttpJdkSolrClient> { |
474 | 553 |
|
|
0 commit comments