All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.jknack.mwa.solr.SolrModule Maven / Gradle / Ivy

package com.github.jknack.mwa.solr;

import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.apache.commons.lang3.Validate.isTrue;
import static org.apache.commons.lang3.Validate.notEmpty;
import static org.apache.commons.lang3.Validate.notNull;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
import org.apache.solr.client.solrj.request.DirectXmlRequest;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrCore;
import org.apache.solr.servlet.SolrDispatchFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.util.ResourceUtils;
import org.xml.sax.SAXException;

import com.github.jknack.mwa.FilterMapping;

/**
 * 

Configure the Solr home:

The following property need to be present in the environment: * *
 * solr.home=
 * 
* * The 'solr.home' property can be a any valid Spring {@link Resource} expression. * Per each Solr 'core' a {@link SolrServer} is created and published into the * Spring Application Context. * The bean's name matches the name of the 'core', so at any time you can inject * a {@link SolrServer} by doing: * *
 * public MyService(@Named("core") SolrServer) {
 * ...
 * }
 * 
* * Of course if there is just one core you don't need to add the Named * annotation. * *

Configure Solr Data Directory

It is recommended to configure a Solr data directoy. * *
 * solr.dataDir=
 * 
* * If solr.dataDir isn't set a temporary directory will be selected. * *

Configure Solr URI mapping:

By default the Solr will be mounted at /search. * You can change that by: * *
 * solr.uri=/query
 * 
* *

Fixtures

You can add test or init data by creating a 'fixtures' directory under a Solr * core. * Test files are described using the Solr XML format for documents.

Fixtures properties

*
    * solr.fixtures: enabled or disabled the loading of test files. Default is: true. * solr.fixtures.async: if true, a new thread will be created for loading the fixtures. Default is: * true. *
* * @author edgar.espina */ @Configuration public class SolrModule { /** * The logging system. */ private static final Logger logger = LoggerFactory.getLogger(SolrModule.class); /** * The solr.home property. */ public static final String SOLR_HOME = "solr.home"; /** * The solr.data property. Default is: ${java.io.tmpdir}/{application.name} */ public static final String SOLR_DATA = "solr.dataDir"; /** * The solr.uri property. Default is: /search. */ public static final String SOLR_URI = "solr.uri"; /** * Enabled or disabled solr fixtures. Default is: true. */ public static final String SOLR_FIXTURES = "solr.fixtures"; /** * Run fixtures in a new thread. Default: is true. */ private static final String SOLR_FIXTURES_ASYNC = SOLR_FIXTURES + ".async"; /** * The application context. */ @Inject private ApplicationContext applicationContext; /** * Creates a {@link CoreContainer} object which has all the Solr cores. * * @param context The application's context. Required. * @return A {@link CoreContainer} object which has all the Solr cores. * @throws IOException If the Solr home cannot be created. * @throws ParserConfigurationException If the any of the Solr XML files are * corrupted. * @throws SAXException If the any of the Solr XML files are corrupted. */ @Bean public static CoreContainer solrCores(final ApplicationContext context) throws IOException, ParserConfigurationException, SAXException { notNull(context, "The application's context is required."); Environment env = context.getEnvironment(); final File solrHome = findSolrHome(context); final File dataDir = findSolrDataDir(env); final Map coreDefs = new LinkedHashMap(); CoreContainer cores = new CoreContainer(solrHome.getAbsolutePath()) { @Override public SolrCore create(final CoreDescriptor coreDescriptor) { String coreName = coreDescriptor.getName(); if (coreName.length() == 0) { coreName = getDefaultCoreName(); } Properties properties = new Properties(); String coreDataDir = new File(dataDir, coreName).getAbsolutePath(); coreDefs.put(coreName, coreDataDir); // default data dir properties.setProperty(CoreDescriptor.CORE_DATADIR, coreDataDir); return super.create(new CoreDescriptor(coreDescriptor.getCoreContainer(), coreDescriptor .getName(), coreDescriptor.getInstanceDir(), properties)); } }; // Initialize cores cores.load(); logger.info("Solr home directory: {}", solrHome); for (Entry core : coreDefs.entrySet()) { logger.info(" core: {}, dataDir: {}", core.getKey(), core.getValue()); } return cores; } /** * Add as many {@link SolrServer} as cores exists. * * @param cores The Solr cores. * @return A {@link BeanFactoryPostProcessor} that will creates as many {@link SolrServer} as * cores exists. */ @Bean public static BeanFactoryPostProcessor solrServerFactory( final CoreContainer cores) { return new BeanFactoryPostProcessor() { @Override public void postProcessBeanFactory( final ConfigurableListableBeanFactory beanFactory) { for (String coreName : coreNames(cores)) { coreName = isEmpty(coreName) ? cores.getDefaultCoreName() : coreName; logger.debug("Creating Solr server for: {}", coreName); SolrServer server = new EmbeddedSolrServer(cores, coreName); beanFactory.registerSingleton(coreName, server); } } }; } /** * Add a Solr dispatcher filter under {@link #SOLR_URI} uri mapping. Default * is: '/search'. * * @param env The application environment. Required. * @param solrCores The Solr cores. Required. * @return A Solr dispatcher filter. */ @Bean public static FilterMapping solrDispatcherFilter(final Environment env, final CoreContainer solrCores) { notNull(env, "The environment is required."); notNull(solrCores, "The Solr's cores are required."); String uri = env.getProperty(SOLR_URI, "/search"); if (!uri.startsWith("/")) { uri = "/" + uri; } if (uri.endsWith("/")) { uri = uri.substring(0, uri.length() - 1); } final String pattern = uri + "/**/*"; return FilterMapping.filter(pattern) .param("path-prefix", uri) .through(new SolrDispatchFilter() { @Override protected CoreContainer createCoreContainer() { logger.info("Solr is listening at: {}", pattern); // Don't initialize cores twice just use the Spring one. return solrCores; } }); } /** *

Fixtures

You can add test or init data by creating a 'fixtures' directory under a Solr * core. * Test files are described using the Solr XML format for documents.

Fixtures properties

*
    * solr.fixtures: enabled or disabled the loading of test files. Default is: true. * solr.fixtures.async: if true, a new thread will be created for loading the fixtures. Default * is: true. *
*/ @PostConstruct public void runFixtures() { Environment env = applicationContext.getEnvironment(); boolean runFixtures = env.getProperty(SOLR_FIXTURES, boolean.class, true); if (runFixtures) { Map servers = applicationContext.getBeansOfType(SolrServer.class); CoreContainer cores = applicationContext.getBean(CoreContainer.class); String solrHome = cores.getSolrHome(); boolean async = env.getProperty(SOLR_FIXTURES_ASYNC, boolean.class, true); for (Entry server : servers.entrySet()) { String coreName = server.getKey(); File coreHome = new File(solrHome, coreName); File fixtures = new File(coreHome, "fixtures"); if (fixtures.exists()) { populate(server.getValue(), coreName, fixtures, async); } } } } /** * Load fixtures and send it to the {@link SolrServer}. * * @param server The solr server. * @param core The core's name. * @param fixturesDir The fixtures directory. * @param async True for using a thread. */ private static void populate(final SolrServer server, final String core, final File fixturesDir, final boolean async) { final Collection xmlFiles = FileUtils.listFiles(fixturesDir, new String[]{"xml" }, true); if (xmlFiles.size() > 0) { Runnable post = new Runnable() { @Override public void run() { try { for (File xmlFile : xmlFiles) { logger.info("[{}]: sending: {}...", core, xmlFile); // Ensure it's in UTF-8 encoding Reader reader = new InputStreamReader( new FileInputStream(xmlFile), "UTF-8"); String body = IOUtils.toString(reader); SolrRequest request = new DirectXmlRequest("/update", body); /** Post the document to the Index */ request.process(server); IOUtils.closeQuietly(reader); } // Commit the changes server.commit(); } catch (Exception ex) { logger.error("Unable to initialize data", ex); } } }; if (async) { Thread thread = new Thread(post, core + "-postData"); thread.setDaemon(true); thread.start(); } else { post.run(); } } } /** * Find the solr's data from the given environment. Default is: * ${java.io.tmpdir}/{application.name}. * * @param env The application's environment. * @return The solr's data directory. */ private static File findSolrDataDir(final Environment env) { String app = env.getProperty("application.name", "solr-data"); File defaultDataDir = new File(env.getProperty("java.io.tmpdir"), app); String dataDirValue = env.getProperty(SOLR_DATA, defaultDataDir.getAbsolutePath()); notEmpty(dataDirValue, "{} is not set", SOLR_DATA); File dataDir = new File(dataDirValue); if (!dataDir.exists()) { dataDir.mkdirs(); } return dataDir; } /** * Find the solr home from the given environment. * * @param context The application's environment. * @return The solr's home directory. * @throws IOException If the solr.home cannot be resolve. */ private static File findSolrHome(final ApplicationContext context) throws IOException { Environment env = context.getEnvironment(); String solrHome = env.getRequiredProperty(SOLR_HOME); File solrHomeDir = new File(solrHome); if (!solrHomeDir.exists()) { // Ask Spring for it Resource resource = context.getResource(solrHome); if (!resource.exists()) { resource = context.getResource(ResourceUtils.CLASSPATH_URL_PREFIX + solrHome); } if (resource.exists()) { solrHomeDir = resource.getFile(); } } isTrue(solrHomeDir.exists(), "Cannot to set: '%s', '%s' doesn't exist", SOLR_HOME, solrHomeDir); isTrue(solrHomeDir.isDirectory(), "Cannot to set: '%s', '%s' must be a directory", SOLR_HOME, solrHomeDir); return solrHomeDir; } /** * Extract core names. * * @param cores All the available cores. * @return The core's name. */ private static Iterable coreNames(final CoreContainer cores) { List names = new ArrayList(); for (SolrCore core : cores.getCores()) { String name = core.getName(); if (isEmpty(name)) { // This is the default core. name = cores.getDefaultCoreName(); } names.add(name); } return names; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy