blob: 0374155571a5926e40d6af104ea0410211e4fc8d [file] [log] [blame]
package org.apache.solr.core;
/*
* 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.
*/
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.util.DOMUtil;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Locale;
/**
*
*/
public class ConfigSolrXml extends ConfigSolr {
protected static Logger log = LoggerFactory.getLogger(ConfigSolrXml.class);
private final CoresLocator coresLocator;
public ConfigSolrXml(Config config) {
super(config);
try {
checkForIllegalConfig();
fillPropMap();
coresLocator = new CorePropertiesLocator(getCoreRootDirectory());
} catch (IOException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
}
}
private void checkForIllegalConfig() throws IOException {
// Do sanity checks - we don't want to find old style config
failIfFound("solr/@coreLoadThreads");
failIfFound("solr/@persistent");
failIfFound("solr/@sharedLib");
failIfFound("solr/@zkHost");
failIfFound("solr/logging/@class");
failIfFound("solr/logging/@enabled");
failIfFound("solr/logging/watcher/@size");
failIfFound("solr/logging/watcher/@threshold");
failIfFound("solr/cores/@adminHandler");
failIfFound("solr/cores/@distribUpdateConnTimeout");
failIfFound("solr/cores/@distribUpdateSoTimeout");
failIfFound("solr/cores/@host");
failIfFound("solr/cores/@hostContext");
failIfFound("solr/cores/@hostPort");
failIfFound("solr/cores/@leaderVoteWait");
failIfFound("solr/cores/@leaderConflictResolveWait");
failIfFound("solr/cores/@genericCoreNodeNames");
failIfFound("solr/cores/@managementPath");
failIfFound("solr/cores/@shareSchema");
failIfFound("solr/cores/@transientCacheSize");
failIfFound("solr/cores/@zkClientTimeout");
// These have no counterpart in 5.0, asking for any of these in Solr 5.0
// will result in an error being
// thrown.
failIfFound("solr/cores/@defaultCoreName");
failIfFound("solr/@persistent");
failIfFound("solr/cores/@adminPath");
}
private void failIfFound(String xPath) {
if (config.getVal(xPath, false) != null) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Should not have found " + xPath +
" solr.xml may be a mix of old and new style formats.");
}
}
private NamedList<Object> readNodeListAsNamedList(String path) {
NodeList nodes = config.getNodeList(path, false);
if (nodes != null) {
NamedList<Object> namedList = DOMUtil.nodesToNamedList(nodes);
return namedList;
}
return new NamedList<>();
}
private void fillPropMap() {
NamedList<Object> unknownConfigParams = new NamedList<>();
// shardHandlerFactory is parsed differently in the base class as a plugin, so we're excluding this node from the node list
fillSolrSection(readNodeListAsNamedList("solr/*[@name][not(name()='shardHandlerFactory')]"));
thereCanBeOnlyOne("solr/solrcloud","<solrcloud>");
fillSolrCloudSection(readNodeListAsNamedList("solr/solrcloud/*[@name]"));
thereCanBeOnlyOne("solr/logging","<logging>");
thereCanBeOnlyOne("solr/logging/watcher","Logging <watcher>");
fillLoggingSection(readNodeListAsNamedList("solr/logging/*[@name]"),
readNodeListAsNamedList("solr/logging/watcher/*[@name]"));
}
private void fillSolrSection(NamedList<Object> nl) {
String s = "Main";
storeConfigPropertyAsString(s, nl, CfgProp.SOLR_ADMINHANDLER, "adminHandler");
storeConfigPropertyAsString(s, nl, CfgProp.SOLR_COLLECTIONSHANDLER, "collectionsHandler");
storeConfigPropertyAsString(s, nl, CfgProp.SOLR_INFOHANDLER, "infoHandler");
storeConfigPropertyAsString(s, nl, CfgProp.SOLR_COREROOTDIRECTORY, "coreRootDirectory");
storeConfigPropertyAsString(s, nl, CfgProp.SOLR_MANAGEMENTPATH, "managementPath");
storeConfigPropertyAsString(s, nl, CfgProp.SOLR_SHAREDLIB, "sharedLib");
storeConfigPropertyAsString(s, nl, CfgProp.SOLR_CONFIGSETBASEDIR, "configSetBaseDir");
storeConfigPropertyAsBoolean(s, nl, CfgProp.SOLR_SHARESCHEMA, "shareSchema");
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_CORELOADTHREADS, "coreLoadThreads");
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_TRANSIENTCACHESIZE, "transientCacheSize");
errorOnLeftOvers(s, nl);
}
private void fillSolrCloudSection(NamedList<Object> nl) {
String s = "<solrcloud>";
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_DISTRIBUPDATECONNTIMEOUT, "distribUpdateConnTimeout");
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_DISTRIBUPDATESOTIMEOUT, "distribUpdateSoTimeout");
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_MAXUPDATECONNECTIONS, "maxUpdateConnections");
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_MAXUPDATECONNECTIONSPERHOST, "maxUpdateConnectionsPerHost");
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_LEADERVOTEWAIT, "leaderVoteWait");
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_LEADERCONFLICTRESOLVEWAIT, "leaderConflictResolveWait");
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_ZKCLIENTTIMEOUT, "zkClientTimeout");
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_AUTOREPLICAFAILOVERBADNODEEXPIRATION, "autoReplicaFailoverBadNodeExpiration");
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_AUTOREPLICAFAILOVERWAITAFTEREXPIRATION, "autoReplicaFailoverWaitAfterExpiration");
storeConfigPropertyAsInt(s, nl, CfgProp.SOLR_AUTOREPLICAFAILOVERWORKLOOPDELAY, "autoReplicaFailoverWorkLoopDelay");
storeConfigPropertyAsString(s, nl, CfgProp.SOLR_HOST, "host");
storeConfigPropertyAsString(s, nl, CfgProp.SOLR_HOSTCONTEXT, "hostContext");
storeConfigPropertyAsString(s, nl, CfgProp.SOLR_HOSTPORT, "hostPort");
storeConfigPropertyAsString(s, nl, CfgProp.SOLR_ZKHOST, "zkHost");
storeConfigPropertyAsBoolean(s, nl, CfgProp.SOLR_GENERICCORENODENAMES, "genericCoreNodeNames");
errorOnLeftOvers(s, nl);
}
private void fillLoggingSection(NamedList<Object> loggingConfig,
NamedList<Object> loggingWatcherConfig) {
String s = "<logging>";
storeConfigPropertyAsString(s, loggingConfig, CfgProp.SOLR_LOGGING_CLASS, "class");
storeConfigPropertyAsBoolean(s, loggingConfig, CfgProp.SOLR_LOGGING_ENABLED, "enabled");
errorOnLeftOvers(s, loggingConfig);
s = "Logging <watcher>";
storeConfigPropertyAsInt(s, loggingWatcherConfig, CfgProp.SOLR_LOGGING_WATCHER_SIZE, "size");
storeConfigPropertyAsString(s, loggingWatcherConfig, CfgProp.SOLR_LOGGING_WATCHER_THRESHOLD, "threshold");
errorOnLeftOvers(s, loggingWatcherConfig);
}
private <T> void storeConfigProperty(String section, NamedList<Object> config, CfgProp propertyKey, String name, Function<Object, T> valueTransformer, Class<T> clazz) {
List<Object> values = config.removeAll(name);
if (null != values && 0 != values.size()) {
if (1 < values.size()) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
String.format(Locale.ROOT,
"%s section of solr.xml contains duplicated '%s'"+
" in solr.xml: %s", section, name, values));
} else {
Object value = values.get(0);
if (value != null) {
if (value.getClass().isAssignableFrom(clazz)) {
propMap.put(propertyKey, value);
} else {
propMap.put(propertyKey, valueTransformer.apply(value));
}
} else {
propMap.put(propertyKey, null);
}
}
}
}
private void storeConfigPropertyAsString(String section, NamedList<Object> config, CfgProp propertyKey, String name) {
storeConfigProperty(section, config, propertyKey, name, Functions.toStringFunction(), String.class);
}
private void storeConfigPropertyAsInt(String section, NamedList<Object> config, CfgProp propertyKey, String xmlElementName) {
storeConfigProperty(section, config, propertyKey, xmlElementName, TO_INT_FUNCTION, Integer.class);
}
private void storeConfigPropertyAsBoolean(String section, NamedList<Object> config, CfgProp propertyKey, String name) {
storeConfigProperty(section, config, propertyKey, name, TO_BOOLEAN_FUNCTION, Boolean.class);
}
/** throws an error if more then one element matching the xpath */
private void thereCanBeOnlyOne(String xpath, String section) {
NodeList lst = config.getNodeList(xpath, false);
if (1 < lst.getLength())
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
lst.getLength() + " instances of " + section + " found in solr.xml");
}
/** logs each item in leftovers and then throws an exception with a summary */
private void errorOnLeftOvers(String section, NamedList<Object> leftovers) {
if (null == leftovers || 0 == leftovers.size()) return;
List<String> unknownElements = new ArrayList<String>(leftovers.size());
for (Map.Entry<String, Object> unknownElement : leftovers) {
log.error("Unknown config parameter in {} section of solr.xml: {} -> {}",
section, unknownElement.getKey(), unknownElement.getValue());
unknownElements.add(unknownElement.getKey());
}
if (! unknownElements.isEmpty() ) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
String.format(Locale.ROOT, "%s section of solr.xml contains %d unknown config parameter(s): %s", section, unknownElements.size(), unknownElements));
}
}
@Override
public String getDefaultCoreName() {
return "collection1";
}
@Override
public boolean isPersistent() {
return true;
}
@Override
protected String getShardHandlerFactoryConfigPath() {
return "solr/shardHandlerFactory";
}
@Override
public String getAdminPath() {
return DEFAULT_CORE_ADMIN_PATH;
}
@Override
public CoresLocator getCoresLocator() {
return coresLocator;
}
private static final Function<Map.Entry<String, Object>, String> GET_KEY_FUNCTION = new Function<Map.Entry<String, Object>, String>() {
@Override
public String apply(Map.Entry<String, Object> input) {
return input.getKey();
}
};
private static final Function<Object, Integer> TO_INT_FUNCTION = new Function<Object, Integer>() {
@Override
public Integer apply(Object input) {
try {
return Integer.parseInt(String.valueOf(input));
} catch (NumberFormatException exc) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
String.format(Locale.ROOT,
"Value of '%s' can not be parsed as 'int'", input));
}
}
};
private static final Function<Object, Boolean> TO_BOOLEAN_FUNCTION = new Function<Object, Boolean>() {
@Override
public Boolean apply(Object input) {
if (input instanceof String) {
return Boolean.valueOf(String.valueOf(input));
}
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, String.format(Locale.ROOT, "Value of '%s' can not be parsed as 'bool'", input));
}
};
}