org.opencommercesearch.SearchServerManager.java Source code

Java tutorial

Introduction

Here is the source code for org.opencommercesearch.SearchServerManager.java

Source

package org.opencommercesearch;

/*
* Licensed to OpenCommerceSearch under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. OpenCommerceSearch 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.
*/

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Locale;
import java.util.Set;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.request.DocumentAnalysisRequest;
import org.apache.solr.client.solrj.request.FieldAnalysisRequest;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.SolrPingResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.util.NamedList;

import atg.multisite.Site;
import atg.nucleus.ServiceException;
import atg.repository.Repository;
import atg.repository.RepositoryException;
import atg.repository.RepositoryItem;
import org.opencommercesearch.client.impl.Facet;

/**
 * This class implements a manager to handle SearchServers (Solr cores) for the integration tests.
 *
 * By default, search test will use a read only server. However, this behavior can be overridden with
 * the {@code @SearchTest} annotation setting the newInstance attribute to true. In such case, the read only
 * server will be clone and the test can modify it. The cloned server gets destroyed when the test finishes running.
 *
 * @gsegura
 * @rmerizalde
 */
public class SearchServerManager {

    static {
        final InputStream inputStream = SearchServerManager.class.getResourceAsStream("/logging.properties");
        try {
            if (inputStream != null) {
                LogManager.getLogManager().readConfiguration(inputStream);
            }
        } catch (final IOException e) {
            Logger.getAnonymousLogger().severe("Could not load default logging.properties file");
            Logger.getAnonymousLogger().severe(e.getMessage());
        }
    }

    private static SearchServerManager searchServerManager;
    private EmbeddedSearchServer searchServer;
    private SearchServer readOnlySearchServer;

    private SearchServerManager() {
    }

    public static synchronized SearchServerManager getInstance() {
        if (searchServerManager == null) {
            searchServerManager = new SearchServerManager();
        }
        return searchServerManager;
    }

    /**
    * Check if there's an instance of the read-only or the read-write solr
    * server running
    *
    * @param roServer Specify which instance to check. If true will check if RO instance is running
    *
    * @return Indicate if the search server instance is running
    */
    public boolean isServerRunning(boolean roServer) {
        if (roServer) {
            return readOnlySearchServer != null;
        } else {
            return searchServer != null;
        }
    }

    /**
     * Returns a readonly search server. Test will fail if they attempt to do updates on such server.
     * @return
     */
    public SearchServer getSearchServer() {
        return getSearchServer(true, true, null, null, null);
    }

    /**
     * Returns a read-write instance which is a copy of the read only server.
     *
     * @param name the name to identify the new server (at its cores)
     *
     * @return a read-write search sever instance
     */
    public SearchServer getSearchServer(String name) {
        return getSearchServer(false, true, name, null, null);
    }

    /**
     * Returns a read-write instance which is a copy of the read only server.
     *
     * @param name the name to identify the new server (at its cores)
     *
     * @return a read-write search sever instance
     */
    public SearchServer getSearchServerWithEmptyIndex(String name) {
        return getSearchServer(false, false, name, null, null);
    }

    /**
     * Returns a read-write instance which is a copy of the read only server.
     *
     * @param name the name to identify the new server (at its cores)
     * @param productDataResource the URL to the XML product data resource
     * @param rulesDataResource the URL to the XML rules data resource
     *
     *
     * @return a read-write search sever instance
     */
    public SearchServer getSearchServerWithResources(String name, String productDataResource,
            String rulesDataResource, Locale locale) {
        return getSearchServer(false, true, name, loadXmlResource(productDataResource),
                loadXmlResource(rulesDataResource), locale);
    }

    /**
     * Returns a read-write instance which is a copy of the read only server.
     *
     * @param name the name to identify the new server (at its cores)
     * @param productDataXml is a String with the XML data for products
     * @param rulesDataXml is a String with the XML data for rules
     *
     * @return a read-write search sever instance
     */
    public SearchServer getSearchServer(String name, String productDataXml, String rulesDataXml) {
        return getSearchServer(false, true, name, productDataXml, rulesDataXml);
    }

    /**
     * Helper method to create a search server. If readonly is set to true, the read only instance is returned.
     * Otherwise, the read only instances is cloned and the given name is used to identify the new server.
     *
     */
    private SearchServer getSearchServer(boolean readOnly, boolean loadBootstrapData, String name,
            String productDataResource, String rulesDataResource) {
        return getSearchServer(readOnly, loadBootstrapData, name, productDataResource, rulesDataResource,
                Locale.ENGLISH);
    }

    /**
     * Helper method to create a search server. If readonly is set to true, the read only instance is returned.
     * Otherwise, the read only instances is cloned and the given name is used to identify the new server.
     *
     */
    private synchronized SearchServer getSearchServer(boolean readOnly, boolean loadBootstrapData, String name,
            String productDataResource, String rulesDataResource, Locale locale) {

        if (searchServer == null) {
            initServer(loadBootstrapData);
        }

        if (readOnly) {
            return readOnlySearchServer;
        }

        try {
            EmbeddedSearchServer copy = searchServer.createCopy(name);
            if (StringUtils.isNotBlank(productDataResource)) {
                copy.updateCollection(copy.getCatalogCollection(), productDataResource, locale);
            }
            if (StringUtils.isNotBlank(rulesDataResource)) {
                copy.updateCollection(copy.getRulesCollection(), rulesDataResource, locale);
            }
            return copy;
        } catch (SolrServerException ex) {
            throw new RuntimeException(ex);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Shutdowns a SearchServer clone through the getSearchServer method.
     *
     * @param server the server to shutdown
     */
    public void shutdown(SearchServer server) {
        if (server instanceof EmbeddedSearchServer) {
            try {
                ((EmbeddedSearchServer) server).shutdownCores();
            } catch (SolrServerException ex) {
                throw new RuntimeException(ex);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    /**
     * Shutdowns the singleton search server
     */
    public void shutdown() {
        if (searchServer != null) {
            shutdown(searchServer);
        }
        readOnlySearchServer = null;
    }

    /**
    * Initializes the read only search server. The ro server is a singleton.
    * If the tests are configured to run in parallel multiple JVMs will be spawn and each will
    * have its own read only server.
    *
    * The default data xml files are:
    *
    *   catalog: /product_catalog/bootstrap_en.xml
    *   rules: /rules/bootstrap_en.xml
    */
    public void initServer(boolean loadBootstrapData) {
        initServerAux(loadBootstrapData, loadXmlResource("/product_catalog/bootstrap_en.xml"),
                loadXmlResource("/rules/bootstrap_en.xml"), null, null);
        try {
            searchServer.updateCollection(searchServer.getCatalogCollection(),
                    loadXmlResource("/product_catalog/bootstrap_fr.xml"), Locale.FRENCH);
            searchServer.updateCollection(searchServer.getRulesCollection(),
                    loadXmlResource("/rules/bootstrap_fr.xml"), Locale.FRENCH);
        } catch (SolrServerException ex) {
            throw new RuntimeException(ex);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Initializes the read only search server. The ro server is a singleton.
     * If the test are configured to run in parallel multiple JVMs will be spawn and each will
     * have its own read only server.
     *
     * This signature allows to use custom catalog and rules xml files.
     */
    public void initServer(boolean loadBootstrapData, String productDataXml, String rulesDataXml,
            RulesBuilder rulesBuilder, Repository searchRepository) {
        initServerAux(loadBootstrapData, productDataXml, rulesDataXml, rulesBuilder, searchRepository);
    }

    /**
     * Helper method to initialize the read only search server. The ro server is a singleton.
     * If the tests are configured to run in parallel multiple JVM will be spawn and each will
     * have its own read only server.
     * @param searchRepository 
     * @param rulesBuilder 
     */
    private void initServerAux(boolean loadBootstrapData, String productDataXml, String rulesDataXml,
            RulesBuilder rulesBuilder, Repository searchRepository) {

        searchServer = new EmbeddedSearchServer();
        searchServer.setCatalogCollection("catalogPreview");
        searchServer.setRulesCollection("rulePreview");
        searchServer.setInMemoryIndex(true);
        searchServer.setEnabled(true);
        searchServer.setSolrConfigUrl("/solr/solr_preview.xml");
        searchServer.setSolrCorePath("solr");
        searchServer.setLoggingDebug(false);
        searchServer.setLoggingInfo(false);
        searchServer.setLoggingWarning(false);
        searchServer.setLoggingTrace(false);
        searchServer.setSearchRepository(searchRepository);
        searchServer.setRulesBuilder(rulesBuilder);

        try {
            searchServer.doStartService();
            if (loadBootstrapData) {
                if (productDataXml != null) {
                    searchServer.updateCollection(searchServer.getCatalogCollection(), productDataXml,
                            Locale.ENGLISH);
                }
                if (rulesDataXml != null) {
                    searchServer.updateCollection(searchServer.getRulesCollection(), rulesDataXml, Locale.ENGLISH);
                }
            }
            readOnlySearchServer = new ReadOnlySearchServer(searchServer);

        } catch (ServiceException ex) {
            throw new RuntimeException(ex);
        } catch (SolrServerException ex) {
            throw new RuntimeException(ex);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Helper method to load an XML resource into a String object
     */
    String loadXmlResource(String resourceName) {

        String out = null;

        if (StringUtils.isBlank(resourceName)) {
            return null;
        }

        InputStream stream = null;

        try {
            stream = getClass().getResourceAsStream(resourceName);
            StringWriter writer = new StringWriter();

            IOUtils.copy(stream, writer);
            out = writer.getBuffer().toString();
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            }
        }
        return out;
    }

    public void updateCollection(SearchServer server, String collectionName, String xmlBody, Locale locale) {
        if (server instanceof EmbeddedSearchServer) {
            try {
                ((EmbeddedSearchServer) server).updateCollection(collectionName, xmlBody, locale);
            } catch (SolrServerException ex) {
                throw new RuntimeException(ex);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        } else {
            throw new UnsupportedOperationException(
                    "Unable to update server with type " + server.getClass().getName());
        }
    }

    /**
     * Simple wrapper class for read-only search server
     */
    private static class ReadOnlySearchServer implements SearchServer {
        private SearchServer server;

        ReadOnlySearchServer(SearchServer server) {
            this.server = server;
        }

        @Override
        public void connect() {
        }

        @Override
        public void close() {
        }

        @Override
        public SearchResponse search(SolrQuery query, FilterQuery... filterQueries) throws SearchServerException {
            return server.search(query, filterQueries);
        }

        @Override
        public SearchResponse search(SolrQuery query, Locale locale, FilterQuery... filterQueries)
                throws SearchServerException {
            return server.search(query, locale, filterQueries);
        }

        @Override
        public SearchResponse search(SolrQuery query, Site site, FilterQuery... filterQueries)
                throws SearchServerException {
            return server.search(query, site, filterQueries);
        }

        @Override
        public SearchResponse search(SolrQuery query, Site site, Locale locale, FilterQuery... filterQueries)
                throws SearchServerException {
            return server.search(query, site, locale, filterQueries);
        }

        @Override
        public SearchResponse search(SolrQuery query, Site site, RepositoryItem catalog,
                FilterQuery... filterQueries) throws SearchServerException {
            return server.search(query, site, catalog, filterQueries);
        }

        @Override
        public SearchResponse search(SolrQuery query, Site site, RepositoryItem catalog, Locale locale,
                FilterQuery... filterQueries) throws SearchServerException {
            return server.search(query, site, catalog, locale, filterQueries);
        }

        @Override
        public UpdateResponse add(Collection<SolrInputDocument> docs) throws SearchServerException {
            throw new UnsupportedOperationException("Can't add a document to read only search server");
        }

        @Override
        public UpdateResponse add(Collection<SolrInputDocument> docs, Locale locale) throws SearchServerException {
            throw new UnsupportedOperationException("Can't add a document to read only search server");
        }

        public UpdateResponse add(Collection<SolrInputDocument> docs, String collection, Locale locale)
                throws SearchServerException {
            throw new UnsupportedOperationException("Can't add a document to read only search server");
        }

        @Override
        public UpdateResponse commit() throws SearchServerException {
            throw new UnsupportedOperationException("Can't coommit on a read only search server");
        }

        @Override
        public UpdateResponse commit(Locale locale) throws SearchServerException {
            throw new UnsupportedOperationException("Can't coommit on a read only search server");
        }

        @Override
        public UpdateResponse commit(String collection, Locale locale) throws SearchServerException {
            throw new UnsupportedOperationException("Can't coommit on a read only search server");
        }

        @Override
        public UpdateResponse rollback() throws SearchServerException {
            throw new UnsupportedOperationException("Can't rollback on a read only search server");
        }

        @Override
        public UpdateResponse rollback(Locale locale) throws SearchServerException {
            throw new UnsupportedOperationException("Can't rollback on a read only search server");
        }

        @Override
        public UpdateResponse rollback(String collection, Locale locale) throws SearchServerException {
            throw new UnsupportedOperationException("Can't rollback on a read only search server");
        }

        @Override
        public UpdateResponse deleteByQuery(String query) throws SearchServerException {
            throw new UnsupportedOperationException("Can't delete documents in a read only search server");
        }

        @Override
        public UpdateResponse deleteByQuery(String query, Locale locale) throws SearchServerException {
            throw new UnsupportedOperationException("Can't delete documents in a read only search server");
        }

        @Override
        public UpdateResponse deleteByQuery(String query, String collection, Locale locale)
                throws SearchServerException {
            throw new UnsupportedOperationException("Can't delete documents in a read only search server");
        }

        @Override
        public SolrPingResponse ping() throws SearchServerException {
            return server.ping();
        }

        @Override
        public SolrPingResponse ping(Locale locale) throws SearchServerException {
            return server.ping(locale);
        }

        @Override
        public NamedList<Object> analyze(DocumentAnalysisRequest request) throws SearchServerException {
            return server.analyze(request);
        }

        @Override
        public NamedList<Object> analyze(DocumentAnalysisRequest request, Locale locale)
                throws SearchServerException {
            return server.analyze(request, locale);
        }

        @Override
        public NamedList<Object> analyze(FieldAnalysisRequest request) throws SearchServerException {
            return server.analyze(request);
        }

        @Override
        public NamedList<Object> analyze(FieldAnalysisRequest request, Locale locale) throws SearchServerException {
            return server.analyze(request, locale);
        }

        @Override
        public NamedList<Object> analyze(FieldAnalysisRequest request, String collection, Locale locale)
                throws SearchServerException {
            return server.analyze(request, locale);
        }

        @Override
        public SearchResponse termVector(String query, String... fields) throws SearchServerException {
            return server.termVector(query, fields);
        }

        @Override
        public SearchResponse termVector(String query, Locale locale, String... fields)
                throws SearchServerException {
            return server.termVector(query, locale, fields);
        }

        @Override
        public void onRepositoryItemChanged(String repositoryName, Set<String> itemDescriptorNames)
                throws RepositoryException, SearchServerException {
            throw new UnsupportedOperationException("Can't modify a search server");
        }

        @Override
        public void onProductChanged(RepositoryItem product) throws RepositoryException, SearchServerException {
            throw new UnsupportedOperationException("Can't modify a search server");
        }

        @Override
        public SearchResponse browse(BrowseOptions options, SolrQuery query, FilterQuery... filterQueries)
                throws SearchServerException {
            return server.browse(options, query, filterQueries);
        }

        @Override
        public SearchResponse browse(BrowseOptions options, SolrQuery query, Locale locale,
                FilterQuery... filterQueries) throws SearchServerException {
            return server.browse(options, query, locale, filterQueries);
        }

        @Override
        public SearchResponse browse(BrowseOptions options, SolrQuery query, Site site,
                FilterQuery... filterQueries) throws SearchServerException {
            return server.browse(options, query, site, filterQueries);
        }

        @Override
        public SearchResponse browse(BrowseOptions options, SolrQuery query, Site site, Locale locale,
                FilterQuery... filterQueries) throws SearchServerException {
            return server.browse(options, query, site, locale, filterQueries);
        }

        @Override
        public Facet getFacet(Site site, Locale locale, String fieldFacet, int facetLimit, int depthLimit,
                String separator, FilterQuery... filterQueries) throws SearchServerException {
            return server.getFacet(site, locale, fieldFacet, facetLimit, depthLimit, separator, filterQueries);
        }

        @Override
        public QueryResponse query(SolrQuery solrQuery, String collection, Locale locale)
                throws SearchServerException {
            return server.query(solrQuery, collection, locale);
        }

    }
}