LUCENE-2510: Make the SOLR backwards layer be implemented directly inside SolrResourceLoader (sorry for the generics and still needed suppress warnings... - maybe somebody has an idea for the map inside map)

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene2510@1365215 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Uwe Schindler 2012-07-24 18:44:40 +00:00
parent b87e306e85
commit d1ad745f53
8 changed files with 95 additions and 98 deletions

View File

@ -152,7 +152,6 @@ public class SynonymFilterFactory extends TokenFilterFactory implements Resource
return parser.build();
}
// nocommit: spi-hack solr.xxx and o.a.solr.analysis.xxx via a delegator
// (there are no tests for this functionality)
private TokenizerFactory loadTokenizerFactory(ResourceLoader loader, String cname){
TokenizerFactory tokFactory = loader.newInstance(cname, TokenizerFactory.class);

View File

@ -28,7 +28,7 @@ import org.apache.lucene.util.SPIClassIterator;
* Helper class for loading named SPIs from classpath (e.g. Tokenizers, TokenStreams).
* @lucene.internal
*/
final class AnalysisSPILoader<S extends AbstractAnalysisFactory> {
public final class AnalysisSPILoader<S extends AbstractAnalysisFactory> {
private final Map<String,Class<? extends S>> services;
private final Class<S> clazz;
@ -37,9 +37,17 @@ final class AnalysisSPILoader<S extends AbstractAnalysisFactory> {
this(clazz, new String[] { clazz.getSimpleName() });
}
public AnalysisSPILoader(Class<S> clazz, ClassLoader loader) {
this(clazz, new String[] { clazz.getSimpleName() }, loader);
}
public AnalysisSPILoader(Class<S> clazz, String[] suffixes) {
this(clazz, suffixes, Thread.currentThread().getContextClassLoader());
}
public AnalysisSPILoader(Class<S> clazz, String[] suffixes, ClassLoader classloader) {
this.clazz = clazz;
final SPIClassIterator<S> loader = SPIClassIterator.get(clazz);
final SPIClassIterator<S> loader = SPIClassIterator.get(clazz, classloader);
final LinkedHashMap<String,Class<? extends S>> services = new LinkedHashMap<String,Class<? extends S>>();
while (loader.hasNext()) {
final Class<? extends S> service = loader.next();

View File

@ -29,13 +29,23 @@ import org.apache.lucene.analysis.CharFilter;
public abstract class CharFilterFactory extends AbstractAnalysisFactory {
private static final AnalysisSPILoader<CharFilterFactory> loader =
new AnalysisSPILoader<CharFilterFactory>(CharFilterFactory.class);
getSPILoader(Thread.currentThread().getContextClassLoader());
/** looks up a charfilter by name */
/**
* Used by e.g. Apache Solr to get a correctly configured instance
* of {@link AnalysisSPILoader} from Solr's classpath.
* @lucene.internal
*/
public static AnalysisSPILoader<CharFilterFactory> getSPILoader(ClassLoader classloader) {
return new AnalysisSPILoader<CharFilterFactory>(CharFilterFactory.class, classloader);
}
/** looks up a charfilter by name from context classpath */
public static CharFilterFactory forName(String name) {
return loader.newInstance(name);
}
/** looks up a charfilter class by name from context classpath */
public static Class<? extends CharFilterFactory> lookupClass(String name) {
return loader.lookupClass(name);
}

View File

@ -28,19 +28,29 @@ import org.apache.lucene.analysis.TokenStream;
public abstract class TokenFilterFactory extends AbstractAnalysisFactory {
private static final AnalysisSPILoader<TokenFilterFactory> loader =
new AnalysisSPILoader<TokenFilterFactory>(TokenFilterFactory.class,
new String[] { "TokenFilterFactory", "FilterFactory" });
getSPILoader(Thread.currentThread().getContextClassLoader());
/** looks up a tokenfilter by name */
/**
* Used by e.g. Apache Solr to get a correctly configured instance
* of {@link AnalysisSPILoader} from Solr's classpath.
* @lucene.internal
*/
public static AnalysisSPILoader<TokenFilterFactory> getSPILoader(ClassLoader classloader) {
return new AnalysisSPILoader<TokenFilterFactory>(TokenFilterFactory.class,
new String[] { "TokenFilterFactory", "FilterFactory" }, classloader);
}
/** looks up a tokenfilter by name from context classpath */
public static TokenFilterFactory forName(String name) {
return loader.newInstance(name);
}
/** looks up a tokenfilter class by name from context classpath */
public static Class<? extends TokenFilterFactory> lookupClass(String name) {
return loader.lookupClass(name);
}
/** returns a list of all available tokenfilter names */
/** returns a list of all available tokenfilter names from context classpath */
public static Set<String> availableTokenFilters() {
return loader.availableServices();
}

View File

@ -29,18 +29,28 @@ import java.util.Set;
public abstract class TokenizerFactory extends AbstractAnalysisFactory {
private static final AnalysisSPILoader<TokenizerFactory> loader =
new AnalysisSPILoader<TokenizerFactory>(TokenizerFactory.class);
getSPILoader(Thread.currentThread().getContextClassLoader());
/** looks up a tokenizer by name */
/**
* Used by e.g. Apache Solr to get a correctly configured instance
* of {@link AnalysisSPILoader} from Solr's classpath.
* @lucene.internal
*/
public static AnalysisSPILoader<TokenizerFactory> getSPILoader(ClassLoader classloader) {
return new AnalysisSPILoader<TokenizerFactory>(TokenizerFactory.class, classloader);
}
/** looks up a tokenizer by name from context classpath */
public static TokenizerFactory forName(String name) {
return loader.newInstance(name);
}
/** looks up a tokenizer class by name from context classpath */
public static Class<? extends TokenizerFactory> lookupClass(String name) {
return loader.lookupClass(name);
}
/** returns a list of all available tokenizer names */
/** returns a list of all available tokenizer names from context classpath */
public static Set<String> availableTokenizers() {
return loader.availableServices();
}

View File

@ -1,61 +0,0 @@
package org.apache.solr.analysis;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.lucene.analysis.util.AbstractAnalysisFactory;
import org.apache.lucene.analysis.util.ResourceLoader;
import org.apache.solr.util.plugin.AbstractPluginLoader;
import org.w3c.dom.Node;
/*
* 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.
*/
public abstract class AnalysisPluginLoader<S extends AbstractAnalysisFactory> extends AbstractPluginLoader<S> {
public AnalysisPluginLoader(String type, Class<S> pluginClassType, boolean preRegister, boolean requireName) {
super(type, pluginClassType, preRegister, requireName);
}
public AnalysisPluginLoader(String type, Class<S> pluginClassType) {
super(type, pluginClassType);
}
@Override
protected S create(ResourceLoader loader, String name, String className, Node node) throws Exception {
Class<? extends S> clazz = null;
Matcher m = legacyPattern.matcher(className);
if (m.matches()) {
try {
clazz = lookupSPI(m.group(4));
} catch (IllegalArgumentException ex) {
// ok
}
}
if (clazz != null) {
className = clazz.getName();
}
return super.create(loader, name, className, node);
}
private static final Pattern legacyPattern =
Pattern.compile("((org\\.apache\\.solr\\.analysis\\.)|(solr\\.))([\\p{L}_$][\\p{L}\\p{N}_$]+?)(TokenFilter|Filter|Tokenizer|CharFilter)Factory");
protected abstract Class<? extends S> lookupSPI(String name);
}

View File

@ -29,11 +29,16 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.lucene.analysis.util.AbstractAnalysisFactory;
import org.apache.lucene.analysis.util.CharFilterFactory;
import org.apache.lucene.analysis.util.ResourceLoaderAware;
import org.apache.lucene.analysis.util.TokenFilterFactory;
import org.apache.lucene.analysis.util.TokenizerFactory;
import org.apache.lucene.analysis.util.AnalysisSPILoader;
import org.apache.lucene.util.WeakIdentityMap;
import org.apache.solr.common.ResourceLoader;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.handler.component.ShardHandlerFactory;
@ -366,8 +371,15 @@ public class SolrResourceLoader implements ResourceLoader
/*
* A static map of short class name to fully qualified class name
*/
private static Map<String, String> classNameCache = new ConcurrentHashMap<String, String>();
private static final Map<String, String> classNameCache = new ConcurrentHashMap<String, String>();
// A static map of AnalysisSPILoaders, keyed by ClassLoader used (because it can change during Solr lifetime) and expected base class:
private static final WeakIdentityMap<ClassLoader, Map<Class<?>,AnalysisSPILoader<?>>> expectedTypesSPILoaders = WeakIdentityMap.newConcurrentHashMap();
// Using this pattern, legacy analysis components from previous Solr versions are identified and delegated to SPI loader:
private static final Pattern legacyAnalysisPattern =
Pattern.compile("((\\Q"+base+".analysis.\\E)|(\\Q"+project+".\\E))([\\p{L}_$][\\p{L}\\p{N}_$]+?)(TokenFilter|Filter|Tokenizer|CharFilter)Factory");
/**
* This method loads a class either with it's FQN or a short-name (solr.class-simplename or class-simplename).
* It tries to load the class with the name that is given first and if it fails, it tries all the known
@ -394,6 +406,33 @@ public class SolrResourceLoader implements ResourceLoader
}
}
Class<? extends T> clazz = null;
// first try legacy analysis patterns, now replaced by Lucene's Analysis package:
final Matcher m = legacyAnalysisPattern.matcher(cname);
if (m.matches()) {
log.trace("Trying to load class from analysis SPI");
// retrieve the map of classLoader -> expectedType -> SPI from cache / regenerate cache
Map<Class<?>,AnalysisSPILoader<?>> spiLoaders = expectedTypesSPILoaders.get(classLoader);
if (spiLoaders == null) {
spiLoaders = new IdentityHashMap<Class<?>,AnalysisSPILoader<?>>();
spiLoaders.put(CharFilterFactory.class, CharFilterFactory.getSPILoader(classLoader));
spiLoaders.put(TokenizerFactory.class, TokenizerFactory.getSPILoader(classLoader));
spiLoaders.put(TokenFilterFactory.class, TokenFilterFactory.getSPILoader(classLoader));
expectedTypesSPILoaders.put(classLoader, spiLoaders);
}
AnalysisSPILoader<? extends AbstractAnalysisFactory> loader = spiLoaders.get(expectedType);
if (loader != null) {
// it's a correct expected type for analysis! Let's go on!
try {
@SuppressWarnings("unchecked")
final Class<? extends T> cl = (Class<? extends T>) loader.lookupClass(m.group(4));
return clazz = cl;
} catch (IllegalArgumentException ex) {
// ok, we fall back to legacy loading
}
}
}
// first try cname == full name
try {
return Class.forName(cname, true, classLoader).asSubclass(expectedType);

View File

@ -22,7 +22,6 @@ import org.apache.lucene.analysis.core.KeywordAnalyzer;
import org.apache.lucene.analysis.core.KeywordTokenizerFactory;
import org.apache.lucene.analysis.util.*;
import org.apache.lucene.util.Version;
import org.apache.solr.analysis.AnalysisPluginLoader;
import org.apache.solr.analysis.TokenizerChain;
import org.apache.solr.common.SolrException;
import org.apache.solr.util.DOMUtil;
@ -263,15 +262,10 @@ public final class FieldTypePluginLoader
final ArrayList<CharFilterFactory> charFilters
= new ArrayList<CharFilterFactory>();
AnalysisPluginLoader<CharFilterFactory> charFilterLoader =
new AnalysisPluginLoader<CharFilterFactory>
AbstractPluginLoader<CharFilterFactory> charFilterLoader =
new AbstractPluginLoader<CharFilterFactory>
("[schema.xml] analyzer/charFilter", CharFilterFactory.class, false, false) {
@Override
protected Class<? extends CharFilterFactory> lookupSPI(String name) {
return CharFilterFactory.lookupClass(name);
}
@Override
protected void init(CharFilterFactory plugin, Node node) throws Exception {
if( plugin != null ) {
@ -301,15 +295,9 @@ public final class FieldTypePluginLoader
final ArrayList<TokenizerFactory> tokenizers
= new ArrayList<TokenizerFactory>(1);
AnalysisPluginLoader<TokenizerFactory> tokenizerLoader =
new AnalysisPluginLoader<TokenizerFactory>
AbstractPluginLoader<TokenizerFactory> tokenizerLoader =
new AbstractPluginLoader<TokenizerFactory>
("[schema.xml] analyzer/tokenizer", TokenizerFactory.class, false, false) {
@Override
protected Class<? extends TokenizerFactory> lookupSPI(String name) {
return TokenizerFactory.lookupClass(name);
}
@Override
protected void init(TokenizerFactory plugin, Node node) throws Exception {
if( !tokenizers.isEmpty() ) {
@ -344,15 +332,9 @@ public final class FieldTypePluginLoader
final ArrayList<TokenFilterFactory> filters
= new ArrayList<TokenFilterFactory>();
AnalysisPluginLoader<TokenFilterFactory> filterLoader =
new AnalysisPluginLoader<TokenFilterFactory>("[schema.xml] analyzer/filter", TokenFilterFactory.class, false, false)
AbstractPluginLoader<TokenFilterFactory> filterLoader =
new AbstractPluginLoader<TokenFilterFactory>("[schema.xml] analyzer/filter", TokenFilterFactory.class, false, false)
{
@Override
protected Class<? extends TokenFilterFactory> lookupSPI(String name) {
return TokenFilterFactory.lookupClass(name);
}
@Override
protected void init(TokenFilterFactory plugin, Node node) throws Exception {
if( plugin != null ) {