You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by no...@apache.org on 2021/04/14 05:18:21 UTC
[solr] 01/01: untested
This is an automated email from the ASF dual-hosted git repository.
noble pushed a commit to branch jira/solr15337
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 102d7cba756be062eb7e3fa4c292f5e1d298b1b1
Author: Noble Paul <no...@gmail.com>
AuthorDate: Wed Apr 14 15:18:03 2021 +1000
untested
---
.../java/org/apache/solr/core/ConfigOverlay.java | 13 +-
.../src/java/org/apache/solr/core/PluginInfo.java | 24 +++
.../src/java/org/apache/solr/core/SolrConfig.java | 210 ++++++++++++---------
.../java/org/apache/solr/search/CacheConfig.java | 28 +--
.../org/apache/solr/update/SolrIndexConfig.java | 33 ++--
.../org/apache/solr/core/TestCodecSupport.java | 6 +-
.../src/test/org/apache/solr/core/TestConfig.java | 18 +-
.../org/apache/solr/cluster/api/SimpleMap.java | 8 +
.../java/org/apache/solr/common/ConfigNode.java | 77 ++++++++
.../apache/solr/common/util/WrappedSimpleMap.java | 5 +
10 files changed, 293 insertions(+), 129 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/core/ConfigOverlay.java b/solr/core/src/java/org/apache/solr/core/ConfigOverlay.java
index 66991a4..069de5d 100644
--- a/solr/core/src/java/org/apache/solr/core/ConfigOverlay.java
+++ b/solr/core/src/java/org/apache/solr/core/ConfigOverlay.java
@@ -16,12 +16,14 @@
*/
package org.apache.solr.core;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.solr.common.MapSerializable;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CoreAdminParams;
@@ -62,6 +64,12 @@ public class ConfigOverlay implements MapSerializable {
return Utils.getObjectByPath(props, onlyPrimitive, hierarchy);
}
+ public Object getXPathProperty(List<String> path) {
+ List<String> hierarchy = new ArrayList<>();
+ if(isEditable(true, hierarchy, path) == null) return null;
+ return Utils.getObjectByPath(props, true, hierarchy);
+ }
+
@SuppressWarnings({"unchecked"})
public ConfigOverlay setUserProperty(String key, Object val) {
@SuppressWarnings({"rawtypes"})
@@ -180,7 +188,10 @@ public class ConfigOverlay implements MapSerializable {
@SuppressWarnings({"rawtypes"})
public static Class checkEditable(String path, boolean isXpath, List<String> hierarchy) {
- List<String> parts = StrUtils.splitSmart(path, isXpath ? '/' : '.');
+ return isEditable(isXpath, hierarchy, StrUtils.splitSmart(path, isXpath ? '/' : '.'));
+ }
+
+ private static Class isEditable(boolean isXpath, List<String> hierarchy, List<String> parts) {
Object obj = editable_prop_map;
for (int i = 0; i < parts.size(); i++) {
String part = parts.get(i);
diff --git a/solr/core/src/java/org/apache/solr/core/PluginInfo.java b/solr/core/src/java/org/apache/solr/core/PluginInfo.java
index 9dff7d2..b32bac7 100644
--- a/solr/core/src/java/org/apache/solr/core/PluginInfo.java
+++ b/solr/core/src/java/org/apache/solr/core/PluginInfo.java
@@ -23,6 +23,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import org.apache.solr.common.ConfigNode;
import org.apache.solr.common.MapSerializable;
import org.apache.solr.common.util.DOMUtil;
import org.apache.solr.common.util.NamedList;
@@ -99,6 +100,18 @@ public class PluginInfo implements MapSerializable {
}
+ public PluginInfo(ConfigNode node, String err,boolean requireName, boolean requireClass) {
+ type = node.name();
+ name = node.requiredStrAttr(NAME,requireName? () -> new RuntimeException(err + ": missing mandatory attribute 'name'"):null);
+ cName = parseClassName(node.requiredStrAttr(CLASS_NAME, requireClass? () -> new RuntimeException(err + ": missing mandatory attribute 'class'"):null ));
+ className = cName.className;
+ pkgName = cName.pkg;
+ initArgs = DOMUtil.childNodesToNamedList(node);
+ attributes = node.attributes().asMap();
+ children = loadSubPlugins(node);
+ isFromSolrConfig = true;
+
+ }
public PluginInfo(Node node, String err, boolean requireName, boolean requireClass) {
type = node.getNodeName();
name = DOMUtil.getAttr(node, NAME, requireName ? err : null);
@@ -143,6 +156,17 @@ public class PluginInfo implements MapSerializable {
isFromSolrConfig = true;
}
+ private List<PluginInfo> loadSubPlugins(ConfigNode node) {
+ List<PluginInfo> children = new ArrayList<>();
+ //if there is another sub tag with a non namedlist tag that has to be another plugin
+ node.forEachChild(nd -> {
+ if (NL_TAGS.contains(nd.name())) return null;
+ PluginInfo pluginInfo = new PluginInfo(nd, null, false, false);
+ if (pluginInfo.isEnabled()) children.add(pluginInfo);
+ return null;
+ });
+ return children.isEmpty() ? Collections.<PluginInfo>emptyList() : unmodifiableList(children);
+ }
private List<PluginInfo> loadSubPlugins(Node node) {
List<PluginInfo> children = new ArrayList<>();
//if there is another sub tag with a non namedlist tag that has to be another plugin
diff --git a/solr/core/src/java/org/apache/solr/core/SolrConfig.java b/solr/core/src/java/org/apache/solr/core/SolrConfig.java
index 804ceab..65f5043 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrConfig.java
@@ -18,7 +18,6 @@ package org.apache.solr.core;
import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.xpath.XPathConstants;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -51,11 +50,12 @@ import org.apache.lucene.util.Version;
import org.apache.solr.client.solrj.io.stream.expr.Expressible;
import org.apache.solr.cloud.RecoveryStrategy;
import org.apache.solr.cloud.ZkSolrResourceLoader;
+import org.apache.solr.common.ConfigNode;
import org.apache.solr.common.MapSerializable;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.common.util.DOMUtil;
import org.apache.solr.common.util.IOUtils;
+import org.apache.solr.common.util.StrUtils;
import org.apache.solr.handler.component.SearchComponent;
import org.apache.solr.pkg.PackageListeners;
import org.apache.solr.pkg.PackageLoader;
@@ -77,11 +77,12 @@ import org.apache.solr.update.SolrIndexConfig;
import org.apache.solr.update.UpdateLog;
import org.apache.solr.update.processor.UpdateRequestProcessorChain;
import org.apache.solr.update.processor.UpdateRequestProcessorFactory;
+import org.apache.solr.util.DOMConfigNode;
+import org.apache.solr.util.DataConfigNode;
import org.apache.solr.util.circuitbreaker.CircuitBreakerManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import static org.apache.solr.common.params.CommonParams.NAME;
@@ -95,6 +96,7 @@ 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;
+import static org.apache.solr.core.XmlConfigFile.assertWarnOrFail;
/**
@@ -102,12 +104,14 @@ import static org.apache.solr.core.SolrConfig.PluginOpts.REQUIRE_NAME_IN_OVERLAY
* configuration data for a Solr instance -- typically found in
* "solrconfig.xml".
*/
-public class SolrConfig extends XmlConfigFile implements MapSerializable {
+public class SolrConfig implements MapSerializable {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final String DEFAULT_CONF_FILE = "solrconfig.xml";
+ private XmlConfigFile xml;
+ ConfigNode root;
private RequestParams requestParams;
@@ -171,25 +175,30 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
private SolrConfig(SolrResourceLoader loader, String name, boolean isConfigsetTrusted, Properties substitutableProperties)
throws ParserConfigurationException, IOException, SAXException {
// insist we have non-null substituteProperties; it might get overlayed
- super(loader, name, null, "/config/", substitutableProperties == null ? new Properties() : substitutableProperties);
+ xml = new XmlConfigFile(loader, name, null, "/config/", substitutableProperties == null ? new Properties() : substitutableProperties );
+ root = new DataConfigNode(new DOMConfigNode(xml.getDocument().getDocumentElement()));
+// super(loader, name, null, "/config/", substitutableProperties == null ? new Properties() : substitutableProperties);
getOverlay();//just in case it is not initialized
getRequestParams();
initLibs(loader, isConfigsetTrusted);
- luceneMatchVersion = SolrConfig.parseLuceneVersionString(getVal(IndexSchema.LUCENE_MATCH_VERSION_PARAM, true));
+ String val = root.child(IndexSchema.LUCENE_MATCH_VERSION_PARAM,
+ () -> new RuntimeException("Missing: "+ IndexSchema.LUCENE_MATCH_VERSION_PARAM)) .textValue() ;
+
+ luceneMatchVersion = SolrConfig.parseLuceneVersionString(val);
log.info("Using Lucene MatchVersion: {}", luceneMatchVersion);
String indexConfigPrefix;
// 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);
+ boolean hasDeprecatedIndexConfig = (root.child("indexDefaults") != null) || (root.child("mainIndex") != null);
if (hasDeprecatedIndexConfig) {
throw new SolrException(ErrorCode.FORBIDDEN, "<indexDefaults> and <mainIndex> configuration sections are discontinued. Use <indexConfig> instead.");
} else {
indexConfigPrefix = "indexConfig";
}
assertWarnOrFail("The <nrtMode> config has been discontinued and NRT mode is always used by Solr." +
- " This config will be removed in future versions.", getNode(indexConfigPrefix + "/nrtMode", false) == null,
+ " This config will be removed in future versions.", root.__("indexDefaults").child("nrtMode") == null,
true
);
assertWarnOrFail("Solr no longer supports forceful unlocking via the 'unlockOnStartup' option. "+
@@ -197,14 +206,17 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
"it would be dangerous and should not be done. For other lockTypes and/or "+
"directoryFactory options it may also be dangerous and users must resolve "+
"problematic locks manually.",
- null == getNode(indexConfigPrefix + "/unlockOnStartup", false),
+ null == root.__(indexConfigPrefix).child( "unlockOnStartup"),
true // 'fail' in trunk
);
// Parse indexConfig section, using mainIndex as backup in case old config is used
indexConfig = new SolrIndexConfig(this, "indexConfig", null);
- booleanQueryMaxClauseCount = getInt("query/maxBooleanClauses", IndexSearcher.getMaxClauseCount());
+ booleanQueryMaxClauseCount = root
+ .__("query")
+ .__( "maxBooleanClauses")
+ ._int(IndexSearcher.getMaxClauseCount());
if (IndexSearcher.getMaxClauseCount() < booleanQueryMaxClauseCount) {
log.warn("solrconfig.xml: <maxBooleanClauses> of {} is greater than global limit of {} {}"
, booleanQueryMaxClauseCount, IndexSearcher.getMaxClauseCount()
@@ -213,9 +225,9 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
// Warn about deprecated / discontinued parameters
// boolToFilterOptimizer has had no effect since 3.1
- if (get("query/boolTofilterOptimizer", null) != null)
+ if (__("query").child("boolTofilterOptimizer") != null)
log.warn("solrconfig.xml: <boolTofilterOptimizer> is currently not implemented and has no effect.");
- if (get("query/HashDocSet", null) != null)
+ if (__("query").child("HashDocSet") != null)
log.warn("solrconfig.xml: <HashDocSet> is deprecated and no longer used.");
// TODO: Old code - in case somebody wants to re-enable. Also see SolrIndexSearcher#search()
@@ -223,15 +235,15 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
// filtOptCacheSize = getInt("query/boolTofilterOptimizer/@cacheSize",32);
// filtOptThreshold = getFloat("query/boolTofilterOptimizer/@threshold",.05f);
- useFilterForSortedQuery = getBool("query/useFilterForSortedQuery", false);
- queryResultWindowSize = Math.max(1, getInt("query/queryResultWindowSize", 1));
- queryResultMaxDocsCached = getInt("query/queryResultMaxDocsCached", Integer.MAX_VALUE);
- enableLazyFieldLoading = getBool("query/enableLazyFieldLoading", false);
+ useFilterForSortedQuery = __("query").__("useFilterForSortedQuery")._bool(false);
+ queryResultWindowSize = Math.max(1, __("query").__("queryResultWindowSize")._int(1));
+ queryResultMaxDocsCached = __("query").__("queryResultMaxDocsCached")._int(Integer.MAX_VALUE);
+ enableLazyFieldLoading = __("query").__("enableLazyFieldLoading")._bool(false);
- filterCacheConfig = CacheConfig.getConfig(this, "query/filterCache");
- queryResultCacheConfig = CacheConfig.getConfig(this, "query/queryResultCache");
- documentCacheConfig = CacheConfig.getConfig(this, "query/documentCache");
- CacheConfig conf = CacheConfig.getConfig(this, "query/fieldValueCache");
+ filterCacheConfig = CacheConfig.getConfig(this, __("query").child("filterCache"), "query/filterCache");
+ queryResultCacheConfig = CacheConfig.getConfig(this, __("query").child("queryResultCache"), "query/queryResultCache");
+ documentCacheConfig = CacheConfig.getConfig(this, __("query").child("documentCache"), "query/documentCache");
+ CacheConfig conf = CacheConfig.getConfig(this, __("query").child("fieldValueCache"), "query/fieldValueCache");
if (conf == null) {
Map<String, String> args = new HashMap<>();
args.put(NAME, "fieldValueCache");
@@ -241,24 +253,25 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
conf = new CacheConfig(CaffeineCache.class, args, null);
}
fieldValueCacheConfig = conf;
- useColdSearcher = getBool("query/useColdSearcher", false);
- dataDir = get("dataDir", null);
+ useColdSearcher = __("query").__("useColdSearcher")._bool(false);
+ dataDir = __("dataDir").textValue();
if (dataDir != null && dataDir.length() == 0) dataDir = null;
org.apache.solr.search.SolrIndexSearcher.initRegenerators(this);
- if (get("jmx", null) != null) {
+ if (root.child("jmx") != null) {
log.warn("solrconfig.xml: <jmx> is no longer supported, use solr.xml:/metrics/reporter section instead");
}
httpCachingConfig = new HttpCachingConfig(this);
- maxWarmingSearchers = getInt("query/maxWarmingSearchers", 1);
- slowQueryThresholdMillis = getInt("query/slowQueryThresholdMillis", -1);
+ maxWarmingSearchers = __("query").__("maxWarmingSearchers")._int(1);
+ slowQueryThresholdMillis = __("query").__("slowQueryThresholdMillis")._int(-1);
for (SolrPluginInfo plugin : plugins) loadPluginInfo(plugin);
- Map<String, CacheConfig> userCacheConfigs = CacheConfig.getMultipleConfigs(this, "query/cache");
+ Map<String, CacheConfig> userCacheConfigs = CacheConfig.getMultipleConfigs(this, "query/cache",
+ __("query").children("cache"));
List<PluginInfo> caches = getPluginInfos(SolrCache.class.getName());
if (!caches.isEmpty()) {
for (PluginInfo c : caches) {
@@ -269,25 +282,19 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
updateHandlerInfo = loadUpdatehandlerInfo();
- multipartUploadLimitKB = getInt(
- "requestDispatcher/requestParsers/@multipartUploadLimitInKB", Integer.MAX_VALUE);
+ multipartUploadLimitKB = __("requestDispatcher").__("requestParsers").intAttr("multipartUploadLimitInKB", Integer.MAX_VALUE);
if (multipartUploadLimitKB == -1) multipartUploadLimitKB = Integer.MAX_VALUE;
- formUploadLimitKB = getInt(
- "requestDispatcher/requestParsers/@formdataUploadLimitInKB", Integer.MAX_VALUE);
+ formUploadLimitKB = __("requestDispatcher").__("requestParsers").intAttr("formdataUploadLimitInKB", Integer.MAX_VALUE);
if (formUploadLimitKB == -1) formUploadLimitKB = Integer.MAX_VALUE;
- enableRemoteStreams = getBool(
- "requestDispatcher/requestParsers/@enableRemoteStreaming", false);
+ enableRemoteStreams = __("requestDispatcher").__("requestParsers").boolAttr("enableRemoteStreaming", false);
- enableStreamBody = getBool(
- "requestDispatcher/requestParsers/@enableStreamBody", false);
+ enableStreamBody = __("requestDispatcher").__("requestParsers").boolAttr("enableStreamBody", false);
- handleSelect = getBool(
- "requestDispatcher/@handleSelect", false);
+ handleSelect = __("requestDispatcher").boolAttr("handleSelect", false);
- addHttpRequestToContext = getBool(
- "requestDispatcher/requestParsers/@addHttpRequestToContext", false);
+ addHttpRequestToContext = __("requestDispatcher").__("requestParsers").boolAttr("addHttpRequestToContext", false);
List<PluginInfo> argsInfos = getPluginInfos(InitParams.class.getName());
if (argsInfos != null) {
@@ -428,15 +435,15 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
}
protected UpdateHandlerInfo loadUpdatehandlerInfo() {
- return new UpdateHandlerInfo(get("updateHandler/@class", null),
- getInt("updateHandler/autoCommit/maxDocs", -1),
- getInt("updateHandler/autoCommit/maxTime", -1),
- convertHeapOptionStyleConfigStringToBytes(get("updateHandler/autoCommit/maxSize", "")),
- getBool("updateHandler/indexWriter/closeWaitsForMerges", true),
- getBool("updateHandler/autoCommit/openSearcher", true),
- getInt("updateHandler/autoSoftCommit/maxDocs", -1),
- getInt("updateHandler/autoSoftCommit/maxTime", -1),
- getBool("updateHandler/commitWithin/softCommit", true));
+ return new UpdateHandlerInfo( __("updateHandler").attr("class"),
+ __("updateHandler").__("autoCommit").__("maxDocs")._int( -1),
+ __("updateHandler").__("autoCommit").__("maxTime")._int( -1),
+ convertHeapOptionStyleConfigStringToBytes(__("updateHandler").__("autoCommit").__("maxSize").txt("")),
+ __("updateHandler").__("indexWriter").__("closeWaitsForMerges")._bool(true),
+ __("updateHandler").__("autoCommit").__("openSearcher")._bool(true),
+ __("updateHandler").__("autoSoftCommit").__("autoSoftCommit")._int(-1),
+ __("updateHandler").__("autoSoftCommit").__("maxTime")._int(-1),
+ __("updateHandler").__("commitWithin").__("maxTime")._bool(true));
}
/**
@@ -494,9 +501,9 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
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++) {
- PluginInfo pluginInfo = new PluginInfo(nodes.item(i), "[solrconfig.xml] " + tag, requireName, requireClass);
+ List<ConfigNode> nodes = root.children(tag);
+ for (int i = 0; i < nodes.size(); i++) {
+ PluginInfo pluginInfo = new PluginInfo(nodes.get(i), "[solrconfig.xml] " + tag, requireName, requireClass);
if (pluginInfo.isEnabled()) result.add(pluginInfo);
}
return result;
@@ -548,9 +555,9 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
/**
* config xpath prefix for getting HTTP Caching options
*/
- private final static String CACHE_PRE
+ /* private final static String CACHE_PRE
= "requestDispatcher/httpCaching/";
-
+*/
/**
* For extracting Expires "ttl" from <cacheControl> config
*/
@@ -589,15 +596,15 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
private HttpCachingConfig(SolrConfig conf) {
- never304 = conf.getBool(CACHE_PRE + "@never304", false);
+ //"requestDispatcher/httpCaching/";
+ never304 = conf.__("requestDispatcher").__("httpCaching").boolAttr("never304", false);
- etagSeed = conf.get(CACHE_PRE + "@etagSeed", "Solr");
+ etagSeed = conf.__("requestDispatcher").__("httpCaching").attr("etagSeed", "Solr");
- lastModFrom = LastModFrom.parse(conf.get(CACHE_PRE + "@lastModFrom",
- "openTime"));
+ lastModFrom = LastModFrom.parse(conf.__("requestDispatcher").__("httpCaching").attr("lastModFrom","openTime"));
- cacheControlHeader = conf.get(CACHE_PRE + "cacheControl", null);
+ cacheControlHeader = conf.__("requestDispatcher").__("httpCaching").__("cacheControl").textValue();
Long tmp = null; // maxAge
if (null != cacheControlHeader) {
@@ -763,8 +770,8 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
}
}
- NodeList nodes = (NodeList) evaluate("lib", XPathConstants.NODESET);
- if (nodes != null && nodes.getLength() > 0) {
+ List<ConfigNode> nodes = root.children("lib");
+ if (nodes != null && nodes.size() > 0) {
if (!isConfigsetTrusted) {
throw new SolrException(ErrorCode.UNAUTHORIZED,
"The configset for this collection was uploaded without any authentication in place,"
@@ -772,14 +779,14 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
+ " after enabling authentication and authorization.");
}
- for (int i = 0; i < nodes.getLength(); i++) {
- Node node = nodes.item(i);
- String baseDir = DOMUtil.getAttr(node, "dir");
- String path = DOMUtil.getAttr(node, PATH);
+ for (int i = 0; i < nodes.size(); i++) {
+ ConfigNode node = nodes.get(i);
+ String baseDir = node.attr("dir");
+ String path = node.attr(PATH);
if (null != baseDir) {
// :TODO: add support for a simpler 'glob' mutually exclusive of regex
Path dir = instancePath.resolve(baseDir);
- String regex = DOMUtil.getAttr(node, "regex");
+ String regex = node.attr("regex");
try {
if (regex == null)
urls.addAll(SolrResourceLoader.getURLs(dir));
@@ -831,42 +838,44 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
return enableStreamBody;
}
- @Override
- public int getInt(String path) {
- return getInt(path, 0);
+ private Object _getVal(String path) {
+ List<String> parts = StrUtils.split(path,'/');
+ Object val = overlay.getXPathProperty(parts);
+ if(val !=null) return val;
+ return root.child(parts);
}
- @Override
- public int getInt(String path, int def) {
- Object val = overlay.getXPathProperty(path);
- if (val != null) return Integer.parseInt(val.toString());
- return super.getInt(path, def);
+
+
+ static int getInt(Object v, int def) {
+ if (v instanceof Number) return ((Number) v).intValue();
+ return v == null ? def : Integer.parseInt(v.toString());
}
- @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);
+ static boolean getBool(Object v, boolean def) {
+ if (v instanceof Boolean) return (Boolean) v;
+ return v == null ? def : Boolean.parseBoolean(v.toString());
}
- @Override
+ public int _getInt(String path, int def) {
+ Object val = _getVal(path);
+ return val == null ? def : Integer.parseInt(val.toString());
+ }
public String get(String path) {
- Object val = overlay.getXPathProperty(path, true);
- return val != null ? val.toString() : super.get(path);
+ Object val = _getVal(path);
+ return val != null ? val.toString() :null;
}
- @Override
public String get(String path, String def) {
- Object val = overlay.getXPathProperty(path, true);
- return val != null ? val.toString() : super.get(path, def);
+ Object val = _getVal(path);
+ return val != null ? val.toString() : xml.get(path, def);
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public Map<String, Object> toMap(Map<String, Object> result) {
- if (getZnodeVersion() > -1) result.put(ZNODEVER, getZnodeVersion());
+ if (xml.getZnodeVersion() > -1) result.put(ZNODEVER, xml.getZnodeVersion());
if(luceneMatchVersion != null) result.put(IndexSchema.LUCENE_MATCH_VERSION_PARAM, luceneMatchVersion.toString());
result.put("updateHandler", getUpdateHandlerInfo());
Map m = new LinkedHashMap();
@@ -927,11 +936,10 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
}
- @Override
public Properties getSubstituteProperties() {
Map<String, Object> p = getOverlay().getUserProps();
- if (p == null || p.isEmpty()) return super.getSubstituteProperties();
- Properties result = new Properties(super.getSubstituteProperties());
+ if (p == null || p.isEmpty()) return xml.getSubstituteProperties();
+ Properties result = new Properties(xml.getSubstituteProperties());
result.putAll(p);
return result;
}
@@ -940,7 +948,7 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
public ConfigOverlay getOverlay() {
if (overlay == null) {
- overlay = getConfigOverlay(getResourceLoader());
+ overlay = getConfigOverlay(xml.getResourceLoader());
}
return overlay;
}
@@ -967,13 +975,37 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
if (o == null || PackageLoader.LATEST.equals(o)) return null;
return o.toString();
}
+ ConfigNode getRoot() {
+ return root;
+ }
public RequestParams refreshRequestParams() {
- requestParams = RequestParams.getFreshRequestParams(getResourceLoader(), requestParams);
+ requestParams = RequestParams.getFreshRequestParams(xml.getResourceLoader(), requestParams);
if (log.isDebugEnabled()) {
log.debug("current version of requestparams : {}", requestParams.getZnodeVersion());
}
return requestParams;
}
+ public SolrResourceLoader getResourceLoader() {
+ return xml.getResourceLoader();
+ }
+
+ public int getZnodeVersion() {
+ return xml.getZnodeVersion();
+ }
+
+ public String getName() {
+ return xml.getName();
+ }
+
+ public String getResourceName() {
+ return xml.getResourceName();
+ }
+
+ public ConfigNode __(String name) {
+ return root.__(name);
+ }
+
+
}
diff --git a/solr/core/src/java/org/apache/solr/search/CacheConfig.java b/solr/core/src/java/org/apache/solr/search/CacheConfig.java
index 1520f80..1f83b56 100644
--- a/solr/core/src/java/org/apache/solr/search/CacheConfig.java
+++ b/solr/core/src/java/org/apache/solr/search/CacheConfig.java
@@ -23,13 +23,17 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.BiConsumer;
import java.util.function.Supplier;
+import org.apache.solr.cluster.api.SimpleMap;
+import org.apache.solr.common.ConfigNode;
import org.apache.solr.common.MapSerializable;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.DOMUtil;
import org.apache.solr.common.util.StrUtils;
+import org.apache.solr.common.util.WrappedSimpleMap;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.core.SolrConfig;
@@ -88,15 +92,13 @@ public class CacheConfig implements MapSerializable{
this.regenerator = regenerator;
}
- public static Map<String, CacheConfig> getMultipleConfigs(SolrConfig solrConfig, String configPath) {
- NodeList nodes = (NodeList) solrConfig.evaluate(configPath, XPathConstants.NODESET);
- if (nodes == null || nodes.getLength() == 0) return new LinkedHashMap<>();
- Map<String, CacheConfig> result = new HashMap<>(nodes.getLength());
- for (int i = 0; i < nodes.getLength(); i++) {
- Node node = nodes.item(i);
- if ("true".equals(DOMUtil.getAttrOrDefault(node, "enabled", "true"))) {
- CacheConfig config = getConfig(solrConfig, node.getNodeName(),
- DOMUtil.toMap(node.getAttributes()), configPath);
+ public static Map<String, CacheConfig> getMultipleConfigs(SolrConfig solrConfig, String configPath, List<ConfigNode> nodes) {
+ if (nodes == null || nodes.size() == 0) return new LinkedHashMap<>();
+ Map<String, CacheConfig> result = new HashMap<>(nodes.size());
+ for (int i = 0; i < nodes.size(); i++) {
+ ConfigNode node = nodes.get(i);
+ if (node.boolAttr( "enabled", true)) {
+ CacheConfig config = getConfig(solrConfig, node.name(),node.attributes().asMap(), configPath);
result.put(config.args.get(NAME), config);
}
}
@@ -105,15 +107,15 @@ public class CacheConfig implements MapSerializable{
@SuppressWarnings({"unchecked"})
- public static CacheConfig getConfig(SolrConfig solrConfig, String xpath) {
- Node node = solrConfig.getNode(xpath, false);
- if(node == null || !"true".equals(DOMUtil.getAttrOrDefault(node, "enabled", "true"))) {
+ public static CacheConfig getConfig(SolrConfig solrConfig, ConfigNode node, String xpath) {
+// Node node = solrConfig.getNode(xpath, false);
+ if(node == null || !"true".equals(node.attributes().get( "enabled", "true"))) {
Map<String, String> m = solrConfig.getOverlay().getEditableSubProperties(xpath);
if(m==null) return null;
List<String> parts = StrUtils.splitSmart(xpath, '/');
return getConfig(solrConfig,parts.get(parts.size()-1) , Collections.EMPTY_MAP,xpath);
}
- return getConfig(solrConfig, node.getNodeName(),DOMUtil.toMap(node.getAttributes()), xpath);
+ return getConfig(solrConfig, node.name(),node.attributes().asMap(), xpath);
}
diff --git a/solr/core/src/java/org/apache/solr/update/SolrIndexConfig.java b/solr/core/src/java/org/apache/solr/update/SolrIndexConfig.java
index a364757..cf24e33 100644
--- a/solr/core/src/java/org/apache/solr/update/SolrIndexConfig.java
+++ b/solr/core/src/java/org/apache/solr/update/SolrIndexConfig.java
@@ -31,6 +31,7 @@ import org.apache.lucene.index.MergePolicy;
import org.apache.lucene.index.MergeScheduler;
import org.apache.lucene.search.Sort;
import org.apache.lucene.util.InfoStream;
+import org.apache.solr.common.ConfigNode;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.DirectoryFactory;
@@ -47,6 +48,7 @@ import org.apache.solr.schema.IndexSchema;
import org.apache.solr.util.SolrPluginUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.w3c.dom.Node;
import static org.apache.solr.core.XmlConfigFile.assertWarnOrFail;
@@ -128,29 +130,29 @@ public class SolrIndexConfig implements MapSerializable {
// sanity check: this will throw an error for us if there is more then one
// config section
- Object unused = solrConfig.getNode(prefix, false);
+// Object unused = solrConfig.getNode(prefix, false);
// Assert that end-of-life parameters or syntax is not in our config.
// Warn for luceneMatchVersion's before LUCENE_3_6, fail fast above
assertWarnOrFail("The <mergeScheduler>myclass</mergeScheduler> syntax is no longer supported in solrconfig.xml. Please use syntax <mergeScheduler class=\"myclass\"/> instead.",
- !((solrConfig.getNode(prefix + "/mergeScheduler", false) != null) && (solrConfig.get(prefix + "/mergeScheduler/@class", null) == null)),
+ !(solrConfig.__(prefix).__("mergeScheduler") != null && (solrConfig.__(prefix).attr("class", null) == null)),
true);
assertWarnOrFail("Beginning with Solr 7.0, <mergePolicy>myclass</mergePolicy> is no longer supported, use <mergePolicyFactory> instead.",
- !((solrConfig.getNode(prefix + "/mergePolicy", false) != null) && (solrConfig.get(prefix + "/mergePolicy/@class", null) == null)),
+ !((solrConfig.__(prefix).__("mergePolicy") != null) && (solrConfig.__(prefix).__("mergePolicy").attr("class", null) == null)),
true);
assertWarnOrFail("The <luceneAutoCommit>true|false</luceneAutoCommit> parameter is no longer valid in solrconfig.xml.",
solrConfig.get(prefix + "/luceneAutoCommit", null) == null,
true);
- useCompoundFile = solrConfig.getBool(prefix+"/useCompoundFile", def.useCompoundFile);
- maxBufferedDocs = solrConfig.getInt(prefix+"/maxBufferedDocs", def.maxBufferedDocs);
- ramBufferSizeMB = solrConfig.getDouble(prefix+"/ramBufferSizeMB", def.ramBufferSizeMB);
- maxCommitMergeWaitMillis = solrConfig.getInt(prefix+"/maxCommitMergeWaitTime", def.maxCommitMergeWaitMillis);
+ useCompoundFile = solrConfig.__(prefix).__("useCompoundFile")._bool(def.useCompoundFile);
+ maxBufferedDocs = solrConfig.__(prefix).__("maxBufferedDocs")._int(def.maxBufferedDocs);
+ ramBufferSizeMB = solrConfig.__(prefix).__("ramBufferSizeMB").doubleVal(def.ramBufferSizeMB);
+ maxCommitMergeWaitMillis = solrConfig.__(prefix).__("maxCommitMergeWaitTime")._int(def.maxCommitMergeWaitMillis);
// how do we validate the value??
- ramPerThreadHardLimitMB = solrConfig.getInt(prefix+"/ramPerThreadHardLimitMB", def.ramPerThreadHardLimitMB);
+ ramPerThreadHardLimitMB = solrConfig.__(prefix).__("ramPerThreadHardLimitMB")._int(def.ramPerThreadHardLimitMB);
- writeLockTimeout=solrConfig.getInt(prefix+"/writeLockTimeout", def.writeLockTimeout);
+ writeLockTimeout= solrConfig.__(prefix).__("writeLockTimeout")._int(def.writeLockTimeout);
lockType=solrConfig.get(prefix+"/lockType", def.lockType);
List<PluginInfo> infos = solrConfig.readPluginInfos(prefix + "/metrics", false, false);
@@ -166,10 +168,10 @@ public class SolrIndexConfig implements MapSerializable {
getPluginInfo(prefix + "/mergePolicy", solrConfig, null) == null,
true);
assertWarnOrFail("Beginning with Solr 7.0, <maxMergeDocs> is no longer supported, configure it on the relevant <mergePolicyFactory> instead.",
- solrConfig.getInt(prefix+"/maxMergeDocs", 0) == 0,
+ solrConfig.__(prefix).__("maxMergeDocs")._int(0) == 0,
true);
assertWarnOrFail("Beginning with Solr 7.0, <mergeFactor> is no longer supported, configure it on the relevant <mergePolicyFactory> instead.",
- solrConfig.getInt(prefix+"/mergeFactor", 0) == 0,
+ solrConfig.__(prefix).__("mergeFactor")._int(0) == 0,
true);
String val = solrConfig.get(prefix + "/termIndexInterval", null);
@@ -177,9 +179,9 @@ public class SolrIndexConfig implements MapSerializable {
throw new IllegalArgumentException("Illegal parameter 'termIndexInterval'");
}
- boolean infoStreamEnabled = solrConfig.getBool(prefix + "/infoStream", false);
+ boolean infoStreamEnabled = solrConfig.__(prefix).__("infoStream")._bool(false);
if(infoStreamEnabled) {
- String infoStreamFile = solrConfig.get(prefix + "/infoStream/@file", null);
+ String infoStreamFile = solrConfig.__(prefix).__("infoStream").attr("file") ;
if (infoStreamFile == null) {
log.info("IndexWriter infoStream solr logging is enabled");
infoStream = new LoggingInfoStream();
@@ -187,10 +189,11 @@ public class SolrIndexConfig implements MapSerializable {
throw new IllegalArgumentException("Remove @file from <infoStream> to output messages to solr's logfile");
}
}
- mergedSegmentWarmerInfo = getPluginInfo(prefix + "/mergedSegmentWarmer", solrConfig, def.mergedSegmentWarmerInfo);
+ ConfigNode warmerInfo = solrConfig.__(prefix).__("mergedSegmentWarmer");
+ mergedSegmentWarmerInfo = warmerInfo==null? def.mergedSegmentWarmerInfo : new PluginInfo(warmerInfo, "[solrconfig.xml] mergedSegmentWarmer" , false, false);
assertWarnOrFail("Beginning with Solr 5.0, <checkIntegrityAtMerge> option is no longer supported and should be removed from solrconfig.xml (these integrity checks are now automatic)",
- (null == solrConfig.getNode(prefix + "/checkIntegrityAtMerge", false)),
+ (null == solrConfig.__(prefix).__( "checkIntegrityAtMerge")),
true);
}
diff --git a/solr/core/src/test/org/apache/solr/core/TestCodecSupport.java b/solr/core/src/test/org/apache/solr/core/TestCodecSupport.java
index cd86d4f..6bc20b3 100644
--- a/solr/core/src/test/org/apache/solr/core/TestCodecSupport.java
+++ b/solr/core/src/test/org/apache/solr/core/TestCodecSupport.java
@@ -200,9 +200,9 @@ public class TestCodecSupport extends SolrTestCaseJ4 {
SolrCore c = null;
SolrConfig config = TestHarness.createConfig(testSolrHome, previousCoreName, "solrconfig_codec2.xml");
- assertEquals("Unexpected codec factory for this test.", "solr.SchemaCodecFactory", config.get("codecFactory/@class"));
- assertNull("Unexpected configuration of codec factory for this test. Expecting empty element",
- config.getNode("codecFactory", false).getFirstChild());
+ assertEquals("Unexpected codec factory for this test.", "solr.SchemaCodecFactory", config.__("codecFactory").attr("class"));
+ assertTrue("Unexpected configuration of codec factory for this test. Expecting empty element",
+ config.__("codecFactory").children(null, (String)null).isEmpty());
IndexSchema schema = IndexSchemaFactory.buildIndexSchema("schema_codec.xml", config);
CoreContainer coreContainer = h.getCoreContainer();
diff --git a/solr/core/src/test/org/apache/solr/core/TestConfig.java b/solr/core/src/test/org/apache/solr/core/TestConfig.java
index ccf3114..7a61fb7 100644
--- a/solr/core/src/test/org/apache/solr/core/TestConfig.java
+++ b/solr/core/src/test/org/apache/solr/core/TestConfig.java
@@ -21,12 +21,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.Collections;
+import java.util.List;
import org.apache.lucene.index.ConcurrentMergeScheduler;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.TieredMergePolicy;
import org.apache.lucene.util.InfoStream;
import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.ConfigNode;
import org.apache.solr.handler.admin.ShowFileRequestHandler;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.IndexSchemaFactory;
@@ -80,24 +82,24 @@ public class TestConfig extends SolrTestCaseJ4 {
public void testJavaProperty() {
// property values defined in build.xml
- String s = solrConfig.get("propTest");
+ String s = solrConfig.__("propTest").textValue();
assertEquals("prefix-proptwo-suffix", s);
- s = solrConfig.get("propTest/@attr1", "default");
+ s = solrConfig.__("propTest").attr("attr1", "default");
assertEquals("propone-${literal}", s);
- s = solrConfig.get("propTest/@attr2", "default");
+ s = solrConfig.__("propTest").attr("attr2", "default");
assertEquals("default-from-config", s);
s = solrConfig.get("propTest[@attr2='default-from-config']", "default");
assertEquals("prefix-proptwo-suffix", s);
- NodeList nl = (NodeList) solrConfig.evaluate("propTest", XPathConstants.NODESET);
- assertEquals(1, nl.getLength());
- assertEquals("prefix-proptwo-suffix", nl.item(0).getTextContent());
+ List<ConfigNode> nl = solrConfig.root.children("propTest");
+ assertEquals(1, nl.size());
+ assertEquals("prefix-proptwo-suffix", nl.get(0).textValue());
- Node node = solrConfig.getNode("propTest", true);
- assertEquals("prefix-proptwo-suffix", node.getTextContent());
+
+ assertEquals("prefix-proptwo-suffix", solrConfig.__("propTest"));
}
// sometime if the config referes to old things, it must be replaced with new stuff
diff --git a/solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java b/solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java
index 81da171..cd9a372 100644
--- a/solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java
+++ b/solr/solrj/src/java/org/apache/solr/cluster/api/SimpleMap.java
@@ -20,6 +20,8 @@ package org.apache.solr.cluster.api;
import org.apache.solr.common.MapWriter;
import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -82,4 +84,10 @@ public interface SimpleMap<T> extends MapWriter {
default void writeMap(EntryWriter ew) throws IOException {
forEachEntry(ew::putNoEx);
}
+
+ default Map<String, T> asMap() {
+ Map<String, T> result = new LinkedHashMap<>();
+ forEachEntry((k, v) -> result.put(k, v));
+ return result;
+ }
}
diff --git a/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java b/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java
index 1a67b52..17fd678 100644
--- a/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java
+++ b/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java
@@ -19,11 +19,15 @@ package org.apache.solr.common;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.function.Supplier;
import org.apache.solr.cluster.api.SimpleMap;
+import org.apache.solr.common.util.WrappedSimpleMap;
/**
* A generic interface that represents a config file, mostly XML
@@ -53,6 +57,46 @@ public interface ConfigNode {
return child(null, name);
}
+ /**
+ * Child by name or return an empty node if null
+ */
+ default ConfigNode __(String name) {
+ ConfigNode child = child(null, name);
+ return child == null? EMPTY: child;
+ }
+
+ default ConfigNode child(List<String> path) {
+ ConfigNode node = this;
+ for (String s : path) {
+ node = node.child(s);
+ if (node == null) break;
+ }
+ return node;
+ }
+
+ default ConfigNode child(String name, Supplier<RuntimeException> err) {
+ ConfigNode n = child(name);
+ if(n == null) throw err.get();
+ return n;
+ }
+
+ default boolean _bool(boolean def) { return __bool(textValue(),def); }
+ default int _int(int def) { return __int(textValue(), def); }
+ default String attr(String name, String def) { return __txt(attributes().get(name), def);}
+ default String attr(String name) { return attributes().get(name);}
+ default String requiredStrAttr(String name, Supplier<RuntimeException> err) {
+ if(attributes().get(name) == null && err != null) throw err.get();
+ return attributes().get(name);
+ }
+ default int intAttr(String name, int def) { return __int(attributes().get(name), def); }
+ default boolean boolAttr(String name, boolean def){ return __bool(attributes().get(name), def); }
+ default String txt(String def) { return textValue() == null ? def : textValue();}
+ default double doubleVal(double def){ return __double(textValue(), def); }
+ default boolean __bool(Object v, boolean def) { return v == null ? def : Boolean.parseBoolean(v.toString()); }
+ default String __txt(Object v, String def) { return v == null ? def : v.toString(); }
+ default int __int(Object v, int def) { return v==null? def: Integer.parseInt(v.toString()); }
+ default double __double(Object v, double def) { return v == null ? def: Double.parseDouble(v.toString()); }
+
/**Iterate through child nodes with the name and return the first child that matches
*/
default ConfigNode child(Predicate<ConfigNode> test, String name) {
@@ -100,5 +144,38 @@ public interface ConfigNode {
*/
void forEachChild(Function<ConfigNode, Boolean> fun);
+ ConfigNode EMPTY = new ConfigNode() {
+ @Override
+ public String name() {
+ return null;
+ }
+
+ @Override
+ public String textValue() {
+ return null;
+ }
+
+ @Override
+ public SimpleMap<String> attributes() {
+ return empty_attrs;
+ }
+
+ @Override
+ public ConfigNode child(String name) {
+ return null;
+ }
+
+ @Override
+ public ConfigNode __(String name) {
+ return EMPTY;
+ }
+
+ @Override
+ public void forEachChild(Function<ConfigNode, Boolean> fun) {
+
+ }
+ } ;
+ SimpleMap<String> empty_attrs = new WrappedSimpleMap<>(Collections.emptyMap());
+
}
diff --git a/solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java b/solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java
index e8f58a5..e8689d2 100644
--- a/solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java
+++ b/solr/solrj/src/java/org/apache/solr/common/util/WrappedSimpleMap.java
@@ -19,6 +19,7 @@ package org.apache.solr.common.util;
import org.apache.solr.cluster.api.SimpleMap;
+import java.util.Collections;
import java.util.Map;
import java.util.function.BiConsumer;
@@ -46,4 +47,8 @@ public class WrappedSimpleMap<T> implements SimpleMap<T> {
this.delegate = delegate;
}
+ @Override
+ public Map<String, T> asMap() {
+ return Collections.unmodifiableMap(delegate);
+ }
}