-
Notifications
You must be signed in to change notification settings - Fork 697
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SOLR-17682: QueryResponseWriter hierarchy refactor #3209
base: main
Are you sure you want to change the base?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,6 @@ | |
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.OutputStream; | ||
import java.io.Writer; | ||
import java.lang.invoke.MethodHandles; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
|
@@ -45,12 +44,14 @@ | |
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(); | ||
|
@@ -59,12 +60,6 @@ public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse resp | |
} | ||
} | ||
|
||
@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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The existence of this was a code-smell that the hierarchy was wrong; promoting my efforts here |
||
} | ||
|
||
@Override | ||
public String getContentType(SolrQueryRequest request, SolrQueryResponse response) { | ||
return BinaryResponseParser.BINARY_CONTENT_TYPE; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,13 +23,17 @@ | |
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 JacksonJsonWriter() { | |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this charSet check logic is new. Since Jackson can specify the encoding and since the contentType (with possibly the encoding) is now passed in (new), made sense to choose the right one. |
||
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 static class WriterImpl extends JSONWriter { | |
|
||
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()); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,12 +47,15 @@ | |
* org.apache.solr.handler.admin.MetricsHandler} | ||
*/ | ||
@SuppressWarnings(value = "unchecked") | ||
public class PrometheusResponseWriter extends RawResponseWriter { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mlbiscoc why did you choose RawResponseWriter? It's a very special QueryResponseWriter and I don't see that the this response writer utilizes it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't fully remember. I think I followed a different writer at the time and this one took an |
||
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"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 @@ | |
* <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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added contentType since the production code path will have this already, and we'll need it in TextQueryResponseWriter There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a debatable change... it'd be less disruptive to leave it as it was and then only the text formats will re-acquire it which is fairly trivial parsing to do a second time. Shrug. |
||
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 void write(Writer writer, SolrQueryRequest request, SolrQueryResponse res | |
* | ||
* @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); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
worth doing in another PR; maybe same JIRA