SOLR-350 -- make MultiCore subclassable so to allow custom persistance methods (for example, SQL)

git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@635334 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Ryan McKinley 2008-03-09 19:29:04 +00:00
parent 705cade858
commit 0d633bdcea
9 changed files with 302 additions and 184 deletions

View File

@ -54,22 +54,31 @@ public class EmbeddedSolrServer extends SolrServer
protected ModifiableSolrParams _invariantParams;
protected ResponseParser _processor;
protected final SolrCore core;
protected final MultiCore multicore; // either multicore
protected final SolrCore core; // or single core
protected final SolrRequestParsers parser;
protected final String coreName; // use MultiCore registry
public EmbeddedSolrServer( SolrCore core )
{
if ( core == null ) {
throw new NullPointerException("SolrCore instance required");
}
this.core = core;
this.multicore = null;
this.coreName = null;
this.parser = init();
}
public EmbeddedSolrServer( String coreName )
public EmbeddedSolrServer( MultiCore multicore, String coreName )
{
if ( multicore == null ) {
throw new NullPointerException("MultiCore instance required");
}
this.core = null;
this.multicore = multicore;
this.coreName = coreName;
SolrCore c = MultiCore.getRegistry().getCore( coreName );
SolrCore c = multicore.getCore( coreName );
if( c == null ) {
throw new RuntimeException( "Unknown core: "+coreName );
}
@ -96,7 +105,6 @@ public class EmbeddedSolrServer extends SolrServer
}
// Check for multicore action
MultiCore multicore = MultiCore.getRegistry();
SolrCore core = this.core;
if( core == null ) {
core = multicore.getCore( coreName );
@ -126,7 +134,7 @@ public class EmbeddedSolrServer extends SolrServer
}
// Perhaps the path is to manage the cores
if( handler == null &&
coreName != null &&
multicore != null &&
path.equals( multicore.getAdminPath() ) &&
multicore.isEnabled() ) {
handler = multicore.getMultiCoreHandler();

View File

@ -25,7 +25,7 @@ import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.client.solrj.request.UpdateRequest.ACTION;
import org.apache.solr.client.solrj.response.MultiCoreResponse;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.MultiCore;
/**
@ -34,6 +34,9 @@ import org.apache.solr.core.SolrCore;
*/
public abstract class MultiCoreExampleTestBase extends SolrExampleTestBase
{
@SuppressWarnings("deprecation")
protected static final MultiCore multicore = org.apache.solr.core.SolrMultiCore.getInstance();
@Override public String getSolrHome() { return "../../../example/multicore/"; }
@Override public String getSchemaFile() { return getSolrHome()+"core0/conf/schema.xml"; }

View File

@ -21,7 +21,6 @@ import java.io.File;
import org.apache.solr.client.solrj.MultiCoreExampleTestBase;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.core.MultiCore;
/**
* This runs SolrServer test using
@ -37,24 +36,24 @@ public class MultiCoreEmbeddedTest extends MultiCoreExampleTestBase {
File home = new File( getSolrHome() );
File f = new File( home, "multicore.xml" );
MultiCore.getRegistry().load( getSolrHome(), f );
multicore.load( getSolrHome(), f );
}
@Override
protected SolrServer getSolrCore0()
{
return new EmbeddedSolrServer( "core0" );
return new EmbeddedSolrServer( multicore, "core0" );
}
@Override
protected SolrServer getSolrCore1()
{
return new EmbeddedSolrServer( "core1" );
return new EmbeddedSolrServer( multicore, "core1" );
}
@Override
protected SolrServer getSolrAdmin()
{
return new EmbeddedSolrServer( "core0" );
return new EmbeddedSolrServer( multicore, "core0" );
}
}

View File

@ -49,30 +49,33 @@ import org.xml.sax.SAXException;
*/
public class MultiCore
{
private static Logger log = Logger.getLogger(MultiCore.class.getName());
private static final MultiCore instance = new MultiCore();
protected static Logger log = Logger.getLogger(MultiCore.class.getName());
// Synchronized map of all cores
private final Map<String, CoreDescriptor> cores = new LinkedHashMap<String, CoreDescriptor>();
protected final Map<String, CoreDescriptor> cores = new LinkedHashMap<String, CoreDescriptor>();
protected boolean enabled = false;
protected boolean persistent = false;
protected String adminPath = null;
protected MultiCoreHandler multiCoreHandler = null;
protected File configFile = null;
protected String libDir = null;
protected ClassLoader libLoader = null;
protected SolrResourceLoader loader = null;
protected java.lang.ref.WeakReference<SolrCore> adminCore = null;
private boolean enabled = false;
private boolean persistent = false;
private String adminPath = null;
private MultiCoreHandler multiCoreHandler = null;
private File configFile = null;
private String libDir = null;
private ClassLoader libLoader = null;
private SolrResourceLoader loader = null;
// no one else can make the registry
private MultiCore() { }
public MultiCore() {
}
//-------------------------------------------------------------------
// Initialization / Cleanup
//-------------------------------------------------------------------
/**
* Load a config file listing the available solr cores
* Load a config file listing the available solr cores.
* @param dir the home directory of all resources.
* @param configFile the configuration file
* @throws javax.xml.parsers.ParserConfigurationException
* @throws java.io.IOException
* @throws org.xml.sax.SAXException
*/
public void load(String dir, File configFile ) throws ParserConfigurationException, IOException, SAXException {
this.configFile = configFile;
@ -94,7 +97,7 @@ public class MultiCore
}
if( adminPath != null ) {
multiCoreHandler = new MultiCoreHandler();
multiCoreHandler = this.createMultiCoreHandler();
}
NodeList nodes = (NodeList)cfg.evaluate("multicore/core", XPathConstants.NODESET);
@ -135,7 +138,9 @@ public class MultiCore
enabled = true;
}
/** Stops all cores. */
/**
* Stops all cores.
*/
public void shutdown() {
synchronized(cores) {
for(Map.Entry<String,CoreDescriptor> e : cores.entrySet()) {
@ -154,15 +159,11 @@ public class MultiCore
shutdown();
}
//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
/** Get the singleton */
public static MultiCore getRegistry() {
return instance;
}
/**
* Registers a SolrCore descriptor in the registry.
* @param descr the Solr core descriptor
* @return a previous descriptor having the same name if it existed, null otherwise
*/
public CoreDescriptor register( CoreDescriptor descr ) {
if( descr == null ) {
throw new RuntimeException( "Can not register a null core." );
@ -186,6 +187,12 @@ public class MultiCore
}
}
/**
* Swaps two SolrCore descriptors.
* @param c0
* @param c1
*/
public void swap(CoreDescriptor c0, CoreDescriptor c1) {
if( c0 == null || c1 == null ) {
throw new RuntimeException( "Can not swap a null core." );
@ -235,10 +242,11 @@ public class MultiCore
}
/**
* Recreates a SolrCore.
* While the new core is loading, requests will continue to be dispatched to
* and processed by the old core
*
* @param core
* @param core the SolrCore to reload
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
@ -314,6 +322,49 @@ public class MultiCore
this.adminPath = adminPath;
}
/**
* Sets the preferred core used to handle MultiCore admin tasks.
* Note that getAdminCore is not symmetrical to this method since
* it will allways return an opened SolrCore.
* This however can be useful implementing a "metacore" (a core of cores).
*/
public void setAdminCore(SolrCore core) {
adminCore = new java.lang.ref.WeakReference<SolrCore>(core);
}
/**
* Gets a core to handle MultiCore admin tasks (@see SolrDispatchFilter).
* This makes the best attempt to reuse the same opened SolrCore accross calls.
*/
public SolrCore getAdminCore() {
SolrCore core = adminCore != null ? adminCore.get() : null;
if (core == null || core.isClosed()) {
for (CoreDescriptor descr : this.cores.values()) {
core = descr.getCore();
if (core == null || core.isClosed()) {
core = null;
} else {
break;
}
}
setAdminCore(core);
}
return core;
}
/**
* Creates a MultiCoreHandler for this MultiCore.
* @return a MultiCoreHandler
*/
public MultiCoreHandler createMultiCoreHandler() {
return new MultiCoreHandler() {
@Override
public MultiCore getMultiCore() {
return MultiCore.this;
}
};
}
public MultiCoreHandler getMultiCoreHandler() {
return multiCoreHandler;
}
@ -439,4 +490,5 @@ public class MultiCore
throw xforward;
}
}
}

View File

@ -32,7 +32,6 @@ import javax.xml.parsers.ParserConfigurationException;
import java.util.Collection;
import java.util.HashSet;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

View File

@ -467,6 +467,10 @@ public final class SolrCore {
}
}
public boolean isClosed() {
return _searcher == null;
}
@Override
protected void finalize() { close(); }
@ -926,7 +930,7 @@ public final class SolrCore {
synchronized (searcherLock) {
if (_searcher != null) {
_searcher.decref(); // dec refcount for this._searcher
_searcher=null;
_searcher=null; // isClosed() does check this
infoRegistry.remove("currentSearcher");
}
}

View File

@ -0,0 +1,47 @@
/**
* 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 org.apache.solr.core;
/**
* A MultiCore singleton.
* Marked as deprecated to avoid usage proliferation of core code that would
* assume MultiCore being a singleton. In solr 2.0, the MultiCore factory
* should be popluated with a standard tool like spring. Until then, this is
* a simple static factory that should not be used widely.
*
* @version $Id$
* @since solr 1.3
*/
@Deprecated
public final class SolrMultiCore extends MultiCore
{
private static MultiCore instance = null;
// no one else can make the registry
private SolrMultiCore() {}
/** Returns a default MultiCore singleton.
* @return
*/
public static synchronized MultiCore getInstance() {
if (instance == null) {
instance = new SolrMultiCore();
}
return instance;
}
}

View File

@ -39,7 +39,7 @@ import org.apache.solr.util.RefCounted;
* @version $Id$
* @since solr 1.3
*/
public class MultiCoreHandler extends RequestHandlerBase
public abstract class MultiCoreHandler extends RequestHandlerBase
{
public MultiCoreHandler()
{
@ -56,11 +56,18 @@ public class MultiCoreHandler extends RequestHandlerBase
"it is a special Handler configured directly by the RequestDispatcher" );
}
/**
* The instance of multicore this handler handles.
* This should be the MultiCore instance that created this handler.
* @return a MultiCore instance
*/
public abstract MultiCore getMultiCore();
@Override
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception
{
// Make sure the manager is enabled
MultiCore manager = MultiCore.getRegistry();
MultiCore manager = getMultiCore();
if( !manager.isEnabled() ) {
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
"MultiCore support must be enabled at startup." );

View File

@ -21,7 +21,6 @@ import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.WeakHashMap;
import java.util.logging.Logger;
import java.util.logging.Level;
@ -74,23 +73,12 @@ public class SolrDispatchFilter implements Filter
this.pathPrefix = config.getInitParameter( "path-prefix" );
this.solrConfigFilename = config.getInitParameter("solrconfig-filename");
multicore = MultiCore.getRegistry();
if( multicore.isEnabled() ) {
log.info( "Using existing multicore configuration" );
}
else {
String instanceDir = SolrResourceLoader.locateInstanceDir();
File multiconfig = new File( instanceDir, "multicore.xml" );
log.info( "looking for multicore.xml: "+multiconfig.getAbsolutePath() );
if( multiconfig.exists() ) {
multicore.load( instanceDir, multiconfig );
}
}
// multicore instantiation
this.multicore = initMultiCore(config);
if(multicore != null && multicore.isEnabled() ) {
abortOnConfigurationError = false;
if( multicore.isEnabled() ) {
singlecore = null;
// if any core aborts on startup, then abort
for( SolrCore c : multicore.getCores() ) {
if( c.getSolrConfig().getBool( "abortOnConfigurationError",false) ) {
@ -100,11 +88,10 @@ public class SolrDispatchFilter implements Filter
}
}
else {
if (this.solrConfigFilename==null) {
singlecore = new SolrCore( null, null, new SolrConfig(), null );
} else {
singlecore = new SolrCore( null, null, new SolrConfig(this.solrConfigFilename), null);
}
SolrConfig cfg = this.solrConfigFilename == null? new SolrConfig() : new SolrConfig(this.solrConfigFilename);
singlecore = new SolrCore( null, null, cfg, null );
abortOnConfigurationError = cfg.getBool(
"abortOnConfigurationError", abortOnConfigurationError);
}
log.info("user.dir=" + System.getProperty("user.dir"));
}
@ -123,10 +110,9 @@ public class SolrDispatchFilter implements Filter
out.println( "Check your log files for more detailed information on what may be wrong.\n" );
out.println( "If you want solr to continue after configuration errors, change: \n");
out.println( " <abortOnConfigurationError>false</abortOnConfigurationError>\n" );
if( multicore.isEnabled() ) {
if (multicore != null && multicore.isEnabled()) {
out.println( "in multicore.xml\n" );
}
else {
} else {
out.println( "in solrconfig.xml\n" );
}
@ -148,24 +134,53 @@ public class SolrDispatchFilter implements Filter
log.info("SolrDispatchFilter.init() done");
}
/**
* Initializes the multicore instance.
* @param config the filter configuration
* @return the multicore instance or null
* @throws java.lang.Exception
*/
protected MultiCore initMultiCore(FilterConfig config) throws Exception {
@SuppressWarnings("deprecation") // since SolrDispatchFilter can be derived & initMultiCore can be overriden
MultiCore mcore = org.apache.solr.core.SolrMultiCore.getInstance();
if (mcore.isEnabled()) {
log.info("Using existing multicore configuration");
} else {
// multicore load
String instanceDir = SolrResourceLoader.locateInstanceDir();
File fconf = new File(instanceDir, "multicore.xml");
log.info("looking for multicore.xml: " + fconf.getAbsolutePath());
if (fconf.exists()) {
mcore.load(instanceDir, fconf);
}
}
return mcore;
}
public void destroy() {
if (multicore != null) {
multicore.shutdown();
multicore = null;
}
if( singlecore != null ) {
singlecore.close();
singlecore = null;
}
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if( abortErrorMessage != null ) {
((HttpServletResponse)response).sendError( 500, abortErrorMessage );
return;
}
if( request instanceof HttpServletRequest) {
SolrQueryRequest solrReq = null;
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse resp = (HttpServletResponse)response;
SolrRequestHandler handler = null;
SolrQueryRequest solrReq = null;
try {
String path = req.getServletPath();
if( req.getPathInfo() != null ) {
@ -183,133 +198,118 @@ public class SolrDispatchFilter implements Filter
}
// By default use the single core. If multicore is enabled, look for one.
SolrRequestHandler handler = null;
SolrCore core = singlecore;
if( core == null ) {
// Perhaps this is a multi-core admin page?
if( path.equals( "/" ) ) {
chain.doFilter(request, response);
return;
}
final SolrCore core;
if (multicore != null && multicore.isEnabled()) {
// if this is the multi-core admin page, it will handle it
if( path.equals( multicore.getAdminPath() ) ) {
handler = multicore.getMultiCoreHandler();
// pick a core to use for output
Collection<SolrCore> cores = multicore.getCores();
if( cores != null && cores.size() > 0 ) {
core = cores.iterator().next();
}
// pick a core to use for output generation
core = multicore.getAdminCore();
if( core == null ) {
throw new RuntimeException( "Can not find a valid core for the multicore admin handler" );
}
}
else {
} else {
//otherwise, we should find a core from the path
idx = path.indexOf( "/", 1 );
if( idx <= 1 ) {
idx = path.length();
}
// try to get the corename as a request parameter first
String corename = path.substring( 1, idx );
path = path.substring( idx );
core = multicore.getCore( corename );
if( path.length() == 0 ) {
path = "/";
}
if( core == null ) {
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "unknown core: "+corename );
if( idx > 1 ) {
// try to get the corename as a request parameter first
String corename = path.substring( 1, idx );
path = path.substring( idx );
core = multicore.getCore( corename );
} else {
core = null;
}
}
}
SolrRequestParsers parser = parsers.get( core );
if( parser == null ) {
parser = new SolrRequestParsers( core.getSolrConfig() );
parsers.put( core, parser );
}
// Only try to parse the handler *if* a valid core exists
// when multi-core is enabled, the path can lead to a null core.
if( handler == null && path.length() > 1 ) { // don't match "" or "/" as valid path
handler = core.getRequestHandler( path );
}
if( handler == null && parser.isHandleSelect() ) {
if( "/select".equals( path ) || "/select/".equals( path ) ) {
solrReq = parser.parse( core, path, req );
String qt = solrReq.getParams().get( CommonParams.QT );
if( qt != null && qt.startsWith( "/" ) ) {
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Invalid query type. Do not use /select to access: "+qt);
}
handler = core.getRequestHandler( qt );
if( handler == null ) {
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "unknown handler: "+qt);
}
}
}
if( handler != null ) {
if( solrReq == null ) {
solrReq = parser.parse( core, path, req );
}
final SolrConfig conf = core.getSolrConfig();
final Method reqMethod = Method.getMethod(req.getMethod());
if (Method.POST != reqMethod) {
HttpCacheHeaderUtil.setCacheControlHeader(conf, resp);
}
// unless we have been explicitly told not to, do cache validation
if (!conf.getHttpCachingConfig().isNever304()) {
// if we've confirmed cache validation, return immediately
if (HttpCacheHeaderUtil.doCacheHeaderValidation(solrReq, req,resp)) {
return;
}
}
SolrQueryResponse solrRsp = new SolrQueryResponse();
/* even for HEAD requests, we need to execute the handler to
* ensure we don't get an error (and to make sure the correct
* QueryResponseWriter is selectedand we get the correct
* Content-Type)
*/
this.execute( req, handler, solrReq, solrRsp );
if( solrRsp.getException() != null ) {
sendError( (HttpServletResponse)response, solrRsp.getException() );
return;
}
// Now write it out
QueryResponseWriter responseWriter = core.getQueryResponseWriter(solrReq);
response.setContentType(responseWriter.getContentType(solrReq, solrRsp));
if (Method.HEAD == Method.getMethod(req.getMethod())) {
// nothing to write out, waited this long just to get ContentType
return;
}
PrintWriter out = response.getWriter();
responseWriter.write(out, solrReq, solrRsp);
return;
}
// otherwise, let's ensure the core is in the SolrCore request attribute so
// the servlet/jsp can retrieve it
else {
req.setAttribute("org.apache.solr.SolrCore", core);
// Let each core have its own admin page...
if( singlecore == null && path.startsWith( "/admin" ) ) {
req.getRequestDispatcher( path ).forward( request, response );
return;
core = singlecore;
}
// With a valid core...
if( core != null ) {
final SolrConfig config = core.getSolrConfig();
// get or create/cache the parser for the core
SolrRequestParsers parser = null;
parser = parsers.get(core);
if( parser == null ) {
parser = new SolrRequestParsers(config);
parsers.put( core, parser );
}
// Determine the handler from the url path if not set
// (we might already have selected the multicore handler)
if( handler == null && path.length() > 1 ) { // don't match "" or "/" as valid path
handler = core.getRequestHandler( path );
// no handler yet but allowed to handle select; let's check
if( handler == null && parser.isHandleSelect() ) {
if( "/select".equals( path ) || "/select/".equals( path ) ) {
solrReq = parser.parse( core, path, req );
String qt = solrReq.getParams().get( CommonParams.QT );
if( qt != null && qt.startsWith( "/" ) ) {
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Invalid query type. Do not use /select to access: "+qt);
}
handler = core.getRequestHandler( qt );
if( handler == null ) {
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "unknown handler: "+qt);
}
}
}
}
// With a valid handler and a valid core...
if( handler != null ) {
// if not a /select, create the request
if( solrReq == null ) {
solrReq = parser.parse( core, path, req );
}
final Method reqMethod = Method.getMethod(req.getMethod());
if (Method.POST != reqMethod) {
HttpCacheHeaderUtil.setCacheControlHeader(config, resp);
}
// unless we have been explicitly told not to, do cache validation
// if we fail cache validation, execute the query
if (config.getHttpCachingConfig().isNever304() ||
!HttpCacheHeaderUtil.doCacheHeaderValidation(solrReq, req, resp)) {
SolrQueryResponse solrRsp = new SolrQueryResponse();
/* even for HEAD requests, we need to execute the handler to
* ensure we don't get an error (and to make sure the correct
* QueryResponseWriter is selectedand we get the correct
* Content-Type)
*/
this.execute( req, handler, solrReq, solrRsp );
if( solrRsp.getException() != null ) {
sendError( (HttpServletResponse)response, solrRsp.getException() );
}
else {
// Now write it out
QueryResponseWriter responseWriter = core.getQueryResponseWriter(solrReq);
response.setContentType(responseWriter.getContentType(solrReq, solrRsp));
if (Method.HEAD != Method.getMethod(req.getMethod())) {
PrintWriter out = response.getWriter();
responseWriter.write(out, solrReq, solrRsp);
}
//else http HEAD request, nothing to write out, waited this long just to get ContentType
}
}
return; // we are done with a valid handler
}
// otherwise (we have a core), let's ensure the core is in the SolrCore request attribute so
// a servlet/jsp can retrieve it
else {
req.setAttribute("org.apache.solr.SolrCore", core);
// Modify the request so each core gets its own /admin
if( singlecore == null && path.startsWith( "/admin" ) ) {
req.getRequestDispatcher( path ).forward( request, response );
return;
}
}
}
}
catch( Throwable ex ) {
log.info("no handler or core retrieved for " + path + ", follow through...");
} catch (Throwable ex) {
sendError( (HttpServletResponse)response, ex );
return;
}
finally {
} finally {
if( solrReq != null ) {
solrReq.close();
}
@ -326,8 +326,7 @@ public class SolrDispatchFilter implements Filter
sreq.getCore().execute( handler, sreq, rsp );
}
protected void sendError(HttpServletResponse res, Throwable ex) throws IOException
{
protected void sendError(HttpServletResponse res, Throwable ex) throws IOException {
int code=500;
String trace = "";
if( ex instanceof SolrException ) {