You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2014/09/03 21:18:20 UTC

svn commit: r1622321 - in /lucene/dev/trunk/solr: ./ core/src/java/org/apache/solr/response/ core/src/java/org/apache/solr/rest/ core/src/java/org/apache/solr/servlet/ core/src/test/org/apache/solr/response/

Author: hossman
Date: Wed Sep  3 19:18:20 2014
New Revision: 1622321

URL: http://svn.apache.org/r1622321
Log:
SOLR-4406: Fix RawResponseWriter to respect 'base' writer

Added:
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/QueryResponseWriterUtil.java   (with props)
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/response/TestRawResponseWriter.java   (with props)
Modified:
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/BaseSolrResource.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1622321&r1=1622320&r2=1622321&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Wed Sep  3 19:18:20 2014
@@ -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
 ---------------------
 

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/QueryResponseWriterUtil.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/QueryResponseWriterUtil.java?rev=1622321&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/QueryResponseWriterUtil.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/QueryResponseWriterUtil.java Wed Sep  3 19:18:20 2014
@@ -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);
+  }
+}

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java?rev=1622321&r1=1622320&r2=1622321&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java Wed Sep  3 19:18:20 2014
@@ -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.SolrQuery
  *
  * @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 implement
   }
 
   // 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 implement
   }
 
   @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");
-    }
-
   }
 }

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/BaseSolrResource.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/BaseSolrResource.java?rev=1622321&r1=1622320&r2=1622321&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/BaseSolrResource.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/BaseSolrResource.java Wed Sep  3 19:18:20 2014
@@ -24,12 +24,11 @@ import org.apache.solr.common.util.Simpl
 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 e
     @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);
       }
     }
   }

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java?rev=1622321&r1=1622320&r2=1622321&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java Wed Sep  3 19:18:20 2014
@@ -66,11 +66,11 @@ import org.apache.solr.request.SolrReque
 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 
   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 
     }
     
     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
   }

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/response/TestRawResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/response/TestRawResponseWriter.java?rev=1622321&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/response/TestRawResponseWriter.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/response/TestRawResponseWriter.java Wed Sep  3 19:18:20 2014
@@ -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;
+  }
+  
+}