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/11/01 02:39:32 UTC
[solr] branch main updated: SOLR-15337: Avoid XPath in
solrconfig.xml parsing (#160)
This is an automated email from the ASF dual-hosted git repository.
noble pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 28139d2 SOLR-15337: Avoid XPath in solrconfig.xml parsing (#160)
28139d2 is described below
commit 28139d2f1228f6eca9df85cbcc825d3e06c301ea
Author: Noble Paul <no...@users.noreply.github.com>
AuthorDate: Mon Nov 1 13:39:26 2021 +1100
SOLR-15337: Avoid XPath in solrconfig.xml parsing (#160)
---
.../java/org/apache/solr/core/ConfigOverlay.java | 4 +
.../org/apache/solr/core/ConfigSetService.java | 6 +
.../org/apache/solr/core/OverlaidConfigNode.java | 109 +++++
.../src/java/org/apache/solr/core/PluginInfo.java | 24 +
.../src/java/org/apache/solr/core/SolrConfig.java | 487 ++++++++++++---------
.../java/org/apache/solr/core/XmlConfigFile.java | 15 +-
.../apache/solr/handler/DumpRequestHandler.java | 1 +
.../apache/solr/schema/FieldTypePluginLoader.java | 6 +-
.../java/org/apache/solr/schema/IndexSchema.java | 25 +-
.../org/apache/solr/schema/IndexSchemaFactory.java | 14 +-
.../org/apache/solr/schema/ManagedIndexSchema.java | 3 +-
.../java/org/apache/solr/search/CacheConfig.java | 40 +-
.../org/apache/solr/update/SolrIndexConfig.java | 82 ++--
.../java/org/apache/solr/update/VersionInfo.java | 4 +-
.../java/org/apache/solr/util/DOMConfigNode.java | 12 +-
.../java/org/apache/solr/util/DataConfigNode.java | 71 +--
.../resources/EditableSolrConfigAttributes.json | 4 -
.../test/org/apache/solr/core/TestBadConfig.java | 12 -
.../org/apache/solr/core/TestCodecSupport.java | 6 +-
.../org/apache/solr/core/TestConfLoadPerf.java | 88 ++++
.../src/test/org/apache/solr/core/TestConfig.java | 25 +-
.../org/apache/solr/core/TestConfigOverlay.java | 3 -
.../org/apache/solr/core/TestSimpleTextCodec.java | 2 +-
.../handler/component/SuggestComponentTest.java | 122 ++++--
.../apache/solr/update/SolrIndexConfigTest.java | 12 +-
.../org/apache/solr/cluster/api/SimpleMap.java | 13 +-
.../java/org/apache/solr/common/ConfigNode.java | 124 +++++-
.../java/org/apache/solr/common/util/DOMUtil.java | 10 +-
.../solr/common/util/LinkedSimpleHashMap.java | 6 +
.../apache/solr/common/util/WrappedSimpleMap.java | 11 +
30 files changed, 914 insertions(+), 427 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 42db6a7..2c66334 100644
--- a/solr/core/src/java/org/apache/solr/core/ConfigOverlay.java
+++ b/solr/core/src/java/org/apache/solr/core/ConfigOverlay.java
@@ -251,6 +251,10 @@ public class ConfigOverlay implements MapSerializable {
return Collections.unmodifiableMap(reqHandlers);
}
+ boolean hasKey(String key) {
+ return props.containsKey(key);
+ }
+
@SuppressWarnings({"unchecked"})
public ConfigOverlay addNamedPlugin(Map<String, Object> info, String typ) {
diff --git a/solr/core/src/java/org/apache/solr/core/ConfigSetService.java b/solr/core/src/java/org/apache/solr/core/ConfigSetService.java
index 5452648..abab0f1 100644
--- a/solr/core/src/java/org/apache/solr/core/ConfigSetService.java
+++ b/solr/core/src/java/org/apache/solr/core/ConfigSetService.java
@@ -33,6 +33,7 @@ import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.solr.cloud.ZkConfigSetService;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.cloud.ZkSolrResourceLoader;
+import org.apache.solr.common.ConfigNode;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.StringUtils;
import org.apache.solr.common.util.NamedList;
@@ -460,4 +461,9 @@ public abstract class ConfigSetService {
*/
public abstract List<String> getAllConfigFiles(String configName) throws IOException;
+ public interface ConfigResource {
+
+ ConfigNode get() throws Exception;
+
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/core/OverlaidConfigNode.java b/solr/core/src/java/org/apache/solr/core/OverlaidConfigNode.java
new file mode 100644
index 0000000..9bb5fdd
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/core/OverlaidConfigNode.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.core;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import org.apache.solr.cluster.api.SimpleMap;
+import org.apache.solr.common.ConfigNode;
+
+/**A config node impl which has an overlay
+ *
+ */
+class OverlaidConfigNode implements ConfigNode {
+
+ private final ConfigOverlay overlay;
+ private final String _name;
+ private final ConfigNode delegate;
+ private final OverlaidConfigNode parent;
+
+ OverlaidConfigNode(ConfigOverlay overlay, String name, OverlaidConfigNode parent, ConfigNode delegate) {
+ this.overlay = overlay;
+ this._name = name;
+ this.delegate = delegate;
+ this.parent = parent;
+ }
+
+ private List<String> path(List<String> path) {
+ if(path== null) path = new ArrayList<>(5);
+ try {
+ if (parent != null) return parent.path(path);
+ } finally {
+ path.add(_name);
+ }
+ return path;
+ }
+
+ @Override
+ public ConfigNode get(String name) {
+ return wrap(delegate.get(name), name);
+ }
+
+ private ConfigNode wrap(ConfigNode n, String name) {
+ return new OverlaidConfigNode(overlay, name,this, n);
+ }
+
+ @Override
+ public ConfigNode get(String name, Predicate<ConfigNode> test) {
+ return wrap(delegate.get(name, test), name);
+ }
+
+ @Override
+ public String txt() {
+ return overlayText(delegate.txt(), null);
+ }
+
+ @Override
+ public ConfigNode get(String name, int idx) {
+ return wrap(delegate.get(name, idx), name);
+ }
+
+ @Override
+ public String name() {
+ return delegate.name();
+ }
+ @Override
+ public SimpleMap<String> attributes() {
+ return delegate.attributes();
+ }
+
+ @Override
+ public boolean exists() {
+ return delegate.exists();
+ }
+
+ @Override
+ public String attr(String name) {
+ return overlayText(delegate.attr(name),name);
+ }
+
+ private String overlayText(String def, String appendToPath) {
+ List<String> path = path(null);
+ if(appendToPath !=null) path.add(appendToPath);
+ Object val = overlay.getXPathProperty(path);
+ return val ==null? def: val.toString();
+ }
+
+ @Override
+ public void forEachChild(Function<ConfigNode, Boolean> fun) {
+ delegate.forEachChild(fun);
+ }
+}
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 fe43338..ffb3fbe 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;
@@ -104,6 +105,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);
@@ -149,6 +162,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.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 abda92a..1cd05df 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrConfig.java
@@ -17,8 +17,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;
@@ -36,25 +34,30 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.collect.ImmutableList;
import org.apache.commons.io.FileUtils;
import org.apache.lucene.index.IndexDeletionPolicy;
+import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
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.Utils;
import org.apache.solr.handler.component.SearchComponent;
@@ -78,12 +81,11 @@ 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;
import static org.apache.solr.common.params.CommonParams.PATH;
@@ -95,6 +97,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 +105,17 @@ 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 final String resourceName;
+ private int znodeVersion;
+ ConfigNode root;
+ private final SolrResourceLoader resourceLoader;
+ private Properties substituteProperties;
private RequestParams requestParams;
@@ -141,7 +149,7 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
* @param name the configuration name used by the loader if the stream is null
*/
public SolrConfig(Path instanceDir, String name)
- throws ParserConfigurationException, IOException, SAXException {
+ throws IOException {
this(new SolrResourceLoader(instanceDir), name, true, null);
}
@@ -158,6 +166,27 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
throw new SolrException(ErrorCode.SERVER_ERROR, "Error loading solr config from " + resource, e);
}
}
+ private class ResourceProvider implements Function<String, InputStream> {
+ int zkVersion;
+ int hash = -1;
+ InputStream in;
+ String fileName;
+
+ ResourceProvider(InputStream in) {
+ this.in = in;
+ if (in instanceof ZkSolrResourceLoader.ZkByteArrayInputStream) {
+ ZkSolrResourceLoader.ZkByteArrayInputStream zkin = (ZkSolrResourceLoader.ZkByteArrayInputStream) in;
+ zkVersion = zkin.getStat().getVersion();
+ hash = Objects.hash(zkVersion, overlay.getZnodeVersion());
+ this.fileName = zkin.fileName;
+ }
+ }
+
+ @Override
+ public InputStream apply(String s) {
+ return in;
+ }
+ }
/**
* Creates a configuration instance from a resource loader, a configuration name and a stream.
@@ -168,140 +197,180 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
* @param isConfigsetTrusted false if configset was uploaded using unsecured configset upload API, true otherwise
* @param substitutableProperties optional properties to substitute into the XML
*/
+ @SuppressWarnings("unchecked")
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);
+ throws IOException {
+ this.resourceLoader = loader;
+ this.resourceName = name;
+ this.substituteProperties = substitutableProperties;
getOverlay();//just in case it is not initialized
- getRequestParams();
- initLibs(loader, isConfigsetTrusted);
- luceneMatchVersion = SolrConfig.parseLuceneVersionString(getVal(IndexSchema.LUCENE_MATCH_VERSION_PARAM, true));
- 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);
- 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,
- true
- );
- assertWarnOrFail("Solr no longer supports forceful unlocking via the 'unlockOnStartup' option. "+
- "This is no longer necessary for the default lockType except in situations where "+
- "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),
- 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());
- if (IndexSearcher.getMaxClauseCount() < booleanQueryMaxClauseCount) {
- log.warn("solrconfig.xml: <maxBooleanClauses> of {} is greater than global limit of {} {}"
- , booleanQueryMaxClauseCount, IndexSearcher.getMaxClauseCount()
- , "and will have no effect set 'maxBooleanClauses' in solr.xml to increase global limit");
- }
-
- // Warn about deprecated / discontinued parameters
- // boolToFilterOptimizer has had no effect since 3.1
- 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)
- log.warn("solrconfig.xml: <HashDocSet> is deprecated and no longer used.");
+ // insist we have non-null substituteProperties; it might get overlaid
+ Map<String, IndexSchemaFactory.VersionedConfig> configCache =null;
+ if (loader.getCoreContainer() != null && loader.getCoreContainer().getObjectCache() != null) {
+ configCache = (Map<String, IndexSchemaFactory.VersionedConfig>) loader.getCoreContainer().getObjectCache()
+ .computeIfAbsent(ConfigSetService.ConfigResource.class.getName(), s -> new ConcurrentHashMap<>());
+ ResourceProvider rp = new ResourceProvider(loader.openResource(name));
+ IndexSchemaFactory.VersionedConfig cfg = rp.fileName == null ? null : configCache.get(rp.fileName);
+ if (cfg != null) {
+ if (rp.hash != -1) {
+ if (rp.hash == cfg.version) {
+ log.debug("LOADED_FROM_CACHE");
+ root = cfg.data;
+ } else {
+ readXml(loader, name, configCache, rp);
+ }
+ }
+ }
+ }
+ if(root == null) {
+ readXml(loader, name, configCache,new ResourceProvider(loader.openResource(name)) );
+ }
+ ConfigNode.SUBSTITUTES.set(key -> {
+ if (substitutableProperties != null && substitutableProperties.containsKey(key)) {
+ return substitutableProperties.getProperty(key);
+ } else {
+ Object o = overlay.getUserProps().get(key);
+ return o == null ? null : o.toString();
+ }
+ });
+ try {
+ getRequestParams();
+ initLibs(loader, isConfigsetTrusted);
+ String val = root.child(IndexSchema.LUCENE_MATCH_VERSION_PARAM,
+ () -> new RuntimeException("Missing: " + IndexSchema.LUCENE_MATCH_VERSION_PARAM)).txt();
+
+ 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 = get("indexDefaults").exists() || get("mainIndex").exists();
+ 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.", get(indexConfigPrefix).get("nrtMode").isNull(),
+ true
+ );
+ assertWarnOrFail("Solr no longer supports forceful unlocking via the 'unlockOnStartup' option. " +
+ "This is no longer necessary for the default lockType except in situations where " +
+ "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.",
+ !get(indexConfigPrefix).get("unlockOnStartup").exists(),
+ true // 'fail' in trunk
+ );
+
+ // Parse indexConfig section, using mainIndex as backup in case old config is used
+ indexConfig = new SolrIndexConfig(get("indexConfig"), null);
+
+ booleanQueryMaxClauseCount = get("query").get("maxBooleanClauses").intVal(BooleanQuery.getMaxClauseCount());
+ if (IndexSearcher.getMaxClauseCount() < booleanQueryMaxClauseCount) {
+ log.warn("solrconfig.xml: <maxBooleanClauses> of {} is greater than global limit of {} and will have no effect {}"
+ , booleanQueryMaxClauseCount, BooleanQuery.getMaxClauseCount()
+ , "set 'maxBooleanClauses' in solr.xml to increase global limit");
+ }
+
+ // Warn about deprecated / discontinued parameters
+ // boolToFilterOptimizer has had no effect since 3.1
+ if (get("query").get("boolTofilterOptimizer").exists())
+ log.warn("solrconfig.xml: <boolTofilterOptimizer> is currently not implemented and has no effect.");
+ if (get("query").get("HashDocSet").exists())
+ 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()
// filtOptEnabled = getBool("query/boolTofilterOptimizer/@enabled", false);
// 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);
-
- 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");
- 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");
- conf = new CacheConfig(CaffeineCache.class, args, null);
- }
- fieldValueCacheConfig = conf;
- useColdSearcher = getBool("query/useColdSearcher", false);
- dataDir = get("dataDir", null);
- if (dataDir != null && dataDir.length() == 0) dataDir = null;
-
-
- org.apache.solr.search.SolrIndexSearcher.initRegenerators(this);
-
- if (get("jmx", null) != 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);
- for (SolrPluginInfo plugin : plugins) loadPluginInfo(plugin);
-
- Map<String, CacheConfig> userCacheConfigs = CacheConfig.getMultipleConfigs(this, "query/cache");
- List<PluginInfo> caches = getPluginInfos(SolrCache.class.getName());
- if (!caches.isEmpty()) {
- for (PluginInfo c : caches) {
- userCacheConfigs.put(c.name, CacheConfig.getConfig(this, "cache", c.attributes, null));
+ useFilterForSortedQuery = get("query").get("useFilterForSortedQuery").boolVal(false);
+ queryResultWindowSize = Math.max(1, get("query").get("queryResultWindowSize").intVal(1));
+ queryResultMaxDocsCached = get("query").get("queryResultMaxDocsCached").intVal(Integer.MAX_VALUE);
+ enableLazyFieldLoading = get("query").get("enableLazyFieldLoading").boolVal(false);
+
+ filterCacheConfig = CacheConfig.getConfig(this, get("query").get("filterCache"), "query/filterCache");
+ queryResultCacheConfig = CacheConfig.getConfig(this, get("query").get("queryResultCache"), "query/queryResultCache");
+ documentCacheConfig = CacheConfig.getConfig(this, get("query").get("documentCache"), "query/documentCache");
+ CacheConfig conf = CacheConfig.getConfig(this, get("query").get("fieldValueCache"), "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");
+ conf = new CacheConfig(CaffeineCache.class, args, null);
}
- }
- this.userCacheConfigs = Collections.unmodifiableMap(userCacheConfigs);
+ fieldValueCacheConfig = conf;
+ useColdSearcher = get("query").get("useColdSearcher").boolVal(false);
+ dataDir = get("dataDir").txt();
+ if (dataDir != null && dataDir.length() == 0) dataDir = null;
+
- updateHandlerInfo = loadUpdatehandlerInfo();
+ org.apache.solr.search.SolrIndexSearcher.initRegenerators(this);
- multipartUploadLimitKB = getInt(
- "requestDispatcher/requestParsers/@multipartUploadLimitInKB", Integer.MAX_VALUE);
- if (multipartUploadLimitKB == -1) multipartUploadLimitKB = Integer.MAX_VALUE;
+ if (get("jmx").exists()) {
+ log.warn("solrconfig.xml: <jmx> is no longer supported, use solr.xml:/metrics/reporter section instead");
+ }
+
+ httpCachingConfig = new HttpCachingConfig(this);
+
+ maxWarmingSearchers = get("query").get("maxWarmingSearchers").intVal(1);
+ slowQueryThresholdMillis = get("query").get("slowQueryThresholdMillis").intVal(-1);
+ for (SolrPluginInfo plugin : plugins) loadPluginInfo(plugin);
- formUploadLimitKB = getInt(
- "requestDispatcher/requestParsers/@formdataUploadLimitInKB", Integer.MAX_VALUE);
- if (formUploadLimitKB == -1) formUploadLimitKB = Integer.MAX_VALUE;
+ Map<String, CacheConfig> userCacheConfigs = CacheConfig.getMultipleConfigs(this, "query/cache",
+ get("query").getAll("cache"));
+ List<PluginInfo> caches = getPluginInfos(SolrCache.class.getName());
+ if (!caches.isEmpty()) {
+ for (PluginInfo c : caches) {
+ userCacheConfigs.put(c.name, CacheConfig.getConfig(this, "cache", c.attributes, null));
+ }
+ }
+ this.userCacheConfigs = Collections.unmodifiableMap(userCacheConfigs);
- enableRemoteStreams = getBool(
- "requestDispatcher/requestParsers/@enableRemoteStreaming", false);
+ updateHandlerInfo = loadUpdatehandlerInfo();
- enableStreamBody = getBool(
- "requestDispatcher/requestParsers/@enableStreamBody", false);
+ multipartUploadLimitKB = get("requestDispatcher").get("requestParsers").intAttr("multipartUploadLimitInKB", Integer.MAX_VALUE);
+ if (multipartUploadLimitKB == -1) multipartUploadLimitKB = Integer.MAX_VALUE;
- handleSelect = getBool(
- "requestDispatcher/@handleSelect", false);
+ formUploadLimitKB = get("requestDispatcher").get("requestParsers").intAttr("formdataUploadLimitInKB", Integer.MAX_VALUE);
+ if (formUploadLimitKB == -1) formUploadLimitKB = Integer.MAX_VALUE;
- addHttpRequestToContext = getBool(
- "requestDispatcher/requestParsers/@addHttpRequestToContext", false);
+ enableRemoteStreams = get("requestDispatcher").get("requestParsers").boolAttr("enableRemoteStreaming", 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);
+ enableStreamBody = get("requestDispatcher").get("requestParsers").boolAttr("enableStreamBody", false);
+
+ handleSelect = get("requestDispatcher").boolAttr("handleSelect", false);
+ addHttpRequestToContext = get("requestDispatcher").get("requestParsers").boolAttr("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);
+ }
+ this.initParams = Collections.unmodifiableMap(argsMap);
}
- this.initParams = Collections.unmodifiableMap(argsMap);
+ solrRequestParsers = new SolrRequestParsers(this);
+ log.debug("Loaded SolrConfig: {}", name);
+ } finally {
+ ConfigNode.SUBSTITUTES.remove();
}
+ }
- solrRequestParsers = new SolrRequestParsers(this);
- log.debug("Loaded SolrConfig: {}", name);
+ private void readXml(SolrResourceLoader loader, String name, Map<String, IndexSchemaFactory.VersionedConfig> configCache, ResourceProvider rp) throws IOException {
+ XmlConfigFile xml = new XmlConfigFile(loader,rp, name, null, "/config/", null);
+ root = new DataConfigNode(new DOMConfigNode(xml.getDocument().getDocumentElement()));
+ this.znodeVersion = rp.zkVersion;
+ if(configCache != null && rp.fileName !=null) {
+ configCache.put(rp.fileName, new IndexSchemaFactory.VersionedConfig(rp.hash, root));
+ }
}
private static final AtomicBoolean versionWarningAlreadyLogged = new AtomicBoolean(false);
@@ -340,20 +409,25 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
// and even then -- only if there is a single SpellCheckComponent
// because of queryConverter.setIndexAnalyzer
.add(new SolrPluginInfo(QueryConverter.class, "queryConverter", REQUIRE_NAME, REQUIRE_CLASS))
- // 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
+ // 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(it -> {
+ List<ConfigNode> result = new ArrayList<>();
+ result.addAll(it.get("query").getAll("listener"));
+ result.addAll( it.get("updateHandler").getAll("listener"));
+ return result;
+ }, SolrEventListener.class, "//listener", REQUIRE_CLASS, MULTI_OK, REQUIRE_NAME_IN_OVERLAY))
.add(new SolrPluginInfo(DirectoryFactory.class, "directoryFactory", REQUIRE_CLASS))
.add(new SolrPluginInfo(RecoveryStrategy.Builder.class, "recoveryStrategy"))
- .add(new SolrPluginInfo(IndexDeletionPolicy.class, "indexConfig/deletionPolicy", REQUIRE_CLASS))
+ .add(new SolrPluginInfo(it -> it.get("indexConfig").getAll("deletionPolicy"), 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(it -> it.get("updateHandler").getAll("updateLog"), UpdateLog.class, "updateHandler/updateLog"))
.add(new SolrPluginInfo(IndexSchemaFactory.class, "schemaFactory", REQUIRE_CLASS))
.add(new SolrPluginInfo(RestManager.class, "restManager"))
.add(new SolrPluginInfo(StatsCache.class, "statsCache", REQUIRE_CLASS))
@@ -372,9 +446,17 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
public final Class<?> clazz;
public final String tag;
public final Set<PluginOpts> options;
+ final Function<SolrConfig, List<ConfigNode>> configReader;
private SolrPluginInfo(Class<?> clz, String tag, PluginOpts... opts) {
+ this(solrConfig -> solrConfig.root.getAll(null, tag), clz, tag, opts);
+
+ }
+
+ private SolrPluginInfo(Function<SolrConfig, List<ConfigNode>> configReader, Class<?> clz, String tag, PluginOpts... opts) {
+ this.configReader = configReader;
+
this.clazz = clz;
this.tag = tag;
this.options = opts == null ? Collections.emptySet() : EnumSet.of(NOOP, opts);
@@ -402,7 +484,7 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
// hopefully no problem, assume no overlay.json file
return new ConfigOverlay(Collections.emptyMap(), -1);
}
-
+
int version = 0; // will be always 0 for file based resourceLoader
if (in instanceof ZkSolrResourceLoader.ZkByteArrayInputStream) {
version = ((ZkSolrResourceLoader.ZkByteArrayInputStream) in).getStat().getVersion();
@@ -426,14 +508,16 @@ 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/autoCommit/openSearcher", true),
- getInt("updateHandler/autoSoftCommit/maxDocs", -1),
- getInt("updateHandler/autoSoftCommit/maxTime", -1),
- getBool("updateHandler/commitWithin/softCommit", true));
+ ConfigNode updateHandler = get("updateHandler");
+ ConfigNode autoCommit = updateHandler.get("autoCommit");
+ return new UpdateHandlerInfo( updateHandler.attr("class"),
+ autoCommit.get("maxDocs").intVal( -1),
+ autoCommit.get("maxTime").intVal( -1),
+ convertHeapOptionStyleConfigStringToBytes(autoCommit.get("maxSize").txt()),
+ autoCommit.get("openSearcher").boolVal(true),
+ updateHandler.get("autoSoftCommit").get("maxDocs").intVal(-1),
+ updateHandler.get("autoSoftCommit").get("maxTime").intVal(-1),
+ updateHandler.get("commitWithin").get("softCommit").boolVal(true));
}
/**
@@ -444,7 +528,7 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
* @return the size, in bytes. -1 if the given config string is empty
*/
protected static long convertHeapOptionStyleConfigStringToBytes(String configStr) {
- if (configStr.isEmpty()) {
+ if (configStr== null || configStr.isEmpty()) {
return -1;
}
long multiplier = 1;
@@ -478,7 +562,7 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
boolean requireName = pluginInfo.options.contains(REQUIRE_NAME);
boolean requireClass = pluginInfo.options.contains(REQUIRE_CLASS);
- List<PluginInfo> result = readPluginInfos(pluginInfo.tag, requireName, requireClass);
+ List<PluginInfo> result = readPluginInfos(pluginInfo, requireName, requireClass);
if (1 < result.size() && !pluginInfo.options.contains(MULTI_OK)) {
throw new SolrException
@@ -489,11 +573,10 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
if (!result.isEmpty()) pluginStore.put(pluginInfo.clazz.getName(), result);
}
- public List<PluginInfo> readPluginInfos(String tag, boolean requireName, boolean requireClass) {
+ public List<PluginInfo> readPluginInfos(SolrPluginInfo info, 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);
+ for (ConfigNode node : info.configReader.apply(this)) {
+ PluginInfo pluginInfo = new PluginInfo(node, "[solrconfig.xml] " + info.tag, requireName, requireClass);
if (pluginInfo.isEnabled()) result.add(pluginInfo);
}
return result;
@@ -543,12 +626,6 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
public static class HttpCachingConfig implements MapSerializable {
/**
- * config xpath prefix for getting HTTP Caching options
- */
- private static final String CACHE_PRE
- = "requestDispatcher/httpCaching/";
-
- /**
* For extracting Expires "ttl" from <cacheControl> config
*/
private static final Pattern MAX_AGE
@@ -563,7 +640,7 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
"cacheControl", cacheControlHeader);
}
- public static enum LastModFrom {
+ public enum LastModFrom {
OPENTIME, DIRLASTMOD, BOGUS;
/**
@@ -584,18 +661,21 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
private final String cacheControlHeader;
private final Long maxAge;
private final LastModFrom lastModFrom;
+ private ConfigNode configNode;
private HttpCachingConfig(SolrConfig conf) {
+ configNode = conf.root;
- never304 = conf.getBool(CACHE_PRE + "@never304", false);
+ //"requestDispatcher/httpCaching/";
+ ConfigNode httpCaching = get("requestDispatcher").get("httpCaching");
+ never304 = httpCaching.boolAttr("never304", false);
- etagSeed = conf.get(CACHE_PRE + "@etagSeed", "Solr");
+ etagSeed = httpCaching.attr("etagSeed", "Solr");
- lastModFrom = LastModFrom.parse(conf.get(CACHE_PRE + "@lastModFrom",
- "openTime"));
+ lastModFrom = LastModFrom.parse(httpCaching.attr("lastModFrom","openTime"));
- cacheControlHeader = conf.get(CACHE_PRE + "cacheControl", null);
+ cacheControlHeader = httpCaching.get("cacheControl").txt();
Long tmp = null; // maxAge
if (null != cacheControlHeader) {
@@ -613,6 +693,9 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
maxAge = tmp;
}
+ private ConfigNode get(String name){
+ return configNode.get(name);
+ }
public boolean isNever304() {
return never304;
@@ -755,23 +838,23 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
}
}
- NodeList nodes = (NodeList) evaluate("lib", XPathConstants.NODESET);
- if (nodes != null && nodes.getLength() > 0) {
+ List<ConfigNode> nodes = root.getAll("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,"
- + " and use of <lib> is not available for collections with untrusted configsets. To use this component, re-upload the configset"
- + " after enabling authentication and authorization.");
+ "The configset for this collection was uploaded without any authentication in place,"
+ + " and use of <lib> is not available for collections with untrusted configsets. To use this component, re-upload the configset"
+ + " 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));
@@ -823,41 +906,10 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
return enableStreamBody;
}
- @Override
- public int getInt(String path) {
- return getInt(path, 0);
- }
-
- @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);
- }
-
- @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(Map<String, Object> result) {
- if (getZnodeVersion() > -1) result.put(ZNODEVER, getZnodeVersion());
+ if (znodeVersion > -1) result.put(ZNODEVER, znodeVersion);
if(luceneMatchVersion != null) result.put(IndexSchema.LUCENE_MATCH_VERSION_PARAM, luceneMatchVersion.toString());
result.put("updateHandler", getUpdateHandlerInfo());
Map<String, Object> m = new LinkedHashMap<>();
@@ -919,11 +971,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 substituteProperties;
+ Properties result = new Properties(substituteProperties);
result.putAll(p);
return result;
}
@@ -932,7 +983,7 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
public ConfigOverlay getOverlay() {
if (overlay == null) {
- overlay = getConfigOverlay(getResourceLoader());
+ overlay = getConfigOverlay(resourceLoader);
}
return overlay;
}
@@ -961,11 +1012,43 @@ public class SolrConfig extends XmlConfigFile implements MapSerializable {
}
public RequestParams refreshRequestParams() {
- requestParams = RequestParams.getFreshRequestParams(getResourceLoader(), requestParams);
+ requestParams = RequestParams.getFreshRequestParams(resourceLoader, requestParams);
if (log.isDebugEnabled()) {
log.debug("current version of requestparams : {}", requestParams.getZnodeVersion());
}
return requestParams;
}
+ public SolrResourceLoader getResourceLoader() {
+ return resourceLoader;
+ }
+
+ public int getZnodeVersion() {
+ return znodeVersion;
+ }
+
+ public String getName() {
+ return resourceName;
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ /**fetches a child node by name. An "empty node" is returned if the child does not exist
+ * This never returns a null
+ *
+ *
+ */
+ public ConfigNode get(String name) {
+ if (!overlay.hasKey(name)) {
+ //there is no overlay
+ return root.get(name);
+ }
+ return new OverlaidConfigNode(overlay, name, null,root.get(name));
+ }
+
+ public ConfigNode get(String name, Predicate<ConfigNode> test) {
+ return root.get(name, test);
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/core/XmlConfigFile.java b/solr/core/src/java/org/apache/solr/core/XmlConfigFile.java
index 5971eb5..6a545d4 100644
--- a/solr/core/src/java/org/apache/solr/core/XmlConfigFile.java
+++ b/solr/core/src/java/org/apache/solr/core/XmlConfigFile.java
@@ -36,6 +36,7 @@ import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
+import java.util.function.Function;
import org.apache.commons.io.IOUtils;
import org.apache.solr.cloud.ZkSolrResourceLoader;
@@ -85,6 +86,15 @@ public class XmlConfigFile { // formerly simply "Config"
{
this(loader, name, is, prefix, null);
}
+ public XmlConfigFile(SolrResourceLoader loader, String name, InputSource is, String prefix, Properties substituteProps) throws ParserConfigurationException, IOException, SAXException{
+ this(loader, s -> {
+ try {
+ return loader.openResource(s);
+ } catch (IOException e) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
+ }
+ },name, is, prefix, substituteProps);
+ }
/**
* Builds a config:
@@ -103,8 +113,7 @@ public class XmlConfigFile { // formerly simply "Config"
* @param prefix an optional prefix that will be prepended to all non-absolute xpath expressions
* @param substituteProps optional property substitution
*/
- public XmlConfigFile(SolrResourceLoader loader, String name, InputSource is, String prefix, Properties substituteProps) throws ParserConfigurationException, IOException, SAXException
- {
+ public XmlConfigFile(SolrResourceLoader loader, Function<String, InputStream> fileSupplier, String name, InputSource is, String prefix, Properties substituteProps) throws IOException {
if (null == loader) throw new NullPointerException("loader");
this.loader = loader;
@@ -115,7 +124,7 @@ public class XmlConfigFile { // formerly simply "Config"
javax.xml.parsers.DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
if (is == null) {
- InputStream in = loader.openResource(name);
+ InputStream in = fileSupplier.apply(name);
if (in instanceof ZkSolrResourceLoader.ZkByteArrayInputStream) {
zkVersion = ((ZkSolrResourceLoader.ZkByteArrayInputStream) in).getStat().getVersion();
log.debug("loaded config {} with version {} ",name,zkVersion);
diff --git a/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java
index bde89d8..87dacef 100644
--- a/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java
@@ -36,6 +36,7 @@ import static org.apache.solr.common.params.CommonParams.NAME;
public class DumpRequestHandler extends RequestHandlerBase
{
+
@Override
@SuppressWarnings({"unchecked"})
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException
diff --git a/solr/core/src/java/org/apache/solr/schema/FieldTypePluginLoader.java b/solr/core/src/java/org/apache/solr/schema/FieldTypePluginLoader.java
index 8693e76..710d428 100644
--- a/solr/core/src/java/org/apache/solr/schema/FieldTypePluginLoader.java
+++ b/solr/core/src/java/org/apache/solr/schema/FieldTypePluginLoader.java
@@ -189,9 +189,9 @@ public final class FieldTypePluginLoader
// check for all of these up front, so we can error if used in
// conjunction with an explicit analyzer class.
- List<ConfigNode> charFilterNodes = node.children("charFilter");
- List<ConfigNode> tokenizerNodes = node.children("tokenizer");
- List<ConfigNode> tokenFilterNodes = node.children("filter");
+ List<ConfigNode> charFilterNodes = node.getAll("charFilter");
+ List<ConfigNode> tokenizerNodes = node.getAll("tokenizer");
+ List<ConfigNode> tokenFilterNodes = node.getAll("filter");
if (analyzerName != null) {
diff --git a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
index e605359..1fb0de7 100644
--- a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
+++ b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
@@ -64,6 +64,7 @@ import org.apache.solr.common.util.Cache;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.Pair;
import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.core.ConfigSetService;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.request.LocalSolrQueryRequest;
@@ -167,11 +168,11 @@ public class IndexSchema {
* By default, this follows the normal config path directory searching rules.
* @see SolrResourceLoader#openResource
*/
- public IndexSchema(String name, IndexSchemaFactory.ConfigResource schemaResource, Version luceneVersion, SolrResourceLoader resourceLoader, Properties substitutableProperties) {
+ public IndexSchema(String name, ConfigSetService.ConfigResource schemaResource, Version luceneVersion, SolrResourceLoader resourceLoader, Properties substitutableProperties) {
this(luceneVersion, resourceLoader, substitutableProperties);
this.resourceName = Objects.requireNonNull(name);
- ConfigNode.SUBSTITUTES.set(substitutableProperties::getProperty);
+ if(substitutableProperties !=null) ConfigNode.SUBSTITUTES.set(substitutableProperties::getProperty);
try {
readSchema(schemaResource);
loader.inform(loader);
@@ -489,7 +490,7 @@ public class IndexSchema {
}
}
- protected void readSchema(IndexSchemaFactory.ConfigResource is) {
+ protected void readSchema(ConfigSetService.ConfigResource is) {
assert null != is : "schema InputSource should never be null";
try {
rootNode = is.get();
@@ -512,9 +513,9 @@ public class IndexSchema {
// load the Field Types
final FieldTypePluginLoader typeLoader = new FieldTypePluginLoader(this, fieldTypes, schemaAware);
- List<ConfigNode> fTypes = rootNode.children(null, FIELDTYPE_KEYS);
+ List<ConfigNode> fTypes = rootNode.getAll(null, FIELDTYPE_KEYS);
ConfigNode types = rootNode.child(TYPES);
- if(types != null) fTypes.addAll(types.children(null, FIELDTYPE_KEYS));
+ if(types != null) fTypes.addAll(types.getAll(null, FIELDTYPE_KEYS));
typeLoader.load(solrClassLoader, fTypes);
// load the fields
@@ -559,7 +560,7 @@ public class IndexSchema {
if (node==null) {
log.warn("no {} specified in schema.", UNIQUE_KEY);
} else {
- uniqueKeyField=getIndexedField(node.textValue().trim());
+ uniqueKeyField=getIndexedField(node.txt().trim());
uniqueKeyFieldName=uniqueKeyField.getName();
uniqueKeyFieldType=uniqueKeyField.getType();
@@ -646,10 +647,11 @@ public class IndexSchema {
ArrayList<DynamicField> dFields = new ArrayList<>();
- List<ConfigNode> nodes = n.children(null, FIELD_KEYS);
+ List<ConfigNode> nodes = n.getAll(null, FIELD_KEYS);
ConfigNode child = n.child(FIELDS);
if(child != null) {
- nodes.addAll(child.children(null, FIELD_KEYS));
+ nodes = new ArrayList<>(nodes);
+ nodes.addAll(child.getAll(null, FIELD_KEYS));
}
for (ConfigNode node : nodes) {
@@ -729,12 +731,11 @@ public class IndexSchema {
* Loads the copy fields
*/
protected synchronized void loadCopyFields(ConfigNode n) {
- List<ConfigNode> nodes = n.children(COPY_FIELD);
+ List<ConfigNode> nodes = n.getAll(COPY_FIELD);
ConfigNode f = n.child(FIELDS);
if (f != null) {
- List<ConfigNode> c = f.children(COPY_FIELD);
- if (nodes.isEmpty()) nodes = c;
- else nodes.addAll(c);
+ nodes = new ArrayList<>(nodes);
+ nodes.addAll(f.getAll(COPY_FIELD));
}
for (ConfigNode node : nodes) {
diff --git a/solr/core/src/java/org/apache/solr/schema/IndexSchemaFactory.java b/solr/core/src/java/org/apache/solr/schema/IndexSchemaFactory.java
index c9d3187..83c605f 100644
--- a/solr/core/src/java/org/apache/solr/schema/IndexSchemaFactory.java
+++ b/solr/core/src/java/org/apache/solr/schema/IndexSchemaFactory.java
@@ -103,11 +103,11 @@ public abstract class IndexSchemaFactory implements NamedListInitializedPlugin {
}
@SuppressWarnings("unchecked")
- public static ConfigResource getConfigResource(ConfigSetService configSetService, InputStream schemaInputStream, SolrResourceLoader loader, String name) throws IOException {
+ public static ConfigSetService.ConfigResource getConfigResource(ConfigSetService configSetService, InputStream schemaInputStream, SolrResourceLoader loader, String name) throws IOException {
if (configSetService instanceof ZkConfigSetService && schemaInputStream instanceof ZkSolrResourceLoader.ZkByteArrayInputStream) {
ZkSolrResourceLoader.ZkByteArrayInputStream is = (ZkSolrResourceLoader.ZkByteArrayInputStream) schemaInputStream;
Map<String, VersionedConfig> configCache = (Map<String, VersionedConfig>) ((ZkConfigSetService) configSetService).getSolrCloudManager().getObjectCache()
- .computeIfAbsent(ConfigResource.class.getName(), s -> new ConcurrentHashMap<>());
+ .computeIfAbsent(ConfigSetService.ConfigResource.class.getName(), s -> new ConcurrentHashMap<>());
VersionedConfig cached = configCache.get(is.fileName);
if (cached != null) {
if (cached.version != is.getStat().getVersion()) {
@@ -134,15 +134,9 @@ public abstract class IndexSchemaFactory implements NamedListInitializedPlugin {
return new DataConfigNode(new DOMConfigNode(schemaConf.getDocument().getDocumentElement()));
}
- public interface ConfigResource {
-
- ConfigNode get() throws Exception;
-
- }
-
public static class VersionedConfig {
- final int version;
- final ConfigNode data;
+ public final int version;
+ public final ConfigNode data;
public VersionedConfig(int version, ConfigNode data) {
this.version = version;
diff --git a/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java b/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java
index c7a7b50..7ebc540 100644
--- a/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java
+++ b/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java
@@ -67,6 +67,7 @@ import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SolrNamedThreadFactory;
+import org.apache.solr.core.ConfigSetService;
import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.rest.schema.FieldTypeXmlAdapter;
@@ -102,7 +103,7 @@ public final class ManagedIndexSchema extends IndexSchema {
* By default, this follows the normal config path directory searching rules.
* @see org.apache.solr.core.SolrResourceLoader#openResource
*/
- ManagedIndexSchema(SolrConfig solrConfig, String name, IndexSchemaFactory.ConfigResource is, boolean isMutable,
+ ManagedIndexSchema(SolrConfig solrConfig, String name, ConfigSetService.ConfigResource is, boolean isMutable,
String managedSchemaResourceName, int schemaZkVersion, Object schemaUpdateLock) {
super(name, is, solrConfig.luceneMatchVersion, solrConfig.getResourceLoader(), solrConfig.getSubstituteProperties());
this.isMutable = isMutable;
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 1335d2d..9067afa 100644
--- a/solr/core/src/java/org/apache/solr/search/CacheConfig.java
+++ b/solr/core/src/java/org/apache/solr/search/CacheConfig.java
@@ -16,7 +16,6 @@
*/
package org.apache.solr.search;
-import javax.xml.xpath.XPathConstants;
import java.lang.invoke.MethodHandles;
import java.util.Collections;
import java.util.HashMap;
@@ -25,19 +24,15 @@ import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
+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.core.PluginInfo;
-
import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrResourceLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
import static org.apache.solr.common.params.CommonParams.NAME;
@@ -70,7 +65,8 @@ public class CacheConfig implements MapSerializable{
private String regenImpl;
- public CacheConfig() {}
+ public CacheConfig() {
+ }
@SuppressWarnings({"rawtypes"})
public CacheConfig(Class<? extends SolrCache> clazz, Map<String,String> args, CacheRegenerator regenerator) {
@@ -88,15 +84,12 @@ 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 (ConfigNode node : nodes) {
+ if (node.boolAttr("enabled", true)) {
+ CacheConfig config = getConfig(solrConfig, node.name(), node.attributes().asMap(), configPath);
result.put(config.args.get(NAME), config);
}
}
@@ -104,19 +97,18 @@ public class CacheConfig implements MapSerializable{
}
- public static CacheConfig getConfig(SolrConfig solrConfig, String xpath) {
- Node node = solrConfig.getNode(xpath, false);
- if(node == null || !"true".equals(DOMUtil.getAttrOrDefault(node, "enabled", "true"))) {
- Map<?, ?> m = solrConfig.getOverlay().getEditableSubProperties(xpath);
- if(m==null) return null;
+ public static CacheConfig getConfig(SolrConfig solrConfig, ConfigNode node, String xpath) {
+ if (!node.exists() || !"true".equals(node.attributes().get("enabled", "true"))) {
+ Map<String, Object> 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.emptyMap(), xpath);
+ return getConfig(solrConfig, parts.get(parts.size() - 1), Collections.emptyMap(), xpath);
}
- return getConfig(solrConfig, node.getNodeName(),DOMUtil.toMap(node.getAttributes()), xpath);
+ return getConfig(solrConfig, node.name(), node.attributes().asMap(), xpath);
}
- public static CacheConfig getConfig(SolrConfig solrConfig, String nodeName, Map<String,String> attrs, String xpath) {
+ public static CacheConfig getConfig(SolrConfig solrConfig, String nodeName, Map<String, String> attrs, String xpath) {
CacheConfig config = new CacheConfig();
config.nodeName = nodeName;
Map<String,String> attrsCopy = new LinkedHashMap<>(attrs.size());
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 111737d..edd9b68 100644
--- a/solr/core/src/java/org/apache/solr/update/SolrIndexConfig.java
+++ b/solr/core/src/java/org/apache/solr/update/SolrIndexConfig.java
@@ -19,7 +19,6 @@ package org.apache.solr.update;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import org.apache.lucene.analysis.Analyzer;
@@ -31,6 +30,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.core.DirectoryFactory;
import org.apache.solr.common.MapSerializable;
@@ -90,11 +90,12 @@ public class SolrIndexConfig implements MapSerializable {
public final PluginInfo mergedSegmentWarmerInfo;
public InfoStream infoStream = InfoStream.NO_OUTPUT;
+ private ConfigNode node;
/**
* Internal constructor for setting defaults based on Lucene Version
*/
- private SolrIndexConfig(SolrConfig solrConfig) {
+ private SolrIndexConfig() {
useCompoundFile = false;
maxBufferedDocs = -1;
ramBufferSizeMB = 100;
@@ -108,88 +109,78 @@ public class SolrIndexConfig implements MapSerializable {
// enable coarse-grained metrics by default
metricsInfo = new PluginInfo("metrics", Collections.emptyMap(), null, null);
}
-
+ private ConfigNode get(String s) { return node.get(s); }
+ public SolrIndexConfig(SolrConfig cfg, SolrIndexConfig def) {
+ this(cfg.get("indexConfig"), def);
+ }
/**
* Constructs a SolrIndexConfig which parses the Lucene related config params in solrconfig.xml
- * @param solrConfig the overall SolrConfig object
- * @param prefix the XPath prefix for which section to parse (mandatory)
* @param def a SolrIndexConfig instance to pick default values from (optional)
*/
- public SolrIndexConfig(SolrConfig solrConfig, String prefix, SolrIndexConfig def) {
- if (prefix == null) {
- prefix = "indexConfig";
- log.debug("Defaulting to prefix '{}' for index configuration", prefix);
- }
-
+ public SolrIndexConfig(ConfigNode cfg, SolrIndexConfig def) {
+ this.node = cfg;
if (def == null) {
- def = new SolrIndexConfig(solrConfig);
+ def = new SolrIndexConfig();
}
+
// 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)),
+ get("mergeScheduler").isNull() || get("mergeScheduler").attr("class") != 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)),
+ get("mergePolicy").isNull() || get("mergePolicy").attr("class") != null,
true);
assertWarnOrFail("The <luceneAutoCommit>true|false</luceneAutoCommit> parameter is no longer valid in solrconfig.xml.",
- solrConfig.get(prefix + "/luceneAutoCommit", null) == null,
+ get("luceneAutoCommit").isNull(),
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 = get("useCompoundFile").boolVal(def.useCompoundFile);
+ maxBufferedDocs = get("maxBufferedDocs").intVal(def.maxBufferedDocs);
+ ramBufferSizeMB = get("ramBufferSizeMB").doubleVal(def.ramBufferSizeMB);
+ maxCommitMergeWaitMillis = get("maxCommitMergeWaitTime").intVal(def.maxCommitMergeWaitMillis);
// how do we validate the value??
- ramPerThreadHardLimitMB = solrConfig.getInt(prefix+"/ramPerThreadHardLimitMB", def.ramPerThreadHardLimitMB);
+ ramPerThreadHardLimitMB = get("ramPerThreadHardLimitMB").intVal(def.ramPerThreadHardLimitMB);
- writeLockTimeout=solrConfig.getInt(prefix+"/writeLockTimeout", def.writeLockTimeout);
- lockType=solrConfig.get(prefix+"/lockType", def.lockType);
+ writeLockTimeout= get("writeLockTimeout").intVal(def.writeLockTimeout);
+ lockType = get("lockType").txt(def.lockType);
- List<PluginInfo> infos = solrConfig.readPluginInfos(prefix + "/metrics", false, false);
- if (infos.isEmpty()) {
- metricsInfo = def.metricsInfo;
- } else {
- metricsInfo = infos.get(0);
- }
- mergeSchedulerInfo = getPluginInfo(prefix + "/mergeScheduler", solrConfig, def.mergeSchedulerInfo);
- mergePolicyFactoryInfo = getPluginInfo(prefix + "/mergePolicyFactory", solrConfig, def.mergePolicyFactoryInfo);
+ metricsInfo = getPluginInfo(get("metrics"), def.metricsInfo);
+ mergeSchedulerInfo = getPluginInfo(get("mergeScheduler"), def.mergeSchedulerInfo);
+ mergePolicyFactoryInfo = getPluginInfo(get("mergePolicyFactory"), def.mergePolicyFactoryInfo);
assertWarnOrFail("Beginning with Solr 7.0, <mergePolicy> is no longer supported, use <mergePolicyFactory> instead.",
- getPluginInfo(prefix + "/mergePolicy", solrConfig, null) == null,
+ get("mergePolicy").isNull(),
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,
+ get("maxMergeDocs").isNull(),
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,
+ get("maxMergeFactor").isNull(),
true);
- String val = solrConfig.get(prefix + "/termIndexInterval", null);
- if (val != null) {
+ if (get("termIndexInterval").exists()) {
throw new IllegalArgumentException("Illegal parameter 'termIndexInterval'");
}
- boolean infoStreamEnabled = solrConfig.getBool(prefix + "/infoStream", false);
- if(infoStreamEnabled) {
- String infoStreamFile = solrConfig.get(prefix + "/infoStream/@file", null);
- if (infoStreamFile == null) {
+ if(get("infoStream").boolVal(false)) {
+ if (get("infoStream").attr("file") == null) {
log.info("IndexWriter infoStream solr logging is enabled");
infoStream = new LoggingInfoStream();
} else {
throw new IllegalArgumentException("Remove @file from <infoStream> to output messages to solr's logfile");
}
}
- mergedSegmentWarmerInfo = getPluginInfo(prefix + "/mergedSegmentWarmer", solrConfig, def.mergedSegmentWarmerInfo);
+ mergedSegmentWarmerInfo = getPluginInfo(get("mergedSegmentWarmer"), def.mergedSegmentWarmerInfo);
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)),
+ get( "checkIntegrityAtMerge").isNull(),
true);
}
@@ -218,9 +209,10 @@ public class SolrIndexConfig implements MapSerializable {
return map;
}
- private PluginInfo getPluginInfo(String path, SolrConfig solrConfig, PluginInfo def) {
- List<PluginInfo> l = solrConfig.readPluginInfos(path, false, true);
- return l.isEmpty() ? def : l.get(0);
+ private PluginInfo getPluginInfo(ConfigNode node , PluginInfo def) {
+ return node != null && node.exists() ?
+ new PluginInfo(node, "[solrconfig.xml] " + node.name(), false, false) :
+ def;
}
private static class DelayedSchemaAnalyzer extends DelegatingAnalyzerWrapper {
diff --git a/solr/core/src/java/org/apache/solr/update/VersionInfo.java b/solr/core/src/java/org/apache/solr/update/VersionInfo.java
index db1006a..cd732d7 100644
--- a/solr/core/src/java/org/apache/solr/update/VersionInfo.java
+++ b/solr/core/src/java/org/apache/solr/update/VersionInfo.java
@@ -94,8 +94,8 @@ public class VersionInfo {
this.ulog = ulog;
IndexSchema schema = ulog.uhandler.core.getLatestSchema();
versionField = getAndCheckVersionField(schema);
- versionBucketLockTimeoutMs = ulog.uhandler.core.getSolrConfig().getInt("updateHandler/versionBucketLockTimeoutMs",
- Integer.parseInt(System.getProperty(SYS_PROP_BUCKET_VERSION_LOCK_TIMEOUT_MS, "0")));
+ versionBucketLockTimeoutMs = ulog.uhandler.core.getSolrConfig().get("updateHandler").get("versionBucketLockTimeoutMs")
+ .intVal(Integer.parseInt(System.getProperty(SYS_PROP_BUCKET_VERSION_LOCK_TIMEOUT_MS, "0")));
buckets = new VersionBucket[ BitUtil.nextHighestPowerOfTwo(nBuckets) ];
for (int i=0; i<buckets.length; i++) {
if (versionBucketLockTimeoutMs > 0) {
diff --git a/solr/core/src/java/org/apache/solr/util/DOMConfigNode.java b/solr/core/src/java/org/apache/solr/util/DOMConfigNode.java
index 6afc5a5..35dd027 100644
--- a/solr/core/src/java/org/apache/solr/util/DOMConfigNode.java
+++ b/solr/core/src/java/org/apache/solr/util/DOMConfigNode.java
@@ -18,7 +18,9 @@
package org.apache.solr.util;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.function.Function;
import org.apache.solr.cluster.api.SimpleMap;
@@ -43,7 +45,7 @@ public class DOMConfigNode implements ConfigNode {
}
@Override
- public String textValue() {
+ public String txt() {
return DOMUtil.getText(node);
}
@@ -54,7 +56,10 @@ public class DOMConfigNode implements ConfigNode {
@Override
public SimpleMap<String> attributes() {
if (attrs != null) return attrs;
- return attrs = new WrappedSimpleMap<>(DOMUtil.toMap(node.getAttributes()));
+ Map<String, String> attrs = DOMUtil.toMap(node.getAttributes());
+ return this.attrs = attrs.size() == 0 ?
+ EMPTY :
+ new WrappedSimpleMap<>(attrs);
}
@Override
@@ -64,7 +69,7 @@ public class DOMConfigNode implements ConfigNode {
}
@Override
- public List<ConfigNode> children(String name) {
+ public List<ConfigNode> getAll(String name) {
List<ConfigNode> result = new ArrayList<>();
forEachChild(it -> {
if (name.equals(it.name())) {
@@ -85,5 +90,6 @@ public class DOMConfigNode implements ConfigNode {
if (Boolean.FALSE == toContinue) break;
}
}
+ private static final SimpleMap<String> EMPTY = new WrappedSimpleMap<>(Collections.emptyMap());
}
diff --git a/solr/core/src/java/org/apache/solr/util/DataConfigNode.java b/solr/core/src/java/org/apache/solr/util/DataConfigNode.java
index 38940ee..78676a2 100644
--- a/solr/core/src/java/org/apache/solr/util/DataConfigNode.java
+++ b/solr/core/src/java/org/apache/solr/util/DataConfigNode.java
@@ -19,7 +19,7 @@ package org.apache.solr.util;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -27,56 +27,64 @@ import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import org.apache.solr.cluster.api.SimpleMap;
import org.apache.solr.common.ConfigNode;
import org.apache.solr.common.util.PropertiesUtil;
+import org.apache.solr.common.util.WrappedSimpleMap;
/**
* ConfigNode impl that copies and maintains data internally from DOM
*/
public class DataConfigNode implements ConfigNode {
- final String name;
- final SimpleMap<String> attributes;
- private final Map<String, List<ConfigNode>> kids = new HashMap<>();
- private final String textData;
+ public final String name;
+ public final SimpleMap<String> attributes;
+ public final SimpleMap<List<ConfigNode>> kids ;
+ public final String textData;
+
public DataConfigNode(ConfigNode root) {
+ Map<String, List<ConfigNode>> kids = new LinkedHashMap<>();
name = root.name();
attributes = wrap(root.attributes());
- textData = root.textValue();
+ textData = root.txt();
root.forEachChild(it -> {
List<ConfigNode> nodes = kids.computeIfAbsent(it.name(),
k -> new ArrayList<>());
-
- nodes.add(new DataConfigNode(it));
+ nodes.add(new DataConfigNode(it));
return Boolean.TRUE;
});
-
+ for (Map.Entry<String, List<ConfigNode>> e : kids.entrySet()) {
+ if(e.getValue() != null) {
+ e.setValue(ImmutableList.copyOf(e.getValue()));
+ }
+ }
+ this.kids = kids.isEmpty()? EMPTY: new WrappedSimpleMap<>(ImmutableMap.copyOf(kids));
}
public String subtituteVal(String s) {
- Function<String, String> props = SUBSTITUTES.get();
- if (props == null) return s;
- return PropertiesUtil.substitute(s, props);
+ return PropertiesUtil.substitute(s, SUBSTITUTES.get());
}
private SimpleMap<String> wrap(SimpleMap<String> delegate) {
+ if(delegate.size() == 0) return delegate;//avoid unnecessary object creation
return new SimpleMap<>() {
- @Override
- public String get(String key) {
- return subtituteVal(delegate.get(key));
- }
+ @Override
+ public String get(String key) {
+ return subtituteVal(delegate.get(key));
+ }
- @Override
- public void forEachEntry(BiConsumer<String, ? super String> fun) {
- delegate.forEachEntry((k, v) -> fun.accept(k, subtituteVal(v)));
- }
+ @Override
+ public void forEachEntry(BiConsumer<String, ? super String> fun) {
+ delegate.forEachEntry((k, v) -> fun.accept(k, subtituteVal(v)));
+ }
- @Override
- public int size() {
- return delegate.size();
- }
- };
+ @Override
+ public int size() {
+ return delegate.size();
+ }
+ };
}
@Override
@@ -85,8 +93,8 @@ public class DataConfigNode implements ConfigNode {
}
@Override
- public String textValue() {
- return subtituteVal(textData);
+ public String txt() {
+ return subtituteVal(textData);
}
@Override
@@ -101,12 +109,12 @@ public class DataConfigNode implements ConfigNode {
}
@Override
- public List<ConfigNode> children(String name) {
- return kids.getOrDefault(name, Collections.emptyList());
+ public List<ConfigNode> getAll(String name) {
+ return kids.get(name, Collections.emptyList());
}
@Override
- public List<ConfigNode> children(Predicate<ConfigNode> test, Set<String> matchNames) {
+ public List<ConfigNode> getAll(Predicate<ConfigNode> test, Set<String> matchNames) {
List<ConfigNode> result = new ArrayList<>();
for (String s : matchNames) {
List<ConfigNode> vals = kids.get(s);
@@ -123,10 +131,11 @@ public class DataConfigNode implements ConfigNode {
@Override
public void forEachChild(Function<ConfigNode, Boolean> fun) {
- kids.forEach((s, configNodes) -> {
+ kids.forEachEntry((s, configNodes) -> {
if (configNodes != null) {
configNodes.forEach(fun::apply);
}
});
}
+ public static final SimpleMap<List<ConfigNode>> EMPTY = new WrappedSimpleMap<>(Collections.emptyMap());
}
diff --git a/solr/core/src/resources/EditableSolrConfigAttributes.json b/solr/core/src/resources/EditableSolrConfigAttributes.json
index 9b27caa..5d67b88 100644
--- a/solr/core/src/resources/EditableSolrConfigAttributes.json
+++ b/solr/core/src/resources/EditableSolrConfigAttributes.json
@@ -55,10 +55,6 @@
"enableLazyFieldLoading":1,
"boolTofilterOptimizer":1,
"maxBooleanClauses":1},
- "jmx":{
- "agentId":0,
- "serviceUrl":0,
- "rootName":0},
"requestDispatcher":{
"handleSelect":0,
"requestParsers":{
diff --git a/solr/core/src/test/org/apache/solr/core/TestBadConfig.java b/solr/core/src/test/org/apache/solr/core/TestBadConfig.java
index 91fd9ae..31ba9fb 100644
--- a/solr/core/src/test/org/apache/solr/core/TestBadConfig.java
+++ b/solr/core/src/test/org/apache/solr/core/TestBadConfig.java
@@ -26,18 +26,6 @@ public class TestBadConfig extends AbstractBadConfigTestBase {
assertConfigs("bad-solrconfig-nrtmode.xml","schema.xml", "nrtMode");
}
- public void testMultipleDirectoryFactories() throws Exception {
- assertConfigs("bad-solrconfig-multiple-dirfactory.xml", "schema12.xml",
- "directoryFactory");
- }
- public void testMultipleIndexConfigs() throws Exception {
- assertConfigs("bad-solrconfig-multiple-indexconfigs.xml", "schema12.xml",
- "indexConfig");
- }
- public void testMultipleCFS() throws Exception {
- assertConfigs("bad-solrconfig-multiple-cfs.xml", "schema12.xml",
- "useCompoundFile");
- }
public void testUpdateLogButNoVersionField() throws Exception {
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 b3bd625..6bfe0ee 100644
--- a/solr/core/src/test/org/apache/solr/core/TestCodecSupport.java
+++ b/solr/core/src/test/org/apache/solr/core/TestCodecSupport.java
@@ -196,9 +196,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.get("codecFactory").attr("class"));
+ assertTrue("Unexpected configuration of codec factory for this test. Expecting empty element",
+ config.get("codecFactory").getAll(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/TestConfLoadPerf.java b/solr/core/src/test/org/apache/solr/core/TestConfLoadPerf.java
new file mode 100644
index 0000000..4f98afa
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/core/TestConfLoadPerf.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.core;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.cloud.ZkSolrResourceLoader;
+import org.apache.solr.common.util.SuppressForbidden;
+import org.apache.solr.util.ExternalPaths;
+import org.apache.zookeeper.data.Stat;
+import org.junit.Ignore;
+
+import static org.apache.solr.core.TestConfigSets.solrxml;
+
+public class TestConfLoadPerf extends SolrTestCaseJ4 {
+
+ @Ignore
+ @SuppressForbidden(reason = "Needed to provide time for tests.")
+ public void testPerf() throws Exception{
+ String sourceHome = ExternalPaths.SOURCE_HOME;
+ File configSetDir = new File(sourceHome, "server/solr/configsets/sample_techproducts_configs/conf");
+
+ String configSetsBaseDir = TEST_PATH().resolve("configsets").toString();
+ byte[] b = Files.readAllBytes(new File(configSetDir, "solrconfig.xml").toPath());
+ Path testDirectory = createTempDir();
+
+ System.setProperty("configsets", configSetsBaseDir);
+
+ CoreContainer container = new CoreContainer(SolrXmlConfig.fromString(testDirectory, solrxml));
+ container.load();
+ container.shutdown();
+
+ SolrResourceLoader srl = new SolrResourceLoader("temp", Collections.emptyList(), container.solrHome, container.getResourceLoader().classLoader){
+
+ @Override
+ public CoreContainer getCoreContainer() {
+ return container;
+ }
+
+ @Override
+ public InputStream openResource(String resource) throws IOException {
+ if(resource.equals("solrconfig.xml")) {
+ Stat stat = new Stat();
+ stat.setVersion(1);
+ return new ZkSolrResourceLoader.ZkByteArrayInputStream(b, new File(configSetDir, "solrconfig.xml").getAbsolutePath(), stat);
+ } else {
+ throw new FileNotFoundException(resource);
+ }
+
+ }
+ };
+ System.gc();
+ long heapSize = Runtime.getRuntime().totalMemory();
+ List<SolrConfig> allConfigs = new ArrayList<>();
+ long startTime = System.currentTimeMillis();
+ for(int i=0;i<100;i++) {
+ allConfigs.add(SolrConfig.readFromResourceLoader(srl, "solrconfig.xml", true, null));
+
+ }
+ System.gc();
+ System.out.println("TIME_TAKEN : "+(System.currentTimeMillis()-startTime));
+ System.out.println("HEAP_SIZE : "+((Runtime.getRuntime().totalMemory()-heapSize)/(1024)));
+ }
+}
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..567262a 100644
--- a/solr/core/src/test/org/apache/solr/core/TestConfig.java
+++ b/solr/core/src/test/org/apache/solr/core/TestConfig.java
@@ -16,17 +16,18 @@
*/
package org.apache.solr.core;
-import javax.xml.xpath.XPathConstants;
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;
@@ -35,8 +36,6 @@ import org.apache.solr.update.SolrIndexConfig;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
public class TestConfig extends SolrTestCaseJ4 {
@@ -80,24 +79,24 @@ public class TestConfig extends SolrTestCaseJ4 {
public void testJavaProperty() {
// property values defined in build.xml
- String s = solrConfig.get("propTest");
+ String s = solrConfig.get("propTest").txt();
assertEquals("prefix-proptwo-suffix", s);
- s = solrConfig.get("propTest/@attr1", "default");
+ s = solrConfig.get("propTest").attr("attr1", "default");
assertEquals("propone-${literal}", s);
- s = solrConfig.get("propTest/@attr2", "default");
+ s = solrConfig.get("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());
+ assertEquals("prefix-proptwo-suffix", solrConfig.get("propTest",
+ it -> "default-from-config".equals(it.attr("attr2"))).txt());
+
+ List<ConfigNode> nl = solrConfig.root.getAll("propTest");
+ assertEquals(1, nl.size());
+ assertEquals("prefix-proptwo-suffix", nl.get(0).txt());
- Node node = solrConfig.getNode("propTest", true);
- assertEquals("prefix-proptwo-suffix", node.getTextContent());
+ assertEquals("prefix-proptwo-suffix", solrConfig.get("propTest").txt());
}
// sometime if the config referes to old things, it must be replaced with new stuff
diff --git a/solr/core/src/test/org/apache/solr/core/TestConfigOverlay.java b/solr/core/src/test/org/apache/solr/core/TestConfigOverlay.java
index 7cbc098..857082c 100644
--- a/solr/core/src/test/org/apache/solr/core/TestConfigOverlay.java
+++ b/solr/core/src/test/org/apache/solr/core/TestConfigOverlay.java
@@ -44,9 +44,6 @@ public class TestConfigOverlay extends SolrTestCase {
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));
diff --git a/solr/core/src/test/org/apache/solr/core/TestSimpleTextCodec.java b/solr/core/src/test/org/apache/solr/core/TestSimpleTextCodec.java
index 9f6776b..abc7723 100644
--- a/solr/core/src/test/org/apache/solr/core/TestSimpleTextCodec.java
+++ b/solr/core/src/test/org/apache/solr/core/TestSimpleTextCodec.java
@@ -33,7 +33,7 @@ public class TestSimpleTextCodec extends SolrTestCaseJ4 {
public void test() throws Exception {
SolrConfig config = h.getCore().getSolrConfig();
- String codecFactory = config.get("codecFactory/@class");
+ String codecFactory = config.get("codecFactory").attr("class");
assertEquals("Unexpected solrconfig codec factory", "solr.SimpleTextCodecFactory", codecFactory);
assertEquals("Unexpected core codec", "SimpleText", h.getCore().getCodec().getName());
diff --git a/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentTest.java b/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentTest.java
index b15e167..a363190 100644
--- a/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentTest.java
@@ -204,14 +204,25 @@ public class SuggestComponentTest extends SolrTestCaseJ4 {
final String suggester = "suggest_doc_default_startup_no_store";
// validate that this suggester is not storing the lookup
- assertEquals(suggester,
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[8]/str[@name='name']", false));
- assertNull(h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[8]/str[@name='storeDir']", false));
+ assertEquals(suggester,
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",7).
+ get("str", n -> "name".equals(n.attr("name"))).txt());
+
+ assertNull( h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",7).
+ get("str", n -> "storeDir".equals(n.attr("name"))).txt());
// validate that this suggester only builds manually and has not buildOnStartup parameter
- assertEquals("false",
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[8]/str[@name='buildOnCommit']", true));
- assertNull(h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[8]/str[@name='buildOnStartup']", false));
+
+ assertEquals("false",
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",7).
+ get("str", n -> "buildOnCommit".equals(n.attr("name"))).txt());
+
+ assertNull(h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",7).
+ get("str", n -> "buildOnStartup".equals(n.attr("name"))).txt());
reloadCore(random().nextBoolean());
@@ -254,15 +265,27 @@ public class SuggestComponentTest extends SolrTestCaseJ4 {
final String suggester = "suggest_doc_default_startup";
// validate that this suggester is storing the lookup
- assertEquals(suggester,
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[7]/str[@name='name']", false));
- assertEquals(suggester,
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[7]/str[@name='storeDir']", false));
+
+ assertEquals(suggester,
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",6)
+ .get("str", n -> "name".equals(n.attr("name"))).txt());
+
+ assertEquals(suggester,
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",6)
+ .get("str", n -> "storeDir".equals(n.attr("name"))).txt());
// validate that this suggester only builds manually and has not buildOnStartup parameter
- assertEquals("false",
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[7]/str[@name='buildOnCommit']", true));
- assertNull(h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[7]/str[@name='buildOnStartup']", false));
+
+ assertEquals("false",
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",6)
+ .get("str", n -> "buildOnCommit".equals(n.attr("name"))).txt());
+
+ assertNull( h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",6)
+ .get("str", n -> "buildOnStartup".equals(n.attr("name"))).txt());
assertQ(req("qt", rh,
SuggesterParams.SUGGEST_DICT, suggester,
@@ -339,16 +362,27 @@ public class SuggestComponentTest extends SolrTestCaseJ4 {
final String suggester = "suggest_fuzzy_doc_manal_build";
// validate that this suggester is storing the lookup
- assertEquals(suggester,
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[6]/str[@name='name']", false));
- assertEquals(suggester,
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[6]/str[@name='storeDir']", false));
+
+ assertEquals(suggester,
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",5).
+ get("str", n -> "name".equals(n.attr("name"))).txt());
+
+ assertEquals(suggester,
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",5).
+ get("str", n -> "storeDir".equals(n.attr("name"))).txt());
// validate that this suggester only builds manually
- assertEquals("false",
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[6]/str[@name='buildOnCommit']", true));
- assertEquals("false",
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[6]/str[@name='buildOnStartup']", true));
+
+ assertEquals("false",
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",5).
+ get("str", n -> "buildOnCommit".equals(n.attr("name"))).txt());
+ assertEquals("false",
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",5).
+ get("str", n -> "buildOnStartup".equals(n.attr("name"))).txt());
// build the suggester manually
assertQ(req("qt", rh,
@@ -402,15 +436,25 @@ public class SuggestComponentTest extends SolrTestCaseJ4 {
h.getCore().getSolrConfig().useColdSearcher);
// validate that this suggester is not storing the lookup and buildOnStartup is not set
- assertEquals(suggesterFuzzy,
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[3]/str[@name='name']", false));
- assertNull(h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[3]/str[@name='storeDir']", false));
+ assertEquals(suggesterFuzzy,
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",2).
+ get("str", n -> "name".equals(n.attr("name"))).txt());
+
+ assertNull(h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",2)
+ .get("str", n -> "storeDir".equals(n.attr("name"))).txt());
+
// assert that buildOnStartup=false
- assertEquals("false",
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[3]/str[@name='buildOnStartup']", false));
- assertEquals("true",
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[3]/str[@name='buildOnCommit']", false));
+ assertEquals("false",
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",2)
+ .get("str", n -> "buildOnStartup".equals(n.attr("name"))).txt());
+ assertEquals("true",
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",2).
+ get("str", n -> "buildOnCommit".equals(n.attr("name"))).txt());
// verify that this suggester is built (there was a commit in setUp)
assertQ(req("qt", rh,
@@ -453,13 +497,21 @@ public class SuggestComponentTest extends SolrTestCaseJ4 {
final String suggestStartup = "suggest_fuzzy_doc_dict_build_startup";
// repeat the test with "suggest_fuzzy_doc_dict_build_startup", it is exactly the same but with buildOnStartup=true
- assertEquals(suggestStartup,
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[5]/str[@name='name']", false));
- assertNull(h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[5]/str[@name='storeDir']", false));
- assertEquals("true",
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[5]/str[@name='buildOnStartup']", false));
- assertEquals("false",
- h.getCore().getSolrConfig().getVal("//searchComponent[@name='suggest']/lst[5]/str[@name='buildOnCommit']", false));
+ assertEquals(suggestStartup,
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",4)
+ .get("str", n -> "name".equals(n.attr("name"))).txt());
+ assertNull( h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",4)
+ .get("str", n -> "storeDir".equals(n.attr("name"))).txt());
+ assertEquals("true",
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",4)
+ .get("str", n -> "buildOnStartup".equals(n.attr("name"))).txt());
+ assertEquals("false",
+ h.getCore().getSolrConfig().get("searchComponent", n -> "suggest".equals(n.attr("name")))
+ .get("lst",4)
+ .get("str", n -> "buildOnCommit".equals(n.attr("name"))).txt());
// reload the core
reloadCore(createNewCores);
diff --git a/solr/core/src/test/org/apache/solr/update/SolrIndexConfigTest.java b/solr/core/src/test/org/apache/solr/update/SolrIndexConfigTest.java
index 63a25ac..93bfdf1 100644
--- a/solr/core/src/test/org/apache/solr/update/SolrIndexConfigTest.java
+++ b/solr/core/src/test/org/apache/solr/update/SolrIndexConfigTest.java
@@ -70,7 +70,7 @@ public class SolrIndexConfigTest extends SolrTestCaseJ4 {
@Test
public void testFailingSolrIndexConfigCreation() throws Exception {
SolrConfig solrConfig = new SolrConfig(instanceDir,"bad-mpf-solrconfig.xml");
- SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null, null);
+ SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null);
IndexSchema indexSchema = IndexSchemaFactory.buildIndexSchema(schemaFileName, solrConfig);
h.getCore().setLatestSchema(indexSchema);
@@ -83,7 +83,7 @@ public class SolrIndexConfigTest extends SolrTestCaseJ4 {
public void testTieredMPSolrIndexConfigCreation() throws Exception {
String solrConfigFileName = solrConfigFileNameTieredMergePolicyFactory;
SolrConfig solrConfig = new SolrConfig(instanceDir, solrConfigFileName);
- SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null, null);
+ SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null);
IndexSchema indexSchema = IndexSchemaFactory.buildIndexSchema(schemaFileName, solrConfig);
h.getCore().setLatestSchema(indexSchema);
@@ -109,7 +109,7 @@ public class SolrIndexConfigTest extends SolrTestCaseJ4 {
public void testConcurrentMergeSchedularSolrIndexConfigCreation() throws Exception {
String solrConfigFileName = solrConfigFileNameConnMSPolicyFactory;
SolrConfig solrConfig = new SolrConfig(instanceDir, solrConfigFileName);
- SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null, null);
+ SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null);
IndexSchema indexSchema = IndexSchemaFactory.buildIndexSchema(schemaFileName, solrConfig);
h.getCore().setLatestSchema(indexSchema);
@@ -133,7 +133,7 @@ public class SolrIndexConfigTest extends SolrTestCaseJ4 {
final boolean expectedFieldSortDescending = true;
SolrConfig solrConfig = new SolrConfig(instanceDir, solrConfigFileNameSortingMergePolicyFactory);
- SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null, null);
+ SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null);
assertNotNull(solrIndexConfig);
IndexSchema indexSchema = IndexSchemaFactory.buildIndexSchema(schemaFileName, solrConfig);
@@ -151,7 +151,7 @@ public class SolrIndexConfigTest extends SolrTestCaseJ4 {
public void testMergedSegmentWarmerIndexConfigCreation() throws Exception {
SolrConfig solrConfig = new SolrConfig(instanceDir, solrConfigFileNameWarmerRandomMergePolicyFactory);
- SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null, null);
+ SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null);
assertNotNull(solrIndexConfig);
assertNotNull(solrIndexConfig.mergedSegmentWarmerInfo);
assertEquals(SimpleMergedSegmentWarmer.class.getName(),
@@ -167,7 +167,7 @@ public class SolrIndexConfigTest extends SolrTestCaseJ4 {
final String solrConfigFileNameTMP = solrConfigFileNameTieredMergePolicyFactory;
final String solrConfigFileName = (random().nextBoolean() ? solrConfigFileNameWarmer : solrConfigFileNameTMP);
SolrConfig solrConfig = new SolrConfig(instanceDir, solrConfigFileName);
- SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null, null);
+ SolrIndexConfig solrIndexConfig = new SolrIndexConfig(solrConfig, null);
assertNotNull(solrIndexConfig);
assertNotNull(solrIndexConfig.mergePolicyFactoryInfo);
if (solrConfigFileName.equals(solrConfigFileNameWarmerRandomMergePolicyFactory)) {
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..a98785a 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;
@@ -67,7 +69,7 @@ public interface SimpleMap<T> extends MapWriter {
* @param fun Consume each entry and return a boolean to signal whether to proceed or not. If true, continue, if false stop
*/
default void abortableForEach(BiFunction<String, ? super T, Boolean> fun) {
- forEachEntry(new BiConsumer<String, T>() {
+ forEachEntry(new BiConsumer<>() {
boolean end = false;
@Override
public void accept(String k, T v) {
@@ -82,4 +84,13 @@ public interface SimpleMap<T> extends MapWriter {
default void writeMap(EntryWriter ew) throws IOException {
forEachEntry(ew::putNoEx);
}
+
+ default Map<String, T> asMap( Map<String, T> sink) {
+ forEachEntry(sink::put);
+ return sink;
+ }
+
+ default Map<String, T> asMap() {
+ return asMap(new LinkedHashMap<>());
+ }
}
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..09aa0a7 100644
--- a/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java
+++ b/solr/solrj/src/java/org/apache/solr/common/ConfigNode.java
@@ -17,16 +17,23 @@
package org.apache.solr.common;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
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;
+
+import static org.apache.solr.common.ConfigNode.Helpers.*;
/**
* A generic interface that represents a config file, mostly XML
+ * Please note that this is an immutable, read-only object.
*/
public interface ConfigNode {
ThreadLocal<Function<String,String>> SUBSTITUTES = new ThreadLocal<>();
@@ -37,11 +44,6 @@ public interface ConfigNode {
String name();
/**
- * Text value of the node
- */
- String textValue();
-
- /**
* Attributes
*/
SimpleMap<String> attributes();
@@ -53,6 +55,47 @@ public interface ConfigNode {
return child(null, name);
}
+ /**
+ * Child by name or return an empty node if null
+ * if there are multiple values , it returns the first elem
+ * This never returns a null
+ */
+ default ConfigNode get(String name) {
+ ConfigNode child = child(null, name);
+ return child == null? EMPTY: child;
+ }
+ default ConfigNode get(String name, Predicate<ConfigNode> test) {
+ List<ConfigNode> children = getAll(test, name);
+ if(children.isEmpty()) return EMPTY;
+ return children.get(0);
+ }
+ default ConfigNode get(String name, int idx) {
+ List<ConfigNode> children = getAll(null, name);
+ if(idx < children.size()) return children.get(idx);
+ return EMPTY;
+
+ }
+
+ default ConfigNode child(String name, Supplier<RuntimeException> err) {
+ ConfigNode n = child(name);
+ if(n == null) throw err.get();
+ return n;
+ }
+
+ default boolean boolVal(boolean def) { return _bool(txt(),def); }
+ default int intVal(int def) { return _int(txt(), 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) {
+ String attr = attr(name);
+ if(attr == null && err != null) throw err.get();
+ return attr;
+ }
+ default int intAttr(String name, int def) { return _int(attr(name), def); }
+ default boolean boolAttr(String name, boolean def){ return _bool(attr(name), def); }
+ default String txt(String def) { return txt() == null ? def : txt();}
+ String txt() ;
+ default double doubleVal(double def){ return _double(txt(), def); }
/**Iterate through child nodes with the name and return the first child that matches
*/
default ConfigNode child(Predicate<ConfigNode> test, String name) {
@@ -72,15 +115,15 @@ public interface ConfigNode {
* @param nodeNames names of tags to be returned
* @param test check for the nodes to be returned
*/
- default List<ConfigNode> children(Predicate<ConfigNode> test, String... nodeNames) {
- return children(test, nodeNames == null ? Collections.emptySet() : Set.of(nodeNames));
+ default List<ConfigNode> getAll(Predicate<ConfigNode> test, String... nodeNames) {
+ return getAll(test, nodeNames == null ? Collections.emptySet() : new HashSet<>(Arrays.asList(nodeNames)));
}
/**Iterate through child nodes with the names and return all the matching children
* @param matchNames names of tags to be returned
* @param test check for the nodes to be returned
*/
- default List<ConfigNode> children(Predicate<ConfigNode> test, Set<String> matchNames) {
+ default List<ConfigNode> getAll(Predicate<ConfigNode> test, Set<String> matchNames) {
List<ConfigNode> result = new ArrayList<>();
forEachChild(it -> {
if (matchNames != null && !matchNames.isEmpty() && !matchNames.contains(it.name())) return Boolean.TRUE;
@@ -90,15 +133,76 @@ public interface ConfigNode {
return result;
}
- default List<ConfigNode> children(String name) {
- return children(null, Collections.singleton(name));
+ default List<ConfigNode> getAll(String name) {
+ return getAll(null, Collections.singleton(name));
}
+ default boolean exists() { return true; }
+ default boolean isNull() { return false; }
+
/** abortable iterate through children
*
* @param fun consume the node and return true to continue or false to abort
*/
void forEachChild(Function<ConfigNode, Boolean> fun);
+ /**An empty node object.
+ * usually returned when the node is absent
+ *
+ */
+ ConfigNode EMPTY = new ConfigNode() {
+ @Override
+ public String name() {
+ return null;
+ }
+
+ @Override
+ public String txt() { return null; }
+
+ @Override
+ public SimpleMap<String> attributes() {
+ return empty_attrs;
+ }
+
+ @Override
+ public String attr(String name) { return null; }
+
+ @Override
+ public String attr(String name, String def) { return def; }
+ @Override
+ public ConfigNode child(String name) { return null; }
+
+ @Override
+ public ConfigNode get(String name) {
+ return EMPTY;
+ }
+
+ public boolean exists() { return false; }
+
+ @Override
+ public boolean isNull() { return true; }
+
+ @Override
+ public void forEachChild(Function<ConfigNode, Boolean> fun) { }
+ } ;
+ SimpleMap<String> empty_attrs = new WrappedSimpleMap<>(Collections.emptyMap());
+
+ class Helpers {
+ static boolean _bool(Object v, boolean def) { return v == null ? def : Boolean.parseBoolean(v.toString()); }
+ static String _txt(Object v, String def) { return v == null ? def : v.toString(); }
+ static int _int(Object v, int def) { return v==null? def: Integer.parseInt(v.toString()); }
+ static double _double(Object v, double def) { return v == null ? def: Double.parseDouble(v.toString()); }
+ public static Predicate<ConfigNode> at(int i) {
+ return new Predicate<ConfigNode>() {
+ int index =0;
+ @Override
+ public boolean test(ConfigNode node) {
+ if(index == i) return true;
+ index++;
+ return false;
+ }
+ };
+ }
+ }
}
diff --git a/solr/solrj/src/java/org/apache/solr/common/util/DOMUtil.java b/solr/solrj/src/java/org/apache/solr/common/util/DOMUtil.java
index 5fd0107..c4c280f 100644
--- a/solr/solrj/src/java/org/apache/solr/common/util/DOMUtil.java
+++ b/solr/solrj/src/java/org/apache/solr/common/util/DOMUtil.java
@@ -39,7 +39,7 @@ public class DOMUtil {
public static final String XML_RESERVED_PREFIX = "xml";
- public static final Set<String> NL_TAGS = Set.of("str", "int","long","float","double","bool");
+ public static final Set<String> NL_TAGS = Set.of("str", "int", "long", "float", "double", "bool", "null");
public static Map<String,String> toMap(NamedNodeMap attrs) {
@@ -209,6 +209,8 @@ public class DOMUtil {
val = Double.valueOf(textValue);
} else if ("bool".equals(type)) {
val = StrUtils.parseBool(textValue);
+ } else if("null".equals(type)) {
+ val = null;
}
// :NOTE: Unexpected Node names are ignored
// :TODO: should we generate an error here?
@@ -228,7 +230,7 @@ public class DOMUtil {
String tag = it.name();
String varName = it.attributes().get("name");
if (NL_TAGS.contains(tag)) {
- result.add(varName, parseVal(tag, varName, it.textValue()));
+ result.add(varName, parseVal(tag, varName, it.txt()));
}
if ("lst".equals(tag)) {
result.add(varName, readNamedListChildren(it));
@@ -237,7 +239,9 @@ public class DOMUtil {
result.add(varName, l);
it.forEachChild(n -> {
if (NL_TAGS.contains(n.name())) {
- l.add(parseVal(n.name(), null, n.textValue()));
+ l.add(parseVal(n.name(), null, n.txt()));
+ } else if("lst".equals(n.name())){
+ l.add(readNamedListChildren(n));
}
return Boolean.TRUE;
});
diff --git a/solr/solrj/src/java/org/apache/solr/common/util/LinkedSimpleHashMap.java b/solr/solrj/src/java/org/apache/solr/common/util/LinkedSimpleHashMap.java
index 1fc6afc..6581c83 100644
--- a/solr/solrj/src/java/org/apache/solr/common/util/LinkedSimpleHashMap.java
+++ b/solr/solrj/src/java/org/apache/solr/common/util/LinkedSimpleHashMap.java
@@ -19,6 +19,7 @@ package org.apache.solr.common.util;
import org.apache.solr.cluster.api.SimpleMap;
import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.function.BiConsumer;
public class LinkedSimpleHashMap<T> extends LinkedHashMap<String, T> implements SimpleMap<T> {
@@ -31,4 +32,9 @@ public class LinkedSimpleHashMap<T> extends LinkedHashMap<String, T> implements
public void forEachEntry(BiConsumer<String, ? super T> fun) {
}
+
+ @Override
+ public Map<String, T> asMap() {
+ return this;
+ }
}
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..160539a 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,14 @@ public class WrappedSimpleMap<T> implements SimpleMap<T> {
this.delegate = delegate;
}
+ @Override
+ public Map<String, T> asMap(Map<String, T> sink) {
+ sink.putAll(delegate);
+ return sink;
+ }
+
+ @Override
+ public Map<String, T> asMap() {
+ return Collections.unmodifiableMap(delegate);
+ }
}