~slub.team/goobi-indexserver/3.x

« back to all changes in this revision

Viewing changes to solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java

  • Committer: Sebastian Meyer
  • Date: 2012-08-03 09:12:40 UTC
  • Revision ID: sebastian.meyer@slub-dresden.de-20120803091240-x6861b0vabq1xror
Remove Lucene and Solr source code and add patches instead
Fix Bug #985487: Auto-suggestion for the search interface

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 
 * contributor license agreements.  See the NOTICE file distributed with
4
 
 * this work for additional information regarding copyright ownership.
5
 
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 
 * (the "License"); you may not use this file except in compliance with
7
 
 * the License.  You may obtain a copy of the License at
8
 
 *
9
 
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 
 *
11
 
 * Unless required by applicable law or agreed to in writing, software
12
 
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 
 * See the License for the specific language governing permissions and
15
 
 * limitations under the License.
16
 
 */
17
 
 
18
 
package org.apache.solr.servlet;
19
 
 
20
 
import java.io.IOException;
21
 
import java.io.Writer;
22
 
import java.io.PrintWriter;
23
 
import java.io.StringWriter;
24
 
import java.io.OutputStreamWriter;
25
 
import java.io.ByteArrayInputStream;
26
 
import java.nio.charset.Charset;
27
 
import java.util.Map;
28
 
import java.util.WeakHashMap;
29
 
import org.slf4j.Logger;
30
 
import org.slf4j.LoggerFactory;
31
 
import org.xml.sax.InputSource;
32
 
 
33
 
import javax.servlet.Filter;
34
 
import javax.servlet.FilterChain;
35
 
import javax.servlet.FilterConfig;
36
 
import javax.servlet.ServletException;
37
 
import javax.servlet.ServletRequest;
38
 
import javax.servlet.ServletResponse;
39
 
import javax.servlet.http.HttpServletRequest;
40
 
import javax.servlet.http.HttpServletResponse;
41
 
 
42
 
import org.apache.solr.common.SolrException;
43
 
import org.apache.solr.common.util.NamedList;
44
 
import org.apache.solr.common.util.SimpleOrderedMap;
45
 
import org.apache.solr.common.params.CommonParams;
46
 
import org.apache.solr.common.util.FastWriter;
47
 
import org.apache.solr.common.util.ContentStreamBase;
48
 
import org.apache.solr.core.*;
49
 
import org.apache.solr.request.*;
50
 
import org.apache.solr.response.BinaryQueryResponseWriter;
51
 
import org.apache.solr.response.QueryResponseWriter;
52
 
import org.apache.solr.response.SolrQueryResponse;
53
 
import org.apache.solr.servlet.cache.HttpCacheHeaderUtil;
54
 
import org.apache.solr.servlet.cache.Method;
55
 
 
56
 
/**
57
 
 * This filter looks at the incoming URL maps them to handlers defined in solrconfig.xml
58
 
 *
59
 
 * @since solr 1.2
60
 
 */
61
 
public class SolrDispatchFilter implements Filter
62
 
{
63
 
  final Logger log = LoggerFactory.getLogger(SolrDispatchFilter.class);
64
 
 
65
 
  protected CoreContainer cores;
66
 
  protected String pathPrefix = null; // strip this from the beginning of a path
67
 
  protected String abortErrorMessage = null;
68
 
  protected String solrConfigFilename = null;
69
 
  protected final Map<SolrConfig, SolrRequestParsers> parsers = new WeakHashMap<SolrConfig, SolrRequestParsers>();
70
 
  protected final SolrRequestParsers adminRequestParser;
71
 
  
72
 
  private static final Charset UTF8 = Charset.forName("UTF-8");
73
 
 
74
 
  public SolrDispatchFilter() {
75
 
    try {
76
 
      adminRequestParser = new SolrRequestParsers(new Config(null,"solr",new InputSource(new ByteArrayInputStream("<root/>".getBytes("UTF-8"))),"") );
77
 
    } catch (Exception e) {
78
 
      //unlikely
79
 
      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,e);
80
 
    }
81
 
  }
82
 
 
83
 
  public void init(FilterConfig config) throws ServletException
84
 
  {
85
 
    log.info("SolrDispatchFilter.init()");
86
 
 
87
 
    boolean abortOnConfigurationError = true;
88
 
    CoreContainer.Initializer init = createInitializer();
89
 
    try {
90
 
      // web.xml configuration
91
 
      this.pathPrefix = config.getInitParameter( "path-prefix" );
92
 
      init.setSolrConfigFilename(config.getInitParameter("solrconfig-filename"));
93
 
 
94
 
      this.cores = init.initialize();
95
 
      abortOnConfigurationError = init.isAbortOnConfigurationError();
96
 
      log.info("user.dir=" + System.getProperty("user.dir"));
97
 
    }
98
 
    catch( Throwable t ) {
99
 
      // catch this so our filter still works
100
 
      log.error( "Could not start Solr. Check solr/home property", t);
101
 
      SolrConfig.severeErrors.add( t );
102
 
      SolrCore.log( t );
103
 
    }
104
 
 
105
 
    // Optionally abort if we found a sever error
106
 
    if( abortOnConfigurationError && SolrConfig.severeErrors.size() > 0 ) {
107
 
      StringWriter sw = new StringWriter();
108
 
      PrintWriter out = new PrintWriter( sw );
109
 
      out.println( "Severe errors in solr configuration.\n" );
110
 
      out.println( "Check your log files for more detailed information on what may be wrong.\n" );
111
 
      out.println( "If you want solr to continue after configuration errors, change: \n");
112
 
      out.println( " <abortOnConfigurationError>false</abortOnConfigurationError>\n" );
113
 
      out.println( "in "+init.getSolrConfigFilename()+"\n" );
114
 
 
115
 
      for( Throwable t : SolrConfig.severeErrors ) {
116
 
        out.println( "-------------------------------------------------------------" );
117
 
        t.printStackTrace( out );
118
 
      }
119
 
      out.flush();
120
 
 
121
 
      // Servlet containers behave slightly differently if you throw an exception during 
122
 
      // initialization.  Resin will display that error for every page, jetty prints it in
123
 
      // the logs, but continues normally.  (We will see a 404 rather then the real error)
124
 
      // rather then leave the behavior undefined, lets cache the error and spit it out 
125
 
      // for every request.
126
 
      abortErrorMessage = sw.toString();
127
 
      //throw new ServletException( abortErrorMessage );
128
 
    }
129
 
 
130
 
    log.info("SolrDispatchFilter.init() done");
131
 
  }
132
 
 
133
 
  /** Method to override to change how CoreContainer initialization is performed. */
134
 
  protected CoreContainer.Initializer createInitializer() {
135
 
    return new CoreContainer.Initializer();
136
 
  }
137
 
  
138
 
  public void destroy() {
139
 
    if (cores != null) {
140
 
      cores.shutdown();
141
 
      cores = null;
142
 
    }    
143
 
  }
144
 
 
145
 
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
146
 
    if( abortErrorMessage != null ) {
147
 
      ((HttpServletResponse)response).sendError( 500, abortErrorMessage );
148
 
      return;
149
 
    }
150
 
 
151
 
    if( request instanceof HttpServletRequest) {
152
 
      HttpServletRequest req = (HttpServletRequest)request;
153
 
      HttpServletResponse resp = (HttpServletResponse)response;
154
 
      SolrRequestHandler handler = null;
155
 
      SolrQueryRequest solrReq = null;
156
 
      SolrCore core = null;
157
 
      String corename = "";
158
 
      try {
159
 
        // put the core container in request attribute
160
 
        req.setAttribute("org.apache.solr.CoreContainer", cores);
161
 
        String path = req.getServletPath();
162
 
        if( req.getPathInfo() != null ) {
163
 
          // this lets you handle /update/commit when /update is a servlet
164
 
          path += req.getPathInfo();
165
 
        }
166
 
        if( pathPrefix != null && path.startsWith( pathPrefix ) ) {
167
 
          path = path.substring( pathPrefix.length() );
168
 
        }
169
 
        // check for management path
170
 
        String alternate = cores.getManagementPath();
171
 
        if (alternate != null && path.startsWith(alternate)) {
172
 
          path = path.substring(0, alternate.length());
173
 
        }
174
 
        // unused feature ?
175
 
        int idx = path.indexOf( ':' );
176
 
        if( idx > 0 ) {
177
 
          // save the portion after the ':' for a 'handler' path parameter
178
 
          path = path.substring( 0, idx );
179
 
        }
180
 
 
181
 
        // Check for the core admin page
182
 
        if( path.equals( cores.getAdminPath() ) ) {
183
 
          handler = cores.getMultiCoreHandler();
184
 
          solrReq =  adminRequestParser.parse(null,path, req);
185
 
          handleAdminRequest(req, response, handler, solrReq);
186
 
          return;
187
 
        }
188
 
        else {
189
 
          //otherwise, we should find a core from the path
190
 
          idx = path.indexOf( "/", 1 );
191
 
          if( idx > 1 ) {
192
 
            // try to get the corename as a request parameter first
193
 
            corename = path.substring( 1, idx );
194
 
            core = cores.getCore(corename);
195
 
            if (core != null) {
196
 
              path = path.substring( idx );
197
 
            }
198
 
          }
199
 
          if (core == null) {
200
 
            corename = "";
201
 
            core = cores.getCore("");
202
 
          }
203
 
        }
204
 
 
205
 
        // With a valid core...
206
 
        if( core != null ) {
207
 
          final SolrConfig config = core.getSolrConfig();
208
 
          // get or create/cache the parser for the core
209
 
          SolrRequestParsers parser = null;
210
 
          parser = parsers.get(config);
211
 
          if( parser == null ) {
212
 
            parser = new SolrRequestParsers(config);
213
 
            parsers.put(config, parser );
214
 
          }
215
 
 
216
 
          // Determine the handler from the url path if not set
217
 
          // (we might already have selected the cores handler)
218
 
          if( handler == null && path.length() > 1 ) { // don't match "" or "/" as valid path
219
 
            handler = core.getRequestHandler( path );
220
 
            // no handler yet but allowed to handle select; let's check
221
 
            if( handler == null && parser.isHandleSelect() ) {
222
 
              if( "/select".equals( path ) || "/select/".equals( path ) ) {
223
 
                solrReq = parser.parse( core, path, req );
224
 
                String qt = solrReq.getParams().get( CommonParams.QT );
225
 
                handler = core.getRequestHandler( qt );
226
 
                if( handler == null ) {
227
 
                  throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "unknown handler: "+qt);
228
 
                }
229
 
              }
230
 
            }
231
 
          }
232
 
 
233
 
          // With a valid handler and a valid core...
234
 
          if( handler != null ) {
235
 
            // if not a /select, create the request
236
 
            if( solrReq == null ) {
237
 
              solrReq = parser.parse( core, path, req );
238
 
            }
239
 
 
240
 
            final Method reqMethod = Method.getMethod(req.getMethod());
241
 
            HttpCacheHeaderUtil.setCacheControlHeader(config, resp, reqMethod);
242
 
            // unless we have been explicitly told not to, do cache validation
243
 
            // if we fail cache validation, execute the query
244
 
            if (config.getHttpCachingConfig().isNever304() ||
245
 
                !HttpCacheHeaderUtil.doCacheHeaderValidation(solrReq, req, reqMethod, resp)) {
246
 
                SolrQueryResponse solrRsp = new SolrQueryResponse();
247
 
                /* even for HEAD requests, we need to execute the handler to
248
 
                 * ensure we don't get an error (and to make sure the correct
249
 
                 * QueryResponseWriter is selected and we get the correct
250
 
                 * Content-Type)
251
 
                 */
252
 
                this.execute( req, handler, solrReq, solrRsp );
253
 
                HttpCacheHeaderUtil.checkHttpCachingVeto(solrRsp, resp, reqMethod);
254
 
              // add info to http headers
255
 
              //TODO: See SOLR-232 and SOLR-267.  
256
 
                /*try {
257
 
                  NamedList solrRspHeader = solrRsp.getResponseHeader();
258
 
                 for (int i=0; i<solrRspHeader.size(); i++) {
259
 
                   ((javax.servlet.http.HttpServletResponse) response).addHeader(("Solr-" + solrRspHeader.getName(i)), String.valueOf(solrRspHeader.getVal(i)));
260
 
                 }
261
 
                } catch (ClassCastException cce) {
262
 
                  log.log(Level.WARNING, "exception adding response header log information", cce);
263
 
                }*/
264
 
               QueryResponseWriter responseWriter = core.getQueryResponseWriter(solrReq);
265
 
              writeResponse(solrRsp, response, responseWriter, solrReq, reqMethod);
266
 
            }
267
 
            return; // we are done with a valid handler
268
 
          }
269
 
          // otherwise (we have a core), let's ensure the core is in the SolrCore request attribute so
270
 
          // a servlet/jsp can retrieve it
271
 
          else {
272
 
            req.setAttribute("org.apache.solr.SolrCore", core);
273
 
            // Modify the request so each core gets its own /admin
274
 
            if( path.startsWith( "/admin" ) ) {
275
 
              req.getRequestDispatcher( pathPrefix == null ? path : pathPrefix + path ).forward( request, response );
276
 
              return;
277
 
            }
278
 
          }
279
 
        }
280
 
        log.debug("no handler or core retrieved for " + path + ", follow through...");
281
 
      } 
282
 
      catch (Throwable ex) {
283
 
        sendError( (HttpServletResponse)response, ex );
284
 
        return;
285
 
      } 
286
 
      finally {
287
 
        if( solrReq != null ) {
288
 
          solrReq.close();
289
 
        }
290
 
        if (core != null) {
291
 
          core.close();
292
 
        }
293
 
      }
294
 
    }
295
 
 
296
 
    // Otherwise let the webapp handle the request
297
 
    chain.doFilter(request, response);
298
 
  }
299
 
 
300
 
  private void handleAdminRequest(HttpServletRequest req, ServletResponse response, SolrRequestHandler handler,
301
 
                                  SolrQueryRequest solrReq) throws IOException {
302
 
    SolrQueryResponse solrResp = new SolrQueryResponse();
303
 
    final NamedList<Object> responseHeader = new SimpleOrderedMap<Object>();
304
 
    solrResp.add("responseHeader", responseHeader);
305
 
    NamedList toLog = solrResp.getToLog();
306
 
    toLog.add("webapp", req.getContextPath());
307
 
    toLog.add("path", solrReq.getContext().get("path"));
308
 
    toLog.add("params", "{" + solrReq.getParamString() + "}");
309
 
    handler.handleRequest(solrReq, solrResp);
310
 
    SolrCore.setResponseHeaderValues(handler, solrReq, solrResp);
311
 
    StringBuilder sb = new StringBuilder();
312
 
    for (int i = 0; i < toLog.size(); i++) {
313
 
      String name = toLog.getName(i);
314
 
      Object val = toLog.getVal(i);
315
 
      sb.append(name).append("=").append(val).append(" ");
316
 
    }
317
 
    QueryResponseWriter respWriter = SolrCore.DEFAULT_RESPONSE_WRITERS.get(solrReq.getParams().get(CommonParams.WT));
318
 
    if (respWriter == null) respWriter = SolrCore.DEFAULT_RESPONSE_WRITERS.get("standard");
319
 
    writeResponse(solrResp, response, respWriter, solrReq, Method.getMethod(req.getMethod()));
320
 
  }
321
 
 
322
 
  private void writeResponse(SolrQueryResponse solrRsp, ServletResponse response,
323
 
                             QueryResponseWriter responseWriter, SolrQueryRequest solrReq, Method reqMethod)
324
 
          throws IOException {
325
 
    if (solrRsp.getException() != null) {
326
 
      sendError((HttpServletResponse) response, solrRsp.getException());
327
 
    } else {
328
 
      // Now write it out
329
 
      final String ct = responseWriter.getContentType(solrReq, solrRsp);
330
 
      // don't call setContentType on null
331
 
      if (null != ct) response.setContentType(ct); 
332
 
 
333
 
      if (Method.HEAD != reqMethod) {
334
 
        if (responseWriter instanceof BinaryQueryResponseWriter) {
335
 
          BinaryQueryResponseWriter binWriter = (BinaryQueryResponseWriter) responseWriter;
336
 
          binWriter.write(response.getOutputStream(), solrReq, solrRsp);
337
 
        } else {
338
 
          String charset = ContentStreamBase.getCharsetFromContentType(ct);
339
 
          Writer out = (charset == null || charset.equalsIgnoreCase("UTF-8"))
340
 
            ? new OutputStreamWriter(response.getOutputStream(), UTF8)
341
 
            : new OutputStreamWriter(response.getOutputStream(), charset);
342
 
          out = new FastWriter(out);
343
 
          responseWriter.write(out, solrReq, solrRsp);
344
 
          out.flush();
345
 
        }
346
 
      }
347
 
      //else http HEAD request, nothing to write out, waited this long just to get ContentType
348
 
    }
349
 
  }
350
 
 
351
 
  protected void execute( HttpServletRequest req, SolrRequestHandler handler, SolrQueryRequest sreq, SolrQueryResponse rsp) {
352
 
    // a custom filter could add more stuff to the request before passing it on.
353
 
    // for example: sreq.getContext().put( "HttpServletRequest", req );
354
 
    // used for logging query stats in SolrCore.execute()
355
 
    sreq.getContext().put( "webapp", req.getContextPath() );
356
 
    sreq.getCore().execute( handler, sreq, rsp );
357
 
  }
358
 
 
359
 
  protected void sendError(HttpServletResponse res, Throwable ex) throws IOException {
360
 
    int code=500;
361
 
    String trace = "";
362
 
    if( ex instanceof SolrException ) {
363
 
      code = ((SolrException)ex).code();
364
 
    }
365
 
 
366
 
    // For any regular code, don't include the stack trace
367
 
    if( code == 500 || code < 100 ) {
368
 
      StringWriter sw = new StringWriter();
369
 
      ex.printStackTrace(new PrintWriter(sw));
370
 
      trace = "\n\n"+sw.toString();
371
 
 
372
 
      SolrException.logOnce(log,null,ex );
373
 
 
374
 
      // non standard codes have undefined results with various servers
375
 
      if( code < 100 ) {
376
 
        log.warn( "invalid return code: "+code );
377
 
        code = 500;
378
 
      }
379
 
    }
380
 
    res.sendError( code, ex.getMessage() + trace );
381
 
  }
382
 
 
383
 
  //---------------------------------------------------------------------
384
 
  //---------------------------------------------------------------------
385
 
 
386
 
  /**
387
 
   * Set the prefix for all paths.  This is useful if you want to apply the
388
 
   * filter to something other then /*, perhaps because you are merging this
389
 
   * filter into a larger web application.
390
 
   *
391
 
   * For example, if web.xml specifies:
392
 
   *
393
 
   * <filter-mapping>
394
 
   *  <filter-name>SolrRequestFilter</filter-name>
395
 
   *  <url-pattern>/xxx/*</url-pattern>
396
 
   * </filter-mapping>
397
 
   *
398
 
   * Make sure to set the PathPrefix to "/xxx" either with this function
399
 
   * or in web.xml.
400
 
   *
401
 
   * <init-param>
402
 
   *  <param-name>path-prefix</param-name>
403
 
   *  <param-value>/xxx</param-value>
404
 
   * </init-param>
405
 
   *
406
 
   */
407
 
  public void setPathPrefix(String pathPrefix) {
408
 
    this.pathPrefix = pathPrefix;
409
 
  }
410
 
 
411
 
  public String getPathPrefix() {
412
 
    return pathPrefix;
413
 
  }
414
 
}