SOLR-142: Added RawResponseWriter and ShowFileRequestHandler. This returns config files directly. If the AdminHandlers is configured, this will be added automatically.

git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@608150 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Ryan McKinley 2008-01-02 17:15:30 +00:00
parent 641e7f3c4a
commit 3a726d0b6d
8 changed files with 377 additions and 5 deletions

View File

@ -176,6 +176,10 @@ New Features
This will register: Luke/SystemInfo/PluginInfo/ThreadDump/PropertiesRequestHandler.
(ryan)
35. SOLR-142: Added RawResponseWriter and ShowFileRequestHandler. This returns config
files directly. If the AdminHandlers is configured, this will be added automatically.
(ryan)
Changes in runtime behavior
Optimizations

View File

@ -502,7 +502,15 @@
<requestHandler name="/admin/plugins" class="org.apache.solr.handler.admin.PluginInfoHandler" />
<requestHandler name="/admin/threads" class="org.apache.solr.handler.admin.ThreadDumpHandler" />
<requestHandler name="/admin/properties" class="org.apache.solr.handler.admin.PropertiesRequestHandler" />
<requestHandler name="/admin/file" class="org.apache.solr.handler.admin.ShowFileRequestHandler" >
If you wish to hide files under ${solr.home}/conf, explicitly register the ShowFileRequestHandler using:
<requestHandler name="/admin/file" class="org.apache.solr.handler.admin.ShowFileRequestHandler" >
<lst name="invariants">
<str name="hidden">synonyms.txt</str>
<str name="hidden">anotherfile.txt</str>
</lst>
</requestHandler>
-->
<requestHandler name="/admin/" class="org.apache.solr.handler.admin.AdminHandlers" />

View File

@ -54,6 +54,7 @@ import org.apache.solr.highlight.SolrHighlighter;
import org.apache.solr.request.JSONResponseWriter;
import org.apache.solr.request.PythonResponseWriter;
import org.apache.solr.request.QueryResponseWriter;
import org.apache.solr.request.RawResponseWriter;
import org.apache.solr.request.RubyResponseWriter;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrQueryResponse;
@ -968,7 +969,9 @@ public final class SolrCore {
if (responseWriters.get("ruby")==null) {
responseWriters.put("ruby", new RubyResponseWriter());
}
if (responseWriters.get("raw")==null) {
responseWriters.put("raw", new RawResponseWriter());
}
}
/** Finds a writer by name, or returns the default writer if not found. */

View File

@ -84,7 +84,8 @@ public class AdminHandlers implements SolrCoreAware, SolrRequestHandler
new StandardHandler( "system", new SystemInfoHandler() ),
new StandardHandler( "plugins", new PluginInfoHandler() ),
new StandardHandler( "threads", new ThreadDumpHandler() ),
new StandardHandler( "properties", new PropertiesRequestHandler() )
new StandardHandler( "properties", new PropertiesRequestHandler() ),
new StandardHandler( "file", new ShowFileRequestHandler() )
};
for( StandardHandler handler : list ) {

View File

@ -0,0 +1,255 @@
/**
* 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.handler.admin;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.ContentStream;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.handler.RequestHandlerUtils;
import org.apache.solr.request.RawResponseWriter;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrQueryResponse;
/**
* This handler uses the RawResponseWriter to give client access to
* files inside ${solr.home}/conf
*
* If you want to selectively restrict access some configuration files, you can list
* these files in the {@link #HIDDEN} invariants. For example to hide
* synonyms.txt and anotherfile.txt, you would register:
*
* <pre>
* &lt;requestHandler name="/admin/file" class="org.apache.solr.handler.admin.ShowFileRequestHandler" &gt;
* &lt;lst name="defaults"&gt;
* &lt;str name="echoParams"&gt;explicit&lt;/str&gt;
* &lt;/lst&gt;
* &lt;lst name="invariants"&gt;
* &lt;str name="hidden"&gt;synonyms.txt&lt;/str&gt;
* &lt;str name="hidden"&gt;anotherfile.txt&lt;/str&gt;
* &lt;/lst&gt;
* &lt;/requestHandler&gt;
* </pre>
*
* The ShowFileRequestHandler uses the {@link RawResponseWriter} (wt=raw) to return
* file contents. If you need to use a different writer, you will need to change
* the registered invarient param for wt.
*
* If you want to override the contentType header returned for a given file, you can
* set it directly using: {@link #USE_CONTENT_TYPE}. For example, to get a plain text
* version of schema.xml, try:
* <pre>
* http://localhost:8983/solr/admin/file?file=schema.xml&contentType=text/plain
* </pre>
*
* @version $Id$
* @since solr 1.3
*/
public class ShowFileRequestHandler extends RequestHandlerBase
{
public static final String HIDDEN = "hidden";
public static final String USE_CONTENT_TYPE = "contentType";
protected Set<String> hiddenFiles;
private static ShowFileRequestHandler instance;
public ShowFileRequestHandler()
{
super();
instance = this; // used so that getFileContents can access hiddenFiles
}
@Override
public void init(NamedList args) {
super.init( args );
// by default, use wt=raw
ModifiableSolrParams params = new ModifiableSolrParams( invariants );
if( params.get( CommonParams.WT ) == null ) {
params.set( CommonParams.WT, "raw" );
}
this.invariants = params;
// Build a list of hidden files
hiddenFiles = new HashSet<String>();
if( invariants != null ) {
String[] hidden = invariants.getParams( HIDDEN );
if( hidden != null ) {
for( String s : hidden ) {
hiddenFiles.add( s.toUpperCase() );
}
}
}
}
@Override
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException
{
File adminFile = null;
final SolrResourceLoader loader = req.getCore().getResourceLoader();
File configdir = new File( loader.getConfigDir() );
String fname = req.getParams().get("file", null);
if( fname == null ) {
adminFile = configdir;
}
else {
fname = fname.replace( '\\', '/' ); // normalize slashes
if( hiddenFiles.contains( fname.toUpperCase() ) ) {
throw new SolrException( ErrorCode.FORBIDDEN, "Can not access: "+fname );
}
if( fname.indexOf( ".." ) >= 0 ) {
throw new SolrException( ErrorCode.FORBIDDEN, "Invalid path: "+fname );
}
adminFile = new File( configdir, fname );
}
// Make sure the file exists, is readable and is not a hidden file
if( !adminFile.exists() ) {
throw new SolrException( ErrorCode.BAD_REQUEST, "Can not find: "+adminFile.getName()
+ " ["+adminFile.getAbsolutePath()+"]" );
}
if( !adminFile.canRead() || adminFile.isHidden() ) {
throw new SolrException( ErrorCode.BAD_REQUEST, "Can not show: "+adminFile.getName()
+ " ["+adminFile.getAbsolutePath()+"]" );
}
// Add a warning
RequestHandlerUtils.addExperimentalFormatWarning(rsp);
// Show a directory listing
if( adminFile.isDirectory() ) {
int basePath = configdir.getAbsolutePath().length() + 1;
NamedList<SimpleOrderedMap<Object>> files = new SimpleOrderedMap<SimpleOrderedMap<Object>>();
for( File f : adminFile.listFiles() ) {
String path = f.getAbsolutePath().substring( basePath );
path = path.replace( '\\', '/' ); // normalize slashes
if( hiddenFiles.contains( path.toUpperCase() ) ) {
continue; // don't show 'hidden' files
}
if( f.isHidden() || f.getName().startsWith( "." ) ) {
continue; // skip hidden system files...
}
SimpleOrderedMap<Object> fileInfo = new SimpleOrderedMap<Object>();
files.add( path, fileInfo );
if( f.isDirectory() ) {
fileInfo.add( "directory", true );
}
else {
// TODO? content type
fileInfo.add( "size", f.length() );
}
fileInfo.add( "modified", new Date( f.lastModified() ) );
}
rsp.add( "files", files );
}
else {
// Check if they want the file as text
final String contentType = req.getParams().get( USE_CONTENT_TYPE );
final File file = adminFile;
//final URLConnection conn = adminFile.toURI().toURL().openConnection();
ContentStream stream = new ContentStream() {
public String getName() { return file.getName(); }
public Long getSize() { return file.length(); }
public String getSourceInfo() { return null; }
public String getContentType() {
if( contentType != null ) {
return contentType;
}
return null; //conn.getContentType();
}
public InputStream getStream() throws IOException {
return loader.openResource( file.getPath() ); //conn.getInputStream();
}
public Reader getReader() throws IOException {
return new FileReader( file );
}
};
rsp.add( RawResponseWriter.CONTENT, stream );
}
}
/**
* This is a utility function that lets you get the contents of an admin file
*
* It is only used so that we can get rid of "/admin/get-file.jsp" and include
* "admin-extra.html" in "/admin/index.html" using jsp scriptlets
*/
@Deprecated
public static String getFileContents( String path )
{
if( instance != null && instance.hiddenFiles != null ) {
if( instance.hiddenFiles.contains( path ) ) {
return ""; // ignore it...
}
}
try {
SolrCore core = SolrCore.getSolrCore();
InputStream input = core.getResourceLoader().openResource(path);
return IOUtils.toString( input );
}
catch( Exception ex ) {} // ignore it
return "";
}
//////////////////////// SolrInfoMBeans methods //////////////////////
@Override
public String getDescription() {
return "Admin Get File -- view config files directly";
}
@Override
public String getVersion() {
return "$Revision$";
}
@Override
public String getSourceId() {
return "$Id$";
}
@Override
public String getSource() {
return "$URL$";
}
}

View File

@ -0,0 +1,91 @@
/**
* 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.request;
import java.io.IOException;
import java.io.Writer;
import org.apache.commons.io.IOUtils;
import org.apache.solr.common.util.ContentStream;
import org.apache.solr.common.util.NamedList;
/**
* Writes a ContentStream directly to the output.
*
* <p>
* This writer is a special case that extends and alters the
* QueryResponseWriter contract. If SolrQueryResponse contains a
* ContentStream added with the key {@link #CONTENT}
* then this writer will output that stream exactly as is (with it's
* Content-Type). if no such ContentStream has been added, then a
* "base" QueryResponseWriter will be used to write the response
* according to the usual contract. The name of the "base" writer can
* be specified as an initialization param for this writer, or it
* defaults to the "standard" writer.
* </p>
*
* @version $Id$
* @since solr 1.3
*/
public class RawResponseWriter implements QueryResponseWriter
{
/**
* The key that should be used to add a ContentStream to the
* SolrQueryResponse if you intend to use this Writer.
*/
public static final String CONTENT = "content";
private String _baseWriter = null;
public void init(NamedList n) {
if( n != null ) {
Object base = n.get( "base" );
if( base != null ) {
_baseWriter = base.toString();
}
}
}
// Even if this is null, it should be ok
protected QueryResponseWriter getBaseWriter( SolrQueryRequest request )
{
return request.getCore().getQueryResponseWriter( _baseWriter );
}
public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
Object obj = response.getValues().get( CONTENT );
if( obj != null && (obj instanceof ContentStream ) ) {
// copy the contents to the writer...
ContentStream content = (ContentStream)obj;
return content.getContentType();
}
return getBaseWriter( request ).getContentType( request, response );
}
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;
IOUtils.copy( content.getStream(), writer );
}
else {
getBaseWriter( request ).write( writer, request, response );
}
}
}

View File

@ -23,6 +23,13 @@
java.io.Reader,
java.util.StringTokenizer"%>
<%
//
// NOTE -- this file is Deprecated - should use the ShowFileRequestHandler instead
//
System.out.println( "WARNING -- using deprecated jsp file: " + request.getServletPath() );
Object ocore = request.getAttribute("org.apache.solr.SolrCore");
SolrCore core = ocore instanceof SolrCore? (SolrCore) ocore : SolrCore.getSolrCore();
String fname = request.getParameter("file");

View File

@ -34,8 +34,8 @@
<h3>Solr</h3>
</td>
<td>
[<a href="get-file.jsp?file=<%=core.getSchemaFile()%>">Schema</a>]
[<a href="get-file.jsp?file=<%=core.getConfigFile()%>">Config</a>]
[<a href="file/?file=<%=core.getSchemaFile()%>">Schema</a>]
[<a href="file/?file=<%=core.getConfigFile()%>">Config</a>]
[<a href="analysis.jsp?highlight=on">Analysis</a>]
<br>
[<a href="stats.jsp">Statistics</a>]
@ -80,7 +80,10 @@ if (cores.size() > 1) {%><tr><td><strong>Cores:</strong><br></td><td><%
</tr>
<jsp:include page="get-file.jsp?file=admin-extra.html&optional=y" flush="true"/>
<%
// a quick hack to get rid of get-file.jsp -- note this still spits out invalid HTML
out.write( org.apache.solr.handler.admin.ShowFileRequestHandler.getFileContents( "admin-extra.html" ) );
%>
</table><P>