| 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)); |
| } |
| }; |
| } |
| |