This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/main by this push:
     new 1f05e73c203 SOLR-17706: SolrJ DocumentObjectBinder, make a singleton 
(#3271)
1f05e73c203 is described below

commit 1f05e73c20379317a25b587a9c541702b86575f8
Author: David Smiley <[email protected]>
AuthorDate: Sat Mar 22 12:14:44 2025 -0400

    SOLR-17706: SolrJ DocumentObjectBinder, make a singleton (#3271)
    
    DocumentObjectBinder should be singleton.
    Remove from SolrClient, and thus update QueryResponse to not get it from 
there.
    Use Java SPI/ServiceLoader to allow someone to customize.  (Manually tested)
    Use ConcurrentHashMap.computeIfAbsent internally.
---
 solr/CHANGES.txt                                   |  3 +++
 .../solr/cloud/DistribJoinFromCollectionTest.java  |  6 ++---
 .../apache/solr/cloud/TestRandomFlRTGCloud.java    |  2 +-
 .../transform/TestSubQueryTransformerDistrib.java  |  3 +--
 .../solr/search/join/ShardToShardJoinAbstract.java |  6 ++---
 .../org/apache/solr/client/solrj/SolrClient.java   | 28 +++++-----------------
 .../client/solrj/beans/DocumentObjectBinder.java   | 26 +++++++++++---------
 .../solr/client/solrj/request/QueryRequest.java    |  2 +-
 .../solr/client/solrj/response/QueryResponse.java  | 20 +++-------------
 .../solrj/beans/TestDocumentObjectBinder.java      |  4 ++--
 .../client/solrj/response/QueryResponseTest.java   | 10 ++++----
 .../solrj/response/TestClusteringResponse.java     |  2 +-
 12 files changed, 44 insertions(+), 68 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 812ab8243a7..48883e6c7fd 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -161,6 +161,9 @@ Other Changes
   Stopped using RawResponseWriter when not needed.  Rename 
BinaryResponseWriter to JavaBinResponseWriter.
   (David Smiley)
 
+* SOLR-17706: SolrJ DocumentObjectBinder is now a global singleton via an 
INSTANCE field and that
+  which is pluggable via Java SPI/ServiceLoader.  SolrClient.getBinder is 
gone.  (David Smiley)
+
 ==================  9.9.0 ==================
 New Features
 ---------------------
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/DistribJoinFromCollectionTest.java 
b/solr/core/src/test/org/apache/solr/cloud/DistribJoinFromCollectionTest.java
index e22770d4222..0d2d7bef066 100644
--- 
a/solr/core/src/test/org/apache/solr/cloud/DistribJoinFromCollectionTest.java
+++ 
b/solr/core/src/test/org/apache/solr/cloud/DistribJoinFromCollectionTest.java
@@ -147,7 +147,7 @@ public class DistribJoinFromCollectionTest extends 
SolrCloudTestCase {
               + fromQ;
       QueryRequest qr =
           new QueryRequest(params("collection", toColl, "q", joinQ, "fl", 
"id,get_s,score"));
-      QueryResponse rsp = new QueryResponse(client.request(qr), client);
+      QueryResponse rsp = qr.process(client);
       SolrDocumentList hits = rsp.getResults();
       assertEquals("Expected 1 doc, got " + hits, 1, hits.getNumFound());
       SolrDocument doc = hits.get(0);
@@ -173,7 +173,7 @@ public class DistribJoinFromCollectionTest extends 
SolrCloudTestCase {
               + fromQ;
       final QueryRequest qr =
           new QueryRequest(params("collection", toColl, "q", joinQ, "fl", 
"id,get_s,score"));
-      final QueryResponse rsp = new QueryResponse(client.request(qr), client);
+      final QueryResponse rsp = qr.process(client);
       final SolrDocumentList hits = rsp.getResults();
       assertEquals("Expected 1 doc", 1, hits.getNumFound());
       SolrDocument doc = hits.get(0);
@@ -195,7 +195,7 @@ public class DistribJoinFromCollectionTest extends 
SolrCloudTestCase {
               + " to=join_s}match_s:d";
       final QueryRequest qr =
           new QueryRequest(params("collection", toColl, "q", joinQ, "fl", 
"id,get_s,score"));
-      final QueryResponse rsp = new QueryResponse(client.request(qr), client);
+      final QueryResponse rsp = qr.process(client);
       final SolrDocumentList hits = rsp.getResults();
       assertEquals("Expected no hits", 0, hits.getNumFound());
     }
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestRandomFlRTGCloud.java 
b/solr/core/src/test/org/apache/solr/cloud/TestRandomFlRTGCloud.java
index ecaeae8b64f..9def712aeab 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestRandomFlRTGCloud.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestRandomFlRTGCloud.java
@@ -624,7 +624,7 @@ public class TestRandomFlRTGCloud extends SolrCloudTestCase 
{
     return getDocsFromRTGResponse(
         expectList,
         new QueryResponse(
-            new RawCapableXMLResponseParser().processResponse(new 
StringReader(rsp)), null));
+            new RawCapableXMLResponseParser().processResponse(new 
StringReader(rsp))));
   }
 
   /**
diff --git 
a/solr/core/src/test/org/apache/solr/response/transform/TestSubQueryTransformerDistrib.java
 
b/solr/core/src/test/org/apache/solr/response/transform/TestSubQueryTransformerDistrib.java
index 5d626446e58..e4cb7ae836f 100644
--- 
a/solr/core/src/test/org/apache/solr/response/transform/TestSubQueryTransformerDistrib.java
+++ 
b/solr/core/src/test/org/apache/solr/response/transform/TestSubQueryTransformerDistrib.java
@@ -182,8 +182,7 @@ public class TestSubQueryTransformerDistrib extends 
SolrCloudTestCase {
     final SolrDocumentList hits;
     {
       final QueryRequest qr = withBasicAuth(new QueryRequest(params));
-      final QueryResponse rsp = new QueryResponse();
-      rsp.setResponse(cluster.getSolrClient().request(qr, people + "," + 
depts));
+      final QueryResponse rsp = qr.process(cluster.getSolrClient(), people + 
"," + depts);
       hits = rsp.getResults();
 
       assertEquals(peopleMultiplier, hits.getNumFound());
diff --git 
a/solr/core/src/test/org/apache/solr/search/join/ShardToShardJoinAbstract.java 
b/solr/core/src/test/org/apache/solr/search/join/ShardToShardJoinAbstract.java
index ece5e5338a6..b2b3f4be01e 100644
--- 
a/solr/core/src/test/org/apache/solr/search/join/ShardToShardJoinAbstract.java
+++ 
b/solr/core/src/test/org/apache/solr/search/join/ShardToShardJoinAbstract.java
@@ -194,7 +194,7 @@ public class ShardToShardJoinAbstract extends 
SolrCloudTestCase {
                     + " to=id}"
                     + fromQ;
             QueryRequest qr = new QueryRequest(params("collection", toColl, 
"q", joinQ, "fl", "*"));
-            QueryResponse rsp = new QueryResponse(client.request(qr), client);
+            QueryResponse rsp = qr.process(client);
             SolrDocumentList hits = rsp.getResults();
             assertEquals("Expected 1 doc, got " + hits, 1, hits.getNumFound());
             SolrDocument doc = hits.get(0);
@@ -216,7 +216,7 @@ public class ShardToShardJoinAbstract extends 
SolrCloudTestCase {
             QueryRequest qr =
                 new QueryRequest(
                     params("collection", fromColl, "q", joinQ, "fl", "*", 
"rows", "20"));
-            QueryResponse rsp = new QueryResponse(client.request(qr), client);
+            QueryResponse rsp = new QueryResponse(client.request(qr));
             SolrDocumentList hits = rsp.getResults();
             assertEquals("Expected 11 doc, got " + hits, 10 + 1, 
hits.getNumFound());
             for (SolrDocument doc : hits) {
@@ -246,7 +246,7 @@ public class ShardToShardJoinAbstract extends 
SolrCloudTestCase {
               + " to=id}"
               + fromQ;
       QueryRequest qr = new QueryRequest(params("collection", toColl, "q", 
joinQ, "fl", "*"));
-      QueryResponse rsp = new QueryResponse(client.request(qr), client);
+      QueryResponse rsp = qr.process(client);
       SolrDocumentList hits = rsp.getResults();
       final Set<String> expect =
           new HashSet<>(
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/SolrClient.java 
b/solr/solrj/src/java/org/apache/solr/client/solrj/SolrClient.java
index 240f9750ed3..acdf6b013b3 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/SolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/SolrClient.java
@@ -52,7 +52,6 @@ public abstract class SolrClient implements Serializable, 
Closeable {
 
   private static final long serialVersionUID = 1L;
 
-  private DocumentObjectBinder binder;
   protected String defaultCollection;
 
   /**
@@ -261,7 +260,7 @@ public abstract class SolrClient implements Serializable, 
Closeable {
    */
   public UpdateResponse addBean(String collection, Object obj, int 
commitWithinMs)
       throws IOException, SolrServerException {
-    return add(collection, getBinder().toSolrInputDocument(obj), 
commitWithinMs);
+    return add(collection, 
DocumentObjectBinder.INSTANCE.toSolrInputDocument(obj), commitWithinMs);
   }
 
   /**
@@ -277,7 +276,7 @@ public abstract class SolrClient implements Serializable, 
Closeable {
    */
   public UpdateResponse addBean(Object obj, int commitWithinMs)
       throws IOException, SolrServerException {
-    return add(null, getBinder().toSolrInputDocument(obj), commitWithinMs);
+    return add(null, DocumentObjectBinder.INSTANCE.toSolrInputDocument(obj), 
commitWithinMs);
   }
 
   /**
@@ -324,15 +323,14 @@ public abstract class SolrClient implements Serializable, 
Closeable {
    * @return an {@link org.apache.solr.client.solrj.response.UpdateResponse} 
from the server
    * @throws IOException if there is a communication error with the server
    * @throws SolrServerException if there is an error on the server
-   * @see SolrClient#getBinder()
+   * @see DocumentObjectBinder
    * @since solr 5.1
    */
   public UpdateResponse addBeans(String collection, Collection<?> beans, int 
commitWithinMs)
       throws SolrServerException, IOException {
-    DocumentObjectBinder binder = this.getBinder();
     ArrayList<SolrInputDocument> docs = new ArrayList<>(beans.size());
     for (Object bean : beans) {
-      docs.add(binder.toSolrInputDocument(bean));
+      docs.add(DocumentObjectBinder.INSTANCE.toSolrInputDocument(bean));
     }
     return add(collection, docs, commitWithinMs);
   }
@@ -348,7 +346,7 @@ public abstract class SolrClient implements Serializable, 
Closeable {
    * @return an {@link org.apache.solr.client.solrj.response.UpdateResponse} 
from the server
    * @throws IOException if there is a communication error with the server
    * @throws SolrServerException if there is an error on the server
-   * @see SolrClient#getBinder()
+   * @see DocumentObjectBinder
    * @since solr 3.5
    */
   public UpdateResponse addBeans(Collection<?> beans, int commitWithinMs)
@@ -380,7 +378,7 @@ public abstract class SolrClient implements Serializable, 
Closeable {
           public SolrInputDocument next() {
             Object o = beanIterator.next();
             if (o == null) return null;
-            return getBinder().toSolrInputDocument(o);
+            return DocumentObjectBinder.INSTANCE.toSolrInputDocument(o);
           }
 
           @Override
@@ -1195,20 +1193,6 @@ public abstract class SolrClient implements 
Serializable, Closeable {
     return request(request, null);
   }
 
-  /**
-   * Get the {@link org.apache.solr.client.solrj.beans.DocumentObjectBinder} 
for this client.
-   *
-   * @return a DocumentObjectBinder
-   * @see SolrClient#addBean
-   * @see SolrClient#addBeans
-   */
-  public DocumentObjectBinder getBinder() {
-    if (binder == null) {
-      binder = new DocumentObjectBinder();
-    }
-    return binder;
-  }
-
   /**
    * This method defines the context in which this Solr client is being used 
(e.g. for internal
    * communication between Solr nodes or as an external client). The default 
value is {@code
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java
 
b/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java
index 7cd139ef21e..fabcfee587f 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java
@@ -33,6 +33,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.ServiceLoader;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Pattern;
 import org.apache.solr.common.SolrDocument;
@@ -41,13 +42,22 @@ import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.util.SuppressForbidden;
 
 /**
- * A class to map objects to and from solr documents.
+ * Maps objects to and from Solr documents.
  *
  * @since solr 1.3
  */
 public class DocumentObjectBinder {
 
-  private final Map<Class<?>, List<DocField>> infocache = new 
ConcurrentHashMap<>();
+  /**
+   * The singleton/default instance. The implementation is loaded via {@link 
ServiceLoader}, so it's
+   * customizable.
+   */
+  public static final DocumentObjectBinder INSTANCE =
+      ServiceLoader.load(DocumentObjectBinder.class)
+          .findFirst()
+          .orElseGet(DocumentObjectBinder::new);
+
+  private final Map<Class<?>, List<DocField>> infoCache = new 
ConcurrentHashMap<>();
 
   public DocumentObjectBinder() {}
 
@@ -126,18 +136,12 @@ public class DocumentObjectBinder {
   }
 
   private List<DocField> getDocFields(Class<?> clazz) {
-    List<DocField> fields = infocache.get(clazz);
-    if (fields == null) {
-      synchronized (infocache) {
-        infocache.put(clazz, fields = collectInfo(clazz));
-      }
-    }
-    return fields;
+    return infoCache.computeIfAbsent(clazz, this::collectInfo);
   }
 
   @SuppressWarnings("removal")
   @SuppressForbidden(reason = "Needs access to possibly private @Field 
annotated fields/methods")
-  private List<DocField> collectInfo(Class<?> clazz) {
+  protected List<DocField> collectInfo(Class<?> clazz) {
     List<DocField> fields = new ArrayList<>();
     Class<?> superClazz = clazz;
     List<AccessibleObject> members = new ArrayList<>();
@@ -169,7 +173,7 @@ public class DocumentObjectBinder {
     return fields;
   }
 
-  private class DocField {
+  protected class DocField {
     private Field annotation;
     private String name;
     private java.lang.reflect.Field field;
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/request/QueryRequest.java 
b/solr/solrj/src/java/org/apache/solr/client/solrj/request/QueryRequest.java
index c8652fa3887..2c26b2ac81d 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/QueryRequest.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/QueryRequest.java
@@ -62,7 +62,7 @@ public class QueryRequest extends 
CollectionRequiringSolrRequest<QueryResponse>
 
   @Override
   protected QueryResponse createResponse(SolrClient client) {
-    return new QueryResponse(client);
+    return new QueryResponse();
   }
 
   @Override
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/response/QueryResponse.java 
b/solr/solrj/src/java/org/apache/solr/client/solrj/response/QueryResponse.java
index e09a073f4d4..11cea6f4587 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/response/QueryResponse.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/response/QueryResponse.java
@@ -23,7 +23,6 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
-import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.beans.DocumentObjectBinder;
 import org.apache.solr.client.solrj.response.json.NestableJsonFacet;
 import org.apache.solr.common.SolrDocumentList;
@@ -102,21 +101,10 @@ public class QueryResponse extends SolrResponseBase {
   private Map<String, Object> _debugMap = null;
   private Map<String, Object> _explainMap = null;
 
-  // utility variable used for automatic binding -- it should not be serialized
-  private final transient SolrClient solrClient;
+  public QueryResponse() {}
 
-  public QueryResponse() {
-    solrClient = null;
-  }
-
-  /** Utility constructor to set the solrServer and namedList */
-  public QueryResponse(NamedList<Object> res, SolrClient solrClient) {
+  public QueryResponse(NamedList<Object> res) {
     this.setResponse(res);
-    this.solrClient = solrClient;
-  }
-
-  public QueryResponse(SolrClient solrClient) {
-    this.solrClient = solrClient;
   }
 
   @Override
@@ -643,9 +631,7 @@ public class QueryResponse extends SolrResponseBase {
   }
 
   public <T> List<T> getBeans(Class<T> type) {
-    return solrClient == null
-        ? new DocumentObjectBinder().getBeans(type, _results)
-        : solrClient.getBinder().getBeans(type, _results);
+    return DocumentObjectBinder.INSTANCE.getBeans(type, _results);
   }
 
   public Map<String, FieldStatsInfo> getFieldStatsInfo() {
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java
index 2740ead1d93..0c746012825 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java
@@ -38,7 +38,7 @@ public class TestDocumentObjectBinder extends SolrTestCase {
     DocumentObjectBinder binder = new DocumentObjectBinder();
     XMLResponseParser parser = new XMLResponseParser();
     NamedList<Object> nl = parser.processResponse(new StringReader(xml));
-    QueryResponse res = new QueryResponse(nl, null);
+    QueryResponse res = new QueryResponse(nl);
 
     SolrDocumentList solDocList = res.getResults();
     List<Item> l = binder.getBeans(Item.class, res.getResults());
@@ -83,7 +83,7 @@ public class TestDocumentObjectBinder extends SolrTestCase {
     DocumentObjectBinder binder = new DocumentObjectBinder();
     XMLResponseParser parser = new XMLResponseParser();
     NamedList<Object> nl = parser.processResponse(new StringReader(xml));
-    QueryResponse res = new QueryResponse(nl, null);
+    QueryResponse res = new QueryResponse(nl);
     List<Item> l = binder.getBeans(Item.class, res.getResults());
 
     assertArrayEquals(
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/response/QueryResponseTest.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/response/QueryResponseTest.java
index a85a3cccebb..40602b73ea6 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/response/QueryResponseTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/response/QueryResponseTest.java
@@ -55,7 +55,7 @@ public class QueryResponseTest extends SolrTestCase {
       }
     }
 
-    QueryResponse qr = new QueryResponse(response, null);
+    QueryResponse qr = new QueryResponse(response);
     assertNotNull(qr);
 
     int counter = 0;
@@ -119,7 +119,7 @@ public class QueryResponseTest extends SolrTestCase {
       }
     }
 
-    QueryResponse qr = new QueryResponse(response, null);
+    QueryResponse qr = new QueryResponse(response);
     assertNotNull(qr);
     GroupResponse groupResponse = qr.getGroupResponse();
     assertNotNull(groupResponse);
@@ -225,7 +225,7 @@ public class QueryResponseTest extends SolrTestCase {
       }
     }
 
-    QueryResponse qr = new QueryResponse(response, null);
+    QueryResponse qr = new QueryResponse(response);
     assertNotNull(qr);
     GroupResponse groupResponse = qr.getGroupResponse();
     assertNotNull(groupResponse);
@@ -267,7 +267,7 @@ public class QueryResponseTest extends SolrTestCase {
       NamedList<Object> response = parser.processResponse(in);
       in.close();
 
-      QueryResponse qr = new QueryResponse(response, null);
+      QueryResponse qr = new QueryResponse(response);
       assertNotNull(qr);
       assertNotNull(qr.getIntervalFacets());
       assertEquals(2, qr.getIntervalFacets().size());
@@ -311,7 +311,7 @@ public class QueryResponseTest extends SolrTestCase {
       }
     }
 
-    QueryResponse qr = new QueryResponse(response, null);
+    QueryResponse qr = new QueryResponse(response);
     assertNotNull(qr);
 
     Map<String, Object> explainMap = qr.getExplainMap();
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/response/TestClusteringResponse.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/response/TestClusteringResponse.java
index 8e1a2631e59..caed38d12f1 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/response/TestClusteringResponse.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/response/TestClusteringResponse.java
@@ -45,7 +45,7 @@ public class TestClusteringResponse extends SolrJettyTestBase 
{
         response = parser.processResponse(in);
       }
     }
-    QueryResponse qr = new QueryResponse(response, null);
+    QueryResponse qr = new QueryResponse(response);
     ClusteringResponse clusteringResponse = qr.getClusteringResponse();
     List<Cluster> clusters = clusteringResponse.getClusters();
     assertEquals(4, clusters.size());

Reply via email to