com.su.search.client.solrj.PaHttpSolrServer.java Source code

Java tutorial

Introduction

Here is the source code for com.su.search.client.solrj.PaHttpSolrServer.java

Source

/**
 * 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 com.su.search.client.solrj;

import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.ClientParamBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.HttpEntityWrapper;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.mime.FormBodyPart;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.solr.client.solrj.ResponseParser;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.BinaryResponseParser;
import org.apache.solr.client.solrj.request.RequestWriter;
import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.client.solrj.util.ClientUtils;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.ContentStream;
import org.apache.solr.common.util.NamedList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.paic.wcm.search.client.SimpleIndexClient;

/**
 * solrj.HttpSolrServer(v3.6.1)??
 * @see org.apache.solr.client.solrj.imp.HttpSolrServer
 * 
 */
public class PaHttpSolrServer extends SolrServer {
    private static final String UTF_8 = "UTF-8";
    private static final String DEFAULT_PATH = "/select";
    private static final long serialVersionUID = 1L;
    /**
     * User-Agent String.
     */
    public static final String AGENT = "Solr[" + PaHttpSolrServer.class.getName() + "] 1.0";

    private static Logger log = LoggerFactory.getLogger(PaHttpSolrServer.class);

    /**
     * The URL of the Solr server.
     */
    protected String baseUrl;

    /**
     * Default value: null / empty.
     * <p/>
     * Parameters that are added to every request regardless. This may be a place
     * to add something like an authentication token.
     */
    protected ModifiableSolrParams invariantParams;

    /**
     * Default response parser is BinaryResponseParser
     * <p/>
     * This parser represents the default Response Parser chosen to parse the
     * response if the parser were not specified as part of the request.
     * 
     * @see org.apache.solr.client.solrj.impl.BinaryResponseParser
     */
    protected ResponseParser parser;

    /**
     * The RequestWriter used to write all requests to Solr
     * 
     * @see org.apache.solr.client.solrj.request.RequestWriter
     */
    protected RequestWriter requestWriter = new RequestWriter();

    private final HttpClient httpClient;

    /**
     * This defaults to false under the assumption that if you are following a
     * redirect to get to a Solr installation, something is misconfigured
     * somewhere.
     */
    private boolean followRedirects = false;

    /**
     * Maximum number of retries to attempt in the event of transient errors.
     * Default: 0 (no) retries. No more than 1 recommended.
     */
    private int maxRetries = 0;

    private ThreadSafeClientConnManager ccm;
    private boolean useMultiPartPost;

    //modified by wangqiang406 2012-07-27
    // ??
    private String password = null;

    public PaHttpSolrServer(String baseURL, String password) {
        this(baseURL, null, new BinaryResponseParser());
        this.password = password;
    }

    /**
     * @param baseURL
     *          The URL of the Solr server. For example, "
     *          <code>http://localhost:8983/solr/</code>" if you are using the
     *          standard distribution Solr webapp on your local machine.
     */
    public PaHttpSolrServer(String baseURL) {
        this(baseURL, null, new BinaryResponseParser());
    }

    public PaHttpSolrServer(String baseURL, HttpClient client) {
        this(baseURL, client, new BinaryResponseParser());
    }

    public PaHttpSolrServer(String baseURL, HttpClient client, ResponseParser parser) {
        this.baseUrl = baseURL;
        if (baseUrl.endsWith("/")) {
            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
        }
        if (baseUrl.indexOf('?') >= 0) {
            throw new RuntimeException(
                    "Invalid base url for solrj.  The base URL must not contain parameters: " + baseUrl);
        }

        if (client != null) {
            httpClient = client;
        } else {
            httpClient = createClient();
        }

        this.parser = parser;
    }

    private DefaultHttpClient createClient() {
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
        schemeRegistry.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));

        ccm = new ThreadSafeClientConnManager(schemeRegistry);
        // Increase default max connection per route to 32
        ccm.setDefaultMaxPerRoute(32);
        // Increase max total connection to 128
        ccm.setMaxTotal(128);
        DefaultHttpClient httpClient = new DefaultHttpClient(ccm);
        httpClient.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, followRedirects);
        return httpClient;
    }

    /**
     * Process the request. If
     * {@link org.apache.solr.client.solrj.SolrRequest#getResponseParser()} is
     * null, then use {@link #getParser()}
     * 
     * @param request
     *          The {@link org.apache.solr.client.solrj.SolrRequest} to process
     * @return The {@link org.apache.solr.common.util.NamedList} result
     * @throws SolrServerException
     * @throws IOException
     * 
     * @see #request(org.apache.solr.client.solrj.SolrRequest,
     *      org.apache.solr.client.solrj.ResponseParser)
     */
    public NamedList<Object> request(final SolrRequest request) throws SolrServerException, IOException {
        ResponseParser responseParser = request.getResponseParser();
        if (responseParser == null) {
            responseParser = parser;
        }
        return request(request, responseParser);
    }

    public NamedList<Object> request(final SolrRequest request, final ResponseParser processor)
            throws SolrServerException, IOException {
        HttpRequestBase method = null;
        InputStream is = null;
        SolrParams params = request.getParams();

        // modified by wangqiang406 2012-07-27
        // ??
        if (null != password) {
            ModifiableSolrParams wparams = new ModifiableSolrParams(params);
            wparams.set(SimpleIndexClient.KEY_PA_AUTH, password);
            params = wparams;
        }

        Collection<ContentStream> streams = requestWriter.getContentStreams(request);
        String path = requestWriter.getPath(request);
        if (path == null || !path.startsWith("/")) {
            path = DEFAULT_PATH;
        }

        ResponseParser parser = request.getResponseParser();
        if (parser == null) {
            parser = this.parser;
        }

        // The parser 'wt=' and 'version=' params are used instead of the original
        // params
        ModifiableSolrParams wparams = new ModifiableSolrParams(params);
        wparams.set(CommonParams.WT, parser.getWriterType());
        wparams.set(CommonParams.VERSION, parser.getVersion());
        if (invariantParams != null) {
            wparams.add(invariantParams);
        }
        params = wparams;

        int tries = maxRetries + 1;
        try {
            while (tries-- > 0) {
                // Note: since we aren't do intermittent time keeping
                // ourselves, the potential non-timeout latency could be as
                // much as tries-times (plus scheduling effects) the given
                // timeAllowed.
                try {
                    if (SolrRequest.METHOD.GET == request.getMethod()) {
                        if (streams != null) {
                            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "GET can't send streams!");
                        }
                        method = new HttpGet(baseUrl + path + ClientUtils.toQueryString(params, false));
                    } else if (SolrRequest.METHOD.POST == request.getMethod()) {

                        String url = baseUrl + path;

                        boolean isMultipart = (streams != null && streams.size() > 1);

                        LinkedList<NameValuePair> postParams = new LinkedList<NameValuePair>();
                        if (streams == null || isMultipart) {
                            HttpPost post = new HttpPost(url);
                            post.setHeader("Content-Charset", "UTF-8");
                            if (!this.useMultiPartPost && !isMultipart) {
                                post.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
                            }

                            List<FormBodyPart> parts = new LinkedList<FormBodyPart>();
                            Iterator<String> iter = params.getParameterNamesIterator();
                            while (iter.hasNext()) {
                                String p = iter.next();
                                String[] vals = params.getParams(p);
                                if (vals != null) {
                                    for (String v : vals) {
                                        if (this.useMultiPartPost || isMultipart) {
                                            parts.add(new FormBodyPart(p,
                                                    new StringBody(v, Charset.forName("UTF-8"))));
                                        } else {
                                            postParams.add(new BasicNameValuePair(p, v));
                                        }
                                    }
                                }
                            }

                            if (isMultipart) {
                                for (ContentStream content : streams) {
                                    parts.add(new FormBodyPart(content.getName(),
                                            new InputStreamBody(content.getStream(), content.getName())));
                                }
                            }

                            if (parts.size() > 0) {
                                MultipartEntity entity = new MultipartEntity(HttpMultipartMode.STRICT);
                                for (FormBodyPart p : parts) {
                                    entity.addPart(p);
                                }
                                post.setEntity(entity);
                            } else {
                                //not using multipart
                                HttpEntity e;
                                post.setEntity(new UrlEncodedFormEntity(postParams, "UTF-8"));
                            }

                            method = post;
                        }
                        // It is has one stream, it is the post body, put the params in the URL
                        else {
                            String pstr = ClientUtils.toQueryString(params, false);

                            HttpPost post = new HttpPost(url + pstr);

                            // Single stream as body
                            // Using a loop just to get the first one
                            final ContentStream[] contentStream = new ContentStream[1];
                            for (ContentStream content : streams) {
                                contentStream[0] = content;
                                break;
                            }
                            if (contentStream[0] instanceof RequestWriter.LazyContentStream) {
                                post.setEntity(new InputStreamEntity(contentStream[0].getStream(), -1) {
                                    @Override
                                    public Header getContentType() {
                                        return new BasicHeader("Content-Type", contentStream[0].getContentType());
                                    }

                                    @Override
                                    public boolean isRepeatable() {
                                        return false;
                                    }

                                });
                            } else {
                                post.setEntity(new InputStreamEntity(contentStream[0].getStream(), -1) {
                                    @Override
                                    public Header getContentType() {
                                        return new BasicHeader("Content-Type", contentStream[0].getContentType());
                                    }

                                    @Override
                                    public boolean isRepeatable() {
                                        return false;
                                    }
                                });
                            }
                            method = post;
                        }
                    } else {
                        throw new SolrServerException("Unsupported method: " + request.getMethod());
                    }
                } catch (NoHttpResponseException r) {
                    method = null;
                    if (is != null) {
                        is.close();
                    }
                    // If out of tries then just rethrow (as normal error).
                    if (tries < 1) {
                        throw r;
                    }
                }
            }
        } catch (IOException ex) {
            throw new SolrServerException("error reading streams", ex);
        }

        // TODO: move to a interceptor?
        method.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, followRedirects);
        method.addHeader("User-Agent", AGENT);

        InputStream respBody = null;

        try {
            // Execute the method.
            final HttpResponse response = httpClient.execute(method);
            int httpStatus = response.getStatusLine().getStatusCode();

            // Read the contents
            String charset = EntityUtils.getContentCharSet(response.getEntity());
            respBody = response.getEntity().getContent();

            // handle some http level checks before trying to parse the response
            switch (httpStatus) {
            case HttpStatus.SC_OK:
                break;
            case HttpStatus.SC_MOVED_PERMANENTLY:
            case HttpStatus.SC_MOVED_TEMPORARILY:
                if (!followRedirects) {
                    throw new SolrServerException(
                            "Server at " + getBaseURL() + " sent back a redirect (" + httpStatus + ").");
                }
                break;
            case HttpStatus.SC_NOT_FOUND:
                throw new SolrServerException("Server at " + getBaseURL() + " was not found (404).");
            default:
                throw new SolrServerException("Server at " + getBaseURL() + " returned non ok status:" + httpStatus
                        + ", message:" + response.getStatusLine().getReasonPhrase());

            }
            NamedList<Object> rsp = processor.processResponse(respBody, charset);
            if (httpStatus != HttpStatus.SC_OK) {
                String reason = null;
                try {
                    NamedList err = (NamedList) rsp.get("error");
                    if (err != null) {
                        reason = (String) err.get("msg");
                        // TODO? get the trace?
                    }
                } catch (Exception ex) {
                }
                if (reason == null) {
                    StringBuilder msg = new StringBuilder();
                    msg.append(response.getStatusLine().getReasonPhrase());
                    msg.append("\n\n");
                    msg.append("request: " + method.getURI());
                    reason = java.net.URLDecoder.decode(msg.toString(), UTF_8);
                }
                throw new SolrException(SolrException.ErrorCode.getErrorCode(httpStatus), reason);
            }
            return rsp;
        } catch (ConnectException e) {
            throw new SolrServerException("Server refused connection at: " + getBaseURL(), e);
        } catch (SocketTimeoutException e) {
            throw new SolrServerException("Timeout occured while waiting response from server at: " + getBaseURL(),
                    e);
        } catch (IOException e) {
            throw new SolrServerException("IOException occured when talking to server at: " + getBaseURL(), e);
        } finally {
            if (respBody != null) {
                try {
                    respBody.close();
                } catch (Throwable t) {
                } // ignore
            }
        }
    }

    // -------------------------------------------------------------------
    // -------------------------------------------------------------------

    /**
     * Retrieve the default list of parameters are added to every request
     * regardless.
     * 
     * @see #invariantParams
     */
    public ModifiableSolrParams getInvariantParams() {
        return invariantParams;
    }

    public String getBaseURL() {
        return baseUrl;
    }

    public void setBaseURL(String baseURL) {
        this.baseUrl = baseURL;
    }

    public ResponseParser getParser() {
        return parser;
    }

    /**
     * Note: This setter method is <b>not thread-safe</b>.
     * 
     * @param processor
     *          Default Response Parser chosen to parse the response if the parser
     *          were not specified as part of the request.
     * @see org.apache.solr.client.solrj.SolrRequest#getResponseParser()
     */
    public void setParser(ResponseParser processor) {
        parser = processor;
    }

    public HttpClient getHttpClient() {
        return httpClient;
    }

    /**
     * HttpConnectionParams.setConnectionTimeout
     * 
     * @param timeout
     *          Timeout in milliseconds
     **/
    public void setConnectionTimeout(int timeout) {
        HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), timeout);
    }

    /**
     * Sets HttpConnectionParams.setSoTimeout (read timeout). This is desirable
     * for queries, but probably not for indexing.
     * 
     * @param timeout
     *          Timeout in milliseconds
     **/
    public void setSoTimeout(int timeout) {
        HttpConnectionParams.setSoTimeout(httpClient.getParams(), timeout);
    }

    /**
     * HttpClientParams.setRedirecting
     * 
     * @see #followRedirects
     */
    public void setFollowRedirects(boolean followRedirects) {
        this.followRedirects = followRedirects;
        new ClientParamBean(httpClient.getParams()).setHandleRedirects(followRedirects);
    }

    private static class UseCompressionRequestInterceptor implements HttpRequestInterceptor {

        public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
            if (!request.containsHeader("Accept-Encoding")) {
                request.addHeader("Accept-Encoding", "gzip, deflate");
            }
        }
    }

    private static class UseCompressionResponseInterceptor implements HttpResponseInterceptor {

        public void process(final HttpResponse response, final HttpContext context)
                throws HttpException, IOException {

            HttpEntity entity = response.getEntity();
            Header ceheader = entity.getContentEncoding();
            if (ceheader != null) {
                HeaderElement[] codecs = ceheader.getElements();
                for (int i = 0; i < codecs.length; i++) {
                    if (codecs[i].getName().equalsIgnoreCase("gzip")) {
                        response.setEntity(new GzipDecompressingEntity(response.getEntity()));
                        return;
                    }
                    if (codecs[i].getName().equalsIgnoreCase("deflate")) {
                        response.setEntity(new DeflateDecompressingEntity(response.getEntity()));
                        return;
                    }
                }
            }
        }
    }

    private static class GzipDecompressingEntity extends HttpEntityWrapper {
        public GzipDecompressingEntity(final HttpEntity entity) {
            super(entity);
        }

        public InputStream getContent() throws IOException, IllegalStateException {
            return new GZIPInputStream(wrappedEntity.getContent());
        }

        public long getContentLength() {
            return -1;
        }
    }

    private static class DeflateDecompressingEntity extends GzipDecompressingEntity {
        public DeflateDecompressingEntity(final HttpEntity entity) {
            super(entity);
        }

        public InputStream getContent() throws IOException, IllegalStateException {
            return new InflaterInputStream(wrappedEntity.getContent());
        }
    }

    /**
     * Allow server->client communication to be compressed. Currently gzip and
     * deflate are supported. If the server supports compression the response will
     * be compressed.
     */
    public void setAllowCompression(boolean allowCompression) {
        if (httpClient instanceof DefaultHttpClient) {
            final DefaultHttpClient client = (DefaultHttpClient) httpClient;
            client.removeRequestInterceptorByClass(UseCompressionRequestInterceptor.class);
            client.removeResponseInterceptorByClass(UseCompressionResponseInterceptor.class);
            if (allowCompression) {
                client.addRequestInterceptor(new UseCompressionRequestInterceptor());
                client.addResponseInterceptor(new UseCompressionResponseInterceptor());
            }
        } else {
            throw new UnsupportedOperationException("HttpClient instance was not of type DefaultHttpClient");
        }
    }

    /**
     * Set maximum number of retries to attempt in the event of transient errors.
     * 
     * @param maxRetries
     *          No more than 1 recommended
     * @see #maxRetries
     */
    public void setMaxRetries(int maxRetries) {
        if (maxRetries > 1) {
            log.warn("CommonsHttpSolrServer: maximum Retries " + maxRetries
                    + " > 1. Maximum recommended retries is 1.");
        }
        this.maxRetries = maxRetries;
    }

    public void setRequestWriter(RequestWriter requestWriter) {
        this.requestWriter = requestWriter;
    }

    /**
     * Adds the documents supplied by the given iterator.
     * 
     * @param docIterator
     *          the iterator which returns SolrInputDocument instances
     * 
     * @return the response from the SolrServer
     */
    public UpdateResponse add(Iterator<SolrInputDocument> docIterator) throws SolrServerException, IOException {
        UpdateRequest req = new UpdateRequest();
        req.setDocIterator(docIterator);
        return req.process(this);
    }

    /**
     * Adds the beans supplied by the given iterator.
     * 
     * @param beanIterator
     *          the iterator which returns Beans
     * 
     * @return the response from the SolrServer
     */
    public UpdateResponse addBeans(final Iterator<?> beanIterator) throws SolrServerException, IOException {
        UpdateRequest req = new UpdateRequest();
        req.setDocIterator(new Iterator<SolrInputDocument>() {

            public boolean hasNext() {
                return beanIterator.hasNext();
            }

            public SolrInputDocument next() {
                Object o = beanIterator.next();
                if (o == null)
                    return null;
                return getBinder().toSolrInputDocument(o);
            }

            public void remove() {
                beanIterator.remove();
            }
        });
        return req.process(this);
    }

    public void shutdown() {
        if (httpClient != null) {
            httpClient.getConnectionManager().shutdown();
        }
    }

    public void setDefaultMaxConnectionsPerHost(int max) {
        if (ccm != null) {
            ccm.setDefaultMaxPerRoute(max);
        } else {
            throw new UnsupportedOperationException("Client was created outside of HttpSolrServer");
        }
    }

    /**
     * Set the maximum number of connections that can be open at any given time.
     */
    public void setMaxTotalConnections(int max) {
        if (ccm != null) {
            ccm.setMaxTotal(max);
        } else {
            throw new UnsupportedOperationException("Client was created outside of HttpSolrServer");
        }
    }
}