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 b4ee7fc8333 SOLR-17682: QueryResponseWriter hierarchy refactor (#3209) b4ee7fc8333 is described below commit b4ee7fc8333b5bba48b85e680f6dae6e45cd651b Author: David Smiley <dsmi...@apache.org> AuthorDate: Thu Mar 13 19:17:18 2025 -0400 SOLR-17682: QueryResponseWriter hierarchy refactor (#3209) Base abstraction is now byte oriented, thus BinaryQueryResponseWriter is gone. Added TextQueryResponseWriter for the text ones. Stopped using RawResponseWriter when not needed. Added writeToString QueryResponseWriterUtil is removed Declare throw IOException from write(...) --- solr/CHANGES.txt | 4 ++ .../client/solrj/embedded/EmbeddedSolrServer.java | 4 +- .../src/java/org/apache/solr/core/SolrCore.java | 3 +- .../org/apache/solr/jersey/MessageBodyWriters.java | 4 +- .../solr/response/BinaryQueryResponseWriter.java | 48 ----------------- .../apache/solr/response/BinaryResponseWriter.java | 13 ++--- .../apache/solr/response/CSVResponseWriter.java | 4 +- .../apache/solr/response/CborResponseWriter.java | 5 +- .../solr/response/GraphMLResponseWriter.java | 7 ++- .../apache/solr/response/JSONResponseWriter.java | 4 +- .../apache/solr/response/JacksonJsonWriter.java | 52 +++++++++++++----- .../solr/response/PrometheusResponseWriter.java | 7 ++- .../apache/solr/response/QueryResponseWriter.java | 37 ++++++++----- .../apache/solr/response/RawResponseWriter.java | 37 ++++--------- .../solr/response/SchemaXmlResponseWriter.java | 2 +- .../apache/solr/response/SmileResponseWriter.java | 15 +++++- ...riterUtil.java => TextQueryResponseWriter.java} | 63 +++++++++++----------- .../apache/solr/response/XMLResponseWriter.java | 4 +- .../apache/solr/servlet/DirectSolrConnection.java | 12 +---- .../java/org/apache/solr/servlet/HttpSolrCall.java | 4 +- .../src/test/org/apache/solr/OutputWriterTest.java | 3 +- .../test/org/apache/solr/TestCrossCoreJoin.java | 30 +++++------ .../test/org/apache/solr/TestTolerantSearch.java | 5 +- .../org/apache/solr/logging/TestLogWatcher.java | 17 +----- .../org/apache/solr/request/TestWriterPerf.java | 19 ++----- .../org/apache/solr/response/JSONWriterTest.java | 6 +-- .../solr/response/TestBinaryResponseWriter.java | 4 +- .../solr/response/TestCSVResponseWriter.java | 7 ++- .../solr/response/TestRawResponseWriter.java | 16 ++---- .../solr/response/TestRetrieveFieldsOptimizer.java | 4 +- .../transform/TestSubQueryTransformer.java | 5 +- .../org/apache/solr/search/TestSmileRequest.java | 11 +++- .../handler/extraction/XLSXResponseWriter.java | 8 +-- .../solr/scripting/xslt/XSLTResponseWriter.java | 4 +- .../xslt/XSLTUpdateRequestHandlerTest.java | 7 +-- .../org/apache/solr/common/util/TextWriter.java | 2 +- .../src/java/org/apache/solr/util/TestHarness.java | 17 +----- 37 files changed, 210 insertions(+), 284 deletions(-) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index e263673d194..ddcb6134f1f 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -154,6 +154,10 @@ Other Changes * SOLR-16903: Switch CoreContainer#getSolrHome to return Path instead of String (Andrey Bozhko) +* SOLR-17682: QueryResponseWriter: refactor the hierarchy. Base abstraction is now byte oriented, + thus BinaryQueryResponseWriter is gone. Added TextQueryResponseWriter for the text ones. + Stopped using RawResponseWriter when not needed. (David Smiley) + ================== 9.9.0 ================== New Features --------------------- diff --git a/solr/core/src/java/org/apache/solr/client/solrj/embedded/EmbeddedSolrServer.java b/solr/core/src/java/org/apache/solr/client/solrj/embedded/EmbeddedSolrServer.java index f1c5c0b5dc5..78880867f84 100644 --- a/solr/core/src/java/org/apache/solr/client/solrj/embedded/EmbeddedSolrServer.java +++ b/solr/core/src/java/org/apache/solr/client/solrj/embedded/EmbeddedSolrServer.java @@ -59,7 +59,6 @@ import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.request.SolrRequestInfo; import org.apache.solr.response.BinaryResponseWriter; -import org.apache.solr.response.QueryResponseWriterUtil; import org.apache.solr.response.ResultContext; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.servlet.SolrRequestParsers; @@ -277,8 +276,7 @@ public class EmbeddedSolrServer extends SolrClient { }; if (callback == null) { - QueryResponseWriterUtil.writeQueryResponse( - byteBuffer, req.getResponseWriter(), req, rsp, null); + req.getResponseWriter().write(byteBuffer, req, rsp); } else { // mostly stream results to the callback; rest goes into the byteBuffer if (!(responseParser instanceof BinaryResponseParser)) diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java index 720a751f07a..a1473d0be14 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrCore.java +++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java @@ -3053,7 +3053,8 @@ public class SolrCore implements SolrInfoBean, Closeable { private static BinaryResponseWriter getFileStreamWriter() { return new BinaryResponseWriter() { @Override - public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse response) + public void write( + OutputStream out, SolrQueryRequest req, SolrQueryResponse response, String contentType) throws IOException { RawWriter rawWriter = (RawWriter) response.getValues().get(ReplicationAPIBase.FILE_STREAM); if (rawWriter != null) { diff --git a/solr/core/src/java/org/apache/solr/jersey/MessageBodyWriters.java b/solr/core/src/java/org/apache/solr/jersey/MessageBodyWriters.java index d2020f4dc6a..f73146a864a 100644 --- a/solr/core/src/java/org/apache/solr/jersey/MessageBodyWriters.java +++ b/solr/core/src/java/org/apache/solr/jersey/MessageBodyWriters.java @@ -39,7 +39,6 @@ import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.BinaryResponseWriter; import org.apache.solr.response.CSVResponseWriter; import org.apache.solr.response.QueryResponseWriter; -import org.apache.solr.response.QueryResponseWriterUtil; import org.apache.solr.response.RawResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.response.XMLResponseWriter; @@ -141,8 +140,7 @@ public class MessageBodyWriters { (SolrQueryResponse) requestContext.getProperty(SOLR_QUERY_RESPONSE); V2ApiUtils.squashIntoSolrResponseWithHeader(solrQueryResponse, toWrite); - QueryResponseWriterUtil.writeQueryResponse( - entityStream, responseWriter, solrQueryRequest, solrQueryResponse, mediaType.toString()); + responseWriter.write(entityStream, solrQueryRequest, solrQueryResponse, mediaType.toString()); } } } diff --git a/solr/core/src/java/org/apache/solr/response/BinaryQueryResponseWriter.java b/solr/core/src/java/org/apache/solr/response/BinaryQueryResponseWriter.java deleted file mode 100644 index c9ecfb8c55d..00000000000 --- a/solr/core/src/java/org/apache/solr/response/BinaryQueryResponseWriter.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.response; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import org.apache.solr.request.SolrQueryRequest; - -/** - * Implementations of <code>BinaryQueryResponseWriter</code> are used to write response in binary - * format. - * - * <p>Functionality is exactly same as its parent class <code>QueryResponseWriter</code> But it may - * not implement the <code> - * write(Writer writer, SolrQueryRequest request, SolrQueryResponse response)</code> method - */ -public interface BinaryQueryResponseWriter extends QueryResponseWriter { - - /** Use it to write the response in a binary format */ - void write(OutputStream out, SolrQueryRequest request, SolrQueryResponse response) - throws IOException; - - default String serializeResponse(SolrQueryRequest req, SolrQueryResponse rsp) { - java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); - try { - write(baos, req, rsp); - return baos.toString(StandardCharsets.UTF_8); - } catch (IOException e) { - // unlikely - throw new RuntimeException(e); - } - } -} diff --git a/solr/core/src/java/org/apache/solr/response/BinaryResponseWriter.java b/solr/core/src/java/org/apache/solr/response/BinaryResponseWriter.java index db58c7a262c..d9f9e799ca7 100644 --- a/solr/core/src/java/org/apache/solr/response/BinaryResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/BinaryResponseWriter.java @@ -22,7 +22,6 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.io.Writer; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collection; @@ -44,12 +43,14 @@ import org.apache.solr.search.ReturnFields; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class BinaryResponseWriter implements BinaryQueryResponseWriter { +/** Solr's "javabin" format. TODO rename accordingly. */ +public class BinaryResponseWriter implements QueryResponseWriter { // public static boolean useUtf8CharSeq = true; private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @Override - public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse response) + public void write( + OutputStream out, SolrQueryRequest req, SolrQueryResponse response, String contentType) throws IOException { Resolver resolver = new Resolver(req, response.getReturnFields()); if (req.getParams().getBool(CommonParams.OMIT_HEADER, false)) response.removeResponseHeader(); @@ -58,12 +59,6 @@ public class BinaryResponseWriter implements BinaryQueryResponseWriter { } } - @Override - public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) - throws IOException { - throw new RuntimeException("This is a binary writer , Cannot write to a characterstream"); - } - @Override public String getContentType(SolrQueryRequest request, SolrQueryResponse response) { return BinaryResponseParser.BINARY_CONTENT_TYPE; diff --git a/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java b/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java index 8cb30080fd7..b630b697d75 100644 --- a/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java @@ -37,8 +37,8 @@ import org.apache.solr.schema.SchemaField; import org.apache.solr.schema.StrField; import org.apache.solr.search.ReturnFields; -/** Response writer for csv data */ -public class CSVResponseWriter implements QueryResponseWriter { +/** Response writer for CSV data */ +public class CSVResponseWriter implements TextQueryResponseWriter { @Override public void write(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { diff --git a/solr/core/src/java/org/apache/solr/response/CborResponseWriter.java b/solr/core/src/java/org/apache/solr/response/CborResponseWriter.java index 294cba59851..f0f3f2a2e46 100644 --- a/solr/core/src/java/org/apache/solr/response/CborResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/CborResponseWriter.java @@ -30,7 +30,7 @@ import org.apache.solr.request.SolrQueryRequest; * A response writer impl that can write results in CBOR (cbor.io) format when wt=cbor. It uses the * jackson library to write the stream out */ -public class CborResponseWriter extends BinaryResponseWriter { +public class CborResponseWriter implements QueryResponseWriter { final CBORFactory cborFactory; final CBORFactory cborFactoryCompact; @@ -40,7 +40,8 @@ public class CborResponseWriter extends BinaryResponseWriter { } @Override - public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse response) + public void write( + OutputStream out, SolrQueryRequest req, SolrQueryResponse response, String contentType) throws IOException { boolean useStringRef = req.getParams().getBool("string_ref", true); WriterImpl writer = diff --git a/solr/core/src/java/org/apache/solr/response/GraphMLResponseWriter.java b/solr/core/src/java/org/apache/solr/response/GraphMLResponseWriter.java index 11a04d10240..f05113bb452 100644 --- a/solr/core/src/java/org/apache/solr/response/GraphMLResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/GraphMLResponseWriter.java @@ -28,7 +28,12 @@ import org.apache.solr.client.solrj.io.stream.TupleStream; import org.apache.solr.handler.GraphHandler; import org.apache.solr.request.SolrQueryRequest; -public class GraphMLResponseWriter implements QueryResponseWriter { +/** + * Used with streaming expressions to export graphs to be visualized. + * + * @see <a href="http://graphml.graphdrawing.org/">GraphML</a> + */ +public class GraphMLResponseWriter implements TextQueryResponseWriter { @Override public String getContentType(SolrQueryRequest req, SolrQueryResponse res) { diff --git a/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java b/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java index f972ca5e362..c4439c20d7e 100644 --- a/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java @@ -29,8 +29,8 @@ import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.search.ReturnFields; -/** */ -public class JSONResponseWriter implements QueryResponseWriter { +/** JSON {@link QueryResponseWriter}. */ +public class JSONResponseWriter implements TextQueryResponseWriter { public static String CONTENT_TYPE_JSON_UTF8 = "application/json; charset=UTF-8"; private String contentType = CONTENT_TYPE_JSON_UTF8; diff --git a/solr/core/src/java/org/apache/solr/response/JacksonJsonWriter.java b/solr/core/src/java/org/apache/solr/response/JacksonJsonWriter.java index 3ab9121c37d..f254f625a52 100644 --- a/solr/core/src/java/org/apache/solr/response/JacksonJsonWriter.java +++ b/solr/core/src/java/org/apache/solr/response/JacksonJsonWriter.java @@ -23,13 +23,17 @@ import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import java.io.IOException; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Arrays; import org.apache.solr.common.PushWriter; +import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.request.SolrQueryRequest; /** A JSON ResponseWriter that uses jackson. */ -public class JacksonJsonWriter extends BinaryResponseWriter { +public class JacksonJsonWriter implements TextQueryResponseWriter { protected final JsonFactory jsonfactory; protected static final DefaultPrettyPrinter pretty = @@ -43,17 +47,42 @@ public class JacksonJsonWriter extends BinaryResponseWriter { jsonfactory = new JsonFactory(); } + // let's also implement the binary version since Jackson supports that (probably faster) @Override - public void write(OutputStream out, SolrQueryRequest request, SolrQueryResponse response) + public void write( + OutputStream out, SolrQueryRequest request, SolrQueryResponse response, String contentType) throws IOException { - WriterImpl sw = new WriterImpl(jsonfactory, out, request, response); + out = new NonFlushingStream(out); + // resolve the encoding + final String charSet = ContentStreamBase.getCharsetFromContentType(contentType); + JsonEncoding jsonEncoding; + if (charSet != null) { + assert JsonEncoding.values().length < 10; // fast to iterate + jsonEncoding = + Arrays.stream(JsonEncoding.values()) + .filter(e -> e.getJavaName().equalsIgnoreCase(charSet)) + .findAny() + .orElseThrow(() -> new UnsupportedEncodingException(charSet)); + } else { + jsonEncoding = JsonEncoding.UTF8; + } + + var sw = new WriterImpl(request, response, jsonfactory.createGenerator(out, jsonEncoding)); + sw.writeResponse(); + sw.close(); + } + + @Override + public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) + throws IOException { + var sw = new WriterImpl(request, response, jsonfactory.createGenerator(writer)); sw.writeResponse(); sw.close(); } public PushWriter getWriter( - OutputStream out, SolrQueryRequest request, SolrQueryResponse response) { - return new WriterImpl(jsonfactory, out, request, response); + OutputStream out, SolrQueryRequest request, SolrQueryResponse response) throws IOException { + return new WriterImpl(request, response, jsonfactory.createGenerator(out, JsonEncoding.UTF8)); } @Override @@ -67,16 +96,11 @@ public class JacksonJsonWriter extends BinaryResponseWriter { protected JsonGenerator gen; - public WriterImpl( - JsonFactory j, OutputStream out, SolrQueryRequest req, SolrQueryResponse rsp) { + public WriterImpl(SolrQueryRequest req, SolrQueryResponse rsp, JsonGenerator generator) { super(null, req, rsp); - try { - gen = j.createGenerator(out, JsonEncoding.UTF8); - if (doIndent) { - gen.setPrettyPrinter(pretty.createInstance()); - } - } catch (IOException e) { - throw new RuntimeException(e); + gen = generator; + if (doIndent) { + gen.setPrettyPrinter(pretty.createInstance()); } } diff --git a/solr/core/src/java/org/apache/solr/response/PrometheusResponseWriter.java b/solr/core/src/java/org/apache/solr/response/PrometheusResponseWriter.java index 8689751a514..5919a0ec858 100644 --- a/solr/core/src/java/org/apache/solr/response/PrometheusResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/PrometheusResponseWriter.java @@ -47,12 +47,15 @@ import org.slf4j.LoggerFactory; * org.apache.solr.handler.admin.MetricsHandler} */ @SuppressWarnings(value = "unchecked") -public class PrometheusResponseWriter extends RawResponseWriter { +public class PrometheusResponseWriter implements QueryResponseWriter { + // not TextQueryResponseWriter because Prometheus libs work with an OutputStream + private static final String CONTENT_TYPE_PROMETHEUS = "text/plain; version=0.0.4"; private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @Override - public void write(OutputStream out, SolrQueryRequest request, SolrQueryResponse response) + public void write( + OutputStream out, SolrQueryRequest request, SolrQueryResponse response, String contentType) throws IOException { NamedList<Object> prometheusRegistries = (NamedList<Object>) response.getValues().get("metrics"); diff --git a/solr/core/src/java/org/apache/solr/response/QueryResponseWriter.java b/solr/core/src/java/org/apache/solr/response/QueryResponseWriter.java index 932df711bd2..673543b8e6a 100644 --- a/solr/core/src/java/org/apache/solr/response/QueryResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/QueryResponseWriter.java @@ -16,14 +16,16 @@ */ package org.apache.solr.response; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.Writer; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import net.jcip.annotations.ThreadSafe; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.util.plugin.NamedListInitializedPlugin; /** - * Implementations of <code>QueryResponseWriter</code> are used to format responses to query - * requests. + * Used to format responses to the client (not necessarily a "query"). * * <p>Different <code>QueryResponseWriter</code>s are registered with the <code>SolrCore</code>. One * way to register a QueryResponseWriter with the core is through the <code>solrconfig.xml</code> @@ -39,23 +41,26 @@ import org.apache.solr.util.plugin.NamedListInitializedPlugin; * <p>A single instance of any registered QueryResponseWriter is created via the default constructor * and is reused for all relevant queries. */ +@ThreadSafe public interface QueryResponseWriter extends NamedListInitializedPlugin { public static String CONTENT_TYPE_XML_UTF8 = "application/xml; charset=UTF-8"; public static String CONTENT_TYPE_TEXT_UTF8 = "text/plain; charset=UTF-8"; public static String CONTENT_TYPE_TEXT_ASCII = "text/plain; charset=US-ASCII"; /** - * Write a SolrQueryResponse, this method must be thread save. - * - * <p>Information about the request (in particular: formatting options) may be obtained from - * <code>req</code> but the dominant source of information should be <code>rsp</code>. - * - * <p>There are no mandatory actions that write must perform. An empty write implementation would - * fulfill all interface obligations. + * Writes the response to the {@link OutputStream}. {@code contentType} is from {@link + * #getContentType(SolrQueryRequest, SolrQueryResponse)}, and it's often ignored. */ - public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) + void write( + OutputStream out, SolrQueryRequest request, SolrQueryResponse response, String contentType) throws IOException; + // should be "final" if interfaces allowed that + default void write(OutputStream out, SolrQueryRequest request, SolrQueryResponse response) + throws IOException { + write(out, request, response, getContentType(request, response)); + } + /** * Return the applicable Content Type for a request, this method must be thread safe. * @@ -64,5 +69,13 @@ public interface QueryResponseWriter extends NamedListInitializedPlugin { * * @return a Content-Type string, which may not be null. */ - public String getContentType(SolrQueryRequest request, SolrQueryResponse response); + String getContentType(SolrQueryRequest request, SolrQueryResponse response); + + /** Writes a String for debugging / testing purposes. */ + default String writeToString(SolrQueryRequest request, SolrQueryResponse response) + throws IOException { + var buffer = new ByteArrayOutputStream(32000); + write(buffer, request, response); + return buffer.toString(StandardCharsets.UTF_8); + } } diff --git a/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java b/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java index d20128ae4e0..922b4932952 100644 --- a/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java @@ -20,8 +20,6 @@ import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.util.NamedList; import org.apache.solr.core.SolrCore; @@ -39,7 +37,7 @@ import org.apache.solr.request.SolrQueryRequest; * * @since solr 1.3 */ -public class RawResponseWriter implements BinaryQueryResponseWriter { +public class RawResponseWriter implements QueryResponseWriter { public static final String CONTENT_TYPE = "application/vnd.apache.solr.raw"; @@ -75,8 +73,7 @@ public class RawResponseWriter implements BinaryQueryResponseWriter { } // Requests to a specific core already have writers, but we still need a 'default writer' for - // non-core - // (i.e. container-level) APIs + // non-core (i.e. container-level) APIs synchronized (this) { if (defaultWriter == null) { defaultWriter = new JSONResponseWriter(); @@ -88,41 +85,27 @@ public class RawResponseWriter implements BinaryQueryResponseWriter { @Override public String getContentType(SolrQueryRequest request, SolrQueryResponse response) { Object obj = response.getValues().get(CONTENT); - if (obj != null && (obj instanceof ContentStream)) { - return ((ContentStream) obj).getContentType(); + if (obj instanceof ContentStream content) { + return content.getContentType(); } return getBaseWriter(request).getContentType(request, response); } @Override - public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) + public void write( + OutputStream out, SolrQueryRequest request, SolrQueryResponse response, String contentType) throws IOException { Object obj = response.getValues().get(CONTENT); - if (obj != null && (obj instanceof ContentStream content)) { - // copy the contents to the writer... - try (Reader reader = content.getReader()) { - reader.transferTo(writer); - } - } else { - getBaseWriter(request).write(writer, request, response); - } - } - - @Override - public void write(OutputStream out, SolrQueryRequest request, SolrQueryResponse response) - throws IOException { - Object obj = response.getValues().get(CONTENT); - if (obj != null && (obj instanceof ContentStream content)) { + if (obj instanceof ContentStream content) { // copy the contents to the writer... try (InputStream in = content.getStream()) { in.transferTo(out); } - } else if (obj != null && (obj instanceof SolrCore.RawWriter rawWriter)) { + } else if (obj instanceof SolrCore.RawWriter rawWriter) { rawWriter.write(out); - if (rawWriter instanceof Closeable) ((Closeable) rawWriter).close(); + if (rawWriter instanceof Closeable closeable) closeable.close(); } else { - QueryResponseWriterUtil.writeQueryResponse( - out, getBaseWriter(request), request, response, getContentType(request, response)); + getBaseWriter(request).write(out, request, response, contentType); } } } diff --git a/solr/core/src/java/org/apache/solr/response/SchemaXmlResponseWriter.java b/solr/core/src/java/org/apache/solr/response/SchemaXmlResponseWriter.java index 340cd91def7..1480004d03b 100644 --- a/solr/core/src/java/org/apache/solr/response/SchemaXmlResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/SchemaXmlResponseWriter.java @@ -22,7 +22,7 @@ import org.apache.solr.client.solrj.impl.XMLResponseParser; import org.apache.solr.request.SolrQueryRequest; /** */ -public class SchemaXmlResponseWriter implements QueryResponseWriter { +public class SchemaXmlResponseWriter implements TextQueryResponseWriter { @Override public void write(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { SchemaXmlWriter w = new SchemaXmlWriter(writer, req, rsp); diff --git a/solr/core/src/java/org/apache/solr/response/SmileResponseWriter.java b/solr/core/src/java/org/apache/solr/response/SmileResponseWriter.java index 00f986200a4..d4b283ec06e 100644 --- a/solr/core/src/java/org/apache/solr/response/SmileResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/SmileResponseWriter.java @@ -24,16 +24,27 @@ import java.math.BigDecimal; import java.math.BigInteger; import org.apache.solr.request.SolrQueryRequest; -public class SmileResponseWriter extends BinaryResponseWriter { +/** + * A Smile formatting {@link QueryResponseWriter}. + * + * @see <a href="https://github.com/FasterXML/smile-format-specification">Smile</a> + */ +public class SmileResponseWriter implements QueryResponseWriter { @Override - public void write(OutputStream out, SolrQueryRequest request, SolrQueryResponse response) + public void write( + OutputStream out, SolrQueryRequest request, SolrQueryResponse response, String contentType) throws IOException { try (SmileWriter sw = new SmileWriter(out, request, response)) { sw.writeResponse(); } } + @Override + public String getContentType(SolrQueryRequest request, SolrQueryResponse response) { + return "application/x-jackson-smile"; + } + // smile format is an equivalent of JSON format . So we extend JSONWriter and override the // relevant methods diff --git a/solr/core/src/java/org/apache/solr/response/QueryResponseWriterUtil.java b/solr/core/src/java/org/apache/solr/response/TextQueryResponseWriter.java similarity index 62% rename from solr/core/src/java/org/apache/solr/response/QueryResponseWriterUtil.java rename to solr/core/src/java/org/apache/solr/response/TextQueryResponseWriter.java index 1ea0642738d..f4df38f830d 100644 --- a/solr/core/src/java/org/apache/solr/response/QueryResponseWriterUtil.java +++ b/solr/core/src/java/org/apache/solr/response/TextQueryResponseWriter.java @@ -14,12 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.solr.response; -import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.nio.charset.StandardCharsets; @@ -27,50 +28,51 @@ import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.FastWriter; import org.apache.solr.request.SolrQueryRequest; -/** Static utility methods relating to {@link QueryResponseWriter}s */ -public final class QueryResponseWriterUtil { - private QueryResponseWriterUtil() { - /* static helpers only */ - } +/** A writer supporting character streams ({@link Writer} based). */ +public interface TextQueryResponseWriter extends QueryResponseWriter { /** - * Writes the response writer's result to the given output stream. This method inspects the - * specified writer to determine if it is a {@link BinaryQueryResponseWriter} or not to delegate - * to the appropriate method. + * Write a SolrQueryResponse in a textual manner. + * + * <p>Information about the request (in particular: formatting options) may be obtained from + * <code>req</code> but the dominant source of information should be <code>rsp</code>. * - * @see BinaryQueryResponseWriter#write(OutputStream,SolrQueryRequest,SolrQueryResponse) - * @see BinaryQueryResponseWriter#write(Writer,SolrQueryRequest,SolrQueryResponse) + * <p>There are no mandatory actions that write must perform. An empty write implementation would + * fulfill all interface obligations. */ - public static void writeQueryResponse( + void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) + throws IOException; + + @Override + default String writeToString(SolrQueryRequest request, SolrQueryResponse response) + throws IOException { + StringWriter sw = new StringWriter(32000); + write(sw, request, response); + return sw.toString(); + } + + @Override + default void write( OutputStream outputStream, - QueryResponseWriter responseWriter, - SolrQueryRequest solrRequest, - SolrQueryResponse solrResponse, + SolrQueryRequest request, + SolrQueryResponse response, String contentType) throws IOException { - - if (responseWriter instanceof JacksonJsonWriter binWriter) { - BufferedOutputStream bos = new BufferedOutputStream(new NonFlushingStream(outputStream)); - binWriter.write(bos, solrRequest, solrResponse); - bos.flush(); - } else if (responseWriter instanceof BinaryQueryResponseWriter binWriter) { - binWriter.write(outputStream, solrRequest, solrResponse); - } else { - OutputStream out = new NonFlushingStream(outputStream); - Writer writer = buildWriter(out, ContentStreamBase.getCharsetFromContentType(contentType)); - responseWriter.write(writer, solrRequest, solrResponse); - writer.flush(); - } + OutputStream out = new NonFlushingStream(outputStream); + Writer writer = buildWriter(out, ContentStreamBase.getCharsetFromContentType(contentType)); + write(writer, request, response); + writer.flush(); } private static Writer buildWriter(OutputStream outputStream, String charset) throws UnsupportedEncodingException { + // note: OutputStreamWriter has an internal buffer; flush is needed Writer writer = (charset == null) ? new OutputStreamWriter(outputStream, StandardCharsets.UTF_8) : new OutputStreamWriter(outputStream, charset); - return new FastWriter(writer); + return new FastWriter(writer); // note: buffered; therefore we need to call flush() } /** @@ -81,7 +83,8 @@ public final class QueryResponseWriterUtil { * * <p>See SOLR-8669. */ - private static class NonFlushingStream extends OutputStream { + // TODO instead do in ServletUtils.closeShield(HttpServletResponse) + class NonFlushingStream extends OutputStream { private final OutputStream outputStream; public NonFlushingStream(OutputStream outputStream) { diff --git a/solr/core/src/java/org/apache/solr/response/XMLResponseWriter.java b/solr/core/src/java/org/apache/solr/response/XMLResponseWriter.java index f0f3672520a..6dbfdddd873 100644 --- a/solr/core/src/java/org/apache/solr/response/XMLResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/XMLResponseWriter.java @@ -21,8 +21,8 @@ import java.io.Writer; import org.apache.solr.client.solrj.impl.XMLResponseParser; import org.apache.solr.request.SolrQueryRequest; -/** */ -public class XMLResponseWriter implements QueryResponseWriter { +/** An XML {@link QueryResponseWriter}. */ +public class XMLResponseWriter implements TextQueryResponseWriter { @Override public void write(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { XMLWriter w = new XMLWriter(writer, req, rsp); diff --git a/solr/core/src/java/org/apache/solr/servlet/DirectSolrConnection.java b/solr/core/src/java/org/apache/solr/servlet/DirectSolrConnection.java index 9b54f9319df..2d6acb19d73 100644 --- a/solr/core/src/java/org/apache/solr/servlet/DirectSolrConnection.java +++ b/solr/core/src/java/org/apache/solr/servlet/DirectSolrConnection.java @@ -16,7 +16,6 @@ */ package org.apache.solr.servlet; -import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import org.apache.solr.common.SolrException; @@ -28,8 +27,6 @@ import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.request.SolrRequestInfo; -import org.apache.solr.response.BinaryResponseWriter; -import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; /** @@ -114,14 +111,7 @@ public class DirectSolrConnection { } // Now write it out - QueryResponseWriter responseWriter = core.getQueryResponseWriter(req); - if (responseWriter instanceof BinaryResponseWriter) { - return ((BinaryResponseWriter) responseWriter).serializeResponse(req, rsp); - } else { - StringWriter out = new StringWriter(); - responseWriter.write(out, req, rsp); - return out.toString(); - } + return req.getResponseWriter().writeToString(req, rsp); } finally { if (req != null) { req.close(); diff --git a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java index 904f3216b35..97a0290fa49 100644 --- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java +++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java @@ -110,7 +110,6 @@ import org.apache.solr.request.SolrQueryRequestBase; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.request.SolrRequestInfo; import org.apache.solr.response.QueryResponseWriter; -import org.apache.solr.response.QueryResponseWriterUtil; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.security.AuditEvent; import org.apache.solr.security.AuditEvent.EventType; @@ -1000,8 +999,7 @@ public class HttpSolrCall { } if (Method.HEAD != reqMethod) { - OutputStream out = response.getOutputStream(); - QueryResponseWriterUtil.writeQueryResponse(out, responseWriter, solrReq, solrRsp, ct); + responseWriter.write(response.getOutputStream(), solrReq, solrRsp, ct); } // else http HEAD request, nothing to write out, waited this long just to get ContentType } catch (EOFException e) { diff --git a/solr/core/src/test/org/apache/solr/OutputWriterTest.java b/solr/core/src/test/org/apache/solr/OutputWriterTest.java index 13eb69d9014..f230733383c 100644 --- a/solr/core/src/test/org/apache/solr/OutputWriterTest.java +++ b/solr/core/src/test/org/apache/solr/OutputWriterTest.java @@ -23,6 +23,7 @@ import org.apache.solr.core.PluginBag; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.response.TextQueryResponseWriter; import org.junit.BeforeClass; import org.junit.Test; @@ -77,7 +78,7 @@ public class OutputWriterTest extends SolrTestCaseJ4 { //////////////////////////////////////////////////////////////////////////// /** An output writer that doesn't do anything useful. */ - public static class UselessOutputWriter implements QueryResponseWriter { + public static class UselessOutputWriter implements TextQueryResponseWriter { public UselessOutputWriter() {} diff --git a/solr/core/src/test/org/apache/solr/TestCrossCoreJoin.java b/solr/core/src/test/org/apache/solr/TestCrossCoreJoin.java index 83f91365294..8cc81184c40 100644 --- a/solr/core/src/test/org/apache/solr/TestCrossCoreJoin.java +++ b/solr/core/src/test/org/apache/solr/TestCrossCoreJoin.java @@ -16,7 +16,6 @@ */ package org.apache.solr; -import java.io.StringWriter; import java.util.Collections; import java.util.Map; import org.apache.solr.common.SolrException.ErrorCode; @@ -27,7 +26,6 @@ import org.apache.solr.request.LocalSolrQueryRequest; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.request.SolrRequestInfo; -import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.search.join.TestScoreJoinQPNoScore; import org.apache.solr.servlet.DirectSolrConnection; @@ -213,11 +211,12 @@ public class TestCrossCoreJoin extends SolrTestCaseJ4 { @Test public void testCoresAreDifferent() throws Exception { assertQEx("schema12.xml" + " has no \"cat\" field", req("cat:*"), ErrorCode.BAD_REQUEST); - final LocalSolrQueryRequest req = - new LocalSolrQueryRequest(fromCore, "cat:*", "/select", 0, 100, Collections.emptyMap()); - final String resp = query(fromCore, req); - assertTrue(resp, resp.contains("numFound=\"1\"")); - assertTrue(resp, resp.contains("<str name=\"id\">10</str>")); + try (var req = + new LocalSolrQueryRequest(fromCore, "cat:*", "/select", 0, 100, Collections.emptyMap())) { + final String resp = query(fromCore, req); + assertTrue(resp, resp.contains("numFound=\"1\"")); + assertTrue(resp, resp.contains("<str name=\"id\">10</str>")); + } } public String query(SolrCore core, SolrQueryRequest req) throws Exception { @@ -232,16 +231,15 @@ public class TestCrossCoreJoin extends SolrTestCaseJ4 { } SolrQueryResponse rsp = new SolrQueryResponse(); SolrRequestInfo.setRequestInfo(new SolrRequestInfo(req, rsp)); - core.execute(core.getRequestHandler(handler), req, rsp); - if (rsp.getException() != null) { - throw rsp.getException(); + try { + core.execute(core.getRequestHandler(handler), req, rsp); + if (rsp.getException() != null) { + throw rsp.getException(); + } + return req.getResponseWriter().writeToString(req, rsp); + } finally { + SolrRequestInfo.clearRequestInfo(); } - StringWriter sw = new StringWriter(32000); - QueryResponseWriter responseWriter = core.getQueryResponseWriter(req); - responseWriter.write(sw, req, rsp); - req.close(); - SolrRequestInfo.clearRequestInfo(); - return sw.toString(); } @AfterClass diff --git a/solr/core/src/test/org/apache/solr/TestTolerantSearch.java b/solr/core/src/test/org/apache/solr/TestTolerantSearch.java index 4995ed378c2..c0571affd6c 100644 --- a/solr/core/src/test/org/apache/solr/TestTolerantSearch.java +++ b/solr/core/src/test/org/apache/solr/TestTolerantSearch.java @@ -258,7 +258,8 @@ public class TestTolerantSearch extends SolrJettyTestBase { } @Override - public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse response) + public void write( + OutputStream out, SolrQueryRequest req, SolrQueryResponse response, String contentType) throws IOException { // I want to fail on the shard request, not the original user request, and only on the @@ -281,7 +282,7 @@ public class TestTolerantSearch extends SolrJettyTestBase { throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "Dummy exception in BadResponseWriter"); } - super.write(out, req, response); + super.write(out, req, response, contentType); } } } 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 34cd05e8cbd..c688956a08c 100644 --- a/solr/core/src/test/org/apache/solr/logging/TestLogWatcher.java +++ b/solr/core/src/test/org/apache/solr/logging/TestLogWatcher.java @@ -16,11 +16,8 @@ */ 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; @@ -32,7 +29,6 @@ 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; @@ -133,18 +129,7 @@ public class TestLogWatcher extends SolrTestCaseJ4 { 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(); - } + String output = responseWriter.writeToString(req, rsp); assertTrue("found: " + output, output.contains(expectMsg)); validateWrite(docs, expectMsg); } diff --git a/solr/core/src/test/org/apache/solr/request/TestWriterPerf.java b/solr/core/src/test/org/apache/solr/request/TestWriterPerf.java index 4ba2c17bedf..676f5622eee 100644 --- a/solr/core/src/test/org/apache/solr/request/TestWriterPerf.java +++ b/solr/core/src/test/org/apache/solr/request/TestWriterPerf.java @@ -18,16 +18,12 @@ package org.apache.solr.request; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; import java.lang.invoke.MethodHandles; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.client.solrj.ResponseParser; import org.apache.solr.client.solrj.impl.BinaryResponseParser; import org.apache.solr.client.solrj.impl.XMLResponseParser; -import org.apache.solr.response.BinaryQueryResponseWriter; import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.util.RTimer; @@ -179,18 +175,9 @@ public class TestWriterPerf extends SolrTestCaseJ4 { System.gc(); RTimer timer = new RTimer(); for (int i = 0; i < encIter; i++) { - if (w instanceof BinaryQueryResponseWriter binWriter) { - out = new ByteArrayOutputStream(); - binWriter.write(out, req, rsp); - out.close(); - } else { - out = new ByteArrayOutputStream(); - // to be fair, from my previous tests, much of the performance will be sucked up - // by java's UTF-8 encoding/decoding, not the actual writing - Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8); - w.write(writer, req, rsp); - writer.close(); - } + out = new ByteArrayOutputStream(32_000); + w.write(out, req, rsp); + out.close(); } double encodeTime = timer.getTime(); diff --git a/solr/core/src/test/org/apache/solr/response/JSONWriterTest.java b/solr/core/src/test/org/apache/solr/response/JSONWriterTest.java index 5ccf19cdecf..7cb358f6904 100644 --- a/solr/core/src/test/org/apache/solr/response/JSONWriterTest.java +++ b/solr/core/src/test/org/apache/solr/response/JSONWriterTest.java @@ -54,9 +54,9 @@ public class JSONWriterTest extends SolrTestCaseJ4 { @Test public void testSimpleJson() throws IOException { - SolrQueryRequest req = req("q", "dummy", "indent", "off"); - SolrQueryResponse rsp = new SolrQueryResponse(); - QueryResponseWriter w = new JSONResponseWriter(); + var req = req("q", "dummy", "indent", "off"); + var rsp = new SolrQueryResponse(); + var w = new JSONResponseWriter(); StringWriter buf = new StringWriter(); diff --git a/solr/core/src/test/org/apache/solr/response/TestBinaryResponseWriter.java b/solr/core/src/test/org/apache/solr/response/TestBinaryResponseWriter.java index 80dfe9db363..4331c58e1c8 100644 --- a/solr/core/src/test/org/apache/solr/response/TestBinaryResponseWriter.java +++ b/solr/core/src/test/org/apache/solr/response/TestBinaryResponseWriter.java @@ -73,10 +73,8 @@ public class TestBinaryResponseWriter extends SolrTestCaseJ4 { assertU(commit()); LocalSolrQueryRequest req = lrf.makeRequest("q", "*:*"); SolrQueryResponse rsp = h.queryAndResponse(req.getParams().get(CommonParams.QT), req); - BinaryQueryResponseWriter writer = - (BinaryQueryResponseWriter) h.getCore().getQueryResponseWriter("javabin"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - writer.write(baos, req, rsp); + h.getCore().getQueryResponseWriter("javabin").write(baos, req, rsp); NamedList<?> res; try (JavaBinCodec jbc = new JavaBinCodec()) { res = (NamedList<?>) jbc.unmarshal(new ByteArrayInputStream(baos.toByteArray())); diff --git a/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java b/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java index 282db9c917b..3c5afeb2085 100644 --- a/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java +++ b/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java @@ -25,7 +25,6 @@ import java.util.stream.Collectors; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; -import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.search.SolrReturnFields; import org.junit.BeforeClass; import org.junit.Test; @@ -285,10 +284,10 @@ public class TestCSVResponseWriter extends SolrTestCaseJ4 { sdl.add(d1); sdl.add(d2); - SolrQueryRequest req = req("q", "*:*"); - SolrQueryResponse rsp = new SolrQueryResponse(); + var req = req("q", "*:*"); + var rsp = new SolrQueryResponse(); rsp.addResponse(sdl); - QueryResponseWriter w = new CSVResponseWriter(); + var w = new CSVResponseWriter(); rsp.setReturnFields(new SolrReturnFields("id,foo_s", req)); StringWriter buf = new StringWriter(); diff --git a/solr/core/src/test/org/apache/solr/response/TestRawResponseWriter.java b/solr/core/src/test/org/apache/solr/response/TestRawResponseWriter.java index adf15bdb706..d84a91cf1f8 100644 --- a/solr/core/src/test/org/apache/solr/response/TestRawResponseWriter.java +++ b/solr/core/src/test/org/apache/solr/response/TestRawResponseWriter.java @@ -19,7 +19,6 @@ package org.apache.solr.response; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.StringWriter; import java.nio.charset.StandardCharsets; import org.apache.lucene.tests.util.TestUtil; import org.apache.solr.SolrTestCaseJ4; @@ -104,9 +103,7 @@ public class TestRawResponseWriter extends SolrTestCaseJ4 { assertEquals(stream.getContentType(), writer.getContentType(req(), rsp)); // we should have the same string if we use a Writer - StringWriter sout = new StringWriter(); - writer.write(sout, req(), rsp); - assertEquals(data, sout.toString()); + assertEquals(data, writer.writeToString(req(), rsp)); // we should have UTF-8 Bytes if we use an OutputStream ByteArrayOutputStream bout = new ByteArrayOutputStream(); @@ -139,23 +136,20 @@ public class TestRawResponseWriter extends SolrTestCaseJ4 { + "<str name=\"content\">test</str>\n" + "<str name=\"foo\">bar</str>\n" + "</response>\n"; - StringWriter xmlSout = new StringWriter(); - writerXmlBase.write(xmlSout, req(), rsp); - assertEquals(xml, xmlSout.toString()); + assertEquals(xml, writerXmlBase.writeToString(req(), rsp)); ByteArrayOutputStream xmlBout = new ByteArrayOutputStream(); writerXmlBase.write(xmlBout, req(), rsp); assertEquals(xml, xmlBout.toString(StandardCharsets.UTF_8.toString())); + // - StringWriter noneSout = new StringWriter(); - writerNoBase.write(noneSout, req(), rsp); - assertEquals(xml, noneSout.toString()); + assertEquals(xml, writerNoBase.writeToString(req(), rsp)); ByteArrayOutputStream noneBout = new ByteArrayOutputStream(); writerNoBase.write(noneBout, req(), rsp); assertEquals(xml, noneBout.toString(StandardCharsets.UTF_8.toString())); // json String json = "{\n" + " \"content\":\"test\",\n" + " \"foo\":\"bar\"}\n"; - assertJSONEquals(json, writerJsonBase.serializeResponse(req(), rsp)); + assertJSONEquals(json, writerJsonBase.writeToString(req(), rsp)); // javabin ByteArrayOutputStream bytes = new ByteArrayOutputStream(); diff --git a/solr/core/src/test/org/apache/solr/response/TestRetrieveFieldsOptimizer.java b/solr/core/src/test/org/apache/solr/response/TestRetrieveFieldsOptimizer.java index e5cbeaf44d1..9550528f810 100644 --- a/solr/core/src/test/org/apache/solr/response/TestRetrieveFieldsOptimizer.java +++ b/solr/core/src/test/org/apache/solr/response/TestRetrieveFieldsOptimizer.java @@ -314,10 +314,8 @@ public class TestRetrieveFieldsOptimizer extends SolrTestCaseJ4 { SolrQueryRequest req = lrf.makeRequest("q", "*:*", CommonParams.FL, fl); SolrQueryResponse rsp = h.queryAndResponse("", req); - BinaryQueryResponseWriter writer = - (BinaryQueryResponseWriter) core.getQueryResponseWriter("javabin"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - writer.write(baos, req, rsp); + core.getQueryResponseWriter("javabin").write(baos, req, rsp); // This is really the main point! assertEquals( diff --git a/solr/core/src/test/org/apache/solr/response/transform/TestSubQueryTransformer.java b/solr/core/src/test/org/apache/solr/response/transform/TestSubQueryTransformer.java index 461fa4c9560..072ac509853 100644 --- a/solr/core/src/test/org/apache/solr/response/transform/TestSubQueryTransformer.java +++ b/solr/core/src/test/org/apache/solr/response/transform/TestSubQueryTransformer.java @@ -36,7 +36,6 @@ import org.apache.solr.common.util.NamedList; import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestInfo; -import org.apache.solr.response.BinaryQueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.junit.BeforeClass; import org.junit.Test; @@ -630,10 +629,8 @@ public class TestSubQueryTransformer extends SolrTestCaseJ4 { SolrQueryResponse response = h.queryAndResponse(johnTwoFL.getParams().get(CommonParams.QT), johnTwoFL); - BinaryQueryResponseWriter responseWriter = - (BinaryQueryResponseWriter) core.getQueryResponseWriter(johnTwoFL); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - responseWriter.write(bytes, johnTwoFL, response); + johnTwoFL.getResponseWriter().write(bytes, johnTwoFL, response); try (JavaBinCodec jbc = new JavaBinCodec()) { unmarshalled = diff --git a/solr/core/src/test/org/apache/solr/search/TestSmileRequest.java b/solr/core/src/test/org/apache/solr/search/TestSmileRequest.java index 2a8d7b2742d..7f282cc39d7 100644 --- a/solr/core/src/test/org/apache/solr/search/TestSmileRequest.java +++ b/solr/core/src/test/org/apache/solr/search/TestSmileRequest.java @@ -18,12 +18,14 @@ package org.apache.solr.search; import java.io.IOException; import java.io.InputStream; +import java.util.Collection; import java.util.Map; +import java.util.Set; import org.apache.solr.JSONTestUtil; import org.apache.solr.SolrTestCaseHS; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.solrj.ResponseParser; import org.apache.solr.client.solrj.SolrClient; -import org.apache.solr.client.solrj.impl.BinaryResponseParser; import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; @@ -90,7 +92,7 @@ public class TestSmileRequest extends SolrTestCaseJ4 { // adding this to core adds the dependency on a few extra jars to our distribution. // So this is not added there - public static class SmileResponseParser extends BinaryResponseParser { + public static class SmileResponseParser extends ResponseParser { @Override public String getWriterType() { @@ -103,5 +105,10 @@ public class TestSmileRequest extends SolrTestCaseJ4 { Map m = (Map) SmileWriterTest.decodeSmile(body); return new NamedList(m); } + + @Override + public Collection<String> getContentTypes() { + return Set.of("application/x-jackson-smile", "application/octet-stream"); + } } } diff --git a/solr/modules/extraction/src/java/org/apache/solr/handler/extraction/XLSXResponseWriter.java b/solr/modules/extraction/src/java/org/apache/solr/handler/extraction/XLSXResponseWriter.java index 60a995b4d5d..c2f9f836761 100644 --- a/solr/modules/extraction/src/java/org/apache/solr/handler/extraction/XLSXResponseWriter.java +++ b/solr/modules/extraction/src/java/org/apache/solr/handler/extraction/XLSXResponseWriter.java @@ -40,7 +40,7 @@ import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.solr.common.SolrDocument; import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.RawResponseWriter; +import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.response.TabularResponseWriter; import org.apache.solr.schema.FieldType; @@ -48,10 +48,12 @@ import org.apache.solr.schema.SchemaField; import org.apache.solr.schema.StrField; import org.apache.solr.search.ReturnFields; -public class XLSXResponseWriter extends RawResponseWriter { +/** A .XLSX spreadsheet format {@link org.apache.solr.response.QueryResponseWriter}. */ +public class XLSXResponseWriter implements QueryResponseWriter { @Override - public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse rsp) + public void write( + OutputStream out, SolrQueryRequest req, SolrQueryResponse rsp, String contentType) throws IOException { // throw away arraywriter just to satisfy super requirements; we're grabbing // all writes before they go to it anyway diff --git a/solr/modules/scripting/src/java/org/apache/solr/scripting/xslt/XSLTResponseWriter.java b/solr/modules/scripting/src/java/org/apache/solr/scripting/xslt/XSLTResponseWriter.java index 2b807a2c019..15b648510dc 100644 --- a/solr/modules/scripting/src/java/org/apache/solr/scripting/xslt/XSLTResponseWriter.java +++ b/solr/modules/scripting/src/java/org/apache/solr/scripting/xslt/XSLTResponseWriter.java @@ -33,8 +33,8 @@ import javax.xml.transform.stream.StreamSource; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.response.TextQueryResponseWriter; import org.apache.solr.response.XMLWriter; /** @@ -44,7 +44,7 @@ import org.apache.solr.response.XMLWriter; * <p>QueryResponseWriter captures the output of the XMLWriter (in memory for now, not optimal * performance-wise), and applies an XSLT transform to it. */ -public class XSLTResponseWriter implements QueryResponseWriter { +public class XSLTResponseWriter implements TextQueryResponseWriter { public static final String DEFAULT_CONTENT_TYPE = "application/xml"; private Integer xsltCacheLifetimeSeconds = null; diff --git a/solr/modules/scripting/src/test/org/apache/solr/scripting/xslt/XSLTUpdateRequestHandlerTest.java b/solr/modules/scripting/src/test/org/apache/solr/scripting/xslt/XSLTUpdateRequestHandlerTest.java index af61f99e860..5397780e2e6 100644 --- a/solr/modules/scripting/src/test/org/apache/solr/scripting/xslt/XSLTUpdateRequestHandlerTest.java +++ b/solr/modules/scripting/src/test/org/apache/solr/scripting/xslt/XSLTUpdateRequestHandlerTest.java @@ -16,7 +16,6 @@ */ package org.apache.solr.scripting.xslt; -import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -29,7 +28,6 @@ import org.apache.solr.core.SolrCore; import org.apache.solr.handler.loader.ContentStreamLoader; import org.apache.solr.request.LocalSolrQueryRequest; import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.update.AddUpdateCommand; import org.apache.solr.update.processor.BufferingRequestProcessor; @@ -80,11 +78,8 @@ public class XSLTUpdateRequestHandlerTest extends SolrTestCaseJ4 { handler.init(new NamedList<>()); handler.handleRequestBody(req, rsp); } - StringWriter sw = new StringWriter(32000); - QueryResponseWriter responseWriter = core.getQueryResponseWriter(req); - responseWriter.write(sw, req, rsp); + String response = req.getResponseWriter().writeToString(req, rsp); req.close(); - String response = sw.toString(); assertU(response); assertU(commit()); diff --git a/solr/solrj/src/java/org/apache/solr/common/util/TextWriter.java b/solr/solrj/src/java/org/apache/solr/common/util/TextWriter.java index 5edf9c2c99c..5e04815c322 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/TextWriter.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/TextWriter.java @@ -41,7 +41,7 @@ import org.apache.solr.common.MapSerializable; import org.apache.solr.common.MapWriter; import org.apache.solr.common.PushWriter; -// Base interface for all text based writers +/** Base interface for all text based writers */ public interface TextWriter extends PushWriter { default void writeVal(String name, Object val) throws IOException { diff --git a/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java b/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java index 12e8ac4805f..87d53af970a 100644 --- a/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java +++ b/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java @@ -16,10 +16,7 @@ */ package org.apache.solr.util; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.StringWriter; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; @@ -49,8 +46,6 @@ import org.apache.solr.request.LocalSolrQueryRequest; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.request.SolrRequestInfo; -import org.apache.solr.response.BinaryQueryResponseWriter; -import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.schema.IndexSchema; import org.apache.solr.schema.IndexSchemaFactory; @@ -354,17 +349,7 @@ public class TestHarness extends BaseTestHarness { if (rsp.getException() != null) { throw rsp.getException(); } - QueryResponseWriter responseWriter = core.getQueryResponseWriter(req); - if (responseWriter instanceof BinaryQueryResponseWriter writer) { - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(32000); - writer.write(byteArrayOutputStream, req, rsp); - return new String(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8); - } else { - StringWriter sw = new StringWriter(32000); - responseWriter.write(sw, req, rsp); - return sw.toString(); - } - + return req.getResponseWriter().writeToString(req, rsp); } finally { req.close(); SolrRequestInfo.clearRequestInfo();