This is an automated email from the ASF dual-hosted git repository.
magibney pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_9x by this push:
new 3a1ccb33441 SOLR-17176: Fix log history V2 API serialization (#2293)
3a1ccb33441 is described below
commit 3a1ccb334410f9e900666b81fe9d5891aa4e195d
Author: Michael Gibney <[email protected]>
AuthorDate: Wed Apr 10 12:24:34 2024 -0400
SOLR-17176: Fix log history V2 API serialization (#2293)
(cherry picked from commit 5335de669cc0726674dd5582bf6bcfcb1692d81b)
---
solr/CHANGES.txt | 2 +
.../java/org/apache/solr/logging/LogWatcher.java | 14 ++++-
.../org/apache/solr/logging/TestLogWatcher.java | 71 +++++++++++++++++++++-
3 files changed, 85 insertions(+), 2 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 84b0ee0ab3d..0653d062861 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -90,6 +90,8 @@ Bug Fixes
* SOLR-17213: Fix spurious warnings about solr url format in Solr CLI when
users aren't providing a deprecated solr url. (Eric Pugh)
+* SOLR-17176: Fix log history V2 API serialization (Michael Gibney)
+
Dependency Upgrades
---------------------
diff --git a/solr/core/src/java/org/apache/solr/logging/LogWatcher.java
b/solr/core/src/java/org/apache/solr/logging/LogWatcher.java
index 86ca1b86dbd..30f4f6e2755 100644
--- a/solr/core/src/java/org/apache/solr/logging/LogWatcher.java
+++ b/solr/core/src/java/org/apache/solr/logging/LogWatcher.java
@@ -21,10 +21,13 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.util.CollectionUtil;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.logging.jul.JulWatcher;
import org.apache.solr.logging.log4j2.Log4j2Watcher;
@@ -75,7 +78,16 @@ public abstract class LogWatcher<E> {
* instance to multiple callers, we should guard against modification.
*/
private static SolrDocument unmodifiable(SolrDocument doc) {
- return new
SolrDocument(Collections.unmodifiableMap(doc.getFieldValueMap()));
+ LinkedHashMap<String, Object> map =
CollectionUtil.newLinkedHashMap(doc.size());
+ for (Map.Entry<String, Object> e : doc) {
+ Object v = e.getValue();
+ if (v instanceof Collection) {
+ map.put(e.getKey(), Collections.unmodifiableCollection((Collection<?>)
v));
+ } else {
+ map.put(e.getKey(), v);
+ }
+ }
+ return new SolrDocument(Collections.unmodifiableMap(map));
}
public long getLastEvent() {
diff --git a/solr/core/src/test/org/apache/solr/logging/TestLogWatcher.java
b/solr/core/src/test/org/apache/solr/logging/TestLogWatcher.java
index 11cdf8e627e..34cd05e8cbd 100644
--- a/solr/core/src/test/org/apache/solr/logging/TestLogWatcher.java
+++ b/solr/core/src/test/org/apache/solr/logging/TestLogWatcher.java
@@ -16,17 +16,31 @@
*/
package org.apache.solr.logging;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
import java.lang.invoke.MethodHandles;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.MapWriter;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.TimeSource;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.request.SolrQueryRequestBase;
+import org.apache.solr.response.BinaryQueryResponseWriter;
+import org.apache.solr.response.JSONResponseWriter;
+import org.apache.solr.response.JacksonJsonWriter;
+import org.apache.solr.response.QueryResponseWriter;
+import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.util.TimeOut;
import org.junit.Before;
import org.junit.Test;
+import org.noggit.CharArr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,7 +59,7 @@ public class TestLogWatcher extends SolrTestCaseJ4 {
// All we really care about is that new watchers get the new messages,
so test for that
// explicitly. See SOLR-12732.
@Test
- public void testLog4jWatcher() throws InterruptedException {
+ public void testLog4jWatcher() throws InterruptedException, IOException {
LogWatcher<?> watcher = null;
int lim = random().nextInt(3) + 2;
// Every time through this loop, ensure that, of all the test messages
that have been logged,
@@ -65,9 +79,12 @@ public class TestLogWatcher extends SolrTestCaseJ4 {
boolean foundNewMsg = false;
boolean foundOldMessage = false;
// In local testing this loop usually succeeds 1-2 tries, so it's not
very expensive to loop.
+ QueryResponseWriter responseWriter =
+ random().nextBoolean() ? new JacksonJsonWriter() : new
JSONResponseWriter();
do {
// Returns an empty (but non-null) list even if there are no messages
yet.
SolrDocumentList events = watcher.getHistory(-1, null);
+ validateWrite(responseWriter, events, msg);
for (SolrDocument doc : events) {
String oneMsg = (String) doc.get("message");
if (oneMsg.equals(msg)) {
@@ -104,4 +121,56 @@ public class TestLogWatcher extends SolrTestCaseJ4 {
oldMessages.add(msg);
}
}
+
+ /**
+ * Here we validate that serialization works as expected for several
different methods. Ideally we
+ * would use actual serialization from Jersey/Jackson, since this is what
really happens in V2
+ * APIs. But this is simpler, and should give us roughly equivalent
assurances.
+ */
+ private static void validateWrite(
+ QueryResponseWriter responseWriter, SolrDocumentList docs, String
expectMsg)
+ throws IOException {
+ SolrQueryRequest req = new SolrQueryRequestBase(null, new
ModifiableSolrParams()) {};
+ SolrQueryResponse rsp = new SolrQueryResponse();
+ rsp.addResponse(docs);
+ String output;
+ if (responseWriter instanceof BinaryQueryResponseWriter) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ((BinaryQueryResponseWriter) responseWriter).write(baos, req, rsp);
+ baos.close();
+ output = baos.toString(StandardCharsets.UTF_8);
+ } else {
+ StringWriter writer = new StringWriter();
+ responseWriter.write(writer, req, rsp);
+ writer.close();
+ output = writer.toString();
+ }
+ assertTrue("found: " + output, output.contains(expectMsg));
+ validateWrite(docs, expectMsg);
+ }
+
+ private static void validateWrite(SolrDocumentList docs, String expectMsg)
throws IOException {
+ CharArr arr = new CharArr();
+ org.noggit.JSONWriter w = new org.noggit.JSONWriter(arr, 2);
+ docs.writeMap(
+ new MapWriter.EntryWriter() {
+ boolean first = true;
+
+ @Override
+ public MapWriter.EntryWriter put(CharSequence k, Object v) {
+ if (first) {
+ first = false;
+ } else {
+ w.writeValueSeparator();
+ }
+ w.indent();
+ w.writeString(k.toString());
+ w.writeNameSeparator();
+ w.write(v);
+ return this;
+ }
+ });
+ String output = arr.toString();
+ assertTrue("found: " + output, output.contains(expectMsg));
+ }
}