org.apache.solr.handler.component.AlfrescoHttpShardHandlerFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.handler.component.AlfrescoHttpShardHandlerFactory.java

Source

/*
 * #%L
 * Alfresco Solr 4
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.apache.solr.handler.component;

import org.apache.commons.lang.StringUtils;
import org.apache.http.client.HttpClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.client.solrj.impl.LBHttpSolrServer;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.URLUtil;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.util.DefaultSolrThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author Andy
 *
 */

public class AlfrescoHttpShardHandlerFactory extends ShardHandlerFactory
        implements org.apache.solr.util.plugin.PluginInfoInitialized {
    protected static Logger log = LoggerFactory.getLogger(HttpShardHandlerFactory.class);
    private static final String DEFAULT_SCHEME = "http";

    // We want an executor that doesn't take up any resources if
    // it's not used, so it could be created statically for
    // the distributed search component if desired.
    //
    // Consider CallerRuns policy and a lower max threads to throttle
    // requests at some point (or should we simply return failure?)
    private ThreadPoolExecutor commExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 5, TimeUnit.SECONDS, // terminate idle threads after 5 sec
            new SynchronousQueue<Runnable>(), // directly hand off tasks
            new DefaultSolrThreadFactory("httpShardExecutor"));

    protected HttpClient defaultClient;
    private LBHttpSolrServer loadbalancer;
    //default values:
    int soTimeout = 0;
    int connectionTimeout = 0;
    int maxConnectionsPerHost = 20;
    int corePoolSize = 0;
    int maximumPoolSize = Integer.MAX_VALUE;
    int keepAliveTime = 5;
    int queueSize = -1;
    boolean accessPolicy = false;

    private String scheme = null;

    private final Random r = new Random();

    // URL scheme to be used in distributed search.
    static final String INIT_URL_SCHEME = "urlScheme";

    // The core size of the threadpool servicing requests
    static final String INIT_CORE_POOL_SIZE = "corePoolSize";

    // The maximum size of the threadpool servicing requests
    static final String INIT_MAX_POOL_SIZE = "maximumPoolSize";

    // The amount of time idle threads persist for in the queue, before being killed
    static final String MAX_THREAD_IDLE_TIME = "maxThreadIdleTime";

    // If the threadpool uses a backing queue, what is its maximum size (-1) to use direct handoff
    static final String INIT_SIZE_OF_QUEUE = "sizeOfQueue";

    // Configure if the threadpool favours fairness over throughput
    static final String INIT_FAIRNESS_POLICY = "fairnessPolicy";

    /**
     * Get {@link ShardHandler} that uses the default http client.
     */
    @Override
    public ShardHandler getShardHandler() {
        return getShardHandler(defaultClient);
    }

    /**
     * Get {@link ShardHandler} that uses custom http client.
     */
    public ShardHandler getShardHandler(final HttpClient httpClient) {
        return new AlfrescoHttpShardHandler(this, httpClient);
    }

    @Override
    public void init(PluginInfo info) {
        NamedList args = info.initArgs;
        this.soTimeout = getParameter(args, HttpClientUtil.PROP_SO_TIMEOUT, soTimeout);
        this.scheme = getParameter(args, INIT_URL_SCHEME, null);
        if (StringUtils.endsWith(this.scheme, "://")) {
            this.scheme = StringUtils.removeEnd(this.scheme, "://");
        }
        this.connectionTimeout = getParameter(args, HttpClientUtil.PROP_CONNECTION_TIMEOUT, connectionTimeout);
        this.maxConnectionsPerHost = getParameter(args, HttpClientUtil.PROP_MAX_CONNECTIONS_PER_HOST,
                maxConnectionsPerHost);
        this.corePoolSize = getParameter(args, INIT_CORE_POOL_SIZE, corePoolSize);
        this.maximumPoolSize = getParameter(args, INIT_MAX_POOL_SIZE, maximumPoolSize);
        this.keepAliveTime = getParameter(args, MAX_THREAD_IDLE_TIME, keepAliveTime);
        this.queueSize = getParameter(args, INIT_SIZE_OF_QUEUE, queueSize);
        this.accessPolicy = getParameter(args, INIT_FAIRNESS_POLICY, accessPolicy);

        // magic sysprop to make tests reproducible: set by SolrTestCaseJ4.
        String v = System.getProperty("tests.shardhandler.randomSeed");
        if (v != null) {
            r.setSeed(Long.parseLong(v));
        }

        BlockingQueue<Runnable> blockingQueue = (this.queueSize == -1)
                ? new SynchronousQueue<Runnable>(this.accessPolicy)
                : new ArrayBlockingQueue<Runnable>(this.queueSize, this.accessPolicy);

        this.commExecutor = new ThreadPoolExecutor(this.corePoolSize, this.maximumPoolSize, this.keepAliveTime,
                TimeUnit.SECONDS, blockingQueue, new DefaultSolrThreadFactory("httpShardExecutor"));

        ModifiableSolrParams clientParams = new ModifiableSolrParams();
        clientParams.set(HttpClientUtil.PROP_MAX_CONNECTIONS_PER_HOST, maxConnectionsPerHost);
        clientParams.set(HttpClientUtil.PROP_MAX_CONNECTIONS, 10000);
        clientParams.set(HttpClientUtil.PROP_SO_TIMEOUT, soTimeout);
        clientParams.set(HttpClientUtil.PROP_CONNECTION_TIMEOUT, connectionTimeout);
        clientParams.set(HttpClientUtil.PROP_USE_RETRY, false);
        this.defaultClient = HttpClientUtil.createClient(clientParams);
        this.loadbalancer = createLoadbalancer(defaultClient);
    }

    protected ThreadPoolExecutor getThreadPoolExecutor() {
        return this.commExecutor;
    }

    protected LBHttpSolrServer createLoadbalancer(HttpClient httpClient) {
        return new LBHttpSolrServer(httpClient);
    }

    protected <T> T getParameter(NamedList initArgs, String configKey, T defaultValue) {
        T toReturn = defaultValue;
        if (initArgs != null) {
            T temp = (T) initArgs.get(configKey);
            toReturn = (temp != null) ? temp : defaultValue;
        }
        log.info("Setting {} to: {}", configKey, toReturn);
        return toReturn;
    }

    @Override
    public void close() {
        try {
            ExecutorUtil.shutdownNowAndAwaitTermination(commExecutor);
        } finally {
            try {
                if (defaultClient != null) {
                    defaultClient.getConnectionManager().shutdown();
                }
            } finally {

                if (loadbalancer != null) {
                    loadbalancer.shutdown();
                }
            }
        }
    }

    /**
     * Makes a request to one or more of the given urls, using the configured load balancer.
     *
     * @param req The solr search request that should be sent through the load balancer
     * @param urls The list of solr server urls to load balance across
     * @return The response from the request
     */
    public LBHttpSolrServer.Rsp makeLoadBalancedRequest(final AlfrescoQueryRequest req, List<String> urls)
            throws SolrServerException, IOException {
        return loadbalancer.request(new LBHttpSolrServer.Req(req, urls));
    }

    /**
     * Creates a randomized list of urls for the given shard.
     *
     * @param shard the urls for the shard, separated by '|'
     * @return A list of valid urls (including protocol) that are replicas for the shard
     */
    public List<String> makeURLList(String shard) {
        List<String> urls = StrUtils.splitSmart(shard, "|", true);

        // convert shard to URL
        for (int i = 0; i < urls.size(); i++) {
            urls.set(i, buildUrl(urls.get(i)));
        }

        //
        // Shuffle the list instead of use round-robin by default.
        // This prevents accidental synchronization where multiple shards could get in sync
        // and query the same replica at the same time.
        //
        if (urls.size() > 1)
            Collections.shuffle(urls, r);

        return urls;
    }

    /**
     * Creates a new completion service for use by a single set of distributed requests.
     */
    public CompletionService newCompletionService() {
        return new ExecutorCompletionService<ShardResponse>(commExecutor);
    }

    /**
     * Rebuilds the URL replacing the URL scheme of the passed URL with the
     * configured scheme replacement.If no scheme was configured, the passed URL's
     * scheme is left alone.
     */
    private String buildUrl(String url) {
        if (!URLUtil.hasScheme(url)) {
            return StringUtils.defaultIfEmpty(scheme, DEFAULT_SCHEME) + "://" + url;
        } else if (StringUtils.isNotEmpty(scheme)) {
            return scheme + "://" + URLUtil.removeScheme(url);
        }

        return url;
    }
}