SOLR-4406: Fix RawResponseWriter to respect 'base' writer

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1622321 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Chris M. Hostetter 2014-09-03 19:18:20 +00:00
parent c52d04197a
commit 1eafd0fa43
6 changed files with 283 additions and 62 deletions

View File

@ -158,6 +158,9 @@ Bug Fixes
* SOLR-5966: Admin UI Menu is fixed and doesn't respect smaller viewports.
(Aman Tandon, steffkes via shalin)
* SOLR-4406: Fix RawResponseWriter to respect 'base' writer
(Steve Davids, hossman)
Other Changes
---------------------

View File

@ -0,0 +1,64 @@
/*
* 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.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import org.apache.solr.common.util.ContentStreamBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.util.FastWriter;
/**
* Static utility methods relating to {@link QueryResponseWriter}s
*/
public final class QueryResponseWriterUtil {
private QueryResponseWriterUtil() { /* static helpers only */ }
/**
* 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 approprate method.
* @see BinaryQueryResponseWriter#write(OutputStream,SolrQueryRequest,SolrQueryResponse)
* @see BinaryQueryResponseWriter#write(Writer,SolrQueryRequest,SolrQueryResponse)
*/
public static void writeQueryResponse(OutputStream outputStream,
QueryResponseWriter responseWriter, SolrQueryRequest solrRequest,
SolrQueryResponse solrResponse, String contentType) throws IOException {
if (responseWriter instanceof BinaryQueryResponseWriter) {
BinaryQueryResponseWriter binWriter = (BinaryQueryResponseWriter) responseWriter;
binWriter.write(outputStream, solrRequest, solrResponse);
} else {
Writer writer = buildWriter(outputStream, ContentStreamBase.getCharsetFromContentType(contentType));
responseWriter.write(writer, solrRequest, solrResponse);
writer.flush();
}
}
private static Writer buildWriter(OutputStream outputStream, String charset) throws UnsupportedEncodingException {
Writer writer = (charset == null) ? new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)
: new OutputStreamWriter(outputStream, charset);
return new FastWriter(writer);
}
}

View File

@ -18,6 +18,7 @@
package org.apache.solr.response;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
@ -45,8 +46,7 @@ import org.apache.solr.request.SolrQueryRequest;
*
* @since solr 1.3
*/
public class RawResponseWriter implements BinaryQueryResponseWriter
{
public class RawResponseWriter implements BinaryQueryResponseWriter {
/**
* The key that should be used to add a ContentStream to the
* SolrQueryResponse if you intend to use this Writer.
@ -65,8 +65,7 @@ public class RawResponseWriter implements BinaryQueryResponseWriter
}
// Even if this is null, it should be ok
protected QueryResponseWriter getBaseWriter( SolrQueryRequest request )
{
protected QueryResponseWriter getBaseWriter( SolrQueryRequest request ) {
return request.getCore().getQueryResponseWriter( _baseWriter );
}
@ -80,42 +79,31 @@ public class RawResponseWriter implements BinaryQueryResponseWriter
}
@Override
public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException
{
public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException {
Object obj = response.getValues().get( CONTENT );
if( obj != null && (obj instanceof ContentStream ) ) {
// copy the contents to the writer...
ContentStream content = (ContentStream)obj;
Reader reader = content.getReader();
try {
try(Reader reader = content.getReader()) {
IOUtils.copy( reader, writer );
} finally {
reader.close();
}
}
else {
} else {
getBaseWriter( request ).write( writer, request, response );
}
}
@Override
public void write(OutputStream out, SolrQueryRequest request,
SolrQueryResponse response) throws IOException {
@Override
public void write(OutputStream out, SolrQueryRequest request, SolrQueryResponse response) throws IOException {
Object obj = response.getValues().get( CONTENT );
if( obj != null && (obj instanceof ContentStream ) ) {
// copy the contents to the writer...
ContentStream content = (ContentStream)obj;
java.io.InputStream in = content.getStream();
try {
try(InputStream in = content.getStream()) {
IOUtils.copy( in, out );
} finally {
in.close();
}
} else {
QueryResponseWriterUtil.writeQueryResponse(out,
getBaseWriter(request), request, response, getContentType(request, response));
}
else {
//getBaseWriter( request ).write( writer, request, response );
throw new IOException("did not find a CONTENT object");
}
}
}

View File

@ -24,12 +24,11 @@ import org.apache.solr.common.util.SimpleOrderedMap;
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.QueryResponseWriter;
import org.apache.solr.response.QueryResponseWriterUtil;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.servlet.ResponseUtils;
import org.apache.solr.util.FastWriter;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Status;
@ -40,19 +39,14 @@ import org.slf4j.Logger;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Base class of all Solr Restlet server resource classes.
*/
public abstract class BaseSolrResource extends ServerResource {
protected static final Charset UTF8 = StandardCharsets.UTF_8;
protected static final String SHOW_DEFAULTS = "showDefaults";
private SolrCore solrCore;
@ -157,18 +151,7 @@ public abstract class BaseSolrResource extends ServerResource {
@Override
public void write(OutputStream outputStream) throws IOException {
if (getRequest().getMethod() != Method.HEAD) {
if (responseWriter instanceof BinaryQueryResponseWriter) {
BinaryQueryResponseWriter binWriter = (BinaryQueryResponseWriter)responseWriter;
binWriter.write(outputStream, solrRequest, solrResponse);
} else {
String charset = ContentStreamBase.getCharsetFromContentType(contentType);
Writer out = (charset == null)
? new OutputStreamWriter(outputStream, UTF8)
: new OutputStreamWriter(outputStream, charset);
out = new FastWriter(out);
responseWriter.write(out, solrRequest, solrResponse);
out.flush();
}
QueryResponseWriterUtil.writeQueryResponse(outputStream, responseWriter, solrRequest, solrResponse, contentType);
}
}
}

View File

@ -66,11 +66,11 @@ 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.QueryResponseWriterUtil;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.servlet.cache.HttpCacheHeaderUtil;
import org.apache.solr.servlet.cache.Method;
import org.apache.solr.update.processor.DistributingUpdateProcessorFactory;
import org.apache.solr.util.FastWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -86,11 +86,7 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -120,8 +116,6 @@ public class SolrDispatchFilter extends BaseSolrFilter {
protected String abortErrorMessage = null;
protected final HttpClient httpClient = HttpClientUtil.createClient(new ModifiableSolrParams());
private static final Charset UTF8 = StandardCharsets.UTF_8;
public SolrDispatchFilter() {
}
@ -756,18 +750,7 @@ public class SolrDispatchFilter extends BaseSolrFilter {
}
if (Method.HEAD != reqMethod) {
if (responseWriter instanceof BinaryQueryResponseWriter) {
BinaryQueryResponseWriter binWriter = (BinaryQueryResponseWriter) responseWriter;
binWriter.write(response.getOutputStream(), solrReq, solrRsp);
} else {
String charset = ContentStreamBase.getCharsetFromContentType(ct);
Writer out = (charset == null)
? new OutputStreamWriter(response.getOutputStream(), UTF8)
: new OutputStreamWriter(response.getOutputStream(), charset);
out = new FastWriter(out);
responseWriter.write(out, solrReq, solrRsp);
out.flush();
}
QueryResponseWriterUtil.writeQueryResponse(response.getOutputStream(), responseWriter, solrReq, solrRsp, ct);
}
//else http HEAD request, nothing to write out, waited this long just to get ContentType
}

View File

@ -0,0 +1,200 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Collections;
import java.nio.charset.StandardCharsets;
import org.apache.lucene.util.TestUtil;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.impl.BinaryResponseParser;
import org.apache.solr.common.util.ContentStreamBase.ByteArrayStream;
import org.apache.solr.common.util.ContentStreamBase.StringStream;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.SolrQueryRequest;
import org.junit.BeforeClass;
import org.junit.AfterClass;
/**
* Tests the {@link RawResponseWriter} behavior, in particular when dealing with "base" writer
*/
public class TestRawResponseWriter extends SolrTestCaseJ4 {
private static RawResponseWriter writerXmlBase;
private static RawResponseWriter writerJsonBase;
private static RawResponseWriter writerBinBase;
private static RawResponseWriter writerNoBase;
private static RawResponseWriter[] allWriters;
@BeforeClass
public static void setupCoreAndWriters() throws Exception {
// we don't directly use this core or it's config, we use
// QueryResponseWriters' constructed programaticly,
// but we do use this core for managing the life cycle of the requests
// we spin up.
initCore("solrconfig.xml","schema.xml");
writerNoBase = newRawResponseWriter(null); /* defaults to standard writer as base */
writerXmlBase = newRawResponseWriter("xml");
writerJsonBase = newRawResponseWriter("json");
writerBinBase = newRawResponseWriter("javabin");
allWriters = new RawResponseWriter[] {
writerXmlBase, writerJsonBase, writerBinBase, writerNoBase
};
}
@AfterClass
public static void cleanupWriters() throws Exception {
writerXmlBase = null;
writerJsonBase = null;
writerBinBase = null;
writerNoBase = null;
allWriters = null;
}
/**
* Regardless of base writer, the bytes in should be the same as the bytes out
* when response is a raw ContentStream written to an OutputStream
*/
public void testRawBinaryContentStream() throws IOException {
SolrQueryResponse rsp = new SolrQueryResponse();
byte[] data = new byte[TestUtil.nextInt(random(), 10, 2048)];
random().nextBytes(data);
ByteArrayStream stream = new ByteArrayStream(data, TestUtil.randomUnicodeString(random()));
stream.setContentType(TestUtil.randomSimpleString(random()));
rsp.add(RawResponseWriter.CONTENT, stream);
for (RawResponseWriter writer : allWriters) {
assertEquals(stream.getContentType(), writer.getContentType(req(), rsp));
ByteArrayOutputStream out = new ByteArrayOutputStream();
writer.write(out, req(), rsp);
assertArrayEquals(data, out.toByteArray());
}
}
/**
* Regardless of base writer, the String in should be the same as the String out
* when response is a raw ContentStream written to a Writer (or OutputStream)
*/
public void testRawStringContentStream() throws IOException {
SolrQueryResponse rsp = new SolrQueryResponse();
String data = TestUtil.randomUnicodeString(random());
StringStream stream = new StringStream(data);
stream.setContentType(TestUtil.randomSimpleString(random()));
rsp.add(RawResponseWriter.CONTENT, stream);
for (RawResponseWriter writer : allWriters) {
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());
// we should have UTF-8 Bytes if we use an OutputStream
ByteArrayOutputStream bout = new ByteArrayOutputStream();
writer.write(bout, req(), rsp);
assertEquals(data, bout.toString(StandardCharsets.UTF_8.toString()));
}
}
/**
* When no real ContentStream is specified, each base writer should be used for formatting
*/
public void testStructuredDataViaBaseWriters() throws IOException {
SolrQueryResponse rsp = new SolrQueryResponse();
// Don't send a ContentStream back, this will fall back to the configured base writer.
// But abuse the CONTENT key to ensure writer is also checking type
rsp.add(RawResponseWriter.CONTENT, "test");
rsp.add("foo", "bar");
// check Content-Type against each writer
assertEquals("application/xml; charset=UTF-8", writerNoBase.getContentType(req(), rsp));
assertEquals("application/xml; charset=UTF-8", writerXmlBase.getContentType(req(), rsp));
assertEquals("application/json; charset=UTF-8", writerJsonBase.getContentType(req(), rsp));
assertEquals("application/octet-stream", writerBinBase.getContentType(req(), rsp));
// check response against each writer
// xml & none (default behavior same as XML)
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<response>\n<str name=\"content\">test</str><str name=\"foo\">bar</str>\n</response>\n";
StringWriter xmlSout = new StringWriter();
writerXmlBase.write(xmlSout, req(), rsp);
assertEquals(xml, xmlSout.toString());
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());
ByteArrayOutputStream noneBout = new ByteArrayOutputStream();
writerNoBase.write(noneBout, req(), rsp);
assertEquals(xml, noneBout.toString(StandardCharsets.UTF_8.toString()));
// json
String json = "{\"content\":\"test\",\"foo\":\"bar\"}\n";
StringWriter jsonSout = new StringWriter();
writerJsonBase.write(jsonSout, req(), rsp);
assertEquals(json, jsonSout.toString());
ByteArrayOutputStream jsonBout = new ByteArrayOutputStream();
writerJsonBase.write(jsonBout, req(), rsp);
assertEquals(json, jsonBout.toString(StandardCharsets.UTF_8.toString()));
// javabin
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
writerBinBase.write(bytes, req(), rsp);
BinaryResponseParser parser = new BinaryResponseParser();
NamedList<Object> out = parser.processResponse
(new ByteArrayInputStream(bytes.toByteArray()), /* encoding irelevent */ null);
assertEquals(RawResponseWriter.CONTENT, out.getName(0));
assertEquals("test", out.getVal(0));
assertEquals("foo", out.getName(1));
assertEquals("bar", out.getVal(1));
}
/**
* Generates a new {@link RawResponseWriter} wraping the specified baseWriter name
* (which much either be an implicitly definied response writer, or one explicitly
* configured in solrconfig.xml)
*
* @param baseWriter null or the name of a valid base writer
*/
private static RawResponseWriter newRawResponseWriter(String baseWriter) {
RawResponseWriter writer = new RawResponseWriter();
NamedList initArgs = new NamedList<Object>();
if (null != baseWriter) {
initArgs.add("base", baseWriter);
}
writer.init(initArgs);
return writer;
}
}