org.dbflute.solr.bhv.AbstractSolrBehavior.java Source code

Java tutorial

Introduction

Here is the source code for org.dbflute.solr.bhv.AbstractSolrBehavior.java

Source

/*
 * Copyright 2015-2016 the original author or authors.
 *
 * Licensed 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 org.dbflute.solr.bhv;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.List;

import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest.METHOD;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.impl.LBHttpSolrClient;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.dbflute.bhv.exception.BehaviorExceptionThrower;
import org.dbflute.optional.OptionalEntity;
import org.dbflute.solr.cbean.SolrCBCall;
import org.dbflute.solr.cbean.SolrConditionBean;
import org.dbflute.solr.entity.AbstractSolrIndexEntity;
import org.dbflute.solr.entity.SolrEntity;
import org.dbflute.solr.exception.SolrException;
import org.dbflute.solr.result.SolrFacetResultBean;
import org.dbflute.solr.result.SolrPagingResultBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The abstract class of behavior.
 * @param <ENTITY> The type of entity handled by this behavior.
 * @param <INDEX> The type of index entity handled by this behavior.
 * @param <CB> The type of condition-bean handled by this behavior.
 * @author FreeGen
 */
@SuppressWarnings("deprecation")
public abstract class AbstractSolrBehavior<ENTITY extends SolrEntity, INDEX extends AbstractSolrIndexEntity, CB extends SolrConditionBean> {

    // ===================================================================================
    //                                                                          Definition
    //                                                                          ==========
    private static final Logger logger = LoggerFactory.getLogger(AbstractSolrBehavior.class);

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    /** Master client */
    protected SolrClient masterSolrClient;

    /** Slave client */
    protected SolrClient slaveSolrClient;

    public abstract CB newConditionBean();

    public abstract Class<ENTITY> getEntityClass();

    public SolrClient getMasterSolrClient() {
        return masterSolrClient;
    }

    public SolrClient getSlaveSolrClient() {
        return slaveSolrClient;
    }

    /**
     * Retrieve count of the number of entities matched to the specified query condition.
     * @param cbLambda ConditionBean callback function (NotNull)
     * @return Total number of entities matched to query condition
     */
    public int selectCount(SolrCBCall<CB> cbLambda) {
        CB cb = createCB(cbLambda);
        SolrQuery query = cb.buildSolrQuery();
        query.setStart(0);
        query.setRows(0);

        if (logger.isDebugEnabled()) {
            logger.debug("cb={}", cb);
        }

        try {
            QueryResponse rsp = getSlaveSolrClient().query(query, METHOD.POST);
            return (int) rsp.getResults().getNumFound();
        } catch (SolrServerException | IOException e) {
            throw new SolrException("Failed to Solr of access.", e);
        }
    }

    /** 
     * Retrieve entities by paging-search.
     * @param cbLambda ConditionBean callback function (NotNull)
     * @return List of the result entities (NotNull)
     */
    public SolrPagingResultBean<ENTITY> selectPage(SolrCBCall<CB> cbLambda) {
        return selectPage(cbLambda, getEntityClass());
    }

    /**
     * Retrieve entities by paging-search.
     * @param cbLambda ConditionBean callback function (NotNull)
     * @param clazz BEAN class for the result search (NotNull)
     * @param <RESULT_BEAN> BEAN for the result of search
     * @return List of the result entities (NotNull)
     */
    public <RESULT_BEAN> SolrPagingResultBean<RESULT_BEAN> selectPage(SolrCBCall<CB> cbLambda,
            Class<RESULT_BEAN> clazz) {
        CB cb = createCB(cbLambda);
        SolrQuery query = cb.buildSolrQuery();

        if (logger.isDebugEnabled()) {
            logger.debug("\n" + cb);
        }

        try {
            QueryResponse rsp = getSlaveSolrClient().query(query, METHOD.POST);

            SolrPagingResultBean<RESULT_BEAN> pagingResult = new SolrPagingResultBean<RESULT_BEAN>();
            List<RESULT_BEAN> beans = rsp.getBeans(clazz);
            if (SolrEntity.class.isAssignableFrom(clazz)) {
                beans.forEach(bean -> {
                    SolrEntity entity = (SolrEntity) bean;
                    entity.markAsSelect();
                    if (cb.isSpecified()) {
                        entity.modifiedToSpecified(cb.getSpecifyPropertys());
                    }
                });
            }

            pagingResult.setSelectedList(beans);
            pagingResult.setPageSize(cb.getPageSize());
            pagingResult.setCurrentPageNumber(cb.getPageNumer());
            long numFound = rsp.getResults().getNumFound();
            pagingResult.setAllRecordCount((int) numFound);
            pagingResult.setQueryTime(rsp.getQTime());
            pagingResult.setQueryString(query.toString());
            return pagingResult;
        } catch (SolrServerException | IOException e) {
            throw new SolrException("Failed to Solr of access.", e);
        }
    }

    /**
     * Retrieve an entity matched to the specified query condition.
     * @param cbLambda ConditionBean callback function (NotNull)
     * @return Result entity (NotNull)
     */
    public OptionalEntity<ENTITY> selectFirst(SolrCBCall<CB> cbLambda) {
        return selectFirst(cbLambda, getEntityClass());
    }

    /**
     * Retrieve an entity matched to the specified query condition.
     * @param cbLambda ConditionBean callback function (NotNull)
     * @param clazz BEAN class for the result search (NotNull)
     * @param <RESULT_BEAN> BEAN for the result of search
     * @return Result entity (NotNull)
     */
    public <RESULT_BEAN> OptionalEntity<RESULT_BEAN> selectFirst(SolrCBCall<CB> cbLambda,
            Class<RESULT_BEAN> clazz) {
        return createOptionalEntity(selectPage(cb -> {
            cbLambda.callback(cb);
            cb.paging(1, 1);
        }, clazz).stream().findFirst().orElse(null), createCB(cb -> {
            cbLambda.callback(cb);
            cb.paging(1, 1);
        }));
    }

    /**
     * Retrieve entities by faceted search.
     * @param cbLambda ConditionBean callback function
     * @return List of faceted search result (NotNull)
     */
    public SolrFacetResultBean selectFacetQuery(SolrCBCall<CB> cbLambda) {
        CB cb = createCB(cbLambda);
        SolrQuery query = cb.buildSolrQuery();
        query.setStart(0);
        query.setRows(0);

        if (logger.isDebugEnabled()) {
            logger.debug("cb={}", cb);
        }

        try {
            QueryResponse rsp = getSlaveSolrClient().query(query, METHOD.POST);

            SolrFacetResultBean resultBean = new SolrFacetResultBean();
            resultBean.setFacetResult(rsp.getFacetQuery());
            resultBean.setFacetFieldList(rsp.getFacetFields());
            resultBean.setQueryString(query.toString());
            resultBean.setQueryTime(rsp.getQTime());
            return resultBean;
        } catch (SolrServerException | IOException e) {
            throw new SolrException("Failed to Solr of access.", e);
        }
    }

    /**
     * Delete all index.
     */
    public void deleteAllIndex() {
        try {
            getMasterSolrClient().deleteByQuery("*:*");
        } catch (SolrServerException | IOException e) {
            throw new SolrException("Failed to Solr of access.", e);
        }
    }

    /**
     * Delete index associated with the assigned ID.
     * @param id ID for an index to be deleted
     */
    public void deleteById(Object id) {
        try {
            getMasterSolrClient().deleteByQuery("id:" + id);
        } catch (SolrServerException | IOException e) {
            throw new SolrException("Failed to Solr of access.", e);
        }
    }

    /**
     * Add index entity list.
     * @param indexEntityList Index entity list (NullAllowed: do nothing if null)
     */
    public void addSolrIndexEntity(List<INDEX> indexEntityList) {
        if (indexEntityList == null || indexEntityList.isEmpty()) {
            return;
        }
        try {
            getMasterSolrClient().addBeans(indexEntityList);
        } catch (SolrServerException | IOException e) {
            throw new SolrException("Failed to Solr of access.", e);
        }
    }

    /**
     * Commit.
     */
    public void commit() {
        try {
            getMasterSolrClient().commit();
        } catch (SolrServerException | IOException e) {
            throw new SolrException("Failed to Solr of access.", e);
        }
    }

    /**
     * Optimize Solr.
     */
    public void optimize() {
        try {
            // Do not wait until the optimization prosess has finished.
            getMasterSolrClient().optimize(false, false);
        } catch (SolrServerException | IOException e) {
            throw new SolrException("failed Solr Optimize.", e);
        }
    }

    public abstract int getConnectionTimeout();

    public abstract int getSocketTimeout();

    public abstract int getDefaultMaxConnectionsPerHost();

    public abstract int getMaxTotalConnections();

    public abstract int getAliveCheckInterval();

    /**
     * Create CondetionBean from the assigned callback function.
     * @param cbLambda ConditionBean callback function (NotNull)
     * @return ConditionBean
     */
    protected CB createCB(SolrCBCall<CB> cbLambda) {
        if (cbLambda == null) {
            throw new SolrException("cbLambda is null.");
        }
        CB cb = newConditionBean();
        cbLambda.callback(cb);
        return cb;
    }

    /**
     * Create HttpSolrClient associated with the assigned URL.
     * @param url URL (NotNull)
     * @return HttpSolrClient (NotNull)
     */
    protected HttpSolrClient createHttpSolrClient(String url) {
        HttpSolrClient client = new HttpSolrClient(url);
        client.setConnectionTimeout(getConnectionTimeout());
        client.setSoTimeout(getSocketTimeout());
        client.setDefaultMaxConnectionsPerHost(getDefaultMaxConnectionsPerHost());
        client.setMaxTotalConnections(getMaxTotalConnections());
        client.setFollowRedirects(false);
        return client;
    }

    /**
     * Create CloudSolrClient associated with the assigned host.
     * @param host Host (NotNull)
     * @return CloudSolrClient (NotNull)
     */
    protected CloudSolrClient createCloudSolrClient(String host) {
        CloudSolrClient client = new CloudSolrClient(host);
        client.setZkConnectTimeout(getConnectionTimeout());
        return client;
    }

    /**
     * Create LBHttpSolrClient associated with the assigned list of URLs.
     * @param urlList List of URL (NotNull)
     * @return LBHttpSolrClient (NotNull)
     */
    protected LBHttpSolrClient createLBHttpSolrClient(List<String> urlList) {
        LBHttpSolrClient client;
        try {
            client = new LBHttpSolrClient(urlList.toArray(new String[] {}));
        } catch (MalformedURLException e) {
            throw new SolrException("Failed to create Select Solr Client", e);
        }
        client.setConnectionTimeout(getConnectionTimeout());
        client.setSoTimeout(getSocketTimeout());
        client.setAliveCheckInterval(getAliveCheckInterval());
        adjustHttpClient(client.getHttpClient());
        return client;
    }

    /**
     * Create HTTP client.
     * @return HTTP client
     */
    protected HttpClient createHttpClient() {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(getMaxTotalConnections());
        connectionManager.setDefaultMaxPerRoute(getDefaultMaxConnectionsPerHost());
        HttpClientBuilder builder = HttpClientBuilder.create();
        builder.setConnectionManager(connectionManager);
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }

    /**
     * Adjust max total connections and default max connections per host of HTTP client 
     * @param httpClient HTTP client
     */
    protected void adjustHttpClient(HttpClient httpClient) {
        ClientConnectionManager connectionManager = httpClient.getConnectionManager();
        if (connectionManager instanceof PoolingClientConnectionManager) {
            PoolingClientConnectionManager poolingClientConnectionManager = (PoolingClientConnectionManager) connectionManager;
            poolingClientConnectionManager.setMaxTotal(getMaxTotalConnections());
            poolingClientConnectionManager.setDefaultMaxPerRoute(getDefaultMaxConnectionsPerHost());
        }
    }

    protected <RESULT> OptionalEntity<RESULT> createOptionalEntity(RESULT entity, Object... searchKey) {
        if (entity != null) {
            return OptionalEntity.of(entity);
        } else { // not serializable here because of select entity (while relation optional is able)
            return OptionalEntity.ofNullable(entity, () -> throwSelectEntityAlreadyDeletedException(searchKey));
        }
    }

    protected void throwSelectEntityAlreadyDeletedException(Object searchKey) {
        createBhvExThrower().throwSelectEntityAlreadyDeletedException(searchKey);
    }

    protected BehaviorExceptionThrower createBhvExThrower() {
        return new BehaviorExceptionThrower();
    }
}