From 05eb3064233b5acab97aa34c2b714ac42401c35e Mon Sep 17 00:00:00 2001 From: Erick Erickson Date: Mon, 17 Jun 2013 00:33:31 +0000 Subject: [PATCH] SOLR-4910, improvements to persisting solr.xml and misc other fixes, see CHANGES.txt git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1493618 13f79535-47bb-0310-9956-ffa450edef68 --- solr/CHANGES.txt | 15 + .../org/apache/solr/core/ConfigSolrXml.java | 2 +- .../apache/solr/core/ConfigSolrXmlOld.java | 4 +- .../org/apache/solr/core/CoreContainer.java | 111 ++-- .../org/apache/solr/core/CoreDescriptor.java | 11 + .../java/org/apache/solr/core/SolrCores.java | 211 +++++--- .../apache/solr/core/SolrXMLSerializer.java | 39 ++ .../solr/handler/admin/CoreAdminHandler.java | 8 +- .../apache/solr/core/TestCoreContainer.java | 18 - .../solr/core/TestSolrXMLSerializer.java | 5 + .../solr/core/TestSolrXmlPersistence.java | 510 ++++++++++++++++++ .../solrj/embedded/TestSolrProperties.java | 2 +- 12 files changed, 809 insertions(+), 127 deletions(-) create mode 100644 solr/core/src/test/org/apache/solr/core/TestSolrXmlPersistence.java diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index e5059903e01..ed12071d1b4 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -141,6 +141,21 @@ Bug Fixes * SOLR-4925 : Collection create throws NPE when 'numShards' param is missing (Noble Paul) +* SOLR-4910: persisting solr.xml is broken. More stringent testing of persistence fixed + up a number of issues and several bugs with persistence. Among them are + > don't persisting implicit properties + > should persist zkHost in the tag (user's list) + > reloading a core that has transient="true" returned an error. reload should load + a transient core if it's not yet loaded. + > No longer persisting loadOnStartup or transient core properties if they were not + specified in the original solr.xml + > Testing flushed out the fact that you couldn't swap a core marked transient=true + loadOnStartup=false because it hadn't been loaded yet. + > SOLR-4862, CREATE fails to persist schema, config, and dataDir + > SOLR-4363, not persisting coreLoadThreads in tag + > SOLR-3900, logWatcher properties not persisted + > SOLR-4852, cores defined as loadOnStartup=true, transient=false can't be searched + Other Changes ---------------------- diff --git a/solr/core/src/java/org/apache/solr/core/ConfigSolrXml.java b/solr/core/src/java/org/apache/solr/core/ConfigSolrXml.java index 0c274aa01bc..57bc281e4ea 100644 --- a/solr/core/src/java/org/apache/solr/core/ConfigSolrXml.java +++ b/solr/core/src/java/org/apache/solr/core/ConfigSolrXml.java @@ -61,7 +61,7 @@ public class ConfigSolrXml extends ConfigSolr { // Do sanity checks - we don't want to find old style config failIfFound("solr/@coreLoadThreads"); - failIfFound("solr/@persist"); + failIfFound("solr/@persistent"); failIfFound("solr/@sharedLib"); failIfFound("solr/@zkHost"); diff --git a/solr/core/src/java/org/apache/solr/core/ConfigSolrXmlOld.java b/solr/core/src/java/org/apache/solr/core/ConfigSolrXmlOld.java index 6d9a08d4256..f7a7d23e377 100644 --- a/solr/core/src/java/org/apache/solr/core/ConfigSolrXmlOld.java +++ b/solr/core/src/java/org/apache/solr/core/ConfigSolrXmlOld.java @@ -138,9 +138,9 @@ public class ConfigSolrXmlOld extends ConfigSolr { propMap.put(CfgProp.SOLR_SHARDHANDLERFACTORY_NAME, config.getVal("solr/shardHandlerFactory/@name", false)); propMap.put(CfgProp.SOLR_SHARDHANDLERFACTORY_CONNTIMEOUT, - config.getVal("solr/shardHandlerFactory/int[@connTimeout]", false)); + config.getVal("solr/shardHandlerFactory/int[@name='connTimeout']", false)); propMap.put(CfgProp.SOLR_SHARDHANDLERFACTORY_SOCKETTIMEOUT, - config.getVal("solr/shardHandlerFactory/int[@socketTimeout]", false)); + config.getVal("solr/shardHandlerFactory/int[@name='socketTimeout']", false)); // These have no counterpart in 5.0, asking, for any of these in Solr 5.0 // will result in an error being diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java index 5f1fd90e06e..f0d2fb526d3 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -114,6 +114,8 @@ public class CoreContainer protected Integer zkClientTimeout; protected String solrHome; protected String defaultCoreName = null; + protected int distribUpdateConnTimeout = 0; + protected int distribUpdateSoTimeout = 0; protected ZkContainer zkSys = new ZkContainer(); @@ -298,8 +300,8 @@ public class CoreContainer shareSchema = cfg.getBool(ConfigSolr.CfgProp.SOLR_SHARESCHEMA, DEFAULT_SHARE_SCHEMA); zkClientTimeout = cfg.getInt(ConfigSolr.CfgProp.SOLR_ZKCLIENTTIMEOUT, DEFAULT_ZK_CLIENT_TIMEOUT); - int distribUpdateConnTimeout = cfg.getInt(ConfigSolr.CfgProp.SOLR_DISTRIBUPDATECONNTIMEOUT, 0); - int distribUpdateSoTimeout = cfg.getInt(ConfigSolr.CfgProp.SOLR_DISTRIBUPDATESOTIMEOUT, 0); + distribUpdateConnTimeout = cfg.getInt(ConfigSolr.CfgProp.SOLR_DISTRIBUPDATECONNTIMEOUT, 0); + distribUpdateSoTimeout = cfg.getInt(ConfigSolr.CfgProp.SOLR_DISTRIBUPDATESOTIMEOUT, 0); // Note: initZooKeeper will apply hardcoded default if cloud mode String hostPort = cfg.get(ConfigSolr.CfgProp.SOLR_HOSTPORT, null); @@ -415,7 +417,13 @@ public class CoreContainer p.setTransient(("true".equalsIgnoreCase(opt) || "on" .equalsIgnoreCase(opt)) ? true : false); } - + + if (p.isTransient() || ! p.isLoadOnStartup()) { + // Store it away for later use. includes non-transient but not + // loaded at startup cores. + solrCores.putDynamicDescriptor(rawName, p); + } + if (p.isLoadOnStartup()) { // The normal case Callable task = new Callable() { @@ -449,10 +457,6 @@ public class CoreContainer }; pending.add(completionService.submit(task)); - } else { - // Store it away for later use. includes non-transient but not - // loaded at startup cores. - solrCores.putDynamicDescriptor(rawName, p); } } catch (Throwable ex) { SolrException.log(log, null, ex); @@ -840,7 +844,7 @@ public class CoreContainer try { name = checkDefault(name); - SolrCore core = solrCores.getCore(name); + SolrCore core = solrCores.getCoreFromAnyList(name); if (core == null) throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No such core: " + name ); @@ -1109,43 +1113,77 @@ public class CoreContainer log.info("Persisting cores config to " + (file == null ? configFile : file)); - // Map rootSolrAttribs = new HashMap(); - if (libDir != null) rootSolrAttribs.put("sharedLib", libDir); - rootSolrAttribs.put("persistent", Boolean.toString(isPersistent())); - + + addAttrib(rootSolrAttribs, ConfigSolr.CfgProp.SOLR_SHAREDLIB, "sharedLib", this.libDir); + addAttrib(rootSolrAttribs, ConfigSolr.CfgProp.SOLR_PERSISTENT, "persistent", Boolean.toString(isPersistent())); + addAttrib(rootSolrAttribs, ConfigSolr.CfgProp.SOLR_CORELOADTHREADS, "coreLoadThreads", + Integer.toString(this.coreLoadThreads), Integer.toString(CORE_LOAD_THREADS)); + addAttrib(rootSolrAttribs, ConfigSolr.CfgProp.SOLR_ZKHOST, "zkHost", this.zkHost); + // Map coresAttribs = new HashMap(); - addCoresAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_ADMINPATH, "adminPath", this.adminPath, null); - addCoresAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_ADMINHANDLER, "adminHandler", this.adminHandler, null); - addCoresAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_SHARESCHEMA,"shareSchema", + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_ADMINPATH, "adminPath", this.adminPath); + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_ADMINHANDLER, "adminHandler", this.adminHandler); + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_SHARESCHEMA, "shareSchema", Boolean.toString(this.shareSchema), Boolean.toString(DEFAULT_SHARE_SCHEMA)); - addCoresAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_HOST, "host", zkSys.getHost(), null); + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_HOST, "host", zkSys.getHost()); if (! (null == defaultCoreName || defaultCoreName.equals("")) ) { coresAttribs.put("defaultCoreName", defaultCoreName); } - addCoresAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_HOSTPORT, "hostPort",zkSys.getHostPort(), null); - addCoresAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_ZKCLIENTTIMEOUT, "zkClientTimeout", + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_HOSTPORT, "hostPort", zkSys.getHostPort()); + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_ZKCLIENTTIMEOUT, "zkClientTimeout", intToString(this.zkClientTimeout), Integer.toString(DEFAULT_ZK_CLIENT_TIMEOUT)); - addCoresAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_HOSTCONTEXT, "hostContext", - zkSys.getHostContext(), null); - addCoresAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_LEADERVOTEWAIT, "leaderVoteWait", + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_HOSTCONTEXT, "hostContext", + zkSys.getHostContext()); + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_LEADERVOTEWAIT, "leaderVoteWait", zkSys.getLeaderVoteWait(), LEADER_VOTE_WAIT); - addCoresAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_CORELOADTHREADS, "coreLoadThreads", - Integer.toString(this.coreLoadThreads), Integer.toString(CORE_LOAD_THREADS)); if (transientCacheSize != Integer.MAX_VALUE) { // This test // is a consequence of testing. I really hate it. - addCoresAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_TRANSIENTCACHESIZE, "transientCacheSize", + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_TRANSIENTCACHESIZE, "transientCacheSize", Integer.toString(this.transientCacheSize), Integer.toString(Integer.MAX_VALUE)); } + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_DISTRIBUPDATECONNTIMEOUT, "distribUpdateConnTimeout", + Integer.toString(this.distribUpdateConnTimeout)); + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_DISTRIBUPDATESOTIMEOUT, "distribUpdateSoTimeout", + Integer.toString(this.distribUpdateSoTimeout)); + addAttrib(coresAttribs, ConfigSolr.CfgProp.SOLR_MANAGEMENTPATH, "managementPath", + this.managementPath); + + // don't forget the logging stuff + Map loggingAttribs = new HashMap(); + addAttrib(loggingAttribs, ConfigSolr.CfgProp.SOLR_LOGGING_CLASS, "class", + cfg.get(ConfigSolr.CfgProp.SOLR_LOGGING_CLASS, null)); + addAttrib(loggingAttribs, ConfigSolr.CfgProp.SOLR_LOGGING_ENABLED, "enabled", + cfg.get(ConfigSolr.CfgProp.SOLR_LOGGING_ENABLED, null)); + + Map watcherAttribs = new HashMap(); + addAttrib(watcherAttribs, ConfigSolr.CfgProp.SOLR_LOGGING_WATCHER_SIZE, "size", + cfg.get(ConfigSolr.CfgProp.SOLR_LOGGING_WATCHER_SIZE, null)); + addAttrib(watcherAttribs, ConfigSolr.CfgProp.SOLR_LOGGING_WATCHER_THRESHOLD, "threshold", + cfg.get(ConfigSolr.CfgProp.SOLR_LOGGING_WATCHER_THRESHOLD, null)); + + + Map shardHandlerAttrib = new HashMap(); + addAttrib(shardHandlerAttrib, ConfigSolr.CfgProp.SOLR_SHARDHANDLERFACTORY_CLASS, "class", + cfg.get(ConfigSolr.CfgProp.SOLR_SHARDHANDLERFACTORY_CLASS, null)); + addAttrib(shardHandlerAttrib, ConfigSolr.CfgProp.SOLR_SHARDHANDLERFACTORY_NAME, "name", + cfg.get(ConfigSolr.CfgProp.SOLR_SHARDHANDLERFACTORY_NAME, null)); + + Map shardHandlerProps = new HashMap(); + addAttrib(shardHandlerProps, ConfigSolr.CfgProp.SOLR_SHARDHANDLERFACTORY_CONNTIMEOUT, "connTimeout", + cfg.get(ConfigSolr.CfgProp.SOLR_SHARDHANDLERFACTORY_CONNTIMEOUT, null)); + addAttrib(shardHandlerProps, ConfigSolr.CfgProp.SOLR_SHARDHANDLERFACTORY_SOCKETTIMEOUT, "socketTimeout", + cfg.get(ConfigSolr.CfgProp.SOLR_SHARDHANDLERFACTORY_SOCKETTIMEOUT, null)); try { - solrCores.persistCores(origCfg, containerProperties, rootSolrAttribs, coresAttribs, file, configFile, loader); + solrCores.persistCores(origCfg, containerProperties, rootSolrAttribs,coresAttribs, + loggingAttribs, watcherAttribs, shardHandlerAttrib, shardHandlerProps, file, loader); } catch (XPathExpressionException e) { throw new SolrException(ErrorCode.SERVER_ERROR, null, e); } @@ -1156,26 +1194,31 @@ public class CoreContainer return Integer.toString(integer); } - private void addCoresAttrib(Map coresAttribs, ConfigSolr.CfgProp prop, - String attribName, String attribValue, String defaultValue) { + private void addAttrib(Map attribs, ConfigSolr.CfgProp prop, + String attribName, String attribValue) { + addAttrib(attribs, prop, attribName, attribValue, null); + } + + private void addAttrib(Map attribs, ConfigSolr.CfgProp prop, + String attribName, String attribValue, String defaultValue) { if (cfg == null) { - coresAttribs.put(attribName, attribValue); + attribs.put(attribName, attribValue); return; } - + if (attribValue != null) { String origValue = cfg.getOrigProp(prop, null); - + if (origValue == null && defaultValue != null && attribValue.equals(defaultValue)) return; if (attribValue.equals(PropertiesUtil.substituteProperty(origValue, loader.getCoreProperties()))) { - coresAttribs.put(attribName, origValue); + attribs.put(attribName, origValue); } else { - coresAttribs.put(attribName, attribValue); + attribs.put(attribName, attribValue); } } } - + public void preRegisterInZk(final CoreDescriptor p) { try { zkSys.getZkController().preRegister(p); @@ -1194,7 +1237,7 @@ public class CoreContainer public String getSolrHome() { return solrHome; } - + public boolean isZooKeeperAware() { return zkSys.getZkController() != null; } diff --git a/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java b/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java index a44abe7e194..3e490714549 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java +++ b/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java @@ -65,6 +65,9 @@ public class CoreDescriptor { // them individually. private Properties coreProperties = new Properties(); + //TODO: 5.0 remove this, this is solely a hack for persistence. + private Properties createdProperties = new Properties(); + private boolean loadedImplicit = false; private final CoreContainer coreContainer; @@ -280,6 +283,14 @@ public class CoreDescriptor { } } + public void addCreatedProperty(String key, String value) { + createdProperties.put(key, value); + } + + public final Properties getCreatedProperties() { + return createdProperties; + } + public CloudDescriptor getCloudDescriptor() { return cloudDesc; } diff --git a/solr/core/src/java/org/apache/solr/core/SolrCores.java b/solr/core/src/java/org/apache/solr/core/SolrCores.java index 2d1acf4094e..0e794cd9421 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrCores.java +++ b/solr/core/src/java/org/apache/solr/core/SolrCores.java @@ -35,6 +35,7 @@ import javax.xml.xpath.XPathExpressionException; import org.apache.commons.lang.StringUtils; import org.apache.solr.cloud.CloudDescriptor; import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.core.SolrXMLSerializer.SolrCoreXMLDef; import org.apache.solr.util.DOMUtil; import org.w3c.dom.Node; @@ -246,10 +247,18 @@ class SolrCores { synchronized (modifyLock) { SolrCore c0 = cores.get(n0); SolrCore c1 = cores.get(n1); - if (c0 == null) - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n0); - if (c1 == null) - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n1); + if (c0 == null) { // Might be an unloaded transient core + c0 = container.getCore(n0); + if (c0 == null) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n0); + } + } + if (c1 == null) { // Might be an unloaded transient core + c1 = container.getCore(n1); + if (c1 == null) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n1); + } + } cores.put(n0, c1); cores.put(n1, c0); @@ -351,15 +360,17 @@ class SolrCores { return coreToOrigName.get(solrCore); } } - + public void persistCores(Config cfg, Properties containerProperties, Map rootSolrAttribs, Map coresAttribs, - File file, File configFile, SolrResourceLoader loader) throws XPathExpressionException { + Map loggingAttribs, Map watcherAttribs, + Map shardHandlerAttrib, Map shardHandlerProps, + File file, SolrResourceLoader loader) throws XPathExpressionException { + - List solrCoreXMLDefs = new ArrayList(); synchronized (modifyLock) { - + persistCores(cfg, cores, loader, solrCoreXMLDefs); persistCores(cfg, transientCores, loader, solrCoreXMLDefs); // add back all the cores that aren't loaded, either in cores or transient @@ -384,9 +395,13 @@ class SolrCores { solrXMLDef.containerProperties = containerProperties; solrXMLDef.solrAttribs = rootSolrAttribs; solrXMLDef.coresAttribs = coresAttribs; + solrXMLDef.loggingAttribs = loggingAttribs; + solrXMLDef.watcherAttribs = watcherAttribs; + solrXMLDef.shardHandlerAttribs = shardHandlerAttrib; + solrXMLDef.shardHandlerProps = shardHandlerProps; SOLR_XML_SERIALIZER.persistFile(file, solrXMLDef); } - + } // Wait here until any pending operations (load, unload or reload) are completed on this core. protected SolrCore waitAddPendingCoreOps(String name) { @@ -442,95 +457,155 @@ class SolrCores { addCoreToPersistList(cfg, loader, solrCore.getCoreDescriptor(), getCoreToOrigName(solrCore), solrCoreXMLDefs); } } - - private void addCoreProperty(Map coreAttribs, SolrResourceLoader loader, Node node, String name, + + private void addCoreProperty(Map propMap, SolrResourceLoader loader, Node node, String name, + String value) { + addCoreProperty(propMap, loader, node, name, value, null); + } + + private void addCoreProperty(Map propMap, SolrResourceLoader loader, Node node, String name, String value, String defaultValue) { - + if (node == null) { - coreAttribs.put(name, value); + propMap.put(name, value); return; } - + if (node != null) { String rawAttribValue = DOMUtil.getAttr(node, name, null); + if (rawAttribValue == null) { + return; // It was never in the original definition. + } + if (value == null) { - coreAttribs.put(name, rawAttribValue); + propMap.put(name, rawAttribValue); return; } - if (rawAttribValue == null && defaultValue != null && value.equals(defaultValue)) { + + // There are some _really stupid_ additions/subtractions of the slash that we should look out for. I'm (EOE) + // ashamed of this but it fixes some things and we're throwing persistence away anyway (although + // maybe not for core.properties files). + String defComp = regularizeAttr(defaultValue); + + if (defComp != null && regularizeAttr(value).equals(defComp)) { return; } - if (rawAttribValue != null && value.equals(DOMUtil.substituteProperty(rawAttribValue, loader.getCoreProperties()))){ - coreAttribs.put(name, rawAttribValue); + String rawComp = regularizeAttr(rawAttribValue); + if (rawComp != null && regularizeAttr(value).equals( + regularizeAttr(DOMUtil.substituteProperty(rawAttribValue, loader.getCoreProperties())))) { + propMap.put(name, rawAttribValue); } else { - coreAttribs.put(name, value); + propMap.put(name, value); } } - } + protected String regularizeAttr(String path) { + if (path == null) + return null; + path = path.replace('/', File.separatorChar); + path = path.replace('\\', File.separatorChar); + if (path.endsWith(File.separator)) { + path = path.substring(0, path.length() - 1); + } + return path; + } protected void addCoreToPersistList(Config cfg, SolrResourceLoader loader, CoreDescriptor dcore, String origCoreName, List solrCoreXMLDefs) throws XPathExpressionException { - - String coreName = dcore.getProperty(CoreDescriptor.CORE_NAME); - + Map coreAttribs = new HashMap(); + Properties newProps = new Properties(); - CloudDescriptor cd = dcore.getCloudDescriptor(); - String collection = null; - if (cd != null) collection = cd.getCollectionName(); + // This is simple, just take anything sent in and saved away in at core creation and write it out. + if (dcore.getCreatedProperties().size() > 0) { + for (String key : dcore.getCreatedProperties().stringPropertyNames()) { + if (CoreAdminParams.ACTION.toString().equals(key)) continue; // Don't persist the "action" verb + if (key.indexOf(CoreAdminParams.PROPERTY_PREFIX) == 0) { + newProps.put(key.substring(CoreAdminParams.PROPERTY_PREFIX.length()), dcore.getCreatedProperties().getProperty(key)); + } else { + coreAttribs.put(key, dcore.getCreatedProperties().getProperty(key)); + } + } + } else { - if (origCoreName == null) { - origCoreName = coreName; + String coreName = dcore.getProperty(CoreDescriptor.CORE_NAME); + + CloudDescriptor cd = dcore.getCloudDescriptor(); + String collection = null; + if (cd != null) collection = cd.getCollectionName(); + + if (origCoreName == null) { + origCoreName = coreName; + } + + Node node = null; + if (cfg != null) { + node = cfg.getNode("/solr/cores/core[@name='" + origCoreName + "']", + false); + } + + coreAttribs.put(CoreDescriptor.CORE_NAME, coreName); + + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_INSTDIR, dcore.getRawInstanceDir(), null); + + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_COLLECTION, + StringUtils.isNotBlank(collection) ? collection : dcore.getName()); + + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_DATADIR, + dcore.getDataDir()); + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_ULOGDIR, + dcore.getUlogDir()); + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_TRANSIENT, + Boolean.toString(dcore.isTransient())); + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_LOADONSTARTUP, + Boolean.toString(dcore.isLoadOnStartup())); + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_CONFIG, + dcore.getConfigName()); + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_SCHEMA, + dcore.getSchemaName()); + + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_COLLECTION, + collection, dcore.getName()); + + String shard = null; + String roles = null; + String node_name = null; + if (cd != null) { + shard = cd.getShardId(); + roles = cd.getRoles(); + node_name = cd.getCoreNodeName(); + } + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_SHARD, + shard); + + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_ROLES, + roles); + + addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_NODE_NAME, + node_name); + + + for (Object key : dcore.getCoreProperties().keySet()) { + + if (cfg != null) { + Node propNode = cfg.getNode("/solr/cores/core[@name='" + origCoreName + "']/property[@name='" + key + "']", + false); + + if (propNode != null) { // This means it was in the original DOM element, so just copy it. + newProps.put(DOMUtil.getAttr(propNode, "name", null), DOMUtil.getAttr(propNode, "value", null)); + } + } + } } - - Properties properties = dcore.getCoreProperties(); - Node node = null; - if (cfg != null) { - node = cfg.getNode("/solr/cores/core[@name='" + origCoreName + "']", - false); - } - - coreAttribs.put(CoreDescriptor.CORE_NAME, coreName); - - addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_INSTDIR, dcore.getRawInstanceDir(), null); - coreAttribs.put(CoreDescriptor.CORE_COLLECTION, - StringUtils.isNotBlank(collection) ? collection : dcore.getName()); - - addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_DATADIR, dcore.getDataDir(), null); - addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_ULOGDIR, dcore.getUlogDir(), null); - addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_TRANSIENT, Boolean.toString(dcore.isTransient()), null); - addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_LOADONSTARTUP, Boolean.toString(dcore.isLoadOnStartup()), null); - - addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_COLLECTION, - collection, dcore.getName()); - - String shard = null; - String roles = null; - if (cd != null) { - shard = cd.getShardId(); - roles = cd.getRoles(); - } - addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_SHARD, - shard, null); - - addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_ROLES, - roles, null); - coreAttribs.put(CoreDescriptor.CORE_LOADONSTARTUP, - Boolean.toString(dcore.isLoadOnStartup())); - coreAttribs.put(CoreDescriptor.CORE_TRANSIENT, - Boolean.toString(dcore.isTransient())); - SolrXMLSerializer.SolrCoreXMLDef solrCoreXMLDef = new SolrXMLSerializer.SolrCoreXMLDef(); solrCoreXMLDef.coreAttribs = coreAttribs; - solrCoreXMLDef.coreProperties = properties; + solrCoreXMLDef.coreProperties = newProps; solrCoreXMLDefs.add(solrCoreXMLDef); - } protected Object getModifyLock() { diff --git a/solr/core/src/java/org/apache/solr/core/SolrXMLSerializer.java b/solr/core/src/java/org/apache/solr/core/SolrXMLSerializer.java index a413997ab6b..853889b9d53 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrXMLSerializer.java +++ b/solr/core/src/java/org/apache/solr/core/SolrXMLSerializer.java @@ -62,6 +62,41 @@ public class SolrXMLSerializer { if (containerProperties != null && !containerProperties.isEmpty()) { writeProperties(w, containerProperties, " "); } + + // Output logging section if any + if (solrXMLDef.loggingAttribs.size() > 0 || solrXMLDef.watcherAttribs.size() > 0) { + w.write(INDENT + " ent : solrXMLDef.loggingAttribs.entrySet()) { + writeAttribute(w, ent.getKey(), ent.getValue()); + } + w.write(">\n"); + + if (solrXMLDef.watcherAttribs.size() > 0) { + w.write(INDENT + INDENT + " ent : solrXMLDef.watcherAttribs.entrySet()) { + writeAttribute(w, ent.getKey(), ent.getValue()); + } + w.write("/>\n"); + } + w.write(INDENT + "\n"); + } + + // Output shard handler section if any + if (solrXMLDef.shardHandlerAttribs.size() > 0 || solrXMLDef.shardHandlerProps.size() > 0) { + w.write(INDENT + " ent : solrXMLDef.shardHandlerAttribs.entrySet()) { + writeAttribute(w, ent.getKey(), ent.getValue()); + } + w.write(">\n"); + if (solrXMLDef.shardHandlerProps.size() > 0) { + for (Map.Entry ent : solrXMLDef.shardHandlerProps.entrySet()) { + w.write(INDENT + INDENT + "" + ent.getValue() + "\n"); + } + } + w.write(INDENT + "\n"); + } + + w.write(INDENT + " coresAttribs = solrXMLDef.coresAttribs; Set coreAttribKeys = coresAttribs.keySet(); @@ -198,6 +233,10 @@ public class SolrXMLSerializer { Properties containerProperties; Map solrAttribs; Map coresAttribs; + Map loggingAttribs; + Map watcherAttribs; + Map shardHandlerAttribs; + Map shardHandlerProps; List coresDefs; } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java index 6b0f1937a9e..0432a124541 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java @@ -498,10 +498,12 @@ public class CoreAdminHandler extends RequestHandlerBase { Iterator parameterNamesIterator = params.getParameterNamesIterator(); while (parameterNamesIterator.hasNext()) { String parameterName = parameterNamesIterator.next(); + String parameterValue = params.get(parameterName); + dcore.addCreatedProperty(parameterName, parameterValue); // Need this junk for silly persistence + if(parameterName.startsWith(CoreAdminParams.PROPERTY_PREFIX)) { - String parameterValue = params.get(parameterName); - String propertyName = parameterName.substring(CoreAdminParams.PROPERTY_PREFIX.length()); // skip prefix - coreProperties.put(propertyName, parameterValue); + String propertyName = parameterName.substring(CoreAdminParams.PROPERTY_PREFIX.length()); // skip prefix + coreProperties.put(propertyName, parameterValue); } } dcore.setCoreProperties(coreProperties); diff --git a/solr/core/src/test/org/apache/solr/core/TestCoreContainer.java b/solr/core/src/test/org/apache/solr/core/TestCoreContainer.java index d56f2bfc6ac..a6f0d911b4b 100644 --- a/solr/core/src/test/org/apache/solr/core/TestCoreContainer.java +++ b/solr/core/src/test/org/apache/solr/core/TestCoreContainer.java @@ -392,22 +392,4 @@ public class TestCoreContainer extends SolrTestCaseJ4 { " \n" + " \n" + ""; - - private static final String SOLR_XML_SAME_NAME ="\n" + - "\n" + - " \n" + - " \n" + - " \n " + - " \n" + - ""; - - private static final String SOLR_XML_SAME_DATADIR ="\n" + - "\n" + - " \n" + - " \n" + - " \n " + - " \n" + - ""; - - } diff --git a/solr/core/src/test/org/apache/solr/core/TestSolrXMLSerializer.java b/solr/core/src/test/org/apache/solr/core/TestSolrXMLSerializer.java index 2aa88f03047..2c4249e57f7 100644 --- a/solr/core/src/test/org/apache/solr/core/TestSolrXMLSerializer.java +++ b/solr/core/src/test/org/apache/solr/core/TestSolrXMLSerializer.java @@ -143,6 +143,11 @@ public class TestSolrXMLSerializer extends LuceneTestCase { solrXMLDef.containerProperties = containerProperties ; solrXMLDef.solrAttribs = rootSolrAttribs; solrXMLDef.coresAttribs = coresAttribs; + solrXMLDef.loggingAttribs = new HashMap(); + solrXMLDef.shardHandlerProps = new HashMap(); + solrXMLDef.shardHandlerAttribs = new HashMap(); + solrXMLDef.loggingAttribs = new HashMap(); + solrXMLDef.watcherAttribs = new HashMap(); return solrXMLDef; } diff --git a/solr/core/src/test/org/apache/solr/core/TestSolrXmlPersistence.java b/solr/core/src/test/org/apache/solr/core/TestSolrXmlPersistence.java new file mode 100644 index 00000000000..ab30e5700ba --- /dev/null +++ b/solr/core/src/test/org/apache/solr/core/TestSolrXmlPersistence.java @@ -0,0 +1,510 @@ +/* + * 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 com.carrotsearch.randomizedtesting.rules.SystemPropertiesRestoreRule; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.lucene.util.IOUtils; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.params.CoreAdminParams; +import org.apache.solr.handler.admin.CoreAdminHandler; +import org.apache.solr.response.SolrQueryResponse; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; +import org.junit.rules.TestRule; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class TestSolrXmlPersistence extends SolrTestCaseJ4 { + + private final File solrHomeDirectory = new File(TEMP_DIR, this.getClass().getName()); + + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig-minimal.xml", "schema-tiny.xml"); + } + + @Rule + public TestRule solrTestRules = + RuleChain.outerRule(new SystemPropertiesRestoreRule()); + + + private CoreContainer init(String solrXmlString, String... subDirs) throws Exception { + if (solrHomeDirectory.exists()) { + FileUtils.deleteDirectory(solrHomeDirectory); + } + + for (String s : subDirs) { + copyMinConf(new File(solrHomeDirectory, s)); + } + + File solrXml = new File(solrHomeDirectory, "solr.xml"); + FileUtils.write(solrXml, solrXmlString, IOUtils.CHARSET_UTF_8.toString()); + final CoreContainer cores = new CoreContainer(solrHomeDirectory.getAbsolutePath()); + cores.load(solrHomeDirectory.getAbsolutePath(), solrXml); + + cores.setPersistent(false); + return cores; + } + + + // take a solr.xml with system vars in , and and tags that have system + // variables defined. Insure that after persisting solr.xml, they're all still there as ${} syntax. + // Also insure that nothing extra crept in. + @Test + public void testSystemVars() throws Exception { + //Set these system props in order to insure that we don't write out the values rather than the ${} syntax. + System.setProperty("solr.zkclienttimeout", "93"); + System.setProperty("solrconfig", "solrconfig-minimal.xml"); + System.setProperty("schema", "schema-tiny.xml"); + System.setProperty("zkHostSet", "localhost:9983"); + + CoreContainer cc = init(SOLR_XML_LOTS_SYSVARS, "SystemVars1", "SystemVars2"); + try { + + // This seems odd, but it's just a little self check to see if the comparison strings are being created correctly + origMatchesPersist(cc, new File(solrHomeDirectory, "solr_copy.xml")); + + // Is everything in the persisted file identical to the original? + final File persistXml = new File(solrHomeDirectory, "sysvars.solr.xml"); + // Side effect here is that the new file is persisted and available later. + origMatchesPersist(cc, persistXml); + + // Is everything in the original contained in the persisted one? + assertXmlFile(persistXml, getAllNodes(new File(solrHomeDirectory, "solr.xml"))); + + } finally { + cc.shutdown(); + if (solrHomeDirectory.exists()) { + FileUtils.deleteDirectory(solrHomeDirectory); + } + } + } + + @Test + public void testReload() throws Exception { + // Whether the core is transient or not can make a difference. + doReloadTest("SystemVars2"); + doReloadTest("SystemVars1"); + + } + + private void doReloadTest(String which) throws Exception { + + CoreContainer cc = init(SOLR_XML_LOTS_SYSVARS, "SystemVars1", "SystemVars2"); + try { + final CoreAdminHandler admin = new CoreAdminHandler(cc); + SolrQueryResponse resp = new SolrQueryResponse(); + admin.handleRequestBody + (req(CoreAdminParams.ACTION, + CoreAdminParams.CoreAdminAction.RELOAD.toString(), + CoreAdminParams.CORE, which), + resp); + assertNull("Exception on reload", resp.getException()); + + origMatchesPersist(cc, new File(solrHomeDirectory, "reload1.solr.xml")); + + } finally { + cc.shutdown(); + if (solrHomeDirectory.exists()) { + FileUtils.deleteDirectory(solrHomeDirectory); + } + } + + } + + @Test + public void testRename() throws Exception { + doTestRename("SystemVars1"); + doTestRename("SystemVars2"); + } + + private void doTestRename(String which) throws Exception { + CoreContainer cc = init(SOLR_XML_LOTS_SYSVARS, "SystemVars1", "SystemVars2"); + try { + final CoreAdminHandler admin = new CoreAdminHandler(cc); + SolrQueryResponse resp = new SolrQueryResponse(); + admin.handleRequestBody + (req(CoreAdminParams.ACTION, + CoreAdminParams.CoreAdminAction.RENAME.toString(), + CoreAdminParams.CORE, which, + CoreAdminParams.OTHER, "RenamedCore"), + resp); + assertNull("Exception on rename", resp.getException()); + + File persistXml = new File(solrHomeDirectory, "rename.solr.xml"); + File origXml = new File(solrHomeDirectory, "solr.xml"); + + // OK, Assure that if I change everything that has been renamed with the original value for the core, it matches + // the old list + cc.persistFile(persistXml); + String[] persistList = getAllNodes(persistXml); + String[] expressions = new String[persistList.length]; + + for (int idx = 0; idx < persistList.length; ++idx) { + expressions[idx] = persistList[idx].replaceAll("RenamedCore", which); + } + + assertXmlFile(origXml, expressions); + + // Now the other way, If I replace the original name in the original XML file with "RenamedCore", does it match + // what was persisted? + persistList = getAllNodes(origXml); + for (int idx = 0; idx < persistList.length; ++idx) { + // /solr/cores/core[@name='SystemVars1' and @collection='${collection:collection1}'] + expressions[idx] = persistList[idx].replace("@name='" + which + "'", "@name='RenamedCore'"); + } + + assertXmlFile(persistXml, expressions); + } finally { + cc.shutdown(); + if (solrHomeDirectory.exists()) { + FileUtils.deleteDirectory(solrHomeDirectory); + } + } + } + + @Test + public void testSwap() throws Exception { + doTestSwap("SystemVars1", "SystemVars2"); + doTestSwap("SystemVars2", "SystemVars1"); + } + + private void doTestSwap(String from, String to) throws Exception { + CoreContainer cc = init(SOLR_XML_LOTS_SYSVARS, "SystemVars1", "SystemVars2"); + try { + final CoreAdminHandler admin = new CoreAdminHandler(cc); + SolrQueryResponse resp = new SolrQueryResponse(); + admin.handleRequestBody + (req(CoreAdminParams.ACTION, + CoreAdminParams.CoreAdminAction.SWAP.toString(), + CoreAdminParams.CORE, from, + CoreAdminParams.OTHER, to), + resp); + assertNull("Exception on swap", resp.getException()); + + File persistXml = new File(solrHomeDirectory, "rename.solr.xml"); + File origXml = new File(solrHomeDirectory, "solr.xml"); + + cc.persistFile(persistXml); + String[] persistList = getAllNodes(persistXml); + String[] expressions = new String[persistList.length]; + + // Now manually change the names back and it should match exactly to the original XML. + for (int idx = 0; idx < persistList.length; ++idx) { + String fromName = "@name='" + from + "'"; + String toName = "@name='" + to + "'"; + if (persistList[idx].contains(fromName)) { + expressions[idx] = persistList[idx].replace(fromName, toName); + } else { + expressions[idx] = persistList[idx].replace(toName, fromName); + } + } + + assertXmlFile(origXml, expressions); + + } finally { + cc.shutdown(); + if (solrHomeDirectory.exists()) { + FileUtils.deleteDirectory(solrHomeDirectory); + } + } + } + + @Test + public void testUnloadCreate() throws Exception { + doTestUnloadCreate("SystemVars1"); + doTestUnloadCreate("SystemVars2"); + } + + private void doTestUnloadCreate(String which) throws Exception { + CoreContainer cc = init(SOLR_XML_LOTS_SYSVARS, "SystemVars1", "SystemVars2"); + try { + final CoreAdminHandler admin = new CoreAdminHandler(cc); + + SolrQueryResponse resp = new SolrQueryResponse(); + admin.handleRequestBody + (req(CoreAdminParams.ACTION, + CoreAdminParams.CoreAdminAction.UNLOAD.toString(), + CoreAdminParams.CORE, which), + resp); + assertNull("Exception on unload", resp.getException()); + + origMatchesPersist(cc, new File(solrHomeDirectory, "unloadcreate1.solr.xml")); + + String instPath = new File(solrHomeDirectory, which).getAbsolutePath(); + admin.handleRequestBody + (req(CoreAdminParams.ACTION, + CoreAdminParams.CoreAdminAction.CREATE.toString(), + CoreAdminParams.INSTANCE_DIR, instPath, + CoreAdminParams.CONFIG, "solrconfig-minimal.xml", + CoreAdminParams.SCHEMA, "schema-tiny.xml", + CoreAdminParams.NAME, which), + resp); + assertNull("Exception on create", resp.getException()); + + File persistXml = new File(solrHomeDirectory, "rename.solr.xml"); + File origXml = new File(solrHomeDirectory, "solr.xml"); + + cc.persistFile(persistXml); + String[] persistList = getAllNodes(persistXml); + String[] expressions = new String[persistList.length]; + + // Now manually change the names back and it should match exactly to the original XML. + for (int idx = 0; idx < persistList.length; ++idx) { + String name = "@name='" + which + "'"; + + if (persistList[idx].contains(name)) { + if (persistList[idx].contains("@schema='schema-tiny.xml'")) { + expressions[idx] = persistList[idx].replace("schema-tiny.xml", "${schema:schema-tiny.xml}"); + } else if (persistList[idx].contains("@config='solrconfig-minimal.xml'")) { + expressions[idx] = persistList[idx].replace("solrconfig-minimal.xml", "${solrconfig:solrconfig-minimal.xml}"); + } else if (persistList[idx].contains("@instanceDir=")) { + expressions[idx] = persistList[idx].replaceFirst("instanceDir\\='.*?'", "instanceDir='" + which + "'"); + } else { + expressions[idx] = persistList[idx]; + } + } else { + expressions[idx] = persistList[idx]; + } + } + + assertXmlFile(origXml, expressions); + + + } finally { + cc.shutdown(); + if (solrHomeDirectory.exists()) { + FileUtils.deleteDirectory(solrHomeDirectory); + } + } + } + + private void origMatchesPersist(CoreContainer cc, File persistXml) throws IOException, SAXException, ParserConfigurationException { + cc.persistFile(persistXml); + // Is everything that's in the original file persisted? + String[] expressions = getAllNodes(persistXml); + assertXmlFile(new File(solrHomeDirectory, "solr.xml"), expressions); + } + + @Test + public void testCreatePersistCore() throws Exception { + // Template for creating a core. + CoreContainer cc = init(SOLR_XML_LOTS_SYSVARS, "SystemVars1", "SystemVars2", "props1", "props2"); + try { + final CoreAdminHandler admin = new CoreAdminHandler(cc); + // create a new core (using CoreAdminHandler) w/ properties + String instPath1 = new File(solrHomeDirectory, "props1").getAbsolutePath(); + SolrQueryResponse resp = new SolrQueryResponse(); + admin.handleRequestBody + (req(CoreAdminParams.ACTION, + CoreAdminParams.CoreAdminAction.CREATE.toString(), + CoreAdminParams.INSTANCE_DIR, instPath1, + CoreAdminParams.NAME, "props1", + CoreAdminParams.TRANSIENT, "true", + CoreAdminParams.LOAD_ON_STARTUP, "true", + CoreAdminParams.PROPERTY_PREFIX + "prefix1", "valuep1", + CoreAdminParams.PROPERTY_PREFIX + "prefix2", "valueP2", + CoreAdminParams.CONFIG, "solrconfig-minimal.xml", + CoreAdminParams.SCHEMA, "schema-tiny.xml"), + resp); + assertNull("Exception on create", resp.getException()); + + String instPath2 = new File(solrHomeDirectory, "props2").getAbsolutePath(); + admin.handleRequestBody + (req(CoreAdminParams.ACTION, + CoreAdminParams.CoreAdminAction.CREATE.toString(), + CoreAdminParams.INSTANCE_DIR, instPath2, + CoreAdminParams.NAME, "props2", + CoreAdminParams.PROPERTY_PREFIX + "prefix2_1", "valuep2_1", + CoreAdminParams.PROPERTY_PREFIX + "prefix2_2", "valueP2_2", + CoreAdminParams.CONFIG, "solrconfig-minimal.xml", + CoreAdminParams.DATA_DIR, "./dataDirTest", + CoreAdminParams.SCHEMA, "schema-tiny.xml"), + resp); + assertNull("Exception on create", resp.getException()); + + // Everything that was in the original XML file should be in the persisted one. + final File persistXml = new File(solrHomeDirectory, "persist_create_core.solr.xml"); + cc.persistFile(persistXml); + String[] expressions = getAllNodes(new File(solrHomeDirectory, "solr.xml")); + assertXmlFile(persistXml, expressions); + + + // And the params for the new core should be in the persisted file. + assertXmlFile + (persistXml + , "/solr/cores/core[@name='props1']/property[@name='prefix1' and @value='valuep1']" + , "/solr/cores/core[@name='props1']/property[@name='prefix2' and @value='valueP2']" + , "/solr/cores/core[@name='props1' and @config='solrconfig-minimal.xml']" + , "/solr/cores/core[@name='props1' and @schema='schema-tiny.xml']" + , "/solr/cores/core[@name='props1' and @transient='true']" + , "/solr/cores/core[@name='props1' and @loadOnStartup='true']" + , "/solr/cores/core[@name='props2']/property[@name='prefix2_1' and @value='valuep2_1']" + , "/solr/cores/core[@name='props2']/property[@name='prefix2_2' and @value='valueP2_2']" + , "/solr/cores/core[@name='props2' and @config='solrconfig-minimal.xml']" + , "/solr/cores/core[@name='props2' and @schema='schema-tiny.xml']" + , "/solr/cores/core[@name='props2' and not(@loadOnStartup)]" + , "/solr/cores/core[@name='props2' and not(@transient)]" + , "/solr/cores/core[@name='props2' and @dataDir='./dataDirTest']" + ); + + } finally { + cc.shutdown(); + if (solrHomeDirectory.exists()) { + FileUtils.deleteDirectory(solrHomeDirectory); + } + + } + // / insure that after you create a core and persist solr.xml the created core has + // all expected and no extraneous values, both attribs and tags. + // How to create this core with sysprops? + } + + private String[] getAllNodes(File xmlFile) throws ParserConfigurationException, IOException, SAXException { + List expressions = new ArrayList(); // XPATH and value for all elements in the indicated XML + DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory + .newInstance(); + DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); + Document document = docBuilder.parse(xmlFile); + + Node root = document.getDocumentElement(); + gatherNodes(root, expressions, ""); + return expressions.toArray(new String[expressions.size()]); + } + + + // Note this is pretty specialized for a solr.xml file because working with the DOM is such a pain. + + private static List qualified = new ArrayList() {{ + add("core"); + add("property"); + add("int"); + add("str"); + add("long"); + add("property"); + }}; + + private static List addText = new ArrayList() {{ + add("int"); + add("str"); + add("long"); + }}; + + // path is the path to parent node + + private void gatherNodes(Node node, List expressions, String path) { + + String nodeName = node.getNodeName(); + String thisPath = path + "/" + nodeName; + //Parent[@id='1']/Children/child[@name] + // Add in the xpaths for verification of any attributes. + NamedNodeMap attrs = node.getAttributes(); + String qualifier = ""; + if (attrs.getLength() > 0) { + // Assemble the prefix for qualifying all of the attributes with the same name + if (qualified.contains(nodeName)) { + qualifier = "@name='" + node.getAttributes().getNamedItem("name").getTextContent() + "'"; + } + + for (int idx = 0; idx < attrs.getLength(); ++idx) { + + Node attr = attrs.item(idx); + if (StringUtils.isNotBlank(qualifier) && "name".equals(attr.getNodeName())) { + continue; // Already added "name" attribute in qualifier string. + } + if (StringUtils.isNotBlank(qualifier)) { + // Create [@name="stuff" and @attrib="value"] fragment + expressions.add(thisPath + + "[" + qualifier + " and @" + attr.getNodeName() + "='" + attr.getTextContent() + "']"); + + } else { + // Create [@attrib="value"] fragment + expressions.add(thisPath + + "[" + qualifier + " @" + attr.getNodeName() + "='" + attr.getTextContent() + "']"); + } + } + } + // Now add the text for special nodes + // a[normalize-space(text())='somesite'] + if (addText.contains(nodeName)) { + expressions.add(thisPath + "[" + qualifier + " and text()='" + node.getTextContent() + "']"); + } + // Now collect all the child element nodes. + NodeList nodeList = node.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + + Node currentNode = nodeList.item(i); + if (currentNode.getNodeType() == Node.ELEMENT_NODE) { + if (StringUtils.isNotBlank(qualifier)) { + gatherNodes(currentNode, expressions, thisPath + "[" + qualifier + "]"); + } else { + gatherNodes(currentNode, expressions, thisPath); + } + } + } + } + + private static String SOLR_XML_LOTS_SYSVARS = + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " ${socketTimeout:120000} \n" + + " ${connTimeout:15000} \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + +} diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestSolrProperties.java b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestSolrProperties.java index 71a0716370e..58645d4f872 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestSolrProperties.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestSolrProperties.java @@ -244,7 +244,7 @@ public class TestSolrProperties extends AbstractEmbeddedSolrServerTestCase { fis = new FileInputStream(new File(tempDir, SOLR_PERSIST_XML)); try { Document document = builder.parse(fis); - assertTrue(exists("/solr/cores/core[@name='collection1' and (@instanceDir='./' or @instanceDir='.\\')]", document)); + assertTrue(exists("/solr/cores/core[@name='collection1' and @instanceDir='.']", document)); } finally { fis.close(); }