Solr 6.0 學習(九) SolrDispatchFilter源碼解析之HttpSolrCall及擴展

這段源碼特別重要,如果想在solr應用之上開發自己的代碼,這段源碼很值得研究。

HttpSolrCall

package org.apache.solr.servlet;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Header;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.InputStreamEntity;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.Aliases;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Replica.State;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.ZkNodeProps;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CollectionParams.CollectionAction;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrConfig.HttpCachingConfig;
import org.apache.solr.core.SolrCore;
import org.apache.solr.handler.ContentStreamHandlerBase;
import org.apache.solr.logging.MDCLoggingContext;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrQueryRequestBase;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.response.QueryResponseWriter;
import org.apache.solr.response.QueryResponseWriterUtil;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.security.AuthenticationPlugin;
import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.security.AuthorizationContext.CollectionRequest;
import org.apache.solr.security.AuthorizationContext.RequestType;
import org.apache.solr.security.AuthorizationPlugin;
import org.apache.solr.security.AuthorizationResponse;
import org.apache.solr.security.PKIAuthenticationPlugin;
import org.apache.solr.servlet.cache.HttpCacheHeaderUtil;
import org.apache.solr.servlet.cache.Method;
import org.apache.solr.util.RTimerTree;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpSolrCall
{
  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
  static final Random random;
  protected final SolrDispatchFilter solrDispatchFilter;
  protected final CoreContainer cores;
  protected final HttpServletRequest req;
  protected final HttpServletResponse response;
  protected final boolean retry;
  protected SolrCore core = null;
  protected SolrQueryRequest solrReq = null;
  protected SolrRequestHandler handler = null;
  protected final SolrParams queryParams;
  protected String path;
  protected SolrDispatchFilter.Action action;
  protected String coreUrl;
  protected SolrConfig config;
  protected Map<String, Integer> invalidStates;
  protected AuthorizationContext.RequestType requestType;
  protected List<String> collectionsList;
  static final String CONNECTION_HEADER = "Connection";
  static final String TRANSFER_ENCODING_HEADER = "Transfer-Encoding";
  static final String CONTENT_LENGTH_HEADER = "Content-Length";

  public AuthorizationContext.RequestType getRequestType()
  {
    return this.requestType;
  }

  public List<String> getCollectionsList()
  {
    return this.collectionsList;
  }

  public HttpSolrCall(SolrDispatchFilter solrDispatchFilter, CoreContainer cores, HttpServletRequest request, HttpServletResponse response, boolean retry)
  {
    this.solrDispatchFilter = solrDispatchFilter;
    this.cores = cores;
    this.req = request;
    this.response = response;
    this.retry = retry;
    this.requestType = AuthorizationContext.RequestType.UNKNOWN;
    //獲取查詢參數
    this.queryParams = SolrRequestParsers.parseQueryString(this.req.getQueryString());
  }

  public String getPath() {
    return this.path;
  }

  public HttpServletRequest getReq()
  {
    return this.req;
  }

  public SolrCore getCore() {
    return this.core;
  }

  public SolrParams getQueryParams() {
    return this.queryParams;
  }

  private void init() throws Exception
  {
    Aliases aliases = null;
    String corename = "";
    String origCorename = null;

    this.req.setAttribute("org.apache.solr.RequestTimer", new RTimerTree());
    //將coreContainer放入web容器,放入之後當前請求整個web容器共享
    //那麼延伸來講,當前請求的web組件共享這個容器,例如在某個servlet中
    //我們可以通過CoreContainer coreContainer=(CoreContainer)customerServletRequest.getAttribute("org.apache.solr.CoreContainer");
    this.req.setAttribute("org.apache.solr.CoreContainer", this.cores);
    //獲取請求的servlet路徑
    //例如:http://localhost:8080/solr/my_solr/select?q=*%3A*&wt=json&indent=true&qt=queryrefine
    //路徑爲:my_solr/select
    this.path = this.req.getServletPath();
    if (this.req.getPathInfo() != null)
    {
      this.path = new StringBuilder().append(this.path).append(this.req.getPathInfo()).toString();
    }

    String alternate = this.cores.getManagementPath();
    if ((alternate != null) && (this.path.startsWith(alternate))) {
      this.path = this.path.substring(0, alternate.length());
    }

    int idx = this.path.indexOf(58);
    if (idx > 0)
    {
      this.path = this.path.substring(0, idx);
    }

    boolean usingAliases = false;
    //通過請求的servlet獲取對於的handler處理器
    //例如:http://localhost:8080/solr/my_solr/select?q=*%3A*&wt=json&indent=true&qt=queryrefine
    //通過"select"獲取對應handler
    //對應我們看到solrconfig.xml中的配置
    //<requestHandler name="/select" class="solr.SearchHandler">
    //實際開發過程中:this.handler = this.cores.getRequestHandler("/coustomerHandler");
    //對應solrconfig.xml中的配置
    //<requestHandler name="/coustomerHandler" class="com.upxiaofeng.solr.coustomerHandler">
    this.handler = this.cores.getRequestHandler(this.path);

    if (this.handler != null) {
      this.solrReq = SolrRequestParsers.DEFAULT.parse(null, this.path, this.req);
      this.solrReq.getContext().put(CoreContainer.class.getName(), this.cores);
      this.requestType = AuthorizationContext.RequestType.ADMIN;
      this.action = SolrDispatchFilter.Action.ADMIN;
      return;
    }

    idx = this.path.indexOf("/", 1);
    if (idx > 1)
    {
      corename = this.path.substring(1, idx);

      if (this.cores.isZooKeeperAware()) {
        origCorename = corename;
        ZkStateReader reader = this.cores.getZkController().getZkStateReader();
        aliases = reader.getAliases();
        if ((aliases != null) && (aliases.collectionAliasSize() > 0)) {
          usingAliases = true;
          String alias = aliases.getCollectionAlias(corename);
          if (alias != null) {
            this.collectionsList = StrUtils.splitSmart(alias, ",", true);
            corename = (String)this.collectionsList.get(0);
          }
        }
      }
      //通過corename獲取對應的solr core,例如我們叫my_solr
      //實際上就是我們core.properties配置的name屬性
      //實際開發中就是通過獲取我們想要的core
      //CoreContainer coreContainer=(CoreContainer)HttpServletRequest.getAttribute("org.apache.solr.CoreContainer");
      //Solrcore core = coreContainer.getCore("my_solr");
      this.core = this.cores.getCore(corename);
      if (this.core != null) {
        this.path = this.path.substring(idx); } else {
        if (this.cores.isCoreLoading(corename)) {
          throw new SolrException(SolrException.ErrorCode.SERVICE_UNAVAILABLE, "SolrCore is loading");
        }

        this.core = this.cores.getCore(corename);
        if (this.core != null) {
          this.path = this.path.substring(idx);
        }
      }
    }
    if ((this.core == null) && 
      (!this.cores.isZooKeeperAware())) {
      this.core = this.cores.getCore("");
    }

    if ((this.core == null) && (this.cores.isZooKeeperAware()))
    {
      this.core = getCoreByCollection(corename);
      if (this.core != null)
      {
        this.path = this.path.substring(idx);
        if (this.collectionsList == null)
          this.collectionsList = new ArrayList();
        this.collectionsList.add(corename);
      }

      extractRemotePath(corename, origCorename, idx);
      if (this.action != null) return;

    }
    //使用solr core實例
    if (this.core != null) {
      MDCLoggingContext.setCore(this.core);
      //獲取SolrConfig對象
      this.config = this.core.getSolrConfig();
      //獲取SolrRequestParsers解析對象
      SolrRequestParsers parser = this.config.getRequestParsers();
      //獲取對應handler
      extractHandlerFromURLPath(parser);
      if (this.action != null) return;

      if (this.handler != null)
      {
        if (this.solrReq == null) {
            //得到查詢請求對象
            //實際開發中可以根據自己的需求來處理獲取對應的查詢對象
            //如:this.solrReq = parser.parse(this.core, "select", this.req);
          this.solrReq = parser.parse(this.core, this.path, this.req);
        }

        if (usingAliases) {
          processAliases(aliases, this.collectionsList);
        }

        this.action = SolrDispatchFilter.Action.PROCESS;
        return;
      }
    }
    log.debug(new StringBuilder().append("no handler or core retrieved for ").append(this.path).append(", follow through...").toString());

    this.action = SolrDispatchFilter.Action.PASSTHROUGH;
  }
  /**
   * 獲取對應handler處理器
   * @param parser
   * @throws Exception
   */
  private void extractHandlerFromURLPath(SolrRequestParsers parser)
    throws Exception
  {
    if ((this.handler == null) && (this.path.length() > 1)) {
      this.handler = this.core.getRequestHandler(this.path);

      if (this.handler == null)
      {
        if ((this.path.equals("/schema")) || (this.path.startsWith("/schema/"))) {
          this.solrReq = parser.parse(this.core, this.path, this.req);
          SolrRequestInfo.setRequestInfo(new SolrRequestInfo(this.solrReq, new SolrQueryResponse()));
          if (this.path.equals(this.req.getServletPath()))
          {
            this.action = SolrDispatchFilter.Action.PASSTHROUGH;
            return;
          }

          this.action = SolrDispatchFilter.Action.FORWARD;
          return;
        }

      }
      //沒有處理器但是允許處理查詢
      if ((this.handler == null) && (parser.isHandleSelect()) && (
        ("/select".equals(this.path)) || ("/select/".equals(this.path)))) {
        this.solrReq = parser.parse(this.core, this.path, this.req);
        this.invalidStates = checkStateIsValid(this.solrReq.getParams().get("_stateVer_"));
        //通過url中傳入的qt參數得到查詢對應的handler
        String qt = this.solrReq.getParams().get("qt");
        this.handler = this.core.getRequestHandler(qt);
        if (this.handler == null) {
          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, new StringBuilder().append("unknown handler: ").append(qt).toString());
        }
        if ((qt != null) && (qt.startsWith("/")) && ((this.handler instanceof ContentStreamHandlerBase)))
        {
          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, new StringBuilder().append("Invalid Request Handler ('qt').  Do not use /select to access: ").append(qt).toString());
        }
      }
    }
  }

  private void extractRemotePath(String corename, String origCorename, int idx) throws UnsupportedEncodingException, KeeperException, InterruptedException
  {
    if ((this.core == null) && (idx > 0)) {
      this.coreUrl = getRemotCoreUrl(corename, origCorename);

      this.invalidStates = checkStateIsValid(this.queryParams.get("_stateVer_"));
      if (this.coreUrl != null)
      {
        if (this.queryParams
          .get("update.distrib") == null)
        {
          this.path = this.path.substring(idx);
          if (this.invalidStates != null)
          {
            throw new SolrException(SolrException.ErrorCode.INVALID_STATE, new String(Utils.toJSON(this.invalidStates), org.apache.lucene.util.IOUtils.UTF_8));
          }
          this.action = SolrDispatchFilter.Action.REMOTEQUERY; return;
        }
      }
      if (!this.retry)
      {
        ZkStateReader reader = this.cores.getZkController()
          .getZkStateReader();
        reader.updateAliases();
        this.action = SolrDispatchFilter.Action.RETRY;
      }
    }
  }

  public SolrDispatchFilter.Action call()
    throws IOException
  {
    MDCLoggingContext.reset();
    MDCLoggingContext.setNode(this.cores);

    if (this.cores == null) {
      sendError(503, "Server is shutting down or failed to initialize");
      return SolrDispatchFilter.Action.RETURN;
    }

    if (this.solrDispatchFilter.abortErrorMessage != null) {
      sendError(500, this.solrDispatchFilter.abortErrorMessage);
      return SolrDispatchFilter.Action.RETURN;
    }
    try
    {
      init();
      AuthorizationResponse authResponse;
      if ((this.cores.getAuthorizationPlugin() != null) && (shouldAuthorize())) {
        AuthorizationContext context = getAuthCtx();
        log.debug("AuthorizationContext : {}", context);
        authResponse = this.cores.getAuthorizationPlugin().authorize(context);
        Map headers;
        if (authResponse.statusCode == AuthorizationResponse.PROMPT.statusCode) {
          headers = (Map)getReq().getAttribute(AuthenticationPlugin.class.getName());
          if (headers != null)
          {
            Map.Entry e;
            for (Iterator localIterator1 = headers.entrySet().iterator(); localIterator1.hasNext(); this.response.setHeader((String)e.getKey(), (String)e.getValue())) e = (Map.Entry)localIterator1.next();
          }
          log.debug(new StringBuilder().append("USER_REQUIRED ").append(this.req.getHeader("Authorization")).append(" ").append(this.req.getUserPrincipal()).toString());
        }
        if ((authResponse.statusCode != 202) && (authResponse.statusCode != 200)) {
          log.info("USER_REQUIRED auth header {} context : {} ", this.req.getHeader("Authorization"), context);
          sendError(authResponse.statusCode, new StringBuilder().append("Unauthorized request, Response code: ").append(authResponse.statusCode).toString());

          return SolrDispatchFilter.Action.RETURN;
        }
      }

      HttpServletResponse resp = this.response;
      switch (3.$SwitchMap$org$apache$solr$servlet$SolrDispatchFilter$Action[this.action.ordinal()]) {
      case 1:
        handleAdminRequest();
        return SolrDispatchFilter.Action.RETURN;
      case 2:
        remoteQuery(new StringBuilder().append(this.coreUrl).append(this.path).toString(), resp);
        return SolrDispatchFilter.Action.RETURN;
      case 3:
          //做對應的http緩存
        Method reqMethod = Method.getMethod(this.req.getMethod());
        HttpCacheHeaderUtil.setCacheControlHeader(this.config, resp, reqMethod);
        //如果沒有緩存,即狀態不爲304
        if ((this.config.getHttpCachingConfig().isNever304()) || 
          (!HttpCacheHeaderUtil.doCacheHeaderValidation(this.solrReq, this.req, reqMethod, resp)))
        {
          //實例化SolrQueryResponse
          solrRsp = new SolrQueryResponse();
          //設置參數
          SolrRequestInfo.setRequestInfo(new SolrRequestInfo(this.solrReq, solrRsp));
          //執行查詢
          execute(solrRsp);
          HttpCacheHeaderUtil.checkHttpCachingVeto(solrRsp, resp, reqMethod);
          Iterator headers = solrRsp.httpHeaders();
          while (headers.hasNext()) {
            Map.Entry entry = (Map.Entry)headers.next();
            resp.addHeader((String)entry.getKey(), (String)entry.getValue());
          }
          //獲取當前請求響應的writer
          //這個方法有重載
          //public final QueryResponseWriter getQueryResponseWriter(String writerName)
          //實際開發過程中,可以自己去實現自己的writer。對應solrconfig.xml中的配置
          //<queryResponseWriter name="upxiaofeng" class="com.upxiaofeng.solr.VanclResponseWriter">    
          //<str name="content-type">text/plain; charset=UTF-8</str>
          //</queryResponseWriter>
          //如:QueryResponseWriter responseWriter = this.core.getQueryResponseWriter("upxiaofeng");
          //public final QueryResponseWriter getQueryResponseWriter(SolrQueryRequest request)
          QueryResponseWriter responseWriter = this.core.getQueryResponseWriter(this.solrReq);
          if (this.invalidStates != null) this.solrReq.getContext().put("_stateVer_", this.invalidStates);
          writeResponse(solrRsp, responseWriter, reqMethod);
        }
        return SolrDispatchFilter.Action.RETURN;
      }return this.action;
    }
    catch (Throwable ex)
    {
      SolrQueryResponse solrRsp;
      sendError(ex);

      Throwable t = ex;
      while (t != null) {
        if ((t instanceof Error)) {
          if (t != ex) {
            log.error("An Error was wrapped in another exception - please report complete stacktrace on SOLR-6161", ex);
          }
          throw ((Error)t);
        }
        t = t.getCause();
      }
      return SolrDispatchFilter.Action.RETURN;
    } finally {
      MDCLoggingContext.clear();
    }
  }

  private boolean shouldAuthorize()
  {
    if ("/admin/info/key".equals(this.path)) return false;

    if ((this.cores.getPkiAuthenticationPlugin() != null) && (this.req.getUserPrincipal() != null)) {
      boolean b = this.cores.getPkiAuthenticationPlugin().needsAuthorization(this.req);
      log.debug("PkiAuthenticationPlugin says authorization required : {} ", Boolean.valueOf(b));
      return b;
    }
    return true;
  }

  void destroy() {
    try {
      if (this.solrReq != null) {
        log.debug("Closing out SolrRequest: {}", this.solrReq);
        this.solrReq.close();
      }
      AuthenticationPlugin authcPlugin;
      try {
        if (this.core != null) this.core.close();

        SolrRequestInfo.clearRequestInfo(); } finally { SolrRequestInfo.clearRequestInfo(); }


      if (authcPlugin != null) authcPlugin.closeRequest();
    }
    finally
    {
      AuthenticationPlugin authcPlugin;
      try
      {
        if (this.core != null) this.core.close();

        SolrRequestInfo.clearRequestInfo(); } finally { SolrRequestInfo.clearRequestInfo(); }


      if (authcPlugin != null) authcPlugin.closeRequest(); 
    }
  }

  private void remoteQuery(String coreUrl, HttpServletResponse resp) throws IOException {
    HttpRequestBase method = null;
    HttpEntity httpEntity = null;
    try {
      String urlstr = new StringBuilder().append(coreUrl).append(this.queryParams.toQueryString()).toString();

      boolean isPostOrPutRequest = ("POST".equals(this.req.getMethod())) || ("PUT".equals(this.req.getMethod()));
      if ("GET".equals(this.req.getMethod())) {
        method = new HttpGet(urlstr);
      } else if ("HEAD".equals(this.req.getMethod())) {
        method = new HttpHead(urlstr);
      } else if (isPostOrPutRequest)
      {
        HttpEntityEnclosingRequestBase entityRequest = "POST"
          .equals(this.req
          .getMethod()) ? new HttpPost(urlstr) : new HttpPut(urlstr);
        HttpEntity entity = new InputStreamEntity(this.req.getInputStream(), this.req.getContentLength());
        entityRequest.setEntity(entity);
        method = entityRequest;
      } else if ("DELETE".equals(this.req.getMethod())) {
        method = new HttpDelete(urlstr);
      }
      else {
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, new StringBuilder().append("Unexpected method type: ")
          .append(this.req
          .getMethod()).toString());
      }

      for (Enumeration e = this.req.getHeaderNames(); e.hasMoreElements(); ) {
        String headerName = (String)e.nextElement();
        if ((!"host".equalsIgnoreCase(headerName)) && 
          (!"authorization"
          .equalsIgnoreCase(headerName)) && 
          (!"accept"
          .equalsIgnoreCase(headerName)))
        {
          method.addHeader(headerName, this.req.getHeader(headerName));
        }
      }

      if ((method instanceof HttpEntityEnclosingRequest)) {
        method.removeHeaders("Transfer-Encoding");
        method.removeHeaders("Content-Length");
      }

      HttpResponse response = this.solrDispatchFilter.httpClient.execute(method);
      int httpStatus = response.getStatusLine().getStatusCode();
      httpEntity = response.getEntity();

      resp.setStatus(httpStatus);
      for (HeaderIterator responseHeaders = response.headerIterator(); responseHeaders.hasNext(); ) {
        Header header = responseHeaders.nextHeader();

        if ((header != null) && (!header.getName().equalsIgnoreCase("Transfer-Encoding")) && 
          (!header
          .getName().equalsIgnoreCase("Connection"))) {
          resp.addHeader(header.getName(), header.getValue());
        }
      }

      if (httpEntity != null) {
        if (httpEntity.getContentEncoding() != null)
          resp.setCharacterEncoding(httpEntity.getContentEncoding().getValue());
        if (httpEntity.getContentType() != null) resp.setContentType(httpEntity.getContentType().getValue());

        InputStream is = httpEntity.getContent();
        OutputStream os = resp.getOutputStream();

        org.apache.commons.io.IOUtils.copyLarge(is, os);
      }
    }
    catch (IOException e) {
      sendError(new SolrException(SolrException.ErrorCode.SERVER_ERROR, new StringBuilder().append("Error trying to proxy request for url: ").append(coreUrl).toString(), e));
    }
    finally
    {
      Utils.consumeFully(httpEntity);
    }
  }

  protected void sendError(Throwable ex) throws IOException
  {
    Exception exp = null;
    SolrCore localCore = null;
    try {
      SolrQueryResponse solrResp = new SolrQueryResponse();
      if ((ex instanceof Exception))
        solrResp.setException((Exception)ex);
      else {
        solrResp.setException(new RuntimeException(ex));
      }
      localCore = this.core;
      if (this.solrReq == null)
      {
        SolrParams solrParams;
        SolrParams solrParams;
        if (this.req != null)
        {
          solrParams = SolrRequestParsers.parseQueryString(this.req.getQueryString());
        }
        else {
          solrParams = new MapSolrParams(Collections.emptyMap());
        }
        this.solrReq = new SolrQueryRequestBase(this.core, solrParams) {
        };
      }
      QueryResponseWriter writer = this.core.getQueryResponseWriter(this.solrReq);
      writeResponse(solrResp, writer, Method.GET);
    }
    catch (Exception e)
    {
      SimpleOrderedMap info;
      int code;
      exp = e;
    }
    finally
    {
      try
      {
        SimpleOrderedMap info;
        int code;
        if (exp != null) {
          SimpleOrderedMap info = new SimpleOrderedMap();
          int code = ResponseUtils.getErrorInfo(ex, info, log);
          sendError(code, info.toString());
        }
      } finally {
        if ((this.core == null) && (localCore != null))
          localCore.close();
      }
    }
  }

  protected void sendError(int code, String message) throws IOException
  {
    try {
      this.response.sendError(code, message);
    } catch (EOFException e) {
      log.info("Unable to write error response, client closed connection or we are shutting down", e);
    }
  }
/**
 * 執行查詢
 * @param rsp
 */
  protected void execute(SolrQueryResponse rsp)
  {
    this.solrReq.getContext().put("webapp", this.req.getContextPath());
    this.solrReq.getCore().execute(this.handler, this.solrReq, rsp);
  }

  private void handleAdminRequest() throws IOException {
    SolrQueryResponse solrResp = new SolrQueryResponse();
    SolrCore.preDecorateResponse(this.solrReq, solrResp);
    this.handler.handleRequest(this.solrReq, solrResp);
    SolrCore.postDecorateResponse(this.handler, this.solrReq, solrResp);
    if ((log.isInfoEnabled()) && (solrResp.getToLog().size() > 0)) {
      log.info(solrResp.getToLogAsString("[admin]"));
    }
    QueryResponseWriter respWriter = (QueryResponseWriter)SolrCore.DEFAULT_RESPONSE_WRITERS.get(this.solrReq.getParams().get("wt"));
    if (respWriter == null) respWriter = (QueryResponseWriter)SolrCore.DEFAULT_RESPONSE_WRITERS.get("standard");
    writeResponse(solrResp, respWriter, Method.getMethod(this.req.getMethod()));
  }

  private void processAliases(Aliases aliases, List<String> collectionsList)
  {
    String collection = this.solrReq.getParams().get("collection");
    if (collection != null) {
      collectionsList = StrUtils.splitSmart(collection, ",", true);
    }
    if (collectionsList != null)
    {
      Set newCollectionsList = new HashSet(collectionsList
        .size());
      for (String col : collectionsList) {
        String al = aliases.getCollectionAlias(col);
        if (al != null) {
          List aliasList = StrUtils.splitSmart(al, ",", true);
          newCollectionsList.addAll(aliasList);
        } else {
          newCollectionsList.add(col);
        }
      }
      if (newCollectionsList.size() > 0) {
        StringBuilder collectionString = new StringBuilder();
        Iterator it = newCollectionsList.iterator();
        int sz = newCollectionsList.size();
        for (int i = 0; i < sz; i++) {
          collectionString.append((String)it.next());
          if (i < newCollectionsList.size() - 1) {
            collectionString.append(",");
          }
        }

        ModifiableSolrParams params = new ModifiableSolrParams(this.solrReq
          .getParams());
        params.set("collection", new String[] { collectionString.toString() });
        this.solrReq.setParams(params);
      }
    }
  }
/**
 * 響應請求
 * @param solrRsp
 * @param responseWriter
 * @param reqMethod
 * @throws IOException
 */
  private void writeResponse(SolrQueryResponse solrRsp, QueryResponseWriter responseWriter, Method reqMethod) throws IOException
  {
    try {
      Object invalidStates = this.solrReq.getContext().get("_stateVer_");

      if (invalidStates != null) solrRsp.add("_stateVer_", invalidStates);

      String ct = responseWriter.getContentType(this.solrReq, solrRsp);

      if (null != ct) this.response.setContentType(ct);

      if (solrRsp.getException() != null) {
        NamedList info = new SimpleOrderedMap();
        int code = ResponseUtils.getErrorInfo(solrRsp.getException(), info, log);
        solrRsp.add("error", info);
        this.response.setStatus(code);
      }

      if (Method.HEAD != reqMethod)
//QueryResponseWriterUtil進一步處理響應        QueryResponseWriterUtil.writeQueryResponse(this.response.getOutputStream(), responseWriter, this.solrReq, solrRsp, ct);
    }
    catch (EOFException e)
    {
      log.info("Unable to write response, client closed connection or we are shutting down", e);
    }
  }

  private Map<String, Integer> checkStateIsValid(String stateVer) {
    Map result = null;

    if ((stateVer != null) && (!stateVer.isEmpty()) && (this.cores.isZooKeeperAware()))
    {
      String[] pairs = StringUtils.split(stateVer, '|');
      for (String pair : pairs) {
        String[] pcs = StringUtils.split(pair, ':');
        if ((pcs.length == 2) && (!pcs[0].isEmpty()) && (!pcs[1].isEmpty())) {
          Integer status = this.cores.getZkController().getZkStateReader().compareStateVersions(pcs[0], Integer.parseInt(pcs[1]));
          if (status != null) {
            if (result == null) result = new HashMap();
            result.put(pcs[0], status);
          }
        }
      }
    }
    return result;
  }

  private SolrCore getCoreByCollection(String collection) {
    ZkStateReader zkStateReader = this.cores.getZkController().getZkStateReader();

    ClusterState clusterState = zkStateReader.getClusterState();
    Map slices = clusterState.getActiveSlicesMap(collection);
    if (slices == null) {
      return null;
    }
    Set liveNodes = clusterState.getLiveNodes();

    Set entries = slices.entrySet();
    SolrCore core = null;

    for (Map.Entry entry : entries)
    {
      Replica leaderProps = clusterState.getLeader(collection, (String)entry.getKey());
      if ((leaderProps != null) && (liveNodes.contains(leaderProps.getNodeName())) && (leaderProps.getState() == Replica.State.ACTIVE)) {
        core = checkProps(leaderProps);
        if (core != null) {
          return core;
        }

      }

      Map shards = ((Slice)entry.getValue()).getReplicasMap();
      Set shardEntries = shards.entrySet();
      for (Map.Entry shardEntry : shardEntries) {
        Replica zkProps = (Replica)shardEntry.getValue();
        if ((liveNodes.contains(zkProps.getNodeName())) && (zkProps.getState() == Replica.State.ACTIVE)) {
          core = checkProps(zkProps);
          if (core != null) {
            return core;
          }
        }
      }
    }
    return null;
  }

  private SolrCore checkProps(ZkNodeProps zkProps)
  {
    SolrCore core = null;
    if (this.cores.getZkController().getNodeName().equals(zkProps.getStr("node_name"))) {
      String corename = zkProps.getStr("core");
      core = this.cores.getCore(corename);
    }
    return core;
  }

  private void getSlicesForCollections(ClusterState clusterState, Collection<Slice> slices, boolean activeSlices)
  {
    if (activeSlices) {
      for (String collection : clusterState.getCollections()) {
        Collection activeCollectionSlices = clusterState.getActiveSlices(collection);
        if (activeCollectionSlices != null)
          slices.addAll(activeCollectionSlices);
      }
    }
    else
      for (String collection : clusterState.getCollections()) {
        Collection collectionSlices = clusterState.getSlices(collection);
        if (collectionSlices != null)
          slices.addAll(collectionSlices);
      }
  }

  private String getRemotCoreUrl(String collectionName, String origCorename)
  {
    ClusterState clusterState = this.cores.getZkController().getClusterState();
    Collection slices = clusterState.getActiveSlices(collectionName);
    boolean byCoreName = false;

    if (slices == null) {
      slices = new ArrayList();

      byCoreName = true;
      getSlicesForCollections(clusterState, slices, true);
      if (slices.isEmpty()) {
        getSlicesForCollections(clusterState, slices, false);
      }
    }

    if (slices.isEmpty()) {
      return null;
    }

    if (this.collectionsList == null) {
      this.collectionsList = new ArrayList();
    }
    this.collectionsList.add(collectionName);
    String coreUrl = getCoreUrl(collectionName, origCorename, clusterState, slices, byCoreName, true);

    if (coreUrl == null) {
      coreUrl = getCoreUrl(collectionName, origCorename, clusterState, slices, byCoreName, false);
    }

    return coreUrl;
  }

  private String getCoreUrl(String collectionName, String origCorename, ClusterState clusterState, Collection<Slice> slices, boolean byCoreName, boolean activeReplicas)
  {
    Set liveNodes = clusterState.getLiveNodes();
    List randomizedSlices = new ArrayList(slices.size());
    randomizedSlices.addAll(slices);
    Collections.shuffle(randomizedSlices, random);

    for (Slice slice : randomizedSlices) {
      List randomizedReplicas = new ArrayList();
      randomizedReplicas.addAll(slice.getReplicas());
      Collections.shuffle(randomizedReplicas, random);

      for (Replica replica : randomizedReplicas)
        if ((!activeReplicas) || ((liveNodes.contains(replica.getNodeName())) && 
          (replica
          .getState() == Replica.State.ACTIVE)))
        {
          if (((!byCoreName) || (collectionName.equals(replica.getStr("core")))) && 
            (!replica.getStr("base_url").equals(this.cores.getZkController().getBaseUrl())))
          {
            String coreUrl;
            String coreUrl;
            if (origCorename != null) {
              coreUrl = new StringBuilder().append(replica.getStr("base_url")).append("/").append(origCorename).toString();
            } else {
              coreUrl = replica.getCoreUrl();
              if (coreUrl.endsWith("/")) {
                coreUrl = coreUrl.substring(0, coreUrl.length() - 1);
              }
            }

            return coreUrl;
          }
        }
    }
    return null;
  }

  private AuthorizationContext getAuthCtx()
  {
    final String resource = getPath();

    SolrParams params = getQueryParams();
    final ArrayList collectionRequests = new ArrayList();
    Object localObject;
    if (getCollectionsList() != null)
      for (localObject = getCollectionsList().iterator(); ((Iterator)localObject).hasNext(); ) { collection = (String)((Iterator)localObject).next();
        collectionRequests.add(new AuthorizationContext.CollectionRequest(collection));
      }
    String collection;
    if (getPath().equals("/admin/collections")) {
      if ((CollectionParams.CollectionAction.CREATE.isEqual(params.get("action"))) || 
        (CollectionParams.CollectionAction.RELOAD
        .isEqual(params
        .get("action"))) || 
        (CollectionParams.CollectionAction.DELETE
        .isEqual(params
        .get("action"))))
      {
        collectionRequests.add(new AuthorizationContext.CollectionRequest(params.get("name")));
      } else if (params.get("collection") != null) {
        collectionRequests.add(new AuthorizationContext.CollectionRequest(params.get("collection")));
      }
    }

    if ((resource.equals("/select")) && (params.get("collection") != null)) {
      collectionRequests.clear();
      localObject = params.get("collection").split(","); collection = localObject.length; for (String str1 = 0; str1 < collection; str1++) { String collection = localObject[str1];
        collectionRequests.add(new AuthorizationContext.CollectionRequest(collection));
      }

    }

    if (this.requestType == AuthorizationContext.RequestType.UNKNOWN) {
      if ((resource.startsWith("/select")) || (resource.startsWith("/get")))
        this.requestType = AuthorizationContext.RequestType.READ;
      if (resource.startsWith("/update")) {
        this.requestType = AuthorizationContext.RequestType.WRITE;
      }

    }

    if ((getCore() != null) && ((getCollectionsList() == null) || (getCollectionsList().size() == 0))) {
      collectionRequests.add(new AuthorizationContext.CollectionRequest(getCore().getCoreDescriptor().getCollectionName()));
    }

    if (getQueryParams().get("collection") != null) {
      collectionRequests.add(new AuthorizationContext.CollectionRequest(getQueryParams().get("collection")));
    }
    return new AuthorizationContext()
    {
      public SolrParams getParams() {
        return HttpSolrCall.this.solrReq.getParams();
      }

      public Principal getUserPrincipal()
      {
        return HttpSolrCall.this.getReq().getUserPrincipal();
      }

      public String getHttpHeader(String s)
      {
        return HttpSolrCall.this.getReq().getHeader(s);
      }

      public Enumeration getHeaderNames()
      {
        return HttpSolrCall.this.getReq().getHeaderNames();
      }

      public List<AuthorizationContext.CollectionRequest> getCollectionRequests()
      {
        return collectionRequests;
      }

      public AuthorizationContext.RequestType getRequestType()
      {
        return HttpSolrCall.this.requestType;
      }

      public String getResource() {
        return HttpSolrCall.this.path;
      }

      public String getHttpMethod()
      {
        return HttpSolrCall.this.getReq().getMethod();
      }

      public String toString()
      {
        StringBuilder response = new StringBuilder("userPrincipal: [").append(getUserPrincipal()).append("]")
          .append(" type: [")
          .append(HttpSolrCall.this.requestType.toString()).append("], collections: [");
        for (AuthorizationContext.CollectionRequest collectionRequest : collectionRequests) {
          response.append(collectionRequest.collectionName).append(", ");
        }
        if (collectionRequests.size() > 0) {
          response.delete(response.length() - 1, response.length());
        }
        response.append("], Path: [").append(resource).append("]");
        response.append(" path : ").append(HttpSolrCall.this.path).append(" params :").append(HttpSolrCall.this.solrReq.getParams());
        return response.toString();
      }

      public String getRemoteAddr()
      {
        return HttpSolrCall.this.getReq().getRemoteAddr();
      }

      public String getRemoteHost()
      {
        return HttpSolrCall.this.getReq().getRemoteHost();
      }
    };
  }

  static
  {
    String seed = System.getProperty("tests.seed");
    if (seed == null)
      random = new Random();
    else
      random = new Random(seed.hashCode());
  }
}

QueryResponseWriterUtil

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;

public final class QueryResponseWriterUtil
{
  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 {
      OutputStream out = new OutputStream()
      {
        public void write(int b) throws IOException {
          this.val$outputStream.write(b);
        }

        public void flush()
          throws IOException
        {
        }
      };
      Writer writer = buildWriter(out, 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);
  }
}

總結

查看源碼,一方面是想知道solr在處理請求的時候有那些過程,另一方面是爲了實際開發做準備。

//獲取CoreContainer容器
CoreContainer coreContainer=(CoreContainer)customerServletRequest.getAttribute("org.apache.solr.CoreContainer");
//獲取對應的solrcore
Solrcore core = coreContainer.getCore("my_solr");
//獲取SolrConfig對象
SolrConfig config = core.getSolrConfig();
//獲取SolrRequestParsers解析對象
SolrRequestParsers parser = config.getRequestParsers();
//得到查詢請求對象
//實際開發中可以根據自己的需求來處理獲取對應的查詢對象
SolrQueryRequest solrReq = parser.parse(this.core, "select", this.req);
//實例化SolrQueryResponse
SolrQueryResponse solrRsp = new SolrQueryResponse();
//獲取對應handler處理器
SolrRequestHandler handler = core.getRequestHandler("/coustomerHandler");
//設置查詢參數
ModifiableSolrParams params = new ModifiableSolrParams(solrReq
          .getParams());
solrReq.setParams(params );
//執行查詢
solrReq.getCore().execute(handler, solrReq , solrRsp );
// 判斷是否出現異常
solrRsp.getException()==null
//獲取QueryResponseWriter 
//實際開發過程中,可以自己去實現自己的writer。對應solrconfig.xml中的配置
//<queryResponseWriter name="upxiaofeng" class="com.upxiaofeng.solr.CustomerResponseWriter">    
//<str name="content-type">text/plain; charset=UTF-8</str>
//</queryResponseWriter>
public final QueryResponseWriter getQueryResponseWriter(String writerName)
QueryResponseWriter responseWriter = core.getQueryResponseWriter("upxiaofeng");

public final QueryResponseWriter getQueryResponseWriter(SolrQueryRequest request)
QueryResponseWriter responseWriter = core.getQueryResponseWriter(solrReq );
//響應
重寫一下writeResponse方法
發佈了84 篇原創文章 · 獲贊 49 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章