/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This 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 2.1 of * the License, or (at your option) any later version. * * This software 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 this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.search.solr.internal; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import org.apache.commons.io.FileUtils; import org.apache.commons.io.input.CloseShieldInputStream; import org.apache.commons.lang3.StringUtils; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer; import org.apache.solr.core.CoreContainer; import org.xwiki.component.annotation.Component; import org.xwiki.component.manager.ComponentLifecycleException; import org.xwiki.component.phase.Disposable; import org.xwiki.component.phase.InitializationException; import org.xwiki.environment.Environment; import org.xwiki.search.solr.internal.api.SolrConfiguration; /** * Embedded Solr instance running in the same JVM. * * @version $Id: c3c6aca47a26d69902f5675f0543aec9c908698d $ * @since 4.3M2 */ @Component @Named(EmbeddedSolrInstance.TYPE) @Singleton public class EmbeddedSolrInstance extends AbstractSolrInstance implements Disposable { /** * Solr instance type for this implementation. */ public static final String TYPE = "embedded"; /** * Default directory name for Solr's configuration and index files. */ public static final String DEFAULT_SOLR_DIRECTORY_NAME = "solr"; /** * Solr configuration. */ @Inject private SolrConfiguration solrConfiguration; /** * Environment used to get the xwiki permanent directory. */ @Inject private Environment environment; /** * Solr CoreContainer. */ private CoreContainer container; @Override public void initialize() throws InitializationException { String solrHome = determineHomeDirectory(); try { // Validate and initialize the home directory if needed. validateAndInitializeHomeDirectory(solrHome); // Start embedded Solr server. this.logger.info("Starting embedded Solr server..."); this.logger.info("Using Solr home directory: [{}]", solrHome); // Initialize the SOLR back-end using an embedded server. this.container = createCoreContainer(solrHome); // If we get here then there is at least one core found. We there are more, we use the first one. String coreName = this.container.getCores().iterator().next().getName(); this.server = new EmbeddedSolrServer(container, coreName); this.logger.info("Started embedded Solr server."); } catch (Exception e) { throw new InitializationException(String.format( "Failed to initialize the Solr embedded server with home directory set to [%s]", solrHome), e); } } private CoreContainer createCoreContainer(String solrHome) throws SolrServerException { CoreContainer coreContainer = new CoreContainer(solrHome); coreContainer.load(); if (coreContainer.getCores().size() == 0) { throw new SolrServerException("Failed to initialize the Solr core. " + "Please check the configuration and log messages."); } else if (coreContainer.getCores().size() > 1) { this.logger.warn("Multiple Solr cores detected: [{}]. Using the first one.", StringUtils.join(coreContainer.getCoreNames(), ", ")); } return coreContainer; } @Override public void dispose() throws ComponentLifecycleException { if (this.server != null) { try { this.server.close(); } catch (IOException e) { this.logger.error("Failed to close server", e); } } if (this.container != null) { this.container.shutdown(); } } /** * Useful when testing. * * @return the container */ protected CoreContainer getContainer() { return this.container; } /** * Checks rights, creates paths and adds default config XML files if they don`t already exist. * * @param solrHome the directory to use as Solr home. * @throws IllegalArgumentException if the provided directory is not usable (is a file, is not writable, etc.). * @throws IOException if the XML files are not copied successfully. */ private void validateAndInitializeHomeDirectory(String solrHome) throws IllegalArgumentException, IOException { // Validate and create the directory if it does not already exist. File solrHomeDirectory = new File(solrHome); if (solrHomeDirectory.exists()) { // Exists but is unusable. if (!solrHomeDirectory.isDirectory() || !solrHomeDirectory.canWrite() || !solrHomeDirectory.canRead()) { throw new IllegalArgumentException(String.format( "The given path [%s] must be a readable and writable directory", solrHomeDirectory)); } } else { // Create the home directory if (!solrHomeDirectory.mkdirs()) { // Does not exist and can not be created. throw new IllegalArgumentException(String.format( "The given path [%s] could not be created due to and invalid value %s", solrHomeDirectory, "or to insufficient filesystem permissions")); } // Initialize the Solr Home with the default configuration files if the folder does not already exist. // Add the configuration files required by Solr. InputStream stream = this.solrConfiguration.getHomeDirectoryConfiguration(); try (ZipInputStream zstream = new ZipInputStream(stream)) { for (ZipEntry entry = zstream.getNextEntry(); entry != null; entry = zstream.getNextEntry()) { if (entry.isDirectory()) { File destinationDirectory = new File(solrHomeDirectory, entry.getName()); destinationDirectory.mkdirs(); } else { File destinationFile = new File(solrHomeDirectory, entry.getName()); FileUtils.copyInputStreamToFile(new CloseShieldInputStream(zstream), destinationFile); } } } } } /** * @return the configured home directory location or the default value if no configuration is present. */ private String determineHomeDirectory() { String defaultValue = getDefaultHomeDirectory(); return this.solrConfiguration.getInstanceConfiguration(TYPE, "home", defaultValue); } /** * @return the default home directory located inside the environment's permanent directory. */ String getDefaultHomeDirectory() { String result = new File(this.environment.getPermanentDirectory(), DEFAULT_SOLR_DIRECTORY_NAME).getPath(); return result; } }