/** * 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.servlet; import java.io.IOException; import java.io.Writer; import java.io.PrintWriter; import java.io.StringWriter; import java.io.OutputStreamWriter; import java.nio.charset.Charset; import java.util.Map; import java.util.WeakHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.util.FastWriter; import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.core.*; import org.apache.solr.request.*; import org.apache.solr.response.BinaryQueryResponseWriter; import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.servlet.cache.HttpCacheHeaderUtil; import org.apache.solr.servlet.cache.Method; public class SolrDispatchFilter implements Filter { final Logger log = LoggerFactory.getLogger(SolrDispatchFilter.class); protected CoreContainer cores; protected String abortErrorMessage = null; protected final Map<SolrConfig, SolrRequestParsers> parsers = new WeakHashMap<SolrConfig, SolrRequestParsers>(); private static final Charset UTF8 = Charset.forName("UTF-8"); public void init(FilterConfig config) throws ServletException { log.info("SolrDispatchFilter.init()"); boolean abortOnConfigurationError = true; CoreContainer.Initializer init = new CoreContainer.Initializer(); try { this.cores = init.initialize(); abortOnConfigurationError = init.isAbortOnConfigurationError(); } catch( Throwable t ) { log.error( "Could not start Solr. Check solr/home property", t); SolrConfig.severeErrors.add( t ); SolrCore.log( t ); CoreContainer.setException(); } if( abortOnConfigurationError && SolrConfig.severeErrors.size() > 0 ) { StringWriter sw = new StringWriter(); PrintWriter out = new PrintWriter( sw ); out.println( "Severe errors in solr configuration.\n" ); for( Throwable t : SolrConfig.severeErrors ) { out.println( "-------------------------------------------------------------" ); t.printStackTrace( out ); } out.flush(); abortErrorMessage = sw.toString(); } log.info("SolrDispatchFilter.init() done"); } public void destroy() { if (cores != null) { cores.shutdown(); cores = null; } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if( abortErrorMessage != null ) { ((HttpServletResponse)response).sendError( 500, abortErrorMessage ); return; } if( request instanceof HttpServletRequest) { HttpServletRequest req = (HttpServletRequest)request; HttpServletResponse resp = (HttpServletResponse)response; SolrRequestHandler handler = null; SolrQueryRequest solrReq = null; SolrCore core = null; String corename = ""; try { // put the core container in request attribute req.setAttribute("org.apache.solr.CoreContainer", cores); String path = req.getServletPath(); if( req.getPathInfo() != null ) { path += req.getPathInfo(); } // unused feature ? int idx = path.indexOf( ':' ); if( idx > 0 ) { path = path.substring( 0, idx ); } idx = path.indexOf( "/", 1 ); if( idx > 1 ) { // try to get the corename as a request parameter first corename = path.substring( 1, idx ); core = cores.getCore(corename); if (core != null) { path = path.substring( idx ); } } if (core == null) { corename = ""; core = cores.getCore(""); } // With a valid core... if( core != null ) { final SolrConfig config = core.getSolrConfig(); SolrRequestParsers parser = null; parser = parsers.get(config); if( parser == null ) { parser = new SolrRequestParsers(config); parsers.put(config, parser ); } if( handler == null && path.length() > 1 ) { // don't match "" or "/" as valid path handler = core.getRequestHandler( path ); if( handler == null && parser.isHandleSelect() ) { if( "/select".equals( path ) || "/select/".equals( path ) ) { solrReq = parser.parse( core, path, req ); String qt = solrReq.getParams().get( CommonParams.QT ); handler = core.getRequestHandler( qt ); if( handler == null ) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "unknown handler: "+qt); } } } } if( handler != null ) { if( solrReq == null ) { solrReq = parser.parse( core, path, req ); } final Method reqMethod = Method.getMethod(req.getMethod()); HttpCacheHeaderUtil.setCacheControlHeader(config, resp, reqMethod); if (config.getHttpCachingConfig().isNever304() || !HttpCacheHeaderUtil.doCacheHeaderValidation(solrReq, req, reqMethod, resp)) { SolrQueryResponse solrRsp = new SolrQueryResponse(); this.execute( req, handler, solrReq, solrRsp ); HttpCacheHeaderUtil.checkHttpCachingVeto(solrRsp, resp, reqMethod); QueryResponseWriter responseWriter = core.getQueryResponseWriter(solrReq); writeResponse(solrRsp, response, responseWriter, solrReq, reqMethod); } return; } } log.debug("no handler or core retrieved for " + path + ", follow through..."); } catch (Throwable ex) { sendError( (HttpServletResponse)response, ex ); return; } finally { if( solrReq != null ) { solrReq.close(); } if (core != null) { core.close(); } } } // Otherwise let the webapp handle the request chain.doFilter(request, response); } private void writeResponse(SolrQueryResponse solrRsp, ServletResponse response, QueryResponseWriter responseWriter, SolrQueryRequest solrReq, Method reqMethod) throws IOException { if (solrRsp.getException() != null) { sendError((HttpServletResponse) response, solrRsp.getException()); } else { // Now write it out final String ct = responseWriter.getContentType(solrReq, solrRsp); // don't call setContentType on null if (null != ct) response.setContentType(ct); 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 || charset.equalsIgnoreCase("UTF-8")) ? new OutputStreamWriter(response.getOutputStream(), UTF8) : new OutputStreamWriter(response.getOutputStream(), charset); out = new FastWriter(out); responseWriter.write(out, solrReq, solrRsp); out.flush(); } } //else http HEAD request, nothing to write out, waited this long just to get ContentType } } protected void execute( HttpServletRequest req, SolrRequestHandler handler, SolrQueryRequest sreq, SolrQueryResponse rsp) { // a custom filter could add more stuff to the request before passing it on. // for example: sreq.getContext().put( "HttpServletRequest", req ); // used for logging query stats in SolrCore.execute() sreq.getContext().put( "webapp", req.getContextPath() ); sreq.getCore().execute( handler, sreq, rsp ); } protected void sendError(HttpServletResponse res, Throwable ex) throws IOException { int code=500; String trace = ""; if( ex instanceof SolrException ) { code = ((SolrException)ex).code(); } // For any regular code, don't include the stack trace if( code == 500 || code < 100 ) { StringWriter sw = new StringWriter(); ex.printStackTrace(new PrintWriter(sw)); trace = "\n\n"+sw.toString(); SolrException.logOnce(log,null,ex ); // non standard codes have undefined results with various servers if( code < 100 ) { log.warn( "invalid return code: "+code ); code = 500; } } res.sendError( code, ex.getMessage() + trace ); } }