From 1f0bb6b9d9c850e324ca8edaee06896daab696e0 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Fri, 22 Mar 2024 10:16:22 -0500 Subject: [PATCH 01/19] SOLR-17211 --- .../client/solrj/impl/HttpJdkSolrClient.java | 134 +++++++++++++----- .../solr/client/solrj/impl/DebugServlet.java | 29 ++-- .../solrj/impl/HttpJdkSolrClientTest.java | 92 +++++++++++- 3 files changed, 208 insertions(+), 47 deletions(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java index 35e56ae2a55..96f406e593e 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java @@ -38,6 +38,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; @@ -51,6 +52,8 @@ import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.request.RequestWriter; +import org.apache.solr.client.solrj.util.AsyncListener; +import org.apache.solr.client.solrj.util.Cancellable; import org.apache.solr.client.solrj.util.ClientUtils; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ModifiableSolrParams; @@ -80,7 +83,7 @@ public class HttpJdkSolrClient extends HttpSolrClientBase { private boolean forceHttp11; - private boolean shutdownExecutor; + private final boolean shutdownExecutor; protected HttpJdkSolrClient(String serverBaseUrl, HttpJdkSolrClient.Builder builder) { super(serverBaseUrl, builder); @@ -133,9 +136,76 @@ protected HttpJdkSolrClient(String serverBaseUrl, HttpJdkSolrClient.Builder buil assert ObjectReleaseTracker.track(this); } + public Cancellable asyncRequest( + SolrRequest solrRequest, + String collection, + AsyncListener> asyncListener) { + try { + PreparedRequest pReq = prepareRequest(solrRequest, collection); + asyncListener.onStart(); + CompletableFuture> resp = + httpClient + .sendAsync(pReq.reqb.build(), HttpResponse.BodyHandlers.ofInputStream()) + .thenApply( + response -> { + try { + return processErrorsAndResponse(solrRequest, pReq.parserToUse, response, pReq.url); + } catch (SolrServerException e) { + throw new RuntimeException(e); + } + }) + .whenComplete( + (nl, t) -> { + if (t != null) { + asyncListener.onFailure(t); + } else { + asyncListener.onSuccess(nl); + } + }); + return () -> resp.cancel(true); + } catch (Exception e) { + asyncListener.onFailure(e); + return () -> {}; + } + } + @Override public NamedList request(SolrRequest solrRequest, String collection) throws SolrServerException, IOException { + PreparedRequest pReq = prepareRequest(solrRequest, collection); + HttpResponse response = null; + try { + response = httpClient.send(pReq.reqb.build(), HttpResponse.BodyHandlers.ofInputStream()); + return processErrorsAndResponse(solrRequest, pReq.parserToUse, response, pReq.url); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } catch (HttpTimeoutException e) { + throw new SolrServerException( + "Timeout occurred while waiting response from server at: " + pReq.url, e); + } catch (SolrException se) { + throw se; + } catch (RuntimeException re) { + throw new SolrServerException(re); + } finally { + if (pReq.contentWritingFuture != null) { + pReq.contentWritingFuture.cancel(true); + } + + // See + // https://docs.oracle.com/en/java/javase/17/docs/api/java.net.http/java/net/http/HttpResponse.BodySubscribers.html#ofInputStream() + if (!wantStream(pReq.parserToUse)) { + try { + response.body().close(); + } catch (Exception e1) { + // ignore + } + } + } + } + + private PreparedRequest prepareRequest(SolrRequest solrRequest, String collection) + throws SolrServerException, IOException { checkClosed(); if (ClientUtils.shouldApplyDefaultCollection(collection, solrRequest)) { collection = defaultCollection; @@ -143,19 +213,19 @@ public NamedList request(SolrRequest solrRequest, String collection) String url = getRequestPath(solrRequest, collection); ResponseParser parserToUse = responseParser(solrRequest); ModifiableSolrParams queryParams = initalizeSolrParams(solrRequest, parserToUse); - HttpResponse resp = null; + var reqb = HttpRequest.newBuilder(); + PreparedRequest pReq = null; try { - var reqb = HttpRequest.newBuilder(); switch (solrRequest.getMethod()) { case GET: { - resp = doGet(url, reqb, solrRequest, queryParams); + pReq = prepareGet(url, reqb, solrRequest, queryParams); break; } case POST: case PUT: { - resp = doPutOrPost(url, solrRequest.getMethod(), reqb, solrRequest, queryParams); + pReq = preparePutOrPost(url, solrRequest.getMethod(), reqb, solrRequest, queryParams); break; } default: @@ -163,50 +233,34 @@ public NamedList request(SolrRequest solrRequest, String collection) throw new IllegalStateException("Unsupported method: " + solrRequest.getMethod()); } } - return processErrorsAndResponse(solrRequest, parserToUse, resp, url); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } catch (HttpTimeoutException e) { - throw new SolrServerException( - "Timeout occurred while waiting response from server at: " + url, e); - } catch (SolrException se) { - throw se; } catch (URISyntaxException | RuntimeException re) { throw new SolrServerException(re); - } finally { - // See - // https://docs.oracle.com/en/java/javase/17/docs/api/java.net.http/java/net/http/HttpResponse.BodySubscribers.html#ofInputStream() - if (!wantStream(parserToUse)) { - try { - resp.body().close(); - } catch (Exception e1) { - // ignore - } - } } + pReq.parserToUse = parserToUse; + pReq.url = url; + return pReq; } - private HttpResponse doGet( + private PreparedRequest prepareGet( String url, HttpRequest.Builder reqb, SolrRequest solrRequest, ModifiableSolrParams queryParams) - throws IOException, InterruptedException, URISyntaxException { + throws IOException, URISyntaxException { validateGetRequest(solrRequest); reqb.GET(); decorateRequest(reqb, solrRequest); reqb.uri(new URI(url + "?" + queryParams)); - return httpClient.send(reqb.build(), HttpResponse.BodyHandlers.ofInputStream()); + return new PreparedRequest(reqb, null); } - private HttpResponse doPutOrPost( + private PreparedRequest preparePutOrPost( String url, SolrRequest.METHOD method, HttpRequest.Builder reqb, SolrRequest solrRequest, ModifiableSolrParams queryParams) - throws IOException, InterruptedException, URISyntaxException { + throws IOException, URISyntaxException { final RequestWriter.ContentWriter contentWriter = requestWriter.getContentWriter(solrRequest); @@ -274,15 +328,21 @@ private HttpResponse doPutOrPost( URI uriWithQueryParams = new URI(url + "?" + queryParams); reqb.uri(uriWithQueryParams); - HttpResponse response; - try { - response = httpClient.send(reqb.build(), HttpResponse.BodyHandlers.ofInputStream()); - } finally { - if (contentWritingFuture != null) { - contentWritingFuture.cancel(true); - } + return new PreparedRequest(reqb, contentWritingFuture); + } + + private static class PreparedRequest { + Future contentWritingFuture; + HttpRequest.Builder reqb; + + ResponseParser parserToUse; + + String url; + + PreparedRequest(HttpRequest.Builder reqb, Future contentWritingFuture) { + this.reqb = reqb; + this.contentWritingFuture = contentWritingFuture; } - return response; } /** diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java index 272d9223dcf..89af7a8a5f8 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java @@ -21,9 +21,11 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -39,7 +41,7 @@ public static void clear() { queryString = null; cookies = null; responseHeaders = null; - responseBody = null; + responseBodyByQueryFragment = new ConcurrentHashMap<>(); } public static Integer errorCode = null; @@ -49,7 +51,7 @@ public static void clear() { public static String queryString = null; public static javax.servlet.http.Cookie[] cookies = null; public static List responseHeaders = null; - public static Object responseBody = null; + public static Map responseBodyByQueryFragment = new LinkedHashMap<>(); public static byte[] requestBody = null; public static void setErrorCode(Integer code) { @@ -65,21 +67,21 @@ public static void addResponseHeader(String headerName, String headerValue) { @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + throws ServletException, IOException { lastMethod = "delete"; recordRequest(req, resp); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + throws ServletException, IOException { lastMethod = "get"; recordRequest(req, resp); } @Override protected void doHead(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + throws ServletException, IOException { lastMethod = "head"; recordRequest(req, resp); } @@ -109,14 +111,14 @@ private void setCookies(HttpServletRequest req) { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + throws ServletException, IOException { lastMethod = "post"; recordRequest(req, resp); } @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + throws ServletException, IOException { lastMethod = "put"; recordRequest(req, resp); } @@ -136,6 +138,17 @@ private void recordRequest(HttpServletRequest req, HttpServletResponse resp) { resp.addHeader(h[0], h[1]); } } + String qs = req.getQueryString(); + qs = qs == null ? "" : qs; + Object responseBody = null; + for(Map.Entry entry : responseBodyByQueryFragment.entrySet()) { + if(qs.contains(entry.getKey())) { + responseBody = entry.getValue(); + break; + } + } + + if (responseBody != null) { try { if (responseBody instanceof String) { @@ -144,7 +157,7 @@ private void recordRequest(HttpServletRequest req, HttpServletResponse resp) { resp.getOutputStream().write((byte[]) responseBody); } else { throw new IllegalArgumentException( - "Only String and byte[] are supported for responseBody."); + "Only String and byte[] are supported for responseBody."); } } catch (IOException ioe) { throw new RuntimeException(ioe); diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index d9e9e7a87a6..f50a87f9a48 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -28,6 +28,7 @@ import java.util.Collections; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -42,11 +43,16 @@ import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.request.RequestWriter; import org.apache.solr.client.solrj.response.SolrPingResponse; +import org.apache.solr.client.solrj.util.AsyncListener; +import org.apache.solr.client.solrj.util.Cancellable; +import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.MapSolrParams; import org.apache.solr.common.util.ExecutorUtil; +import org.apache.solr.common.util.NamedList; import org.apache.solr.util.SSLTestConfig; import org.junit.After; import org.junit.BeforeClass; @@ -90,6 +96,88 @@ public void workaroundToReleaseThreads_noClosableUntilJava21() { System.gc(); } + @Test + public void testAsyncGet() throws Exception { + ResponseParser rp = new XMLResponseParser(); + DebugServlet.clear(); + DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); + String url = getBaseUrl() + DEBUG_SERVLET_PATH; + HttpJdkSolrClient.Builder b = builder(url).withResponseParser(rp); + int limit = 10; + CountDownLatch cdl = new CountDownLatch(limit); + DebugAsyncListener[] listeners = new DebugAsyncListener[limit]; + Cancellable[] cancellables = new Cancellable[limit]; + try (HttpJdkSolrClient client = b.build()) { + for (int i = 0; i < limit; i++) { + DebugServlet.responseBodyByQueryFragment.put(("id=KEY-" + i), + "\nKEY-" + + i + + ""); + QueryRequest query = + new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "KEY-" + i))); + listeners[i] = new DebugAsyncListener(cdl); + client.asyncRequest(query, "collection1", listeners[i]); + } + cdl.await(1, TimeUnit.MINUTES); + } + + for (int i = 0; i < limit; i++) { + NamedList result = listeners[i].onSuccessResult; + SolrDocumentList sdl = (SolrDocumentList) result.get("response"); + assertEquals(2, sdl.getNumFound()); + assertEquals(1, sdl.getStart()); + assertTrue(sdl.getNumFoundExact()); + assertEquals(1, sdl.size()); + assertEquals(1, sdl.iterator().next().size()); + assertEquals("KEY-"+i, sdl.iterator().next().get("id")); + + assertNull(listeners[i].onFailureResult); + assertTrue(listeners[i].onStartCalled); + } + } + + public static class DebugAsyncListener implements AsyncListener> { + + private final CountDownLatch cdl; + + public DebugAsyncListener(CountDownLatch cdl) { + this.cdl = cdl; + } + + public volatile boolean onStartCalled; + + public volatile boolean latchCounted; + + public volatile NamedList onSuccessResult = null; + + public volatile Throwable onFailureResult = null; + + @Override + public void onStart() { + onStartCalled = true; + } + + @Override + public void onSuccess(NamedList entries) { + onSuccessResult = entries; + if (latchCounted) { + fail("either 'onSuccess' or 'onFailure' should be called exactly once."); + } + cdl.countDown(); + latchCounted = true; + } + + @Override + public void onFailure(Throwable throwable) { + onFailureResult = throwable; + if (latchCounted) { + fail("either 'onSuccess' or 'onFailure' should be called exactly once."); + } + cdl.countDown(); + latchCounted = true; + } + } + @Test @Override public void testQueryGet() throws Exception { @@ -164,10 +252,10 @@ protected void testQuerySetup(SolrRequest.METHOD method, ResponseParser rp) thro DebugServlet.clear(); if (rp instanceof XMLResponseParser) { DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); - DebugServlet.responseBody = "\n"; + DebugServlet.responseBodyByQueryFragment.put("", "\n"); } else { DebugServlet.addResponseHeader("Content-Type", "application/octet-stream"); - DebugServlet.responseBody = javabinResponse(); + DebugServlet.responseBodyByQueryFragment.put("", javabinResponse()); } String url = getBaseUrl() + DEBUG_SERVLET_PATH; SolrQuery q = new SolrQuery("foo"); From b2592ce70301f6c75df3556cc16f5bda10bfca03 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Fri, 22 Mar 2024 12:55:55 -0500 Subject: [PATCH 02/19] Test cancel and exception --- .../client/solrj/impl/HttpJdkSolrClient.java | 26 +++- .../solrj/impl/HttpJdkSolrClientTest.java | 143 +++++++++++++----- 2 files changed, 129 insertions(+), 40 deletions(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java index 96f406e593e..f825bd2f377 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java @@ -143,13 +143,13 @@ public Cancellable asyncRequest( try { PreparedRequest pReq = prepareRequest(solrRequest, collection); asyncListener.onStart(); - CompletableFuture> resp = + CompletableFuture> response = httpClient .sendAsync(pReq.reqb.build(), HttpResponse.BodyHandlers.ofInputStream()) .thenApply( - response -> { + httpResponse -> { try { - return processErrorsAndResponse(solrRequest, pReq.parserToUse, response, pReq.url); + return processErrorsAndResponse(solrRequest, pReq.parserToUse, httpResponse, pReq.url); } catch (SolrServerException e) { throw new RuntimeException(e); } @@ -162,13 +162,31 @@ public Cancellable asyncRequest( asyncListener.onSuccess(nl); } }); - return () -> resp.cancel(true); + return new HttpJdkSolrClientCancellable(response); } catch (Exception e) { asyncListener.onFailure(e); return () -> {}; } } + protected class HttpJdkSolrClientCancellable implements Cancellable { + private final CompletableFuture> response; + + HttpJdkSolrClientCancellable(CompletableFuture> response) { + this.response = response; + } + + protected CompletableFuture> getResponse() { + return response; + } + + + @Override + public void cancel() { + response.cancel(true); + } + } + @Override public NamedList request(SolrRequest solrRequest, String collection) throws SolrServerException, IOException { diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index f50a87f9a48..20324162754 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -28,8 +28,11 @@ import java.util.Collections; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.net.ssl.KeyManagerFactory; @@ -136,46 +139,53 @@ public void testAsyncGet() throws Exception { } } - public static class DebugAsyncListener implements AsyncListener> { - - private final CountDownLatch cdl; - - public DebugAsyncListener(CountDownLatch cdl) { - this.cdl = cdl; - } - - public volatile boolean onStartCalled; - - public volatile boolean latchCounted; - - public volatile NamedList onSuccessResult = null; - - public volatile Throwable onFailureResult = null; - - @Override - public void onStart() { - onStartCalled = true; + @Test + public void testAsyncAndCancel() throws Exception { + ResponseParser rp = new XMLResponseParser(); + DebugServlet.clear(); + DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); + DebugServlet.responseBodyByQueryFragment.put("", "\n"); + String url = getBaseUrl() + DEBUG_SERVLET_PATH; + HttpJdkSolrClient.Builder b = builder(url).withResponseParser(rp); + CountDownLatch cdl = new CountDownLatch(0); + DebugAsyncListener listener = new DebugAsyncListener(cdl); + Cancellable cancelMe = null; + try (HttpJdkSolrClient client = b.build()) { + QueryRequest query = + new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); + listener.pause(); + cancelMe = client.asyncRequest(query, "collection1", listener); + cancelMe.cancel(); + listener.unPause(); } + assertTrue(listener.onStartCalled); + assertTrue(cancelMe instanceof HttpJdkSolrClient.HttpJdkSolrClientCancellable); + CompletableFuture> response = ((HttpJdkSolrClient.HttpJdkSolrClientCancellable) cancelMe).getResponse(); + assertTrue(response.isCancelled()); + } - @Override - public void onSuccess(NamedList entries) { - onSuccessResult = entries; - if (latchCounted) { - fail("either 'onSuccess' or 'onFailure' should be called exactly once."); - } - cdl.countDown(); - latchCounted = true; + @Test + public void testAsyncException() throws Exception { + ResponseParser rp = new XMLResponseParser(); + DebugServlet.clear(); + DebugServlet.addResponseHeader("Content-Type", "Wrong Content Type!"); + String url = getBaseUrl() + DEBUG_SERVLET_PATH; + HttpJdkSolrClient.Builder b = builder(url).withResponseParser(rp); + CountDownLatch cdl = new CountDownLatch(1); + DebugAsyncListener listener = new DebugAsyncListener(cdl); + try (HttpJdkSolrClient client = b.build()) { + QueryRequest query = + new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); + client.asyncRequest(query, "collection1", listener); + cdl.await(1, TimeUnit.MINUTES); } - @Override - public void onFailure(Throwable throwable) { - onFailureResult = throwable; - if (latchCounted) { - fail("either 'onSuccess' or 'onFailure' should be called exactly once."); - } - cdl.countDown(); - latchCounted = true; - } + assertTrue(listener.onFailureResult instanceof CompletionException); + CompletionException ce = (CompletionException) listener.onFailureResult; + assertTrue(ce.getCause() instanceof BaseHttpSolrClient.RemoteSolrException); + assertTrue(ce.getMessage(), ce.getMessage().contains("mime type")); + assertTrue(listener.onStartCalled); + assertNull(listener.onSuccessResult); } @Test @@ -651,6 +661,67 @@ private byte[] javabinResponse() { + "70 6f 6e 73 65 0c 84 60 60 " + "00 01 80"; + public static class DebugAsyncListener implements AsyncListener> { + + private final CountDownLatch cdl; + + private final Semaphore wait = new Semaphore(1); + + public volatile boolean onStartCalled; + + public volatile boolean latchCounted; + + public volatile NamedList onSuccessResult = null; + + public volatile Throwable onFailureResult = null; + + public DebugAsyncListener(CountDownLatch cdl) { + this.cdl = cdl; + } + + @Override + public void onStart() { + onStartCalled = true; + } + + public void pause() { + try { + wait.acquire(); + } catch(InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + + public void unPause() { + wait.release(); + } + + + @Override + public void onSuccess(NamedList entries) { + pause(); + onSuccessResult = entries; + if (latchCounted) { + fail("either 'onSuccess' or 'onFailure' should be called exactly once."); + } + cdl.countDown(); + latchCounted = true; + unPause(); + } + + @Override + public void onFailure(Throwable throwable) { + pause(); + onFailureResult = throwable; + if (latchCounted) { + fail("either 'onSuccess' or 'onFailure' should be called exactly once."); + } + cdl.countDown(); + latchCounted = true; + unPause(); + } + } + /** * Taken from: https://www.baeldung.com/java-httpclient-ssl sec 4.1, 2024/02/12. This is an * all-trusting Trust Manager. Works with self-signed certificates. From 305f9cc0ba47ebfe3f9faff79437540af2fa68b4 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Fri, 22 Mar 2024 16:10:11 -0500 Subject: [PATCH 03/19] test PUT and POST --- .../solr/client/solrj/impl/HttpJdkSolrClient.java | 9 ++++----- .../client/solrj/impl/HttpJdkSolrClientTest.java | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java index f825bd2f377..25047a16e6e 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java @@ -176,15 +176,14 @@ protected class HttpJdkSolrClientCancellable implements Cancellable { this.response = response; } - protected CompletableFuture> getResponse() { - return response; - } - - @Override public void cancel() { response.cancel(true); } + + protected CompletableFuture> getResponse() { + return response; + } } @Override diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index 20324162754..acb8b1a01ef 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -101,6 +101,20 @@ public void workaroundToReleaseThreads_noClosableUntilJava21() { @Test public void testAsyncGet() throws Exception { + testQueryAsync(SolrRequest.METHOD.GET); + } + + @Test + public void testAsyncPost() throws Exception { + testQueryAsync(SolrRequest.METHOD.GET); + } + + @Test + public void testAsyncPut() throws Exception { + testQueryAsync(SolrRequest.METHOD.GET); + } + + protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { ResponseParser rp = new XMLResponseParser(); DebugServlet.clear(); DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); @@ -118,6 +132,7 @@ public void testAsyncGet() throws Exception { + ""); QueryRequest query = new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "KEY-" + i))); + query.setMethod(SolrRequest.METHOD.GET); listeners[i] = new DebugAsyncListener(cdl); client.asyncRequest(query, "collection1", listeners[i]); } From 844c2f75e94ff6b70136c5d30dbbf3476d7ac075 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Fri, 22 Mar 2024 16:11:25 -0500 Subject: [PATCH 04/19] tidy --- .../client/solrj/impl/HttpJdkSolrClient.java | 3 ++- .../solr/client/solrj/impl/DebugServlet.java | 17 ++++++------- .../solrj/impl/HttpJdkSolrClientTest.java | 25 ++++++++++--------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java index 25047a16e6e..02a08ae0336 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java @@ -149,7 +149,8 @@ public Cancellable asyncRequest( .thenApply( httpResponse -> { try { - return processErrorsAndResponse(solrRequest, pReq.parserToUse, httpResponse, pReq.url); + return processErrorsAndResponse( + solrRequest, pReq.parserToUse, httpResponse, pReq.url); } catch (SolrServerException e) { throw new RuntimeException(e); } diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java index 89af7a8a5f8..d4fd3fcab3c 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java @@ -67,21 +67,21 @@ public static void addResponseHeader(String headerName, String headerValue) { @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + throws ServletException, IOException { lastMethod = "delete"; recordRequest(req, resp); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + throws ServletException, IOException { lastMethod = "get"; recordRequest(req, resp); } @Override protected void doHead(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + throws ServletException, IOException { lastMethod = "head"; recordRequest(req, resp); } @@ -111,14 +111,14 @@ private void setCookies(HttpServletRequest req) { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + throws ServletException, IOException { lastMethod = "post"; recordRequest(req, resp); } @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + throws ServletException, IOException { lastMethod = "put"; recordRequest(req, resp); } @@ -141,14 +141,13 @@ private void recordRequest(HttpServletRequest req, HttpServletResponse resp) { String qs = req.getQueryString(); qs = qs == null ? "" : qs; Object responseBody = null; - for(Map.Entry entry : responseBodyByQueryFragment.entrySet()) { - if(qs.contains(entry.getKey())) { + for (Map.Entry entry : responseBodyByQueryFragment.entrySet()) { + if (qs.contains(entry.getKey())) { responseBody = entry.getValue(); break; } } - if (responseBody != null) { try { if (responseBody instanceof String) { @@ -157,7 +156,7 @@ private void recordRequest(HttpServletRequest req, HttpServletResponse resp) { resp.getOutputStream().write((byte[]) responseBody); } else { throw new IllegalArgumentException( - "Only String and byte[] are supported for responseBody."); + "Only String and byte[] are supported for responseBody."); } } catch (IOException ioe) { throw new RuntimeException(ioe); diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index acb8b1a01ef..07b43fc50df 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -114,7 +114,7 @@ public void testAsyncPut() throws Exception { testQueryAsync(SolrRequest.METHOD.GET); } - protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { + protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { ResponseParser rp = new XMLResponseParser(); DebugServlet.clear(); DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); @@ -126,7 +126,8 @@ protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { Cancellable[] cancellables = new Cancellable[limit]; try (HttpJdkSolrClient client = b.build()) { for (int i = 0; i < limit; i++) { - DebugServlet.responseBodyByQueryFragment.put(("id=KEY-" + i), + DebugServlet.responseBodyByQueryFragment.put( + ("id=KEY-" + i), "\nKEY-" + i + ""); @@ -147,7 +148,7 @@ protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { assertTrue(sdl.getNumFoundExact()); assertEquals(1, sdl.size()); assertEquals(1, sdl.iterator().next().size()); - assertEquals("KEY-"+i, sdl.iterator().next().get("id")); + assertEquals("KEY-" + i, sdl.iterator().next().get("id")); assertNull(listeners[i].onFailureResult); assertTrue(listeners[i].onStartCalled); @@ -159,15 +160,15 @@ public void testAsyncAndCancel() throws Exception { ResponseParser rp = new XMLResponseParser(); DebugServlet.clear(); DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); - DebugServlet.responseBodyByQueryFragment.put("", "\n"); + DebugServlet.responseBodyByQueryFragment.put( + "", "\n"); String url = getBaseUrl() + DEBUG_SERVLET_PATH; HttpJdkSolrClient.Builder b = builder(url).withResponseParser(rp); CountDownLatch cdl = new CountDownLatch(0); DebugAsyncListener listener = new DebugAsyncListener(cdl); Cancellable cancelMe = null; try (HttpJdkSolrClient client = b.build()) { - QueryRequest query = - new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); + QueryRequest query = new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); listener.pause(); cancelMe = client.asyncRequest(query, "collection1", listener); cancelMe.cancel(); @@ -175,7 +176,8 @@ public void testAsyncAndCancel() throws Exception { } assertTrue(listener.onStartCalled); assertTrue(cancelMe instanceof HttpJdkSolrClient.HttpJdkSolrClientCancellable); - CompletableFuture> response = ((HttpJdkSolrClient.HttpJdkSolrClientCancellable) cancelMe).getResponse(); + CompletableFuture> response = + ((HttpJdkSolrClient.HttpJdkSolrClientCancellable) cancelMe).getResponse(); assertTrue(response.isCancelled()); } @@ -189,8 +191,7 @@ public void testAsyncException() throws Exception { CountDownLatch cdl = new CountDownLatch(1); DebugAsyncListener listener = new DebugAsyncListener(cdl); try (HttpJdkSolrClient client = b.build()) { - QueryRequest query = - new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); + QueryRequest query = new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); client.asyncRequest(query, "collection1", listener); cdl.await(1, TimeUnit.MINUTES); } @@ -277,7 +278,8 @@ protected void testQuerySetup(SolrRequest.METHOD method, ResponseParser rp) thro DebugServlet.clear(); if (rp instanceof XMLResponseParser) { DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); - DebugServlet.responseBodyByQueryFragment.put("", "\n"); + DebugServlet.responseBodyByQueryFragment.put( + "", "\n"); } else { DebugServlet.addResponseHeader("Content-Type", "application/octet-stream"); DebugServlet.responseBodyByQueryFragment.put("", javabinResponse()); @@ -702,7 +704,7 @@ public void onStart() { public void pause() { try { wait.acquire(); - } catch(InterruptedException ie) { + } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } @@ -711,7 +713,6 @@ public void unPause() { wait.release(); } - @Override public void onSuccess(NamedList entries) { pause(); From 05df3310f1fe7b1034025b5d0ae7fc0366c2306f Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Fri, 22 Mar 2024 16:57:24 -0500 Subject: [PATCH 05/19] Also test Http2SolrClient async --- .../client/solrj/impl/Http2SolrClient.java | 3 +- .../client/solrj/impl/HttpJdkSolrClient.java | 37 +-- .../client/solrj/impl/HttpSolrClientBase.java | 15 ++ .../client/solrj/impl/DebugAsyncListener.java | 68 ++++++ .../solrj/impl/Http2SolrClientTest.java | 20 ++ .../solrj/impl/HttpJdkSolrClientTest.java | 218 ++++-------------- .../solrj/impl/HttpSolrClientTestBase.java | 68 ++++++ 7 files changed, 242 insertions(+), 187 deletions(-) create mode 100644 solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugAsyncListener.java diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java index 6a97bd13ac3..c0277b1e081 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java @@ -422,6 +422,7 @@ public void send(OutStream outStream, SolrRequest req, String collection) thr private static final Cancellable FAILED_MAKING_REQUEST_CANCELLABLE = () -> {}; + @Override public Cancellable asyncRequest( SolrRequest solrRequest, String collection, @@ -470,7 +471,7 @@ public void onFailure(Response response, Throwable failure) { } } }; - + asyncListener.onStart(); req = makeRequestAndSend(solrRequest, url, listener, true); } catch (SolrServerException | IOException e) { asyncListener.onFailure(e); diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java index 02a08ae0336..1474dc10f17 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java @@ -136,6 +136,7 @@ protected HttpJdkSolrClient(String serverBaseUrl, HttpJdkSolrClient.Builder buil assert ObjectReleaseTracker.track(this); } + @Override public Cancellable asyncRequest( SolrRequest solrRequest, String collection, @@ -163,30 +164,13 @@ public Cancellable asyncRequest( asyncListener.onSuccess(nl); } }); - return new HttpJdkSolrClientCancellable(response); + return new HttpSolrClientCancellable(response); } catch (Exception e) { asyncListener.onFailure(e); return () -> {}; } } - protected class HttpJdkSolrClientCancellable implements Cancellable { - private final CompletableFuture> response; - - HttpJdkSolrClientCancellable(CompletableFuture> response) { - this.response = response; - } - - @Override - public void cancel() { - response.cancel(true); - } - - protected CompletableFuture> getResponse() { - return response; - } - } - @Override public NamedList request(SolrRequest solrRequest, String collection) throws SolrServerException, IOException { @@ -547,6 +531,23 @@ protected String allProcessorSupportedContentTypesCommaDelimited( .collect(Collectors.joining(", ")); } + protected static class HttpSolrClientCancellable implements Cancellable { + private final CompletableFuture> response; + + protected HttpSolrClientCancellable(CompletableFuture> response) { + this.response = response; + } + + @Override + public void cancel() { + response.cancel(true); + } + + protected CompletableFuture> getResponse() { + return response; + } + } + public static class Builder extends HttpSolrClientBuilderBase { diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java index c1171af454f..5fff2bb6e9b 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java @@ -39,6 +39,8 @@ import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.request.RequestWriter; import org.apache.solr.client.solrj.request.V2Request; +import org.apache.solr.client.solrj.util.AsyncListener; +import org.apache.solr.client.solrj.util.Cancellable; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.ModifiableSolrParams; @@ -368,6 +370,19 @@ protected void setParser(ResponseParser parser) { protected abstract void updateDefaultMimeTypeForParser(); + /** + * Execute an asynchronous request to a Solr collection + * + * @param solrRequest the request to perform + * @param collection if null the default collection is used + * @param asyncListener callers should provide an implementation to handle events: start, success, exception + * @return Cancellable allowing the caller to attempt cancellation + */ + public abstract Cancellable asyncRequest( + SolrRequest solrRequest, + String collection, + AsyncListener> asyncListener) ; + public boolean isV2ApiRequest(final SolrRequest request) { return request instanceof V2Request || request.getPath().contains("/____v2"); } diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugAsyncListener.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugAsyncListener.java new file mode 100644 index 00000000000..3ab1c0c55cf --- /dev/null +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugAsyncListener.java @@ -0,0 +1,68 @@ +package org.apache.solr.client.solrj.impl; + +import org.apache.solr.client.solrj.util.AsyncListener; +import org.apache.solr.common.util.NamedList; +import org.junit.Assert; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; + +public class DebugAsyncListener implements AsyncListener> { + + private final CountDownLatch cdl; + + private final Semaphore wait = new Semaphore(1); + + public volatile boolean onStartCalled; + + public volatile boolean latchCounted; + + public volatile NamedList onSuccessResult = null; + + public volatile Throwable onFailureResult = null; + + public DebugAsyncListener(CountDownLatch cdl) { + this.cdl = cdl; + } + + @Override + public void onStart() { + onStartCalled = true; + } + + public void pause() { + try { + wait.acquire(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + + public void unPause() { + wait.release(); + } + + @Override + public void onSuccess(NamedList entries) { + pause(); + onSuccessResult = entries; + if (latchCounted) { + Assert.fail("either 'onSuccess' or 'onFailure' should be called exactly once."); + } + cdl.countDown(); + latchCounted = true; + unPause(); + } + + @Override + public void onFailure(Throwable throwable) { + pause(); + onFailureResult = throwable; + if (latchCounted) { + Assert.fail("either 'onSuccess' or 'onFailure' should be called exactly once."); + } + cdl.countDown(); + latchCounted = true; + unPause(); + } +} diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java index 5b4f7eb86af..6f3ce942cb9 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java @@ -263,6 +263,26 @@ public void testUpdateJavabin() throws Exception { } } + @Test + public void testAsyncGet() throws Exception { + super.testQueryAsync(SolrRequest.METHOD.GET); + } + + @Test + public void testAsyncPost() throws Exception { + super.testQueryAsync(SolrRequest.METHOD.GET); + } + + @Test + public void testAsyncPut() throws Exception { + super.testQueryAsync(SolrRequest.METHOD.GET); + } + + @Test + public void testAsyncException() throws Exception { + super.testAsyncExceptionBase(); + } + @Test public void testFollowRedirect() throws Exception { final String clientUrl = getBaseUrl() + REDIRECT_SERVLET_PATH; diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index 07b43fc50df..1a905606bc9 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -32,7 +32,6 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.net.ssl.KeyManagerFactory; @@ -49,9 +48,7 @@ import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.request.RequestWriter; import org.apache.solr.client.solrj.response.SolrPingResponse; -import org.apache.solr.client.solrj.util.AsyncListener; import org.apache.solr.client.solrj.util.Cancellable; -import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.MapSolrParams; import org.apache.solr.common.util.ExecutorUtil; @@ -99,111 +96,6 @@ public void workaroundToReleaseThreads_noClosableUntilJava21() { System.gc(); } - @Test - public void testAsyncGet() throws Exception { - testQueryAsync(SolrRequest.METHOD.GET); - } - - @Test - public void testAsyncPost() throws Exception { - testQueryAsync(SolrRequest.METHOD.GET); - } - - @Test - public void testAsyncPut() throws Exception { - testQueryAsync(SolrRequest.METHOD.GET); - } - - protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { - ResponseParser rp = new XMLResponseParser(); - DebugServlet.clear(); - DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); - String url = getBaseUrl() + DEBUG_SERVLET_PATH; - HttpJdkSolrClient.Builder b = builder(url).withResponseParser(rp); - int limit = 10; - CountDownLatch cdl = new CountDownLatch(limit); - DebugAsyncListener[] listeners = new DebugAsyncListener[limit]; - Cancellable[] cancellables = new Cancellable[limit]; - try (HttpJdkSolrClient client = b.build()) { - for (int i = 0; i < limit; i++) { - DebugServlet.responseBodyByQueryFragment.put( - ("id=KEY-" + i), - "\nKEY-" - + i - + ""); - QueryRequest query = - new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "KEY-" + i))); - query.setMethod(SolrRequest.METHOD.GET); - listeners[i] = new DebugAsyncListener(cdl); - client.asyncRequest(query, "collection1", listeners[i]); - } - cdl.await(1, TimeUnit.MINUTES); - } - - for (int i = 0; i < limit; i++) { - NamedList result = listeners[i].onSuccessResult; - SolrDocumentList sdl = (SolrDocumentList) result.get("response"); - assertEquals(2, sdl.getNumFound()); - assertEquals(1, sdl.getStart()); - assertTrue(sdl.getNumFoundExact()); - assertEquals(1, sdl.size()); - assertEquals(1, sdl.iterator().next().size()); - assertEquals("KEY-" + i, sdl.iterator().next().get("id")); - - assertNull(listeners[i].onFailureResult); - assertTrue(listeners[i].onStartCalled); - } - } - - @Test - public void testAsyncAndCancel() throws Exception { - ResponseParser rp = new XMLResponseParser(); - DebugServlet.clear(); - DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); - DebugServlet.responseBodyByQueryFragment.put( - "", "\n"); - String url = getBaseUrl() + DEBUG_SERVLET_PATH; - HttpJdkSolrClient.Builder b = builder(url).withResponseParser(rp); - CountDownLatch cdl = new CountDownLatch(0); - DebugAsyncListener listener = new DebugAsyncListener(cdl); - Cancellable cancelMe = null; - try (HttpJdkSolrClient client = b.build()) { - QueryRequest query = new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); - listener.pause(); - cancelMe = client.asyncRequest(query, "collection1", listener); - cancelMe.cancel(); - listener.unPause(); - } - assertTrue(listener.onStartCalled); - assertTrue(cancelMe instanceof HttpJdkSolrClient.HttpJdkSolrClientCancellable); - CompletableFuture> response = - ((HttpJdkSolrClient.HttpJdkSolrClientCancellable) cancelMe).getResponse(); - assertTrue(response.isCancelled()); - } - - @Test - public void testAsyncException() throws Exception { - ResponseParser rp = new XMLResponseParser(); - DebugServlet.clear(); - DebugServlet.addResponseHeader("Content-Type", "Wrong Content Type!"); - String url = getBaseUrl() + DEBUG_SERVLET_PATH; - HttpJdkSolrClient.Builder b = builder(url).withResponseParser(rp); - CountDownLatch cdl = new CountDownLatch(1); - DebugAsyncListener listener = new DebugAsyncListener(cdl); - try (HttpJdkSolrClient client = b.build()) { - QueryRequest query = new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); - client.asyncRequest(query, "collection1", listener); - cdl.await(1, TimeUnit.MINUTES); - } - - assertTrue(listener.onFailureResult instanceof CompletionException); - CompletionException ce = (CompletionException) listener.onFailureResult; - assertTrue(ce.getCause() instanceof BaseHttpSolrClient.RemoteSolrException); - assertTrue(ce.getMessage(), ce.getMessage().contains("mime type")); - assertTrue(listener.onStartCalled); - assertNull(listener.onSuccessResult); - } - @Test @Override public void testQueryGet() throws Exception { @@ -306,6 +198,56 @@ public void testGetById() throws Exception { } } + @Test + public void testAsyncGet() throws Exception { + super.testQueryAsync(SolrRequest.METHOD.GET); + } + + @Test + public void testAsyncPost() throws Exception { + super.testQueryAsync(SolrRequest.METHOD.GET); + } + + @Test + public void testAsyncPut() throws Exception { + super.testQueryAsync(SolrRequest.METHOD.GET); + } + + @Test + public void testAsyncException() throws Exception { + DebugAsyncListener listener = super.testAsyncExceptionBase(); + assertTrue(listener.onFailureResult instanceof CompletionException); + CompletionException ce = (CompletionException) listener.onFailureResult; + assertTrue(ce.getCause() instanceof BaseHttpSolrClient.RemoteSolrException); + assertTrue(ce.getMessage(), ce.getMessage().contains("mime type")); + } + + @Test + public void testAsyncAndCancel() throws Exception { + ResponseParser rp = new XMLResponseParser(); + DebugServlet.clear(); + DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); + DebugServlet.responseBodyByQueryFragment.put( + "", "\n"); + String url = getBaseUrl() + DEBUG_SERVLET_PATH; + HttpJdkSolrClient.Builder b = builder(url).withResponseParser(rp); + CountDownLatch cdl = new CountDownLatch(0); + DebugAsyncListener listener = new DebugAsyncListener(cdl); + Cancellable cancelMe = null; + try (HttpJdkSolrClient client = b.build()) { + QueryRequest query = new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); + listener.pause(); + cancelMe = client.asyncRequest(query, "collection1", listener); + cancelMe.cancel(); + listener.unPause(); + } + assertTrue(listener.onStartCalled); + assertTrue(cancelMe instanceof HttpJdkSolrClient.HttpSolrClientCancellable); + CompletableFuture> response = + ((HttpJdkSolrClient.HttpSolrClientCancellable) cancelMe).getResponse(); + assertTrue(response.isCancelled()); + } + @Test public void testTimeout() throws Exception { SolrQuery q = new SolrQuery("*:*"); @@ -678,66 +620,6 @@ private byte[] javabinResponse() { + "70 6f 6e 73 65 0c 84 60 60 " + "00 01 80"; - public static class DebugAsyncListener implements AsyncListener> { - - private final CountDownLatch cdl; - - private final Semaphore wait = new Semaphore(1); - - public volatile boolean onStartCalled; - - public volatile boolean latchCounted; - - public volatile NamedList onSuccessResult = null; - - public volatile Throwable onFailureResult = null; - - public DebugAsyncListener(CountDownLatch cdl) { - this.cdl = cdl; - } - - @Override - public void onStart() { - onStartCalled = true; - } - - public void pause() { - try { - wait.acquire(); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - } - } - - public void unPause() { - wait.release(); - } - - @Override - public void onSuccess(NamedList entries) { - pause(); - onSuccessResult = entries; - if (latchCounted) { - fail("either 'onSuccess' or 'onFailure' should be called exactly once."); - } - cdl.countDown(); - latchCounted = true; - unPause(); - } - - @Override - public void onFailure(Throwable throwable) { - pause(); - onFailureResult = throwable; - if (latchCounted) { - fail("either 'onSuccess' or 'onFailure' should be called exactly once."); - } - cdl.countDown(); - latchCounted = true; - unPause(); - } - } - /** * Taken from: https://www.baeldung.com/java-httpclient-ssl sec 4.1, 2024/02/12. This is an * all-trusting Trust Manager. Works with self-signed certificates. diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java index a6a0ab2a7d6..4de26f69a8a 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java @@ -28,6 +28,9 @@ import java.util.Iterator; import java.util.Locale; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + import org.apache.solr.SolrJettyTestBase; import org.apache.solr.client.solrj.ResponseParser; import org.apache.solr.client.solrj.SolrQuery; @@ -36,9 +39,13 @@ import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.request.SolrPing; import org.apache.solr.client.solrj.request.UpdateRequest; +import org.apache.solr.client.solrj.util.Cancellable; +import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.MapSolrParams; +import org.apache.solr.common.util.NamedList; import org.apache.solr.embedded.JettyConfig; import org.eclipse.jetty.servlet.ServletHolder; import org.hamcrest.MatcherAssert; @@ -532,4 +539,65 @@ protected void testUseOptionalCredentialsWithNull(HttpSolrClientBase client) { assertNull( "No authorization headers expected. Headers: " + DebugServlet.headers, authorizationHeader); } + + protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { + ResponseParser rp = new XMLResponseParser(); + DebugServlet.clear(); + DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); + String url = getBaseUrl() + DEBUG_SERVLET_PATH; + HttpSolrClientBuilderBase b = builder(url, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT).withResponseParser(rp); + int limit = 10; + CountDownLatch cdl = new CountDownLatch(limit); + DebugAsyncListener[] listeners = new DebugAsyncListener[limit]; + Cancellable[] cancellables = new Cancellable[limit]; + try (HttpSolrClientBase client = b.build()) { + for (int i = 0; i < limit; i++) { + DebugServlet.responseBodyByQueryFragment.put( + ("id=KEY-" + i), + "\nKEY-" + + i + + ""); + QueryRequest query = + new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "KEY-" + i))); + query.setMethod(SolrRequest.METHOD.GET); + listeners[i] = new DebugAsyncListener(cdl); + client.asyncRequest(query, null, listeners[i]); + } + cdl.await(1, TimeUnit.MINUTES); + } + + for (int i = 0; i < limit; i++) { + NamedList result = listeners[i].onSuccessResult; + SolrDocumentList sdl = (SolrDocumentList) result.get("response"); + assertEquals(2, sdl.getNumFound()); + assertEquals(1, sdl.getStart()); + assertTrue(sdl.getNumFoundExact()); + assertEquals(1, sdl.size()); + assertEquals(1, sdl.iterator().next().size()); + assertEquals("KEY-" + i, sdl.iterator().next().get("id")); + + assertNull(listeners[i].onFailureResult); + assertTrue(listeners[i].onStartCalled); + } + } + + protected DebugAsyncListener testAsyncExceptionBase() throws Exception { + ResponseParser rp = new XMLResponseParser(); + DebugServlet.clear(); + DebugServlet.addResponseHeader("Content-Type", "Wrong Content Type!"); + String url = getBaseUrl() + DEBUG_SERVLET_PATH; + HttpSolrClientBuilderBase b = builder(url, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT).withResponseParser(rp); + CountDownLatch cdl = new CountDownLatch(1); + DebugAsyncListener listener = new DebugAsyncListener(cdl); + try (HttpSolrClientBase client = b.build()) { + QueryRequest query = new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); + client.asyncRequest(query, "collection1", listener); + cdl.await(1, TimeUnit.MINUTES); + } + + assertNotNull(listener.onFailureResult); + assertTrue(listener.onStartCalled); + assertNull(listener.onSuccessResult); + return listener; + } } From ad507b2608dd41e0f4bcb87fa6c2a7201809c878 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Fri, 22 Mar 2024 16:59:03 -0500 Subject: [PATCH 06/19] tidy --- .../client/solrj/impl/HttpSolrClientBase.java | 9 ++++---- .../client/solrj/impl/DebugAsyncListener.java | 22 ++++++++++++++++--- .../solrj/impl/HttpJdkSolrClientTest.java | 4 ++-- .../solrj/impl/HttpSolrClientTestBase.java | 17 +++++++------- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java index 5fff2bb6e9b..a491a119c86 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java @@ -375,13 +375,14 @@ protected void setParser(ResponseParser parser) { * * @param solrRequest the request to perform * @param collection if null the default collection is used - * @param asyncListener callers should provide an implementation to handle events: start, success, exception + * @param asyncListener callers should provide an implementation to handle events: start, success, + * exception * @return Cancellable allowing the caller to attempt cancellation */ public abstract Cancellable asyncRequest( - SolrRequest solrRequest, - String collection, - AsyncListener> asyncListener) ; + SolrRequest solrRequest, + String collection, + AsyncListener> asyncListener); public boolean isV2ApiRequest(final SolrRequest request) { return request instanceof V2Request || request.getPath().contains("/____v2"); diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugAsyncListener.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugAsyncListener.java index 3ab1c0c55cf..b3eaf5f5d9d 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugAsyncListener.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugAsyncListener.java @@ -1,12 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.solr.client.solrj.impl; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; import org.apache.solr.client.solrj.util.AsyncListener; import org.apache.solr.common.util.NamedList; import org.junit.Assert; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Semaphore; - public class DebugAsyncListener implements AsyncListener> { private final CountDownLatch cdl; diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index 1a905606bc9..27ee4739e2e 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -228,7 +228,7 @@ public void testAsyncAndCancel() throws Exception { DebugServlet.clear(); DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); DebugServlet.responseBodyByQueryFragment.put( - "", "\n"); + "", "\n"); String url = getBaseUrl() + DEBUG_SERVLET_PATH; HttpJdkSolrClient.Builder b = builder(url).withResponseParser(rp); CountDownLatch cdl = new CountDownLatch(0); @@ -244,7 +244,7 @@ public void testAsyncAndCancel() throws Exception { assertTrue(listener.onStartCalled); assertTrue(cancelMe instanceof HttpJdkSolrClient.HttpSolrClientCancellable); CompletableFuture> response = - ((HttpJdkSolrClient.HttpSolrClientCancellable) cancelMe).getResponse(); + ((HttpJdkSolrClient.HttpSolrClientCancellable) cancelMe).getResponse(); assertTrue(response.isCancelled()); } diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java index 4de26f69a8a..6875ee56fa8 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java @@ -30,7 +30,6 @@ import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; - import org.apache.solr.SolrJettyTestBase; import org.apache.solr.client.solrj.ResponseParser; import org.apache.solr.client.solrj.SolrQuery; @@ -545,7 +544,8 @@ protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { DebugServlet.clear(); DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); String url = getBaseUrl() + DEBUG_SERVLET_PATH; - HttpSolrClientBuilderBase b = builder(url, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT).withResponseParser(rp); + HttpSolrClientBuilderBase b = + builder(url, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT).withResponseParser(rp); int limit = 10; CountDownLatch cdl = new CountDownLatch(limit); DebugAsyncListener[] listeners = new DebugAsyncListener[limit]; @@ -553,12 +553,12 @@ protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { try (HttpSolrClientBase client = b.build()) { for (int i = 0; i < limit; i++) { DebugServlet.responseBodyByQueryFragment.put( - ("id=KEY-" + i), - "\nKEY-" - + i - + ""); + ("id=KEY-" + i), + "\nKEY-" + + i + + ""); QueryRequest query = - new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "KEY-" + i))); + new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "KEY-" + i))); query.setMethod(SolrRequest.METHOD.GET); listeners[i] = new DebugAsyncListener(cdl); client.asyncRequest(query, null, listeners[i]); @@ -586,7 +586,8 @@ protected DebugAsyncListener testAsyncExceptionBase() throws Exception { DebugServlet.clear(); DebugServlet.addResponseHeader("Content-Type", "Wrong Content Type!"); String url = getBaseUrl() + DEBUG_SERVLET_PATH; - HttpSolrClientBuilderBase b = builder(url, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT).withResponseParser(rp); + HttpSolrClientBuilderBase b = + builder(url, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT).withResponseParser(rp); CountDownLatch cdl = new CountDownLatch(1); DebugAsyncListener listener = new DebugAsyncListener(cdl); try (HttpSolrClientBase client = b.build()) { From 6e79aa4a2a6aae0abd7c08457fe88602f4e8aed1 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Mon, 25 Mar 2024 12:02:36 -0500 Subject: [PATCH 07/19] no need for separate "put" test --- .../apache/solr/client/solrj/impl/Http2SolrClientTest.java | 6 ------ .../solr/client/solrj/impl/HttpJdkSolrClientTest.java | 5 ----- 2 files changed, 11 deletions(-) diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java index 6f3ce942cb9..6559fe3b76b 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java @@ -272,12 +272,6 @@ public void testAsyncGet() throws Exception { public void testAsyncPost() throws Exception { super.testQueryAsync(SolrRequest.METHOD.GET); } - - @Test - public void testAsyncPut() throws Exception { - super.testQueryAsync(SolrRequest.METHOD.GET); - } - @Test public void testAsyncException() throws Exception { super.testAsyncExceptionBase(); diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index 27ee4739e2e..4cb613faefc 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -208,11 +208,6 @@ public void testAsyncPost() throws Exception { super.testQueryAsync(SolrRequest.METHOD.GET); } - @Test - public void testAsyncPut() throws Exception { - super.testQueryAsync(SolrRequest.METHOD.GET); - } - @Test public void testAsyncException() throws Exception { DebugAsyncListener listener = super.testAsyncExceptionBase(); From d66c85054a71c6c0c6bc00f96b7d9fcd3e824885 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Mon, 25 Mar 2024 13:04:22 -0500 Subject: [PATCH 08/19] Comments in "testAsyncAndCancel" --- .../solrj/impl/HttpJdkSolrClientTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index 4cb613faefc..d30f3e4ea55 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -231,16 +231,33 @@ public void testAsyncAndCancel() throws Exception { Cancellable cancelMe = null; try (HttpJdkSolrClient client = b.build()) { QueryRequest query = new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); + + // We are pausing in the "whenComplete" stage, in the unlikely event the http request + // finishes before the test calls "cancel". listener.pause(); + + // Make the request then immediately cancel it! cancelMe = client.asyncRequest(query, "collection1", listener); cancelMe.cancel(); + + // We are safe to unpause our client, having guaranteed that our cancel was before everything completed. listener.unPause(); } + + // "onStart" fires before the async call. This part of the request cannot be cancelled. assertTrue(listener.onStartCalled); + + // The client exposes the CompletableFuture to us via this inner class assertTrue(cancelMe instanceof HttpJdkSolrClient.HttpSolrClientCancellable); CompletableFuture> response = ((HttpJdkSolrClient.HttpSolrClientCancellable) cancelMe).getResponse(); + + // Even if our cancel didn't happen until we were at "whenComplete", the CompletableFuture will + // have set "isCancelled". assertTrue(response.isCancelled()); + + // But we cannot guarantee the response will have been returned, or that "onFailure" was fired with + // a "CompletionException". This depends on where we were when the cancellation hit. } @Test From 1e5d2740b1c5aa6a1d46266cf09c245b9c07756f Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Mon, 25 Mar 2024 13:12:07 -0500 Subject: [PATCH 09/19] slight improvement to AsyncListener doc comments --- .../org/apache/solr/client/solrj/util/AsyncListener.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/util/AsyncListener.java b/solr/solrj/src/java/org/apache/solr/client/solrj/util/AsyncListener.java index c8e9ce72d69..e345fde48e2 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/util/AsyncListener.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/util/AsyncListener.java @@ -17,12 +17,18 @@ package org.apache.solr.client.solrj.util; -/** Listener for async requests */ +/** + * Listener for async requests + * + * @param The result type returned by the {@code onSuccess} method + */ public interface AsyncListener { /** Callback method invoked before processing the request */ default void onStart() {} + /** Callback method invoked when the request completes successfully */ void onSuccess(T t); + /** Callback method invoked when the request completes in failure */ void onFailure(Throwable throwable); } From b17913aa810cc87b02579157da123d8e60c25df9 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Mon, 25 Mar 2024 13:21:33 -0500 Subject: [PATCH 10/19] Javadoc on Cancellable --- .../org/apache/solr/client/solrj/util/Cancellable.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java b/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java index 323916a4fdc..b7a4f49a365 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java @@ -17,6 +17,13 @@ package org.apache.solr.client.solrj.util; +/** + * The return type for solrJ asynchronous requests, providing a mechanism whereby callers may request cancellation. + */ public interface Cancellable { + + /** + * Request to cancel the asynchronous request. This may be a no-op in some situations, for instance, if the request failed or otherwise is finished. + */ void cancel(); } From ecb0051f258c80b08a339fe275c503f1d9f87f73 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Mon, 25 Mar 2024 13:23:04 -0500 Subject: [PATCH 11/19] tidy --- .../org/apache/solr/client/solrj/util/AsyncListener.java | 4 ++-- .../java/org/apache/solr/client/solrj/util/Cancellable.java | 2 +- .../apache/solr/client/solrj/impl/Http2SolrClientTest.java | 1 + .../solr/client/solrj/impl/HttpJdkSolrClientTest.java | 6 ++++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/util/AsyncListener.java b/solr/solrj/src/java/org/apache/solr/client/solrj/util/AsyncListener.java index e345fde48e2..7e135ac3569 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/util/AsyncListener.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/util/AsyncListener.java @@ -26,9 +26,9 @@ public interface AsyncListener { /** Callback method invoked before processing the request */ default void onStart() {} - /** Callback method invoked when the request completes successfully */ + /** Callback method invoked when the request completes successfully */ void onSuccess(T t); - /** Callback method invoked when the request completes in failure */ + /** Callback method invoked when the request completes in failure */ void onFailure(Throwable throwable); } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java b/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java index b7a4f49a365..04894492647 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java @@ -23,7 +23,7 @@ public interface Cancellable { /** - * Request to cancel the asynchronous request. This may be a no-op in some situations, for instance, if the request failed or otherwise is finished. + * Request to cancel the asynchronous request. This may be a no-op in some situations, for instance, if the request failed or otherwise is complete. */ void cancel(); } diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java index 6559fe3b76b..3a3a7d7f3fa 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java @@ -272,6 +272,7 @@ public void testAsyncGet() throws Exception { public void testAsyncPost() throws Exception { super.testQueryAsync(SolrRequest.METHOD.GET); } + @Test public void testAsyncException() throws Exception { super.testAsyncExceptionBase(); diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index d30f3e4ea55..0010052e0a3 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -240,7 +240,8 @@ public void testAsyncAndCancel() throws Exception { cancelMe = client.asyncRequest(query, "collection1", listener); cancelMe.cancel(); - // We are safe to unpause our client, having guaranteed that our cancel was before everything completed. + // We are safe to unpause our client, having guaranteed that our cancel was before everything + // completed. listener.unPause(); } @@ -256,7 +257,8 @@ public void testAsyncAndCancel() throws Exception { // have set "isCancelled". assertTrue(response.isCancelled()); - // But we cannot guarantee the response will have been returned, or that "onFailure" was fired with + // But we cannot guarantee the response will have been returned, or that "onFailure" was fired + // with // a "CompletionException". This depends on where we were when the cancellation hit. } From d0b0e1efdb19ac2050eaa7214758ff74fd896dff Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Mon, 25 Mar 2024 13:29:02 -0500 Subject: [PATCH 12/19] tidy --- .../java/org/apache/solr/client/solrj/util/Cancellable.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java b/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java index 04894492647..52691129052 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/util/Cancellable.java @@ -18,12 +18,14 @@ package org.apache.solr.client.solrj.util; /** - * The return type for solrJ asynchronous requests, providing a mechanism whereby callers may request cancellation. + * The return type for solrJ asynchronous requests, providing a mechanism whereby callers may + * request cancellation. */ public interface Cancellable { /** - * Request to cancel the asynchronous request. This may be a no-op in some situations, for instance, if the request failed or otherwise is complete. + * Request to cancel the asynchronous request. This may be a no-op in some situations, for + * instance, if the request failed or otherwise is complete. */ void cancel(); } From 5760fb53063aca7025cf26a8cfc8769e90822180 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Mon, 25 Mar 2024 14:00:17 -0500 Subject: [PATCH 13/19] fix test: was not testing POST --- .../apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java | 3 +-- .../apache/solr/client/solrj/impl/HttpSolrClientTestBase.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index 0010052e0a3..ed1d61b9568 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -258,8 +258,7 @@ public void testAsyncAndCancel() throws Exception { assertTrue(response.isCancelled()); // But we cannot guarantee the response will have been returned, or that "onFailure" was fired - // with - // a "CompletionException". This depends on where we were when the cancellation hit. + // with a "CompletionException". This depends on where we were when the cancellation hit. } @Test diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java index 6875ee56fa8..43de0ad1181 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java @@ -559,7 +559,7 @@ protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { + ""); QueryRequest query = new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "KEY-" + i))); - query.setMethod(SolrRequest.METHOD.GET); + query.setMethod(method); listeners[i] = new DebugAsyncListener(cdl); client.asyncRequest(query, null, listeners[i]); } From 7451def080161ffa8af5e93f2ad51f38d7cd6143 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Tue, 26 Mar 2024 16:59:57 -0500 Subject: [PATCH 14/19] - fix test bugs - "POST" test does not pass! (TODO) --- .../test/org/apache/solr/client/solrj/impl/DebugServlet.java | 3 +-- .../org/apache/solr/client/solrj/impl/Http2SolrClientTest.java | 2 +- .../apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java index d4fd3fcab3c..94f688944e5 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -51,7 +50,7 @@ public static void clear() { public static String queryString = null; public static javax.servlet.http.Cookie[] cookies = null; public static List responseHeaders = null; - public static Map responseBodyByQueryFragment = new LinkedHashMap<>(); + public static Map responseBodyByQueryFragment = new ConcurrentHashMap<>(); public static byte[] requestBody = null; public static void setErrorCode(Integer code) { diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java index 3a3a7d7f3fa..81a61aa0249 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java @@ -270,7 +270,7 @@ public void testAsyncGet() throws Exception { @Test public void testAsyncPost() throws Exception { - super.testQueryAsync(SolrRequest.METHOD.GET); + super.testQueryAsync(SolrRequest.METHOD.POST); } @Test diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index ed1d61b9568..7acd580f608 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -205,7 +205,7 @@ public void testAsyncGet() throws Exception { @Test public void testAsyncPost() throws Exception { - super.testQueryAsync(SolrRequest.METHOD.GET); + super.testQueryAsync(SolrRequest.METHOD.POST); } @Test From 92e12a6733eec10596be0a970d7cb3b628ece91f Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Tue, 2 Apr 2024 15:39:30 -0500 Subject: [PATCH 15/19] SOLR-17211 - could not use DebugServlet to do a thorough post test, switch to indexing actual solr documents --- .../solr/client/solrj/impl/DebugServlet.java | 2 + .../solrj/impl/Http2SolrClientTest.java | 4 +- .../solrj/impl/HttpJdkSolrClientTest.java | 4 +- .../solrj/impl/HttpSolrClientTestBase.java | 48 ++++++++++++++++--- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java index 94f688944e5..7627b1d3eeb 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java @@ -140,6 +140,8 @@ private void recordRequest(HttpServletRequest req, HttpServletResponse resp) { String qs = req.getQueryString(); qs = qs == null ? "" : qs; Object responseBody = null; + + // Tests can set this up to return different response bodies based on substrings in the query string for (Map.Entry entry : responseBodyByQueryFragment.entrySet()) { if (qs.contains(entry.getKey())) { responseBody = entry.getValue(); diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java index 81a61aa0249..a8831fd4842 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java @@ -265,12 +265,12 @@ public void testUpdateJavabin() throws Exception { @Test public void testAsyncGet() throws Exception { - super.testQueryAsync(SolrRequest.METHOD.GET); + super.testQueryAsync(); } @Test public void testAsyncPost() throws Exception { - super.testQueryAsync(SolrRequest.METHOD.POST); + super.testUpdateAsync(); } @Test diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java index 7acd580f608..81d118873c7 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java @@ -200,12 +200,12 @@ public void testGetById() throws Exception { @Test public void testAsyncGet() throws Exception { - super.testQueryAsync(SolrRequest.METHOD.GET); + super.testQueryAsync(); } @Test public void testAsyncPost() throws Exception { - super.testQueryAsync(SolrRequest.METHOD.POST); + super.testUpdateAsync(); } @Test diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java index 43de0ad1181..a2d45ce0e70 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java @@ -38,6 +38,7 @@ import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.request.SolrPing; import org.apache.solr.client.solrj.request.UpdateRequest; +import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.util.Cancellable; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrException; @@ -59,6 +60,7 @@ public abstract class HttpSolrClientTestBase extends SolrJettyTestBase { protected static final String DEBUG_SERVLET_REGEX = DEBUG_SERVLET_PATH + "/*"; protected static final String REDIRECT_SERVLET_PATH = "/redirect"; protected static final String REDIRECT_SERVLET_REGEX = REDIRECT_SERVLET_PATH + "/*"; + protected static final String COLLECTION_1 = "collection1"; @BeforeClass public static void beforeTest() throws Exception { @@ -312,13 +314,13 @@ protected void testCollectionParameters( try { SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", "collection"); - baseUrlClient.add("collection1", doc); - baseUrlClient.commit("collection1"); + baseUrlClient.add(COLLECTION_1, doc); + baseUrlClient.commit(COLLECTION_1); assertEquals( 1, baseUrlClient - .query("collection1", new SolrQuery("id:collection")) + .query(COLLECTION_1, new SolrQuery("id:collection")) .getResults() .getNumFound()); @@ -539,7 +541,41 @@ protected void testUseOptionalCredentialsWithNull(HttpSolrClientBase client) { "No authorization headers expected. Headers: " + DebugServlet.headers, authorizationHeader); } - protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { + protected void testUpdateAsync() throws Exception { + ResponseParser rp = new XMLResponseParser(); + String url = getBaseUrl(); + HttpSolrClientBuilderBase b = + builder(url, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT).withResponseParser(rp); + int limit = 10; + CountDownLatch cdl = new CountDownLatch(limit); + DebugAsyncListener[] listeners = new DebugAsyncListener[limit]; + Cancellable[] cancellables = new Cancellable[limit]; + try (HttpSolrClientBase client = b.build()) { + + // ensure the collection is empty to start + client.deleteByQuery(COLLECTION_1, "*:*"); + client.commit(COLLECTION_1); + QueryResponse qr = client.query(COLLECTION_1, new MapSolrParams(Collections.singletonMap("q", "*:*")), SolrRequest.METHOD.POST); + assertEquals(0, qr.getResults().getNumFound()); + + for (int i = 0; i < limit; i++) { + listeners[i] = new DebugAsyncListener(cdl); + UpdateRequest ur = new UpdateRequest(); + ur.add("id", "KEY-" + i); + ur.setMethod(SolrRequest.METHOD.POST); + client.asyncRequest(ur, COLLECTION_1, listeners[i]); + } + cdl.await(1, TimeUnit.MINUTES); + client.commit(COLLECTION_1); + + // check that the correct number of documents were added + qr = client.query(COLLECTION_1, new MapSolrParams(Collections.singletonMap("q", "*:*")), SolrRequest.METHOD.POST); + assertEquals(limit, qr.getResults().getNumFound()); + } + + } + + protected void testQueryAsync() throws Exception { ResponseParser rp = new XMLResponseParser(); DebugServlet.clear(); DebugServlet.addResponseHeader("Content-Type", "application/xml; charset=UTF-8"); @@ -559,7 +595,7 @@ protected void testQueryAsync(SolrRequest.METHOD method) throws Exception { + ""); QueryRequest query = new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "KEY-" + i))); - query.setMethod(method); + query.setMethod(SolrRequest.METHOD.GET); listeners[i] = new DebugAsyncListener(cdl); client.asyncRequest(query, null, listeners[i]); } @@ -592,7 +628,7 @@ protected DebugAsyncListener testAsyncExceptionBase() throws Exception { DebugAsyncListener listener = new DebugAsyncListener(cdl); try (HttpSolrClientBase client = b.build()) { QueryRequest query = new QueryRequest(new MapSolrParams(Collections.singletonMap("id", "1"))); - client.asyncRequest(query, "collection1", listener); + client.asyncRequest(query, COLLECTION_1, listener); cdl.await(1, TimeUnit.MINUTES); } From 7d39ab26006ce4e7b7d54ffe412e2d903a020d6a Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Tue, 2 Apr 2024 15:40:51 -0500 Subject: [PATCH 16/19] tidy --- .../solr/client/solrj/impl/DebugServlet.java | 3 ++- .../client/solrj/impl/HttpSolrClientTestBase.java | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java index 7627b1d3eeb..9cfc31c31f8 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java @@ -141,7 +141,8 @@ private void recordRequest(HttpServletRequest req, HttpServletResponse resp) { qs = qs == null ? "" : qs; Object responseBody = null; - // Tests can set this up to return different response bodies based on substrings in the query string + // Tests can set this up to return different response bodies based on substrings in the query + // string for (Map.Entry entry : responseBodyByQueryFragment.entrySet()) { if (qs.contains(entry.getKey())) { responseBody = entry.getValue(); diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java index a2d45ce0e70..0bb017aa4c6 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java @@ -545,7 +545,7 @@ protected void testUpdateAsync() throws Exception { ResponseParser rp = new XMLResponseParser(); String url = getBaseUrl(); HttpSolrClientBuilderBase b = - builder(url, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT).withResponseParser(rp); + builder(url, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT).withResponseParser(rp); int limit = 10; CountDownLatch cdl = new CountDownLatch(limit); DebugAsyncListener[] listeners = new DebugAsyncListener[limit]; @@ -555,7 +555,11 @@ protected void testUpdateAsync() throws Exception { // ensure the collection is empty to start client.deleteByQuery(COLLECTION_1, "*:*"); client.commit(COLLECTION_1); - QueryResponse qr = client.query(COLLECTION_1, new MapSolrParams(Collections.singletonMap("q", "*:*")), SolrRequest.METHOD.POST); + QueryResponse qr = + client.query( + COLLECTION_1, + new MapSolrParams(Collections.singletonMap("q", "*:*")), + SolrRequest.METHOD.POST); assertEquals(0, qr.getResults().getNumFound()); for (int i = 0; i < limit; i++) { @@ -569,10 +573,13 @@ protected void testUpdateAsync() throws Exception { client.commit(COLLECTION_1); // check that the correct number of documents were added - qr = client.query(COLLECTION_1, new MapSolrParams(Collections.singletonMap("q", "*:*")), SolrRequest.METHOD.POST); + qr = + client.query( + COLLECTION_1, + new MapSolrParams(Collections.singletonMap("q", "*:*")), + SolrRequest.METHOD.POST); assertEquals(limit, qr.getResults().getNumFound()); } - } protected void testQueryAsync() throws Exception { From cfaed30bb28a8392cc54f7d143002d4feac953e5 Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Wed, 3 Apr 2024 09:16:27 -0500 Subject: [PATCH 17/19] test cleanup --- .../apache/solr/client/solrj/impl/HttpSolrClientTestBase.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java index 0bb017aa4c6..3d8be7a4729 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java @@ -579,6 +579,10 @@ protected void testUpdateAsync() throws Exception { new MapSolrParams(Collections.singletonMap("q", "*:*")), SolrRequest.METHOD.POST); assertEquals(limit, qr.getResults().getNumFound()); + + // clean up + client.deleteByQuery(COLLECTION_1, "*:*"); + client.commit(COLLECTION_1); } } From d157dc7dbb0e5efcee50c3e599f5cab2149480cb Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Wed, 3 Apr 2024 09:18:50 -0500 Subject: [PATCH 18/19] Reference manual citation --- solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc index 32bf0aca411..29995993199 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc @@ -98,7 +98,7 @@ Requests are sent in the form of {solr-javadocs}/solrj/org/apache/solr/client/so - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/HttpSolrClient.html[`HttpSolrClient`] - geared towards query-centric workloads, though also a good general-purpose client. Communicates directly with a single Solr node. - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/Http2SolrClient.html[`Http2SolrClient`] - async, non-blocking and general-purpose client that leverage HTTP/2 using the Jetty Http library. -- {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.html[`HttpJdkSolrClient`] - General-purpose client using the JDK's built-in Http Client. Supports both Http/2 and Http/1.1. Targeted for those users wishing to minimize application dependencies. +- {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.html[`HttpJdkSolrClient`] - General-purpose client using the JDK's built-in Http Client. Supports both Http/2 and Http/1.1. Supports async. Targeted for those users wishing to minimize application dependencies. - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/LBHttpSolrClient.html[`LBHttpSolrClient`] - balances request load across a list of Solr nodes. Adjusts the list of "in-service" nodes based on node health. - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.html[`LBHttp2SolrClient`] - just like `LBHttpSolrClient` but using `Http2SolrClient` instead, with the Jetty Http library. From a7956f7e94de46e2a356a44c393627a14ee2d8ba Mon Sep 17 00:00:00 2001 From: jdyer1 Date: Wed, 3 Apr 2024 09:23:57 -0500 Subject: [PATCH 19/19] CHANGES.txt entry --- solr/CHANGES.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 0cd8f12ae70..d9a80386e18 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -122,6 +122,8 @@ Improvements * SOLR-17164: Add 2 arg variant of vectorSimilarity() function (Sanjay Dutt, hossman) +* SOLR-17211: New SolrJ JDK client supports Async (James Dyer) + Optimizations --------------------- * SOLR-17144: Close searcherExecutor thread per core after 1 minute (Pierre Salagnac, Christine Poerschke)