SOLR-7226: Make /query/* jmx/* , requestDispatcher/*, <listener> <initParams> properties in solrconfig.xml editable

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1669368 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Noble Paul 2015-03-26 16:39:14 +00:00
parent f2874dd9d3
commit ad913e4013
8 changed files with 607 additions and 389 deletions

View File

@ -221,6 +221,11 @@ New Features
Example: q=price:[ ${low:0} TO ${high} ]&low=100&high=200
(yonik)
* SOLR-7226: Make /query/* jmx/* , requestDispatcher/*, <listener> <initParams>
properties in solrconfig.xml editable (Noble Paul)
Bug Fixes
----------------------

View File

@ -19,7 +19,6 @@ package org.apache.solr.core;
import java.io.IOException;
import java.io.StringReader;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
@ -30,39 +29,39 @@ import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.request.SolrRequestHandler;
import org.noggit.CharArr;
import org.noggit.JSONParser;
import org.noggit.JSONWriter;
import org.noggit.ObjectBuilder;
/**This class encapsulates the config overlay json file. It is immutable
/**
* This class encapsulates the config overlay json file. It is immutable
* and any edit operations performed on tbhis gives a new copy of the object
* with the changed value
*
*/
public class ConfigOverlay implements MapSerializable{
private final int znodeVersion ;
public class ConfigOverlay implements MapSerializable {
private final int znodeVersion;
private final Map<String, Object> data;
private Map<String,Object> props;
private Map<String,Object> userProps;
private Map<String, Object> props;
private Map<String, Object> userProps;
public ConfigOverlay(Map<String,Object> jsonObj, int znodeVersion){
if(jsonObj == null) jsonObj= Collections.EMPTY_MAP;
public ConfigOverlay(Map<String, Object> jsonObj, int znodeVersion) {
if (jsonObj == null) jsonObj = Collections.EMPTY_MAP;
this.znodeVersion = znodeVersion;
data = Collections.unmodifiableMap(jsonObj);
props = (Map<String, Object>) data.get("props");
if(props == null) props= Collections.EMPTY_MAP;
if (props == null) props = Collections.EMPTY_MAP;
userProps = (Map<String, Object>) data.get("userProps");
if(userProps == null) userProps= Collections.EMPTY_MAP;
if (userProps == null) userProps = Collections.EMPTY_MAP;
}
public Object getXPathProperty(String xpath){
return getXPathProperty(xpath,true);
public Object getXPathProperty(String xpath) {
return getXPathProperty(xpath, true);
}
public Object getXPathProperty(String xpath, boolean onlyPrimitive) {
List<String> hierarchy = checkEditable(xpath, true, false);
if(hierarchy == null) return null;
if (hierarchy == null) return null;
return getObjectByPath(props, onlyPrimitive, hierarchy);
}
@ -70,10 +69,10 @@ public class ConfigOverlay implements MapSerializable{
Map obj = root;
for (int i = 0; i < hierarchy.size(); i++) {
String s = hierarchy.get(i);
if(i < hierarchy.size()-1){
if (i < hierarchy.size() - 1) {
if (!(obj.get(s) instanceof Map)) return null;
obj = (Map) obj.get(s);
if(obj == null) return null;
if (obj == null) return null;
} else {
Object val = obj.get(s);
if (onlyPrimitive && val instanceof Map) {
@ -86,15 +85,16 @@ public class ConfigOverlay implements MapSerializable{
return false;
}
public ConfigOverlay setUserProperty(String key, Object val){
public ConfigOverlay setUserProperty(String key, Object val) {
Map copy = new LinkedHashMap(userProps);
copy.put(key,val);
copy.put(key, val);
Map<String, Object> jsonObj = new LinkedHashMap<>(this.data);
jsonObj.put("userProps", copy);
return new ConfigOverlay(jsonObj, znodeVersion);
}
public ConfigOverlay unsetUserProperty(String key){
if(!userProps.containsKey(key)) return this;
public ConfigOverlay unsetUserProperty(String key) {
if (!userProps.containsKey(key)) return this;
Map copy = new LinkedHashMap(userProps);
copy.remove(key);
Map<String, Object> jsonObj = new LinkedHashMap<>(this.data);
@ -103,18 +103,18 @@ public class ConfigOverlay implements MapSerializable{
}
public ConfigOverlay setProperty(String name, Object val) {
List<String> hierarchy = checkEditable(name,false, true);
List<String> hierarchy = checkEditable(name, false, true);
Map deepCopy = getDeepCopy(props);
Map obj = deepCopy;
for (int i = 0; i < hierarchy.size(); i++) {
String s = hierarchy.get(i);
if (i < hierarchy.size()-1) {
if(obj.get(s) == null || (!(obj.get(s) instanceof Map))) {
if (i < hierarchy.size() - 1) {
if (obj.get(s) == null || (!(obj.get(s) instanceof Map))) {
obj.put(s, new LinkedHashMap<>());
}
obj = (Map) obj.get(s);
} else {
obj.put(s,val);
obj.put(s, val);
}
}
@ -125,7 +125,6 @@ public class ConfigOverlay implements MapSerializable{
}
private Map getDeepCopy(Map map) {
return (Map) ZkStateReader.fromJSON(ZkStateReader.toJSON(map));
}
@ -134,8 +133,9 @@ public class ConfigOverlay implements MapSerializable{
private List<String> checkEditable(String propName, boolean isXPath, boolean failOnError) {
LinkedList<String> hierarchy = new LinkedList<>();
if(!isEditableProp(propName, isXPath,hierarchy)) {
if(failOnError) throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, StrUtils.formatString( NOT_EDITABLE,propName));
if (!isEditableProp(propName, isXPath, hierarchy)) {
if (failOnError)
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, StrUtils.formatString(NOT_EDITABLE, propName));
else return null;
}
return hierarchy;
@ -143,13 +143,13 @@ public class ConfigOverlay implements MapSerializable{
}
public ConfigOverlay unsetProperty(String name) {
List<String> hierarchy = checkEditable(name,false, true);
List<String> hierarchy = checkEditable(name, false, true);
Map deepCopy = getDeepCopy(props);
Map obj = deepCopy;
for (int i = 0; i < hierarchy.size(); i++) {
String s = hierarchy.get(i);
if (i < hierarchy.size()-1) {
if(obj.get(s) == null || (!(obj.get(s) instanceof Map))) {
if (i < hierarchy.size() - 1) {
if (obj.get(s) == null || (!(obj.get(s) instanceof Map))) {
return this;
}
obj = (Map) obj.get(s);
@ -169,7 +169,7 @@ public class ConfigOverlay implements MapSerializable{
}
public int getZnodeVersion(){
public int getZnodeVersion() {
return znodeVersion;
}
@ -187,65 +187,129 @@ public class ConfigOverlay implements MapSerializable{
public static final String RESOURCE_NAME = "configoverlay.json";
private static final Long XML_ATTR = 0L;
private static final Long XML_NODE = 1L;
private static final Long STR_ATTR = 0L;
private static final Long STR_NODE = 1L;
private static final Long BOOL_ATTR = 10L;
private static final Long BOOL_NODE = 11L;
private static final Long INT_ATTR = 20L;
private static final Long INT_NODE = 21L;
private static final Long FLOAT_ATTR = 30L;
private static final Long FLOAT_NODE = 31L;
private static Map editable_prop_map ;
public static final String MAPPING = "{ updateHandler : {" +
" autoCommit : { maxDocs:1, maxTime:1, openSearcher:1 }," +
" autoSoftCommit : { maxDocs:1, maxTime :1}," +
" commitWithin : {softCommit:1}," +
" commitIntervalLowerBound:1," +
" indexWriter : {closeWaitsForMerges:1}" +
" }," +
" query : {" +
" filterCache : {class:0, size:0, initialSize:0 , autowarmCount:0 , regenerator:0}," +
" queryResultCache :{class:0, size:0, initialSize:0,autowarmCount:0,regenerator:0}," +
" documentCache :{class:0, size:0, initialSize:0 ,autowarmCount:0,regenerator:0}," +
" fieldValueCache :{class:0, size:0, initialSize:0 ,autowarmCount:0,regenerator:0}" +
"}}";
static{
private static Map editable_prop_map;
//The path maps to the xml xpath and value of 1 means it is a tag with a string value and value
// of 0 means it is an attribute with string value
public static final String MAPPING = "{" +
" updateHandler:{" +
" autoCommit:{" +
" maxDocs:20," +
" maxTime:20," +
" openSearcher:11}," +
" autoSoftCommit:{" +
" maxDocs:20," +
" maxTime:20}," +
" commitWithin:{softCommit:11}," +
" commitIntervalLowerBound:21," +
" indexWriter:{closeWaitsForMerges:11}}," +
" query:{" +
" filterCache:{" +
" class:0," +
" size:0," +
" initialSize:20," +
" autowarmCount:20," +
" regenerator:0}," +
" queryResultCache:{" +
" class:0," +
" size:20," +
" initialSize:20," +
" autowarmCount:20," +
" regenerator:0}," +
" documentCache:{" +
" class:0," +
" size:20," +
" initialSize:20," +
" autowarmCount:20," +
" regenerator:0}," +
" fieldValueCache:{" +
" class:0," +
" size:20," +
" initialSize:20," +
" autowarmCount:20," +
" regenerator:0}," +
" useFilterForSortedQuery:1," +
" queryResultWindowSize:1," +
" queryResultMaxDocsCached:1," +
" enableLazyFieldLoading:1," +
" boolTofilterOptimizer:1," +
" maxBooleanClauses:1}," +
" jmx:{" +
" agentId:0," +
" serviceUrl:0," +
" rootName:0}," +
" requestDispatcher:{" +
" handleSelect:0," +
" requestParsers:{" +
" multipartUploadLimitInKB:0," +
" formdataUploadLimitInKB:0," +
" enableRemoteStreaming:0," +
" addHttpRequestToContext:0}}}";
static {
try {
editable_prop_map = (Map)new ObjectBuilder(new JSONParser(new StringReader(
editable_prop_map = (Map) new ObjectBuilder(new JSONParser(new StringReader(
MAPPING))).getObject();
} catch (IOException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "error parsing mapping ", e);
}
}
public static boolean isEditableProp(String path, boolean isXpath, List<String> hierarchy) {
List<String> parts = StrUtils.splitSmart(path, isXpath? '/':'.');
return !(checkEditable(path, isXpath, hierarchy) == null);
}
public static Class checkEditable(String path, boolean isXpath, List<String> hierarchy) {
List<String> parts = StrUtils.splitSmart(path, isXpath ? '/' : '.');
Object obj = editable_prop_map;
for (int i = 0; i < parts.size(); i++) {
String part = parts.get(i);
boolean isAttr = isXpath && part.startsWith("@");
if(isAttr){
if (isAttr) {
part = part.substring(1);
}
if(hierarchy != null) hierarchy.add(part);
if(obj ==null) return false;
if(i == parts.size()-1) {
if (hierarchy != null) hierarchy.add(part);
if (obj == null) return null;
if (i == parts.size() - 1) {
if (obj instanceof Map) {
Map map = (Map) obj;
if(isXpath && isAttr){
return XML_ATTR.equals(map.get(part));
} else {
return XML_ATTR.equals( map.get(part)) || XML_NODE.equals(map.get(part));
}
Object o = map.get(part);
return checkType(o, isXpath, isAttr);
}
return false;
return null;
}
obj = ((Map) obj).get(part);
}
return false;
return null;
}
static Class[] types = new Class[]{String.class, Boolean.class, Integer.class, Float.class};
private static Class checkType(Object o, boolean isXpath, boolean isAttr) {
if (o instanceof Long) {
Long aLong = (Long) o;
int ten = aLong.intValue() / 10;
int one = aLong.intValue() % 10;
if (isXpath && isAttr && one != 0) return null;
return types[ten];
} else {
return null;
}
}
public Map<String, String> getEditableSubProperties(String xpath) {
Object o = getObjectByPath(props,false,StrUtils.splitSmart(xpath,'/'));
Object o = getObjectByPath(props, false, StrUtils.splitSmart(xpath, '/'));
if (o instanceof Map) {
return (Map) o;
return (Map) o;
} else {
return null;
}
@ -258,34 +322,39 @@ public class ConfigOverlay implements MapSerializable{
@Override
public Map<String, Object> toMap() {
Map result = new LinkedHashMap();
result.put(ZNODEVER,znodeVersion);
result.put(ZNODEVER, znodeVersion);
result.putAll(data);
return result;
}
public Map<String, Map> getNamedPlugins(String typ){
public Map<String, Map> getNamedPlugins(String typ) {
Map<String, Map> reqHandlers = (Map<String, Map>) data.get(typ);
if(reqHandlers == null) return Collections.EMPTY_MAP;
if (reqHandlers == null) return Collections.EMPTY_MAP;
return Collections.unmodifiableMap(reqHandlers);
}
public ConfigOverlay addNamedPlugin(Map<String, Object> info, String typ) {
Map dataCopy = RequestParams.getDeepCopy(data, 4);
Map dataCopy = RequestParams.getDeepCopy(data, 4);
Map reqHandler = (Map) dataCopy.get(typ);
if(reqHandler== null) dataCopy.put(typ, reqHandler = new LinkedHashMap());
reqHandler.put(info.get(CoreAdminParams.NAME) , info);
if (reqHandler == null) dataCopy.put(typ, reqHandler = new LinkedHashMap());
reqHandler.put(info.get(CoreAdminParams.NAME), info);
return new ConfigOverlay(dataCopy, this.znodeVersion);
}
public ConfigOverlay deleteNamedPlugin(String name, String typ) {
Map dataCopy = RequestParams.getDeepCopy(data,4);
Map dataCopy = RequestParams.getDeepCopy(data, 4);
Map reqHandler = (Map) dataCopy.get(typ);
if(reqHandler==null) return this;
if (reqHandler == null) return this;
reqHandler.remove(name);
return new ConfigOverlay(dataCopy,this.znodeVersion);
return new ConfigOverlay(dataCopy, this.znodeVersion);
}
public static final String ZNODEVER = "znodeVersion";
public static final String NAME = "overlay";
public static void main(String[] args) {
}
}

View File

@ -20,7 +20,6 @@ package org.apache.solr.core;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -57,7 +56,9 @@ public class PluginBag<T> implements AutoCloseable {
private SolrCore core;
private final SolrConfig.SolrPluginInfo meta;
/** Pass needThreadSafety=true if plugins can be added and removed concurrently with lookups. */
/**
* Pass needThreadSafety=true if plugins can be added and removed concurrently with lookups.
*/
public PluginBag(Class<T> klass, SolrCore core, boolean needThreadSafety) {
this.core = core;
this.klass = klass;
@ -72,12 +73,14 @@ public class PluginBag<T> implements AutoCloseable {
}
}
/** Constructs a non-threadsafe plugin registry */
/**
* Constructs a non-threadsafe plugin registry
*/
public PluginBag(Class<T> klass, SolrCore core) {
this(klass, core, false);
}
static void initInstance(Object inst, PluginInfo info, SolrCore core) {
static void initInstance(Object inst, PluginInfo info) {
if (inst instanceof PluginInfoInitialized) {
((PluginInfoInitialized) inst).init(info);
} else if (inst instanceof NamedListInitializedPlugin) {
@ -94,16 +97,16 @@ public class PluginBag<T> implements AutoCloseable {
}
PluginHolder<T> createPlugin(PluginInfo info, SolrCore core) {
PluginHolder<T> createPlugin(PluginInfo info) {
if ("true".equals(String.valueOf(info.attributes.get("runtimeLib")))) {
log.info(" {} : '{}' created with runtimeLib=true ", meta.tag, info.name);
log.info(" {} : '{}' created with runtimeLib=true ", meta.getCleanTag(), info.name);
return new LazyPluginHolder<>(meta, info, core, core.getMemClassLoader());
} else if ("lazy".equals(info.attributes.get("startup")) && meta.options.contains(SolrConfig.PluginOpts.LAZY)) {
log.info("{} : '{}' created with startup=lazy ", meta.tag, info.name);
log.info("{} : '{}' created with startup=lazy ", meta.getCleanTag(), info.name);
return new LazyPluginHolder<T>(meta, info, core, core.getResourceLoader());
} else {
T inst = core.createInstance(info.className, (Class<T>) meta.clazz, meta.tag, null, core.getResourceLoader());
initInstance(inst, info, core);
T inst = core.createInstance(info.className, (Class<T>) meta.clazz, meta.getCleanTag(), null, core.getResourceLoader());
initInstance(inst, info);
return new PluginHolder<>(info, inst);
}
}
@ -164,7 +167,7 @@ public class PluginBag<T> implements AutoCloseable {
void setDefault(String def) {
if (!registry.containsKey(def)) return;
if (this.def != null) log.warn("Multiple defaults for : " + meta.tag);
if (this.def != null) log.warn("Multiple defaults for : " + meta.getCleanTag());
this.def = def;
}
@ -197,11 +200,11 @@ public class PluginBag<T> implements AutoCloseable {
void init(Map<String, T> defaults, SolrCore solrCore, List<PluginInfo> infos) {
core = solrCore;
for (PluginInfo info : infos) {
PluginHolder<T> o = createPlugin(info, solrCore);
PluginHolder<T> o = createPlugin(info);
String name = info.name;
if (meta.clazz.equals(SolrRequestHandler.class)) name = RequestHandlers.normalize(info.name);
PluginHolder<T> old = put(name, o);
if (old != null) log.warn("Multiple entries of {} with name {}", meta.tag, name);
if (old != null) log.warn("Multiple entries of {} with name {}", meta.getCleanTag(), name);
}
for (Map.Entry<String, T> e : defaults.entrySet()) {
if (!contains(e.getKey())) {
@ -238,7 +241,7 @@ public class PluginBag<T> implements AutoCloseable {
try {
e.getValue().close();
} catch (Exception exp) {
log.error("Error closing plugin " + e.getKey() + " of type : " + meta.tag, exp);
log.error("Error closing plugin " + e.getKey() + " of type : " + meta.getCleanTag(), exp);
}
}
}
@ -331,14 +334,14 @@ public class PluginBag<T> implements AutoCloseable {
private synchronized boolean createInst() {
if (lazyInst != null) return false;
log.info("Going to create a new {} with {} ", pluginMeta.tag, pluginInfo.toString());
log.info("Going to create a new {} with {} ", pluginMeta.getCleanTag(), pluginInfo.toString());
if (resourceLoader instanceof MemClassLoader) {
MemClassLoader loader = (MemClassLoader) resourceLoader;
loader.loadJars();
}
Class<T> clazz = (Class<T>) pluginMeta.clazz;
T localInst = core.createInstance(pluginInfo.className, clazz, pluginMeta.tag, null, resourceLoader);
initInstance(localInst, pluginInfo, core);
T localInst = core.createInstance(pluginInfo.className, clazz, pluginMeta.getCleanTag(), null, resourceLoader);
initInstance(localInst, pluginInfo);
if (localInst instanceof SolrCoreAware) {
SolrResourceLoader.assertAwareCompatibility(SolrCoreAware.class, localInst);
((SolrCoreAware) localInst).inform(core);

View File

@ -73,6 +73,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -81,6 +82,7 @@ import static org.apache.solr.core.SolrConfig.PluginOpts.MULTI_OK;
import static org.apache.solr.core.SolrConfig.PluginOpts.NOOP;
import static org.apache.solr.core.SolrConfig.PluginOpts.REQUIRE_CLASS;
import static org.apache.solr.core.SolrConfig.PluginOpts.REQUIRE_NAME;
import static org.apache.solr.core.SolrConfig.PluginOpts.REQUIRE_NAME_IN_OVERLAY;
/**
@ -88,22 +90,23 @@ import static org.apache.solr.core.SolrConfig.PluginOpts.REQUIRE_NAME;
* configuration data for a a Solr instance -- typically found in
* "solrconfig.xml".
*/
public class SolrConfig extends Config implements MapSerializable{
public class SolrConfig extends Config implements MapSerializable {
public static final Logger log = LoggerFactory.getLogger(SolrConfig.class);
public static final String DEFAULT_CONF_FILE = "solrconfig.xml";
private RequestParams requestParams;
public static enum PluginOpts {
MULTI_OK,
MULTI_OK,
REQUIRE_NAME,
REQUIRE_NAME_IN_OVERLAY,
REQUIRE_CLASS,
LAZY,
// EnumSet.of and/or EnumSet.copyOf(Collection) are anoying
// because of type determination
NOOP
}
}
private int multipartUploadLimitKB;
@ -116,49 +119,56 @@ public class SolrConfig extends Config implements MapSerializable{
private boolean addHttpRequestToContext;
private final SolrRequestParsers solrRequestParsers;
/** Creates a default instance from the solrconfig.xml. */
public SolrConfig()
throws ParserConfigurationException, IOException, SAXException {
this( (SolrResourceLoader) null, DEFAULT_CONF_FILE, null );
}
/** Creates a configuration instance from a configuration name.
* A default resource loader will be created (@see SolrResourceLoader)
*@param name the configuration name used by the loader
/**
* Creates a default instance from the solrconfig.xml.
*/
public SolrConfig(String name)
throws ParserConfigurationException, IOException, SAXException {
this( (SolrResourceLoader) null, name, null);
public SolrConfig()
throws ParserConfigurationException, IOException, SAXException {
this((SolrResourceLoader) null, DEFAULT_CONF_FILE, null);
}
/** Creates a configuration instance from a configuration name and stream.
/**
* Creates a configuration instance from a configuration name.
* A default resource loader will be created (@see SolrResourceLoader)
*
* @param name the configuration name used by the loader
*/
public SolrConfig(String name)
throws ParserConfigurationException, IOException, SAXException {
this((SolrResourceLoader) null, name, null);
}
/**
* Creates a configuration instance from a configuration name and stream.
* A default resource loader will be created (@see SolrResourceLoader).
* If the stream is null, the resource loader will open the configuration stream.
* If the stream is not null, no attempt to load the resource will occur (the name is not used).
*@param name the configuration name
*@param is the configuration stream
*
* @param name the configuration name
* @param is the configuration stream
*/
public SolrConfig(String name, InputSource is)
throws ParserConfigurationException, IOException, SAXException {
this( (SolrResourceLoader) null, name, is );
throws ParserConfigurationException, IOException, SAXException {
this((SolrResourceLoader) null, name, is);
}
/** Creates a configuration instance from an instance directory, configuration name and stream.
*@param instanceDir the directory used to create the resource loader
*@param name the configuration name used by the loader if the stream is null
*@param is the configuration stream
/**
* Creates a configuration instance from an instance directory, configuration name and stream.
*
* @param instanceDir the directory used to create the resource loader
* @param name the configuration name used by the loader if the stream is null
* @param is the configuration stream
*/
public SolrConfig(String instanceDir, String name, InputSource is)
throws ParserConfigurationException, IOException, SAXException {
throws ParserConfigurationException, IOException, SAXException {
this(new SolrResourceLoader(instanceDir), name, is);
}
public static SolrConfig readFromResourceLoader(SolrResourceLoader loader, String name) {
try {
return new SolrConfig(loader, name, null);
}
catch (Exception e) {
} catch (Exception e) {
String resource;
if (loader instanceof ZkSolrResourceLoader) {
resource = name;
@ -168,16 +178,18 @@ public class SolrConfig extends Config implements MapSerializable{
throw new SolrException(ErrorCode.SERVER_ERROR, "Error loading solr config from " + resource, e);
}
}
/** Creates a configuration instance from a resource loader, a configuration name and a stream.
/**
* Creates a configuration instance from a resource loader, a configuration name and a stream.
* If the stream is null, the resource loader will open the configuration stream.
* If the stream is not null, no attempt to load the resource will occur (the name is not used).
*@param loader the resource loader
*@param name the configuration name
*@param is the configuration stream
*
* @param loader the resource loader
* @param name the configuration name
* @param is the configuration stream
*/
public SolrConfig(SolrResourceLoader loader, String name, InputSource is)
throws ParserConfigurationException, IOException, SAXException {
throws ParserConfigurationException, IOException, SAXException {
super(loader, name, is, "/config/");
getOverlay();//just in case it is not initialized
getRequestParams();
@ -188,7 +200,7 @@ public class SolrConfig extends Config implements MapSerializable{
// Old indexDefaults and mainIndex sections are deprecated and fails fast for luceneMatchVersion=>LUCENE_4_0_0.
// For older solrconfig.xml's we allow the old sections, but never mixed with the new <indexConfig>
boolean hasDeprecatedIndexConfig = (getNode("indexDefaults", false) != null) || (getNode("mainIndex", false) != null);
if(hasDeprecatedIndexConfig){
if (hasDeprecatedIndexConfig) {
throw new SolrException(ErrorCode.FORBIDDEN, "<indexDefaults> and <mainIndex> configuration sections are discontinued. Use <indexConfig> instead.");
} else {
defaultIndexConfig = mainIndexConfig = null;
@ -207,9 +219,9 @@ public class SolrConfig extends Config implements MapSerializable{
// Warn about deprecated / discontinued parameters
// boolToFilterOptimizer has had no effect since 3.1
if(get("query/boolTofilterOptimizer", null) != null)
if (get("query/boolTofilterOptimizer", null) != null)
log.warn("solrconfig.xml: <boolTofilterOptimizer> is currently not implemented and has no effect.");
if(get("query/HashDocSet", null) != null)
if (get("query/HashDocSet", null) != null)
log.warn("solrconfig.xml: <HashDocSet> is deprecated and no longer recommended used.");
// TODO: Old code - in case somebody wants to re-enable. Also see SolrIndexSearcher#search()
@ -228,62 +240,62 @@ public class SolrConfig extends Config implements MapSerializable{
documentCacheConfig = CacheConfig.getConfig(this, "query/documentCache");
CacheConfig conf = CacheConfig.getConfig(this, "query/fieldValueCache");
if (conf == null) {
Map<String,String> args = new HashMap<>();
args.put("name","fieldValueCache");
args.put("size","10000");
args.put("initialSize","10");
args.put("showItems","-1");
Map<String, String> args = new HashMap<>();
args.put("name", "fieldValueCache");
args.put("size", "10000");
args.put("initialSize", "10");
args.put("showItems", "-1");
conf = new CacheConfig(FastLRUCache.class, args, null);
}
fieldValueCacheConfig = conf;
unlockOnStartup = getBool(indexConfigPrefix+"/unlockOnStartup", false);
useColdSearcher = getBool("query/useColdSearcher",false);
unlockOnStartup = getBool(indexConfigPrefix + "/unlockOnStartup", false);
useColdSearcher = getBool("query/useColdSearcher", false);
dataDir = get("dataDir", null);
if (dataDir != null && dataDir.length()==0) dataDir=null;
if (dataDir != null && dataDir.length() == 0) dataDir = null;
userCacheConfigs = CacheConfig.getMultipleConfigs(this, "query/cache");
org.apache.solr.search.SolrIndexSearcher.initRegenerators(this);
hashSetInverseLoadFactor = 1.0f / getFloat("//HashDocSet/@loadFactor",0.75f);
hashDocSetMaxSize= getInt("//HashDocSet/@maxSize",3000);
hashSetInverseLoadFactor = 1.0f / getFloat("//HashDocSet/@loadFactor", 0.75f);
hashDocSetMaxSize = getInt("//HashDocSet/@maxSize", 3000);
httpCachingConfig = new HttpCachingConfig(this);
Node jmx = getNode("jmx", false);
if (jmx != null) {
jmxConfig = new JmxConfiguration(true,
get("jmx/@agentId", null),
get("jmx/@serviceUrl", null),
get("jmx/@rootName", null));
get("jmx/@agentId", null),
get("jmx/@serviceUrl", null),
get("jmx/@rootName", null));
} else {
jmxConfig = new JmxConfiguration(false, null, null, null);
}
maxWarmingSearchers = getInt("query/maxWarmingSearchers",Integer.MAX_VALUE);
maxWarmingSearchers = getInt("query/maxWarmingSearchers", Integer.MAX_VALUE);
slowQueryThresholdMillis = getInt("query/slowQueryThresholdMillis", -1);
for (SolrPluginInfo plugin : plugins) loadPluginInfo(plugin);
updateHandlerInfo = loadUpdatehandlerInfo();
multipartUploadLimitKB = getInt(
"requestDispatcher/requestParsers/@multipartUploadLimitInKB", 2048 );
formUploadLimitKB = getInt(
"requestDispatcher/requestParsers/@formdataUploadLimitInKB", 2048 );
enableRemoteStreams = getBool(
"requestDispatcher/requestParsers/@enableRemoteStreaming", false );
// Let this filter take care of /select?xxx format
handleSelect = getBool(
"requestDispatcher/@handleSelect", true );
addHttpRequestToContext = getBool(
"requestDispatcher/requestParsers/@addHttpRequestToContext", false );
updateHandlerInfo = loadUpdatehandlerInfo();
List<PluginInfo> argsInfos = pluginStore.get(InitParams.class.getName()) ;
if(argsInfos!=null){
Map<String,InitParams> argsMap = new HashMap<>();
multipartUploadLimitKB = getInt(
"requestDispatcher/requestParsers/@multipartUploadLimitInKB", 2048);
formUploadLimitKB = getInt(
"requestDispatcher/requestParsers/@formdataUploadLimitInKB", 2048);
enableRemoteStreams = getBool(
"requestDispatcher/requestParsers/@enableRemoteStreaming", false);
// Let this filter take care of /select?xxx format
handleSelect = getBool(
"requestDispatcher/@handleSelect", true);
addHttpRequestToContext = getBool(
"requestDispatcher/requestParsers/@addHttpRequestToContext", false);
List<PluginInfo> argsInfos = getPluginInfos(InitParams.class.getName());
if (argsInfos != null) {
Map<String, InitParams> argsMap = new HashMap<>();
for (PluginInfo p : argsInfos) {
InitParams args = new InitParams(p);
argsMap.put(args.name == null ? String.valueOf(args.hashCode()) : args.name, args);
@ -296,7 +308,7 @@ public class SolrConfig extends Config implements MapSerializable{
Config.log.info("Loaded SolrConfig: " + name);
}
public static final List<SolrPluginInfo> plugins = ImmutableList.<SolrPluginInfo>builder()
public static final List<SolrPluginInfo> plugins = ImmutableList.<SolrPluginInfo>builder()
.add(new SolrPluginInfo(SolrRequestHandler.class, SolrRequestHandler.TYPE, REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK, LAZY))
.add(new SolrPluginInfo(QParserPlugin.class, "queryParser", REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK))
.add(new SolrPluginInfo(QueryResponseWriter.class, "queryResponseWriter", REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK, LAZY))
@ -304,36 +316,38 @@ public class SolrConfig extends Config implements MapSerializable{
.add(new SolrPluginInfo(TransformerFactory.class, "transformer", REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK))
.add(new SolrPluginInfo(SearchComponent.class, "searchComponent", REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK))
.add(new SolrPluginInfo(UpdateRequestProcessorFactory.class, "updateProcessor", REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK))
// TODO: WTF is up with queryConverter???
// it aparently *only* works as a singleton? - SOLR-4304
// and even then -- only if there is a single SpellCheckComponent
// because of queryConverter.setIndexAnalyzer
// TODO: WTF is up with queryConverter???
// it aparently *only* works as a singleton? - SOLR-4304
// and even then -- only if there is a single SpellCheckComponent
// because of queryConverter.setIndexAnalyzer
.add(new SolrPluginInfo(QueryConverter.class, "queryConverter", REQUIRE_NAME, REQUIRE_CLASS))
.add(new SolrPluginInfo(PluginBag.RuntimeLib.class, "runtimeLib", REQUIRE_NAME, MULTI_OK))
// this is hackish, since it picks up all SolrEventListeners,
// regardless of when/how/why they are used (or even if they are
// declared outside of the appropriate context) but there's no nice
// way around that in the PluginInfo framework
.add(new SolrPluginInfo(SolrEventListener.class, "//listener", REQUIRE_CLASS, MULTI_OK))
// this is hackish, since it picks up all SolrEventListeners,
// regardless of when/how/why they are used (or even if they are
// declared outside of the appropriate context) but there's no nice
// way around that in the PluginInfo framework
.add(new SolrPluginInfo(InitParams.class, InitParams.TYPE, MULTI_OK, REQUIRE_NAME_IN_OVERLAY))
.add(new SolrPluginInfo(SolrEventListener.class, "//listener", REQUIRE_CLASS, MULTI_OK, REQUIRE_NAME_IN_OVERLAY))
.add(new SolrPluginInfo(DirectoryFactory.class, "directoryFactory", REQUIRE_CLASS))
.add(new SolrPluginInfo(IndexDeletionPolicy.class, "indexConfig/deletionPolicy", REQUIRE_CLASS))
.add(new SolrPluginInfo(CodecFactory.class, "codecFactory", REQUIRE_CLASS))
.add(new SolrPluginInfo(IndexReaderFactory.class, "indexReaderFactory", REQUIRE_CLASS))
.add(new SolrPluginInfo(UpdateRequestProcessorChain.class,"updateRequestProcessorChain", MULTI_OK))
.add(new SolrPluginInfo(UpdateLog.class,"updateHandler/updateLog"))
.add(new SolrPluginInfo(UpdateRequestProcessorChain.class, "updateRequestProcessorChain", MULTI_OK))
.add(new SolrPluginInfo(UpdateLog.class, "updateHandler/updateLog"))
.add(new SolrPluginInfo(IndexSchemaFactory.class, "schemaFactory", REQUIRE_CLASS))
.add(new SolrPluginInfo(RestManager.class, "restManager"))
.add(new SolrPluginInfo(InitParams.class, InitParams.TYPE, MULTI_OK))
.add(new SolrPluginInfo(StatsCache.class, "statsCache", REQUIRE_CLASS))
.build();
public static final Map<String, SolrPluginInfo> classVsSolrPluginInfo;
static {
Map<String, SolrPluginInfo> map = new HashMap<>();
for (SolrPluginInfo plugin : plugins) map.put(plugin.clazz.getName(), plugin);
classVsSolrPluginInfo = Collections.unmodifiableMap(map);
}
public static class SolrPluginInfo{
public static class SolrPluginInfo {
public final Class clazz;
public final String tag;
@ -343,17 +357,26 @@ public class SolrConfig extends Config implements MapSerializable{
private SolrPluginInfo(Class clz, String tag, PluginOpts... opts) {
this.clazz = clz;
this.tag = tag;
this.options= opts == null? Collections.EMPTY_SET : EnumSet.of(NOOP, opts);
this.options = opts == null ? Collections.EMPTY_SET : EnumSet.of(NOOP, opts);
}
public String getCleanTag() {
return tag.replaceAll("/", "");
}
public String getTagCleanLower() {
return getCleanTag().toLowerCase(Locale.ROOT);
}
}
public static ConfigOverlay getConfigOverlay(SolrResourceLoader loader) {
public static ConfigOverlay getConfigOverlay(SolrResourceLoader loader) {
InputStream in = null;
try {
in = loader.openResource(ConfigOverlay.RESOURCE_NAME);
} catch (IOException e) {
//no problem no overlay.json file
return new ConfigOverlay(Collections.EMPTY_MAP,-1);
return new ConfigOverlay(Collections.EMPTY_MAP, -1);
}
try {
@ -363,27 +386,29 @@ public class SolrConfig extends Config implements MapSerializable{
log.info("config overlay loaded . version : {} ", version);
}
Map m = (Map) ObjectBuilder.getVal(new JSONParser(new InputStreamReader(in, StandardCharsets.UTF_8)));
return new ConfigOverlay(m,version);
return new ConfigOverlay(m, version);
} catch (Exception e) {
throw new SolrException(ErrorCode.SERVER_ERROR,"Error reading config overlay",e);
throw new SolrException(ErrorCode.SERVER_ERROR, "Error reading config overlay", e);
}
}
private Map<String,InitParams> initParams = Collections.emptyMap();
private Map<String, InitParams> initParams = Collections.emptyMap();
public Map<String, InitParams> getInitParams() {
return initParams;
}
protected UpdateHandlerInfo loadUpdatehandlerInfo() {
return new UpdateHandlerInfo(get("updateHandler/@class",null),
getInt("updateHandler/autoCommit/maxDocs",-1),
getInt("updateHandler/autoCommit/maxTime",-1),
getBool("updateHandler/indexWriter/closeWaitsForMerges",true),
getBool("updateHandler/autoCommit/openSearcher",true),
getInt("updateHandler/commitIntervalLowerBound",-1),
getInt("updateHandler/autoSoftCommit/maxDocs",-1),
getInt("updateHandler/autoSoftCommit/maxTime",-1),
getBool("updateHandler/commitWithin/softCommit",true));
return new UpdateHandlerInfo(get("updateHandler/@class", null),
getInt("updateHandler/autoCommit/maxDocs", -1),
getInt("updateHandler/autoCommit/maxTime", -1),
getBool("updateHandler/indexWriter/closeWaitsForMerges", true),
getBool("updateHandler/autoCommit/openSearcher", true),
getInt("updateHandler/commitIntervalLowerBound", -1),
getInt("updateHandler/autoSoftCommit/maxDocs", -1),
getInt("updateHandler/autoSoftCommit/maxTime", -1),
getBool("updateHandler/commitWithin/softCommit", true));
}
private void loadPluginInfo(SolrPluginInfo pluginInfo) {
@ -392,37 +417,37 @@ public class SolrConfig extends Config implements MapSerializable{
List<PluginInfo> result = readPluginInfos(pluginInfo.tag, requireName, requireClass);
if (1 < result.size() && ! pluginInfo.options.contains(MULTI_OK)) {
throw new SolrException
if (1 < result.size() && !pluginInfo.options.contains(MULTI_OK)) {
throw new SolrException
(SolrException.ErrorCode.SERVER_ERROR,
"Found " + result.size() + " configuration sections when at most "
+ "1 is allowed matching expression: " + pluginInfo.tag);
"Found " + result.size() + " configuration sections when at most "
+ "1 is allowed matching expression: " + pluginInfo.getCleanTag());
}
if(!result.isEmpty()) pluginStore.put(pluginInfo.clazz.getName(),result);
if (!result.isEmpty()) pluginStore.put(pluginInfo.clazz.getName(), result);
}
public List<PluginInfo> readPluginInfos(String tag, boolean requireName, boolean requireClass) {
ArrayList<PluginInfo> result = new ArrayList<>();
NodeList nodes = (NodeList) evaluate(tag, XPathConstants.NODESET);
for (int i=0; i<nodes.getLength(); i++) {
for (int i = 0; i < nodes.getLength(); i++) {
PluginInfo pluginInfo = new PluginInfo(nodes.item(i), "[solrconfig.xml] " + tag, requireName, requireClass);
if(pluginInfo.isEnabled()) result.add(pluginInfo);
if (pluginInfo.isEnabled()) result.add(pluginInfo);
}
return result;
}
public SolrRequestParsers getRequestParsers() {
return solrRequestParsers;
}
/* The set of materialized parameters: */
public final int booleanQueryMaxClauseCount;
// SolrIndexSearcher - nutch optimizer -- Disabled since 3.1
// SolrIndexSearcher - nutch optimizer -- Disabled since 3.1
// public final boolean filtOptEnabled;
// public final int filtOptCacheSize;
// public final float filtOptThreshold;
// SolrIndexSearcher - caches configurations
public final CacheConfig filterCacheConfig ;
public final CacheConfig filterCacheConfig;
public final CacheConfig queryResultCacheConfig;
public final CacheConfig documentCacheConfig;
public final CacheConfig fieldValueCacheConfig;
@ -443,7 +468,7 @@ public class SolrConfig extends Config implements MapSerializable{
// IndexConfig settings
public final SolrIndexConfig indexConfig;
protected UpdateHandlerInfo updateHandlerInfo ;
protected UpdateHandlerInfo updateHandlerInfo;
private Map<String, List<PluginInfo>> pluginStore = new LinkedHashMap<>();
@ -453,23 +478,24 @@ public class SolrConfig extends Config implements MapSerializable{
public final Version luceneMatchVersion;
protected String dataDir;
public final int slowQueryThresholdMillis; // threshold above which a query is considered slow
//JMX configuration
public final JmxConfiguration jmxConfig;
private final HttpCachingConfig httpCachingConfig;
public HttpCachingConfig getHttpCachingConfig() {
return httpCachingConfig;
}
public static class JmxConfiguration implements MapSerializable{
public static class JmxConfiguration implements MapSerializable {
public boolean enabled = false;
public String agentId;
public String serviceUrl;
public String rootName;
public JmxConfiguration(boolean enabled,
String agentId,
public JmxConfiguration(boolean enabled,
String agentId,
String serviceUrl,
String rootName) {
this.enabled = enabled;
@ -479,180 +505,217 @@ public class SolrConfig extends Config implements MapSerializable{
if (agentId != null && serviceUrl != null) {
throw new SolrException
(SolrException.ErrorCode.SERVER_ERROR,
"Incorrect JMX Configuration in solrconfig.xml, "+
"both agentId and serviceUrl cannot be specified at the same time");
(SolrException.ErrorCode.SERVER_ERROR,
"Incorrect JMX Configuration in solrconfig.xml, " +
"both agentId and serviceUrl cannot be specified at the same time");
}
}
@Override
public Map<String, Object> toMap() {
LinkedHashMap map = new LinkedHashMap();
map.put("agentId",agentId);
map.put("serviceUrl",serviceUrl);
map.put("rootName",rootName);
map.put("agentId", agentId);
map.put("serviceUrl", serviceUrl);
map.put("rootName", rootName);
return map;
}
}
public static class HttpCachingConfig implements MapSerializable{
public static class HttpCachingConfig implements MapSerializable {
/** config xpath prefix for getting HTTP Caching options */
/**
* config xpath prefix for getting HTTP Caching options
*/
private final static String CACHE_PRE
= "requestDispatcher/httpCaching/";
/** For extracting Expires "ttl" from <cacheControl> config */
= "requestDispatcher/httpCaching/";
/**
* For extracting Expires "ttl" from <cacheControl> config
*/
private final static Pattern MAX_AGE
= Pattern.compile("\\bmax-age=(\\d+)");
= Pattern.compile("\\bmax-age=(\\d+)");
@Override
public Map<String, Object> toMap() {
return ZkNodeProps.makeMap("never304",never304,
"etagSeed",etagSeed,
"lastModFrom",lastModFrom.name().toLowerCase(Locale.ROOT),
"cacheControl",cacheControlHeader);
return ZkNodeProps.makeMap("never304", never304,
"etagSeed", etagSeed,
"lastModFrom", lastModFrom.name().toLowerCase(Locale.ROOT),
"cacheControl", cacheControlHeader);
}
public static enum LastModFrom {
OPENTIME, DIRLASTMOD, BOGUS;
/** Input must not be null */
/**
* Input must not be null
*/
public static LastModFrom parse(final String s) {
try {
return valueOf(s.toUpperCase(Locale.ROOT));
} catch (Exception e) {
log.warn( "Unrecognized value for lastModFrom: " + s, e);
log.warn("Unrecognized value for lastModFrom: " + s, e);
return BOGUS;
}
}
}
private final boolean never304;
private final String etagSeed;
private final String cacheControlHeader;
private final Long maxAge;
private final LastModFrom lastModFrom;
private HttpCachingConfig(SolrConfig conf) {
never304 = conf.getBool(CACHE_PRE+"@never304", false);
etagSeed = conf.get(CACHE_PRE+"@etagSeed", "Solr");
never304 = conf.getBool(CACHE_PRE + "@never304", false);
lastModFrom = LastModFrom.parse(conf.get(CACHE_PRE+"@lastModFrom",
"openTime"));
cacheControlHeader = conf.get(CACHE_PRE+"cacheControl",null);
etagSeed = conf.get(CACHE_PRE + "@etagSeed", "Solr");
lastModFrom = LastModFrom.parse(conf.get(CACHE_PRE + "@lastModFrom",
"openTime"));
cacheControlHeader = conf.get(CACHE_PRE + "cacheControl", null);
Long tmp = null; // maxAge
if (null != cacheControlHeader) {
try {
try {
final Matcher ttlMatcher = MAX_AGE.matcher(cacheControlHeader);
final String ttlStr = ttlMatcher.find() ? ttlMatcher.group(1) : null;
tmp = (null != ttlStr && !"".equals(ttlStr))
? Long.valueOf(ttlStr)
: null;
? Long.valueOf(ttlStr)
: null;
} catch (Exception e) {
log.warn( "Ignoring exception while attempting to " +
"extract max-age from cacheControl config: " +
cacheControlHeader, e);
log.warn("Ignoring exception while attempting to " +
"extract max-age from cacheControl config: " +
cacheControlHeader, e);
}
}
maxAge = tmp;
}
public boolean isNever304() { return never304; }
public String getEtagSeed() { return etagSeed; }
/** null if no Cache-Control header */
public String getCacheControlHeader() { return cacheControlHeader; }
/** null if no max age limitation */
public Long getMaxAge() { return maxAge; }
public LastModFrom getLastModFrom() { return lastModFrom; }
public boolean isNever304() {
return never304;
}
public String getEtagSeed() {
return etagSeed;
}
/**
* null if no Cache-Control header
*/
public String getCacheControlHeader() {
return cacheControlHeader;
}
/**
* null if no max age limitation
*/
public Long getMaxAge() {
return maxAge;
}
public LastModFrom getLastModFrom() {
return lastModFrom;
}
}
public static class UpdateHandlerInfo implements MapSerializable{
public static class UpdateHandlerInfo implements MapSerializable {
public final String className;
public final int autoCommmitMaxDocs,autoCommmitMaxTime,commitIntervalLowerBound,
autoSoftCommmitMaxDocs,autoSoftCommmitMaxTime;
public final int autoCommmitMaxDocs, autoCommmitMaxTime, commitIntervalLowerBound,
autoSoftCommmitMaxDocs, autoSoftCommmitMaxTime;
public final boolean indexWriterCloseWaitsForMerges;
public final boolean openSearcher; // is opening a new searcher part of hard autocommit?
public final boolean commitWithinSoftCommit;
/**
* @param autoCommmitMaxDocs set -1 as default
* @param autoCommmitMaxTime set -1 as default
* @param autoCommmitMaxDocs set -1 as default
* @param autoCommmitMaxTime set -1 as default
* @param commitIntervalLowerBound set -1 as default
*/
public UpdateHandlerInfo(String className, int autoCommmitMaxDocs, int autoCommmitMaxTime, boolean indexWriterCloseWaitsForMerges, boolean openSearcher, int commitIntervalLowerBound,
int autoSoftCommmitMaxDocs, int autoSoftCommmitMaxTime, boolean commitWithinSoftCommit) {
int autoSoftCommmitMaxDocs, int autoSoftCommmitMaxTime, boolean commitWithinSoftCommit) {
this.className = className;
this.autoCommmitMaxDocs = autoCommmitMaxDocs;
this.autoCommmitMaxTime = autoCommmitMaxTime;
this.indexWriterCloseWaitsForMerges = indexWriterCloseWaitsForMerges;
this.openSearcher = openSearcher;
this.commitIntervalLowerBound = commitIntervalLowerBound;
this.autoSoftCommmitMaxDocs = autoSoftCommmitMaxDocs;
this.autoSoftCommmitMaxTime = autoSoftCommmitMaxTime;
this.commitWithinSoftCommit = commitWithinSoftCommit;
}
@Override
public Map<String, Object> toMap() {
LinkedHashMap result = new LinkedHashMap();
result.put("class",className);
result.put("autoCommmitMaxDocs",autoCommmitMaxDocs);
result.put("indexWriterCloseWaitsForMerges",indexWriterCloseWaitsForMerges);
result.put("openSearcher",openSearcher);
result.put("commitIntervalLowerBound",commitIntervalLowerBound);
result.put("commitWithinSoftCommit",commitWithinSoftCommit);
result.put("class", className);
result.put("autoCommmitMaxDocs", autoCommmitMaxDocs);
result.put("indexWriterCloseWaitsForMerges", indexWriterCloseWaitsForMerges);
result.put("openSearcher", openSearcher);
result.put("commitIntervalLowerBound", commitIntervalLowerBound);
result.put("commitWithinSoftCommit", commitWithinSoftCommit);
result.put("autoCommit", ZkNodeProps.makeMap(
"maxDocs", autoCommmitMaxDocs,
"maxTime",autoCommmitMaxTime,
"maxTime", autoCommmitMaxTime,
"commitIntervalLowerBound", commitIntervalLowerBound
));
result.put("autoSoftCommit" ,
result.put("autoSoftCommit",
ZkNodeProps.makeMap("maxDocs", autoSoftCommmitMaxDocs,
"maxTime",autoSoftCommmitMaxTime));
"maxTime", autoSoftCommmitMaxTime));
return result;
}
}
// public Map<String, List<PluginInfo>> getUpdateProcessorChainInfo() { return updateProcessorChainInfo; }
public UpdateHandlerInfo getUpdateHandlerInfo() { return updateHandlerInfo; }
public UpdateHandlerInfo getUpdateHandlerInfo() {
return updateHandlerInfo;
}
public String getDataDir() { return dataDir; }
public String getDataDir() {
return dataDir;
}
/**SolrConfig keeps a repository of plugins by the type. The known interfaces are the types.
/**
* SolrConfig keeps a repository of plugins by the type. The known interfaces are the types.
*
* @param type The key is FQN of the plugin class there are a few known types : SolrFormatter, SolrFragmenter
* SolrRequestHandler,QParserPlugin, QueryResponseWriter,ValueSourceParser,
* SearchComponent, QueryConverter, SolrEventListener, DirectoryFactory,
* IndexDeletionPolicy, IndexReaderFactory, {@link TransformerFactory}
* SolrRequestHandler,QParserPlugin, QueryResponseWriter,ValueSourceParser,
* SearchComponent, QueryConverter, SolrEventListener, DirectoryFactory,
* IndexDeletionPolicy, IndexReaderFactory, {@link TransformerFactory}
*/
public List<PluginInfo> getPluginInfos(String type) {
List<PluginInfo> result = pluginStore.get(type);
SolrPluginInfo info = classVsSolrPluginInfo.get(type);
if (info != null && info.options.contains(REQUIRE_NAME)) {
Map<String, Map> infos = overlay.getNamedPlugins(info.tag);
if (info != null &&
(info.options.contains(REQUIRE_NAME) || info.options.contains(REQUIRE_NAME_IN_OVERLAY))) {
Map<String, Map> infos = overlay.getNamedPlugins(info.getCleanTag());
if (!infos.isEmpty()) {
LinkedHashMap<String, PluginInfo> map = new LinkedHashMap<>();
if (result != null) for (PluginInfo pluginInfo : result) map.put(pluginInfo.name, pluginInfo);
if (result != null) for (PluginInfo pluginInfo : result) {
//just create a UUID for the time being so that map key is not null
String name = pluginInfo.name == null ?
UUID.randomUUID().toString().toLowerCase(Locale.ROOT) :
pluginInfo.name;
map.put(name, pluginInfo);
}
for (Map.Entry<String, Map> e : infos.entrySet()) {
map.put(e.getKey(), new PluginInfo(info.tag, e.getValue()));
map.put(e.getKey(), new PluginInfo(info.getCleanTag(), e.getValue()));
}
result = new ArrayList<>(map.values());
}
}
return result == null ? Collections.<PluginInfo>emptyList() : result;
}
public PluginInfo getPluginInfo(String type){
public PluginInfo getPluginInfo(String type) {
List<PluginInfo> result = pluginStore.get(type);
if (result == null || result.isEmpty()) {
return null;
@ -662,9 +725,9 @@ public class SolrConfig extends Config implements MapSerializable{
}
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"Multiple plugins configured for type: " + type);
"Multiple plugins configured for type: " + type);
}
private void initLibs() {
NodeList nodes = (NodeList) evaluate("lib", XPathConstants.NODESET);
if (nodes == null || nodes.getLength() == 0) return;
@ -728,23 +791,36 @@ public class SolrConfig extends Config implements MapSerializable{
@Override
public int getInt(String path, int def) {
Object v = overlay.getXPathProperty(path);
Object val = overlay.getXPathProperty(path);
if (val != null) return Integer.parseInt(val.toString());
return super.getInt(path, def);
}
@Override
public boolean getBool(String path, boolean def) {
Object val = overlay.getXPathProperty(path);
if (val != null) return Boolean.parseBoolean(val.toString());
return super.getBool(path, def);
}
@Override
public String get(String path) {
Object val = overlay.getXPathProperty(path, true);
return val != null ? val.toString() : super.get(path);
}
@Override
public String get(String path, String def) {
Object val = overlay.getXPathProperty(path, true);
return val != null ? val.toString() : super.get(path, def);
}
@Override
public Map<String, Object> toMap() {
LinkedHashMap result = new LinkedHashMap();
if(getZnodeVersion() > -1) result.put("znodeVersion",getZnodeVersion());
result.put("luceneMatchVersion",luceneMatchVersion);
if (getZnodeVersion() > -1) result.put("znodeVersion", getZnodeVersion());
result.put("luceneMatchVersion", luceneMatchVersion);
result.put("updateHandler", getUpdateHandlerInfo().toMap());
Map m = new LinkedHashMap();
result.put("query", m);
@ -753,22 +829,22 @@ public class SolrConfig extends Config implements MapSerializable{
m.put("queryResultMaxDocsCached", queryResultMaxDocsCached);
m.put("enableLazyFieldLoading", enableLazyFieldLoading);
m.put("maxBooleanClauses", booleanQueryMaxClauseCount);
if (jmxConfig != null) result.put("jmx", jmxConfig.toMap());
for (SolrPluginInfo plugin : plugins) {
List<PluginInfo> infos = getPluginInfos(plugin.clazz.getName());
if(infos == null || infos.isEmpty()) continue;
String tag = plugin.tag;
tag = tag.replace("/","");
if(plugin.options.contains(PluginOpts.REQUIRE_NAME)){
if (infos == null || infos.isEmpty()) continue;
String tag = plugin.getCleanTag();
tag = tag.replace("/", "");
if (plugin.options.contains(PluginOpts.REQUIRE_NAME)) {
LinkedHashMap items = new LinkedHashMap();
for (PluginInfo info : infos) items.put(info.name, info.toMap());
for (Map.Entry e : overlay.getNamedPlugins(plugin.tag).entrySet()) items.put(e.getKey(), e.getValue());
result.put(tag, items);
} else {
if(plugin.options.contains(MULTI_OK)){
if (plugin.options.contains(MULTI_OK)) {
ArrayList<Map> l = new ArrayList<>();
for (PluginInfo info : infos) l.add(info.toMap());
result.put(tag,l);
result.put(tag, l);
} else {
result.put(tag, infos.get(0).toMap());
}
@ -778,16 +854,16 @@ public class SolrConfig extends Config implements MapSerializable{
}
addCacheConfig(m,filterCacheConfig,queryResultCacheConfig,documentCacheConfig,fieldValueCacheConfig);
if(jmxConfig != null) result.put("jmx",jmxConfig.toMap());
addCacheConfig(m, filterCacheConfig, queryResultCacheConfig, documentCacheConfig, fieldValueCacheConfig);
if (jmxConfig != null) result.put("jmx", jmxConfig.toMap());
m = new LinkedHashMap();
result.put("requestDispatcher", m);
m.put("handleSelect",handleSelect);
if(httpCachingConfig!=null) m.put("httpCaching", httpCachingConfig.toMap());
m.put("requestParsers", ZkNodeProps.makeMap("multipartUploadLimitKB",multipartUploadLimitKB,
"formUploadLimitKB",formUploadLimitKB,
"addHttpRequestToContext",addHttpRequestToContext));
if(indexConfig != null) result.put("indexConfig",indexConfig.toMap());
m.put("handleSelect", handleSelect);
if (httpCachingConfig != null) m.put("httpCaching", httpCachingConfig.toMap());
m.put("requestParsers", ZkNodeProps.makeMap("multipartUploadLimitKB", multipartUploadLimitKB,
"formUploadLimitKB", formUploadLimitKB,
"addHttpRequestToContext", addHttpRequestToContext));
if (indexConfig != null) result.put("indexConfig", indexConfig.toMap());
//TODO there is more to add
@ -795,38 +871,39 @@ public class SolrConfig extends Config implements MapSerializable{
}
private void addCacheConfig(Map queryMap, CacheConfig... cache) {
if(cache==null)return;
for (CacheConfig config : cache) if(config !=null) queryMap.put(config.getNodeName(),config.toMap());
if (cache == null) return;
for (CacheConfig config : cache) if (config != null) queryMap.put(config.getNodeName(), config.toMap());
}
@Override
protected Properties getSubstituteProperties() {
Map<String, Object> p = getOverlay().getUserProps();
if(p==null || p.isEmpty()) return super.getSubstituteProperties();
if (p == null || p.isEmpty()) return super.getSubstituteProperties();
Properties result = new Properties(super.getSubstituteProperties());
result.putAll(p);
return result;
}
private ConfigOverlay overlay;
public ConfigOverlay getOverlay() {
if(overlay ==null) {
if (overlay == null) {
overlay = getConfigOverlay(getResourceLoader());
}
return overlay;
}
public RequestParams getRequestParams() {
if(requestParams == null){
if (requestParams == null) {
return refreshRequestParams();
}
return requestParams;
}
public RequestParams refreshRequestParams(){
requestParams = RequestParams.getFreshRequestParams(getResourceLoader(),requestParams);
public RequestParams refreshRequestParams() {
requestParams = RequestParams.getFreshRequestParams(getResourceLoader(), requestParams);
log.info("current version of requestparams : {}", requestParams.getZnodeVersion());
return requestParams;
}

View File

@ -19,7 +19,6 @@ package org.apache.solr.handler;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -57,9 +56,11 @@ import org.slf4j.LoggerFactory;
import static java.util.Collections.singletonList;
import static org.apache.solr.common.params.CoreAdminParams.NAME;
import static org.apache.solr.common.util.StrUtils.formatString;
import static org.apache.solr.core.ConfigOverlay.NOT_EDITABLE;
import static org.apache.solr.core.SolrConfig.PluginOpts.REQUIRE_CLASS;
import static org.apache.solr.core.SolrConfig.PluginOpts.REQUIRE_NAME;
import static org.apache.solr.core.SolrConfig.PluginOpts.REQUIRE_NAME_IN_OVERLAY;
import static org.apache.solr.schema.FieldType.CLASS_NAME;
public class SolrConfigHandler extends RequestHandlerBase {
@ -70,9 +71,8 @@ public class SolrConfigHandler extends RequestHandlerBase {
static {
Map<String, SolrConfig.SolrPluginInfo> map = new HashMap<>();
for (SolrConfig.SolrPluginInfo plugin : SolrConfig.plugins) {
if (plugin.options.contains(REQUIRE_NAME)) {
map.put(plugin.tag.toLowerCase(Locale.ROOT), plugin);
if (plugin.options.contains(REQUIRE_NAME) || plugin.options.contains(REQUIRE_NAME_IN_OVERLAY)) {
map.put(plugin.getCleanTag().toLowerCase(Locale.ROOT), plugin);
}
}
namedPlugins = Collections.unmodifiableMap(map);
@ -252,7 +252,7 @@ public class SolrConfigHandler extends RequestHandlerBase {
if (op.hasError()) break;
for (String s : name) {
if (params.getParams(s) == null) {
op.addError(StrUtils.formatString("can't delete . No such params ''{0}'' exist", s));
op.addError(formatString("can't delete . No such params ''{0}'' exist", s));
}
params = params.setParams(s, null);
}
@ -302,19 +302,19 @@ public class SolrConfigHandler extends RequestHandlerBase {
default: {
List<String> pcs = StrUtils.splitSmart(op.name.toLowerCase(Locale.ROOT), '-');
if (pcs.size() != 2) {
op.addError(StrUtils.formatString("Unknown operation ''{0}'' ", op.name));
op.addError(formatString("Unknown operation ''{0}'' ", op.name));
} else {
String prefix = pcs.get(0);
String name = pcs.get(1);
if (cmdPrefixes.contains(prefix) && namedPlugins.containsKey(name)) {
SolrConfig.SolrPluginInfo info = namedPlugins.get(name);
if ("delete".equals(prefix)) {
overlay = deleteNamedComponent(op, overlay, info.tag);
overlay = deleteNamedComponent(op, overlay, info.getCleanTag());
} else {
overlay = updateNamedPlugin(info, op, overlay, prefix.equals("create") || prefix.equals("add"));
}
} else {
op.addError(StrUtils.formatString("Unknown operation ''{0}'' ", op.name));
op.addError(formatString("Unknown operation ''{0}'' ", op.name));
}
}
}
@ -347,7 +347,7 @@ public class SolrConfigHandler extends RequestHandlerBase {
if (overlay.getNamedPlugins(typ).containsKey(name)) {
return overlay.deleteNamedPlugin(name, typ);
} else {
op.addError(StrUtils.formatString("NO such {0} ''{1}'' ", typ, name));
op.addError(formatString("NO such {0} ''{1}'' ", typ, name));
return overlay;
}
}
@ -360,18 +360,18 @@ public class SolrConfigHandler extends RequestHandlerBase {
op.getMap(PluginInfo.APPENDS, null);
if (op.hasError()) return overlay;
if (!verifyClass(op, clz, info.clazz)) return overlay;
if (overlay.getNamedPlugins(info.tag).containsKey(name)) {
if (overlay.getNamedPlugins(info.getCleanTag()).containsKey(name)) {
if (isCeate) {
op.addError(StrUtils.formatString(" ''{0}'' already exists . Do an ''{1}'' , if you want to change it ", name, "update-" + info.tag.toLowerCase(Locale.ROOT)));
op.addError(formatString(" ''{0}'' already exists . Do an ''{1}'' , if you want to change it ", name, "update-" + info.getTagCleanLower()));
return overlay;
} else {
return overlay.addNamedPlugin(op.getDataMap(), info.tag);
return overlay.addNamedPlugin(op.getDataMap(), info.getCleanTag());
}
} else {
if (isCeate) {
return overlay.addNamedPlugin(op.getDataMap(), info.tag);
return overlay.addNamedPlugin(op.getDataMap(), info.getCleanTag());
} else {
op.addError(StrUtils.formatString(" ''{0}'' does not exist . Do an ''{1}'' , if you want to create it ", name, "create-" + info.tag.toLowerCase(Locale.ROOT)));
op.addError(formatString(" ''{0}'' does not exist . Do an ''{1}'' , if you want to create it ", name, "create-" + info.getTagCleanLower()));
return overlay;
}
}
@ -379,7 +379,7 @@ public class SolrConfigHandler extends RequestHandlerBase {
private boolean verifyClass(CommandOperation op, String clz, Class expected) {
if (clz == null) return true;
if ( !"true".equals(String.valueOf(op.getStr("runtimeLib", null)))) {
if (!"true".equals(String.valueOf(op.getStr("runtimeLib", null)))) {
//this is not dynamically loaded so we can verify the class right away
try {
req.getCore().createInitInstance(new PluginInfo(SolrRequestHandler.TYPE, op.getDataMap()), expected, clz, "");
@ -408,7 +408,7 @@ public class SolrConfigHandler extends RequestHandlerBase {
if (op.hasError()) return overlay;
for (String o : name) {
if (!overlay.getUserProps().containsKey(o)) {
op.addError(StrUtils.formatString("No such property ''{0}''", name));
op.addError(formatString("No such property ''{0}''", name));
} else {
overlay = overlay.unsetUserProperty(o);
}
@ -423,7 +423,7 @@ public class SolrConfigHandler extends RequestHandlerBase {
for (String o : name) {
if (!ConfigOverlay.isEditableProp(o, false, null)) {
op.addError(StrUtils.formatString(NOT_EDITABLE, name));
op.addError(formatString(NOT_EDITABLE, name));
} else {
overlay = overlay.unsetProperty(o);
}
@ -437,10 +437,42 @@ public class SolrConfigHandler extends RequestHandlerBase {
for (Map.Entry<String, Object> e : m.entrySet()) {
String name = e.getKey();
Object val = e.getValue();
if (!ConfigOverlay.isEditableProp(name, false, null)) {
op.addError(StrUtils.formatString(NOT_EDITABLE, name));
Class typ = ConfigOverlay.checkEditable(name, false, null);
if (typ == null) {
op.addError(formatString(NOT_EDITABLE, name));
continue;
}
if (val != null) {
if (typ == String.class) val = val.toString();
String typeErr = "Property {0} must be of {1} type ";
if (typ == Boolean.class) {
try {
val = Boolean.parseBoolean(val.toString());
} catch (Exception exp) {
op.addError(formatString(typeErr, name, typ.getSimpleName()));
continue;
}
} else if (typ == Integer.class) {
try {
val = Integer.parseInt(val.toString());
} catch (Exception exp) {
op.addError(formatString(typeErr, typ.getSimpleName()));
continue;
}
} else if (typ == Float.class) {
try {
val = Float.parseFloat(val.toString());
} catch (Exception exp) {
op.addError(formatString(typeErr, typ.getSimpleName()));
continue;
}
}
}
overlay = overlay.setProperty(name, val);
}
return overlay;
@ -459,7 +491,7 @@ public class SolrConfigHandler extends RequestHandlerBase {
c == '.'
) continue;
else {
return StrUtils.formatString("''{0}'' name should only have chars [a-zA-Z_-.0-9] ", s);
return formatString("''{0}'' name should only have chars [a-zA-Z_-.0-9] ", s);
}
}
return null;
@ -487,7 +519,7 @@ public class SolrConfigHandler extends RequestHandlerBase {
static {
for (SolrConfig.SolrPluginInfo solrPluginInfo : SolrConfig.plugins)
subPaths.add("/" + solrPluginInfo.tag.replaceAll("/", ""));
subPaths.add("/" + solrPluginInfo.getCleanTag());
}

View File

@ -42,6 +42,21 @@ public class TestConfigOverlay extends LuceneTestCase {
assertTrue(isEditableProp("updateHandler.autoCommit.maxTime", false, null));
assertTrue(isEditableProp("updateHandler.commitWithin.softCommit", false, null));
assertTrue(isEditableProp("updateHandler.indexWriter.closeWaitsForMerges", false, null));
assertTrue(isEditableProp("query.useFilterForSortedQuery", false, null));
assertTrue(isEditableProp("query.queryResultWindowSize", false, null));
assertTrue(isEditableProp("query.queryResultMaxDocsCached", false, null));
assertTrue(isEditableProp("query.enableLazyFieldLoading", false, null));
assertTrue(isEditableProp("query.boolTofilterOptimizer", false, null));
assertTrue(isEditableProp("jmx.agentId", false, null));
assertTrue(isEditableProp("jmx.serviceUrl", false, null));
assertTrue(isEditableProp("jmx.rootName", false, null));
assertTrue(isEditableProp("requestDispatcher.requestParsers.multipartUploadLimitInKB", false, null));
assertTrue(isEditableProp("requestDispatcher.requestParsers.formdataUploadLimitInKB", false, null));
assertTrue(isEditableProp("requestDispatcher.requestParsers.enableRemoteStreaming", false, null));
assertTrue(isEditableProp("requestDispatcher.requestParsers.addHttpRequestToContext", false, null));
assertTrue(isEditableProp("requestDispatcher.handleSelect", false, null));
assertTrue(isEditableProp("updateHandler/commitIntervalLowerBound", true, null));
assertFalse(isEditableProp("updateHandler/commitIntervalLowerBound1", true, null));

View File

@ -67,7 +67,7 @@ public class TestSolrConfigHandler extends RestTestBase {
tmpConfDir = new File(tmpSolrHome, confDir);
FileUtils.copyDirectory(new File(TEST_HOME()), tmpSolrHome.getAbsoluteFile());
final SortedMap<ServletHolder,String> extraServlets = new TreeMap<>();
final SortedMap<ServletHolder, String> extraServlets = new TreeMap<>();
final ServletHolder solrRestApi = new ServletHolder("SolrSchemaRestApi", ServerServlet.class);
solrRestApi.setInitParameter("org.restlet.application", "org.apache.solr.rest.SolrSchemaRestApi");
extraServlets.put(solrRestApi, "/schema/*"); // '/schema/*' matches '/schema', '/schema/', and '/schema/whatever...'
@ -93,62 +93,63 @@ public class TestSolrConfigHandler extends RestTestBase {
}
public void testProperty() throws Exception{
public void testProperty() throws Exception {
RestTestHarness harness = restTestHarness;
Map confMap = getRespMap("/config?wt=json" ,harness);
assertNotNull( getObjectByPath(confMap,false,Arrays.asList("config","requestHandler","/admin/luke")));
assertNotNull( getObjectByPath(confMap,false,Arrays.asList("config","requestHandler","/admin/system")));
assertNotNull( getObjectByPath(confMap,false,Arrays.asList("config","requestHandler","/admin/mbeans")));
assertNotNull( getObjectByPath(confMap,false,Arrays.asList("config","requestHandler","/admin/plugins")));
assertNotNull( getObjectByPath(confMap,false,Arrays.asList("config","requestHandler","/admin/threads")));
assertNotNull( getObjectByPath(confMap,false,Arrays.asList("config","requestHandler","/admin/properties")));
assertNotNull( getObjectByPath(confMap,false,Arrays.asList("config","requestHandler","/admin/logging")));
assertNotNull( getObjectByPath(confMap,false,Arrays.asList("config","requestHandler","/admin/file")));
assertNotNull( getObjectByPath(confMap,false,Arrays.asList("config","requestHandler","/admin/ping")));
Map confMap = getRespMap("/config?wt=json", harness);
assertNotNull(getObjectByPath(confMap, false, Arrays.asList("config", "requestHandler", "/admin/luke")));
assertNotNull(getObjectByPath(confMap, false, Arrays.asList("config", "requestHandler", "/admin/system")));
assertNotNull(getObjectByPath(confMap, false, Arrays.asList("config", "requestHandler", "/admin/mbeans")));
assertNotNull(getObjectByPath(confMap, false, Arrays.asList("config", "requestHandler", "/admin/plugins")));
assertNotNull(getObjectByPath(confMap, false, Arrays.asList("config", "requestHandler", "/admin/threads")));
assertNotNull(getObjectByPath(confMap, false, Arrays.asList("config", "requestHandler", "/admin/properties")));
assertNotNull(getObjectByPath(confMap, false, Arrays.asList("config", "requestHandler", "/admin/logging")));
assertNotNull(getObjectByPath(confMap, false, Arrays.asList("config", "requestHandler", "/admin/file")));
assertNotNull(getObjectByPath(confMap, false, Arrays.asList("config", "requestHandler", "/admin/ping")));
String payload= "{\n" +
" 'set-property' : { 'updateHandler.autoCommit.maxDocs':100, 'updateHandler.autoCommit.maxTime':10 } \n" +
String payload = "{\n" +
" 'set-property' : { 'updateHandler.autoCommit.maxDocs':100, 'updateHandler.autoCommit.maxTime':10 , 'requestDispatcher.requestParsers.addHttpRequestToContext':true} \n" +
" }";
runConfigCommand( harness,"/config?wt=json", payload);
runConfigCommand(harness, "/config?wt=json", payload);
Map m = (Map) getRespMap("/config/overlay?wt=json" ,harness).get("overlay");
Map m = (Map) getRespMap("/config/overlay?wt=json", harness).get("overlay");
Map props = (Map) m.get("props");
assertNotNull(props);
assertEquals("100", String.valueOf(getObjectByPath(props, true, ImmutableList.of("updateHandler", "autoCommit", "maxDocs")) ));
assertEquals("10", String.valueOf(getObjectByPath(props, true, ImmutableList.of("updateHandler", "autoCommit", "maxTime")) ));
assertEquals("100", String.valueOf(getObjectByPath(props, true, ImmutableList.of("updateHandler", "autoCommit", "maxDocs"))));
assertEquals("10", String.valueOf(getObjectByPath(props, true, ImmutableList.of("updateHandler", "autoCommit", "maxTime"))));
m = (Map) getRespMap("/config?wt=json" ,harness).get("config");
m = (Map) getRespMap("/config?wt=json", harness).get("config");
assertNotNull(m);
assertEquals( "100",String.valueOf(getObjectByPath(m, true, ImmutableList.of("updateHandler", "autoCommit", "maxDocs"))));
assertEquals( "10",String.valueOf(getObjectByPath(m, true, ImmutableList.of("updateHandler", "autoCommit", "maxTime"))));
payload= "{\n" +
assertEquals("100", String.valueOf(getObjectByPath(m, true, ImmutableList.of("updateHandler", "autoCommit", "maxDocs"))));
assertEquals("10", String.valueOf(getObjectByPath(m, true, ImmutableList.of("updateHandler", "autoCommit", "maxTime"))));
assertEquals("true", String.valueOf(getObjectByPath(m, true, ImmutableList.of("requestDispatcher", "requestParsers", "addHttpRequestToContext"))));
payload = "{\n" +
" 'unset-property' : 'updateHandler.autoCommit.maxDocs'} \n" +
" }";
runConfigCommand(harness, "/config?wt=json", payload);
m = (Map) getRespMap("/config/overlay?wt=json" ,harness).get("overlay");
m = (Map) getRespMap("/config/overlay?wt=json", harness).get("overlay");
props = (Map) m.get("props");
assertNotNull(props);
assertNull(getObjectByPath(props, true, ImmutableList.of("updateHandler", "autoCommit", "maxDocs")));
assertEquals("10", String.valueOf(getObjectByPath(props, true, ImmutableList.of("updateHandler", "autoCommit", "maxTime"))));
assertEquals("10", String.valueOf(getObjectByPath(props, true, ImmutableList.of("updateHandler", "autoCommit", "maxTime"))));
}
public void testUserProp() throws Exception{
public void testUserProp() throws Exception {
RestTestHarness harness = restTestHarness;
String payload= "{\n" +
String payload = "{\n" +
" 'set-user-property' : { 'my.custom.variable.a':'MODIFIEDA'," +
" 'my.custom.variable.b':'MODIFIEDB' } \n" +
" }";
runConfigCommand(harness,"/config?wt=json", payload);
runConfigCommand(harness, "/config?wt=json", payload);
Map m = (Map) getRespMap("/config/overlay?wt=json" ,harness).get("overlay");
Map m = (Map) getRespMap("/config/overlay?wt=json", harness).get("overlay");
Map props = (Map) m.get("userProps");
assertNotNull(props);
assertEquals(props.get("my.custom.variable.a"), "MODIFIEDA");
assertEquals(props.get("my.custom.variable.b"),"MODIFIEDB");
assertEquals(props.get("my.custom.variable.b"), "MODIFIEDB");
m = (Map) getRespMap("/dump?wt=json&json.nl=map&initArgs=true" ,harness).get("initArgs");
m = (Map) getRespMap("/dump?wt=json&json.nl=map&initArgs=true", harness).get("initArgs");
m = (Map) m.get(PluginInfo.DEFAULTS);
assertEquals("MODIFIEDA", m.get("a"));
@ -157,21 +158,21 @@ public class TestSolrConfigHandler extends RestTestBase {
}
public void testReqHandlerAPIs() throws Exception {
reqhandlertests(restTestHarness, null,null);
reqhandlertests(restTestHarness, null, null);
}
public static void runConfigCommand(RestTestHarness harness, String uri, String payload) throws IOException {
public static void runConfigCommand(RestTestHarness harness, String uri, String payload) throws IOException {
String response = harness.post(uri, SolrTestCaseJ4.json(payload));
Map map = (Map) ObjectBuilder.getVal(new JSONParser(new StringReader(response)));
assertNull(response, map.get("errors"));
assertNull(response, map.get("errors"));
}
public static void reqhandlertests(RestTestHarness writeHarness,String testServerBaseUrl, CloudSolrClient cloudSolrServer) throws Exception {
public static void reqhandlertests(RestTestHarness writeHarness, String testServerBaseUrl, CloudSolrClient cloudSolrServer) throws Exception {
String payload = "{\n" +
"'create-requesthandler' : { 'name' : '/x', 'class': 'org.apache.solr.handler.DumpRequestHandler' , 'startup' : 'lazy'}\n" +
"}";
runConfigCommand(writeHarness,"/config?wt=json", payload);
runConfigCommand(writeHarness, "/config?wt=json", payload);
testForResponseElement(writeHarness,
testServerBaseUrl,
@ -184,7 +185,7 @@ public class TestSolrConfigHandler extends RestTestBase {
payload = "{\n" +
"'update-requesthandler' : { 'name' : '/x', 'class': 'org.apache.solr.handler.DumpRequestHandler' , 'startup' : 'lazy' , 'a':'b' , 'defaults': {'def_a':'def A val'}}\n" +
"}";
runConfigCommand(writeHarness,"/config?wt=json", payload);
runConfigCommand(writeHarness, "/config?wt=json", payload);
testForResponseElement(writeHarness,
testServerBaseUrl,
@ -205,21 +206,21 @@ public class TestSolrConfigHandler extends RestTestBase {
payload = "{\n" +
"'delete-requesthandler' : '/x'" +
"}";
runConfigCommand(writeHarness,"/config?wt=json", payload);
runConfigCommand(writeHarness, "/config?wt=json", payload);
boolean success = false;
long startTime = System.nanoTime();
int maxTimeoutSeconds = 10;
while ( TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) < maxTimeoutSeconds) {
while (TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) < maxTimeoutSeconds) {
String uri = "/config/overlay?wt=json";
Map m = testServerBaseUrl ==null? getRespMap(uri,writeHarness) : TestSolrConfigHandlerConcurrent.getAsMap(testServerBaseUrl+uri ,cloudSolrServer) ;
if(null == ConfigOverlay.getObjectByPath(m, true, Arrays.asList("overlay", "requestHandler", "/x","a"))) {
Map m = testServerBaseUrl == null ? getRespMap(uri, writeHarness) : TestSolrConfigHandlerConcurrent.getAsMap(testServerBaseUrl + uri, cloudSolrServer);
if (null == ConfigOverlay.getObjectByPath(m, true, Arrays.asList("overlay", "requestHandler", "/x", "a"))) {
success = true;
break;
}
Thread.sleep(100);
}
assertTrue( "Could not delete requestHandler ", success);
assertTrue("Could not delete requestHandler ", success);
payload = "{\n" +
"'create-queryconverter' : { 'name' : 'qc', 'class': 'org.apache.solr.spelling.SpellingQueryConverter'}\n" +
@ -290,7 +291,7 @@ public class TestSolrConfigHandler extends RestTestBase {
Arrays.asList("config", "searchComponent", "tc"),
null,
10);
//<valueSourceParser name="countUsage" class="org.apache.solr.core.CountUsageValueSourceParser"/>
//<valueSourceParser name="countUsage" class="org.apache.solr.core.CountUsageValueSourceParser"/>
payload = "{\n" +
"'create-valuesourceparser' : { 'name' : 'cu', 'class': 'org.apache.solr.core.CountUsageValueSourceParser'}\n" +
"}";
@ -351,7 +352,7 @@ public class TestSolrConfigHandler extends RestTestBase {
testServerBaseUrl,
"/config?wt=json",
cloudSolrServer,
Arrays.asList("config", "transformer","mytrans","value"),
Arrays.asList("config", "transformer", "mytrans", "value"),
"6",
10);
@ -366,29 +367,44 @@ public class TestSolrConfigHandler extends RestTestBase {
Arrays.asList("config", "transformer", "mytrans"),
null,
10);
payload = "{\n" +
"'create-initparams' : { 'name' : 'hello', 'key':'val'}\n" +
"}";
runConfigCommand(writeHarness, "/config?wt=json", payload);
Map map = testForResponseElement(writeHarness,
testServerBaseUrl,
"/config?wt=json",
cloudSolrServer,
Arrays.asList("config", "transformer", "mytrans"),
null,
10);
List l = (List) ConfigOverlay.getObjectByPath(map,false, Arrays.asList("config", "initParams"));
assertEquals( 1, l.size());
assertEquals( "val", ((Map)l.get(0)).get("key") );
}
public static Map testForResponseElement(RestTestHarness harness,
String testServerBaseUrl,
String uri,
CloudSolrClient cloudSolrServer,List<String> jsonPath,
Object expected,
long maxTimeoutSeconds ) throws Exception {
String testServerBaseUrl,
String uri,
CloudSolrClient cloudSolrServer, List<String> jsonPath,
Object expected,
long maxTimeoutSeconds) throws Exception {
boolean success = false;
long startTime = System.nanoTime();
Map m = null;
while ( TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) < maxTimeoutSeconds) {
while (TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) < maxTimeoutSeconds) {
try {
m = testServerBaseUrl ==null? getRespMap(uri,harness) : TestSolrConfigHandlerConcurrent.getAsMap(testServerBaseUrl + uri, cloudSolrServer) ;
m = testServerBaseUrl == null ? getRespMap(uri, harness) : TestSolrConfigHandlerConcurrent.getAsMap(testServerBaseUrl + uri, cloudSolrServer);
} catch (Exception e) {
Thread.sleep(100);
continue;
}
if(Objects.equals(expected,ConfigOverlay.getObjectByPath(m, false, jsonPath))) {
if (Objects.equals(expected, ConfigOverlay.getObjectByPath(m, false, jsonPath))) {
success = true;
break;
}
@ -400,7 +416,7 @@ public class TestSolrConfigHandler extends RestTestBase {
return m;
}
public void testReqParams() throws Exception{
public void testReqParams() throws Exception {
RestTestHarness harness = restTestHarness;
String payload = " {\n" +
" 'set' : {'x': {" +
@ -410,7 +426,7 @@ public class TestSolrConfigHandler extends RestTestBase {
" }";
TestSolrConfigHandler.runConfigCommand(harness,"/config/params?wt=json", payload);
TestSolrConfigHandler.runConfigCommand(harness, "/config/params?wt=json", payload);
TestSolrConfigHandler.testForResponseElement(
harness,
@ -464,7 +480,7 @@ public class TestSolrConfigHandler extends RestTestBase {
"'create-requesthandler' : { 'name' : '/dump1', 'class': 'org.apache.solr.handler.DumpRequestHandler', 'useParams':'x' }\n" +
"}";
TestSolrConfigHandler.runConfigCommand(harness,"/config?wt=json", payload);
TestSolrConfigHandler.runConfigCommand(harness, "/config?wt=json", payload);
TestSolrConfigHandler.testForResponseElement(harness,
null,
@ -484,7 +500,6 @@ public class TestSolrConfigHandler extends RestTestBase {
5);
payload = " {\n" +
" 'set' : {'y':{\n" +
" 'c':'CY val',\n" +
@ -494,7 +509,7 @@ public class TestSolrConfigHandler extends RestTestBase {
" }";
TestSolrConfigHandler.runConfigCommand(harness,"/config/params?wt=json", payload);
TestSolrConfigHandler.runConfigCommand(harness, "/config/params?wt=json", payload);
TestSolrConfigHandler.testForResponseElement(
harness,
@ -538,7 +553,7 @@ public class TestSolrConfigHandler extends RestTestBase {
"/dump1?wt=json&useParams=y",
null,
Arrays.asList("params", "d"),
Arrays.asList("val 1", "val 2") ,
Arrays.asList("val 1", "val 2"),
5);
payload = " {\n" +
@ -551,7 +566,7 @@ public class TestSolrConfigHandler extends RestTestBase {
" }";
TestSolrConfigHandler.runConfigCommand(harness,"/config/params?wt=json", payload);
TestSolrConfigHandler.runConfigCommand(harness, "/config/params?wt=json", payload);
TestSolrConfigHandler.testForResponseElement(
harness,
@ -580,7 +595,7 @@ public class TestSolrConfigHandler extends RestTestBase {
" }";
TestSolrConfigHandler.runConfigCommand(harness,"/config/params?wt=json", payload);
TestSolrConfigHandler.runConfigCommand(harness, "/config/params?wt=json", payload);
TestSolrConfigHandler.testForResponseElement(
harness,
null,
@ -599,7 +614,7 @@ public class TestSolrConfigHandler extends RestTestBase {
null,
10);
payload = " {'delete' : 'y'}";
TestSolrConfigHandler.runConfigCommand(harness,"/config/params?wt=json", payload);
TestSolrConfigHandler.runConfigCommand(harness, "/config/params?wt=json", payload);
TestSolrConfigHandler.testForResponseElement(
harness,
null,

View File

@ -306,6 +306,8 @@ public class StrUtils {
}
}
/**Format using MesssageFormat but with the ROOT locale
*/
public static String formatString(String pattern, Object... args) {
return new MessageFormat(pattern, Locale.ROOT).format(args);
}