001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.solr.servlet;
017:
018: import java.io.IOException;
019: import java.io.PrintWriter;
020: import java.io.StringWriter;
021: import java.util.logging.Logger;
022:
023: import javax.servlet.Filter;
024: import javax.servlet.FilterChain;
025: import javax.servlet.FilterConfig;
026: import javax.servlet.ServletException;
027: import javax.servlet.ServletRequest;
028: import javax.servlet.ServletResponse;
029: import javax.servlet.http.HttpServletRequest;
030: import javax.servlet.http.HttpServletResponse;
031:
032: import org.apache.solr.core.SolrConfig;
033: import org.apache.solr.core.SolrCore;
034: import org.apache.solr.core.SolrException;
035: import org.apache.solr.request.QueryResponseWriter;
036: import org.apache.solr.request.SolrParams;
037: import org.apache.solr.request.SolrQueryRequest;
038: import org.apache.solr.request.SolrQueryResponse;
039: import org.apache.solr.request.SolrRequestHandler;
040:
041: /**
042: * This filter looks at the incoming URL maps them to handlers defined in solrconfig.xml
043: */
044: public class SolrDispatchFilter implements Filter {
045: final Logger log = Logger.getLogger(SolrDispatchFilter.class
046: .getName());
047:
048: protected SolrCore core;
049: protected SolrRequestParsers parsers;
050: protected boolean handleSelect = false;
051: protected String pathPrefix = null; // strip this from the begging of a path
052: protected String abortErrorMessage = null;
053:
054: public void init(FilterConfig config) throws ServletException {
055: log.info("SolrDispatchFilter.init()");
056:
057: try {
058: // web.xml configuration
059: this .pathPrefix = config.getInitParameter("path-prefix");
060:
061: // Let this filter take care of /select?xxx format
062: this .handleSelect = SolrConfig.config.getBool(
063: "requestDispatcher/@handleSelect", false);
064:
065: log.info("user.dir=" + System.getProperty("user.dir"));
066: core = SolrCore.getSolrCore();
067: parsers = new SolrRequestParsers(core, SolrConfig.config);
068: } catch (Throwable t) {
069: // catch this so our filter still works
070: SolrConfig.severeErrors.add(t);
071: SolrCore.log(t);
072: }
073:
074: // Optionally abort if we found a sever error
075: boolean abortOnConfigurationError = SolrConfig.config.getBool(
076: "abortOnConfigurationError", true);
077: if (abortOnConfigurationError
078: && SolrConfig.severeErrors.size() > 0) {
079: StringWriter sw = new StringWriter();
080: PrintWriter out = new PrintWriter(sw);
081: out.println("Severe errors in solr configuration.\n");
082: out
083: .println("Check your log files for more detailed infomation on what may be wrong.\n");
084: out
085: .println("If you want solr to continue after configuration errors, change: \n");
086: out
087: .println(" <abortOnConfigurationError>false</abortOnConfigurationError>\n");
088: out.println("in solrconfig.xml\n");
089:
090: for (Throwable t : SolrConfig.severeErrors) {
091: out
092: .println("-------------------------------------------------------------");
093: t.printStackTrace(out);
094: }
095: out.flush();
096:
097: // Servlet containers behave slightly differntly if you throw an exception durring
098: // initalization. Resin will display that error for every page, jetty prints it in
099: // the logs, but continues normally. (We will see a 404 rather then the real error)
100: // rather then leave the behavior undefined, lets cache the error and spit it out
101: // for every request.
102: abortErrorMessage = sw.toString();
103: //throw new ServletException( abortErrorMessage );
104: }
105:
106: log.info("SolrDispatchFilter.init() done");
107: }
108:
109: public void destroy() {
110: core.close();
111: }
112:
113: public void doFilter(ServletRequest request,
114: ServletResponse response, FilterChain chain)
115: throws IOException, ServletException {
116: if (abortErrorMessage != null) {
117: ((HttpServletResponse) response).sendError(500,
118: abortErrorMessage);
119: return;
120: }
121:
122: if (request instanceof HttpServletRequest) {
123: SolrQueryRequest solrReq = null;
124: HttpServletRequest req = (HttpServletRequest) request;
125: try {
126: String path = req.getServletPath();
127: if (req.getPathInfo() != null) {
128: // this lets you handle /update/commit when /update is a servlet
129: path += req.getPathInfo();
130: }
131: if (pathPrefix != null && path.startsWith(pathPrefix)) {
132: path = path.substring(pathPrefix.length());
133: }
134:
135: int idx = path.indexOf(':');
136: if (idx > 0) {
137: // save the portion after the ':' for a 'handler' path parameter
138: path = path.substring(0, idx);
139: }
140:
141: SolrRequestHandler handler = null;
142: if (path.length() > 1) { // don't match "" or "/" as valid path
143: handler = core.getRequestHandler(path);
144: }
145: if (handler == null && handleSelect) {
146: if ("/select".equals(path)
147: || "/select/".equals(path)) {
148: solrReq = parsers.parse(path, req);
149: String qt = solrReq.getParams().get(
150: SolrParams.QT);
151: if (qt != null && qt.startsWith("/")) {
152: throw new SolrException(
153: SolrException.ErrorCode.BAD_REQUEST,
154: "Invalid query type. Do not use /select to access: "
155: + qt);
156: }
157: handler = core.getRequestHandler(qt);
158: if (handler == null) {
159: throw new SolrException(
160: SolrException.ErrorCode.BAD_REQUEST,
161: "unknown handler: " + qt);
162: }
163: }
164: }
165: if (handler != null) {
166: if (solrReq == null) {
167: solrReq = parsers.parse(path, req);
168: }
169: SolrQueryResponse solrRsp = new SolrQueryResponse();
170: this .execute(req, handler, solrReq, solrRsp);
171: if (solrRsp.getException() != null) {
172: sendError((HttpServletResponse) response,
173: solrRsp.getException());
174: return;
175: }
176:
177: // Now write it out
178: QueryResponseWriter responseWriter = core
179: .getQueryResponseWriter(solrReq);
180: response.setContentType(responseWriter
181: .getContentType(solrReq, solrRsp));
182: PrintWriter out = response.getWriter();
183: responseWriter.write(out, solrReq, solrRsp);
184: return;
185: }
186: } catch (Throwable ex) {
187: sendError((HttpServletResponse) response, ex);
188: return;
189: } finally {
190: if (solrReq != null) {
191: solrReq.close();
192: }
193: }
194: }
195:
196: // Otherwise let the webapp handle the request
197: chain.doFilter(request, response);
198: }
199:
200: protected void execute(HttpServletRequest req,
201: SolrRequestHandler handler, SolrQueryRequest sreq,
202: SolrQueryResponse rsp) {
203: // a custom filter could add more stuff to the request before passing it on.
204: // for example: sreq.getContext().put( "HttpServletRequest", req );
205: core.execute(handler, sreq, rsp);
206: }
207:
208: protected void sendError(HttpServletResponse res, Throwable ex)
209: throws IOException {
210: int code = 500;
211: String trace = "";
212: if (ex instanceof SolrException) {
213: code = ((SolrException) ex).code();
214: }
215:
216: // For any regular code, don't include the stack trace
217: if (code == 500 || code < 100) {
218: StringWriter sw = new StringWriter();
219: ex.printStackTrace(new PrintWriter(sw));
220: trace = "\n\n" + sw.toString();
221:
222: SolrException.logOnce(log, null, ex);
223:
224: // non standard codes have undefined results with various servers
225: if (code < 100) {
226: log.warning("invalid return code: " + code);
227: code = 500;
228: }
229: }
230: res.sendError(code, ex.getMessage() + trace);
231: }
232:
233: //---------------------------------------------------------------------
234: //---------------------------------------------------------------------
235:
236: /**
237: * Should the filter handle /select even if it is not mapped in solrconfig.xml
238: *
239: * This will use consistent error handling for /select?qt=xxx and /update/xml
240: *
241: */
242: public boolean isHandleSelect() {
243: return handleSelect;
244: }
245:
246: public void setHandleSelect(boolean handleSelect) {
247: this .handleSelect = handleSelect;
248: }
249:
250: /**
251: * set the prefix for all paths. This is useful if you want to apply the
252: * filter to something other then *.
253: *
254: * For example, if web.xml specifies:
255: *
256: * <filter-mapping>
257: * <filter-name>SolrRequestFilter</filter-name>
258: * <url-pattern>/xxx/*</url-pattern>
259: * </filter-mapping>
260: *
261: * Make sure to set the PathPrefix to "/xxx" either with this function
262: * or in web.xml
263: *
264: * <init-param>
265: * <param-name>path-prefix</param-name>
266: * <param-value>/xxx</param-value>
267: * </init-param>
268: *
269: */
270: public void setPathPrefix(String pathPrefix) {
271: this .pathPrefix = pathPrefix;
272: }
273:
274: public String getPathPrefix() {
275: return pathPrefix;
276: }
277: }
|