diff --git a/solr/core/src/java/org/apache/solr/core/Config.java b/solr/core/src/java/org/apache/solr/core/Config.java index 76ad9d52300..795d78d46bb 100644 --- a/solr/core/src/java/org/apache/solr/core/Config.java +++ b/solr/core/src/java/org/apache/solr/core/Config.java @@ -71,7 +71,12 @@ public class Config { { this( loader, name, null, null ); } - + + + public Config(SolrResourceLoader loader, String name, InputSource is, String prefix) throws ParserConfigurationException, IOException, SAXException + { + this(loader, name, is, prefix, true); + } /** * Builds a config: *

@@ -91,7 +96,7 @@ public class Config { * @throws java.io.IOException * @throws org.xml.sax.SAXException */ - public Config(SolrResourceLoader loader, String name, InputSource is, String prefix) throws ParserConfigurationException, IOException, SAXException + public Config(SolrResourceLoader loader, String name, InputSource is, String prefix, boolean subProps) throws ParserConfigurationException, IOException, SAXException { if( loader == null ) { loader = new SolrResourceLoader( null ); @@ -126,8 +131,9 @@ public class Config { // some XML parsers are broken and don't close the byte stream (but they should according to spec) IOUtils.closeQuietly(is.getByteStream()); } - - DOMUtil.substituteProperties(doc, loader.getCoreProperties()); + if (subProps) { + DOMUtil.substituteProperties(doc, loader.getCoreProperties()); + } } catch (ParserConfigurationException e) { SolrException.log(log, "Exception during parsing file: " + name, e); throw e; @@ -139,6 +145,13 @@ public class Config { throw e; } } + + public Config(SolrResourceLoader loader, String name, Document doc) { + this.prefix = null; + this.doc = doc; + this.name = name; + this.loader = loader; + } /** * @since solr 1.3 @@ -170,6 +183,10 @@ public class Config { private String normalize(String path) { return (prefix==null || path.startsWith("/")) ? path : prefix+path; } + + public void substituteProperties() { + DOMUtil.substituteProperties(doc, loader.getCoreProperties()); + } public Object evaluate(String path, QName type) { 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 92351bba26c..2a12b03bf2b 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -17,28 +17,43 @@ package org.apache.solr.core; -import java.io.*; -import java.util.*; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeoutException; -import java.text.SimpleDateFormat; - -import org.apache.solr.handler.component.HttpShardHandlerFactory; -import org.apache.solr.handler.component.ShardHandlerFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.xml.parsers.ParserConfigurationException; -import javax.xml.xpath.XPathConstants; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; +import org.apache.commons.io.IOUtils; import org.apache.solr.cloud.CloudDescriptor; import org.apache.solr.cloud.CurrentCoreDescriptorProvider; import org.apache.solr.cloud.SolrZkServer; import org.apache.solr.cloud.ZkController; import org.apache.solr.cloud.ZkSolrResourceLoader; import org.apache.solr.common.SolrException; +import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.cloud.ZkStateReader; import org.apache.solr.common.cloud.ZooKeeperException; import org.apache.solr.common.params.CoreAdminParams; @@ -48,13 +63,17 @@ import org.apache.solr.common.util.SystemIdResolver; import org.apache.solr.core.SolrXMLSerializer.SolrCoreXMLDef; import org.apache.solr.core.SolrXMLSerializer.SolrXMLDef; import org.apache.solr.handler.admin.CoreAdminHandler; +import org.apache.solr.handler.component.HttpShardHandlerFactory; +import org.apache.solr.handler.component.ShardHandlerFactory; import org.apache.solr.schema.IndexSchema; import org.apache.zookeeper.KeeperException; -import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; import org.xml.sax.InputSource; +import org.xml.sax.SAXException; /** @@ -63,8 +82,12 @@ import org.xml.sax.InputSource; */ public class CoreContainer { + private static final String DEFAULT_HOST_CONTEXT = "solr"; + private static final String DEFAULT_HOST_PORT = "8983"; + private static final int DEFAULT_ZK_CLIENT_TIMEOUT = 10000; private static final String DEFAULT_DEFAULT_CORE_NAME = "collection1"; - + private static final boolean DEFAULT_SHARE_SCHEMA = false; + protected static Logger log = LoggerFactory.getLogger(CoreContainer.class); protected final Map cores = new LinkedHashMap(); @@ -92,6 +115,8 @@ public class CoreContainer private ShardHandlerFactory shardHandlerFactory; private String zkHost; + private Map coreToOrigName = new ConcurrentHashMap(); + { log.info("New CoreContainer " + System.identityHashCode(this)); @@ -301,7 +326,18 @@ public class CoreContainer throws ParserConfigurationException, IOException, SAXException { this.loader = new SolrResourceLoader(dir); solrHome = loader.getInstanceDir(); - Config cfg = new Config(loader, null, cfgis, null); + + Config cfg = new Config(loader, null, cfgis, null, false); + + // keep orig config for persist to consult + try { + this.cfg = new Config(loader, null, copyDoc(cfg.getDocument())); + } catch (TransformerException e) { + throw new SolrException(ErrorCode.SERVER_ERROR, "", e); + } + + cfg.substituteProperties(); + String dcoreName = cfg.get("solr/cores/@defaultCoreName", null); if(dcoreName != null) { defaultCoreName = dcoreName; @@ -310,12 +346,12 @@ public class CoreContainer libDir = cfg.get("solr/@sharedLib", null); zkHost = cfg.get("solr/@zkHost" , null); adminPath = cfg.get("solr/cores/@adminPath", null); - shareSchema = cfg.getBool("solr/cores/@shareSchema", false); - zkClientTimeout = cfg.getInt("solr/cores/@zkClientTimeout", 10000); + shareSchema = cfg.getBool("solr/cores/@shareSchema", DEFAULT_SHARE_SCHEMA); + zkClientTimeout = cfg.getInt("solr/cores/@zkClientTimeout", DEFAULT_ZK_CLIENT_TIMEOUT); - hostPort = cfg.get("solr/cores/@hostPort", "8983"); + hostPort = cfg.get("solr/cores/@hostPort", DEFAULT_HOST_PORT); - hostContext = cfg.get("solr/cores/@hostContext", "solr"); + hostContext = cfg.get("solr/cores/@hostContext", DEFAULT_HOST_CONTEXT); host = cfg.get("solr/cores/@host", null); if(shareSchema){ @@ -342,7 +378,7 @@ public class CoreContainer } try { - containerProperties = readProperties(cfg, ((NodeList) cfg.evaluate("solr", XPathConstants.NODESET)).item(0)); + containerProperties = readProperties(cfg, ((NodeList) cfg.evaluate(DEFAULT_HOST_CONTEXT, XPathConstants.NODESET)).item(0)); } catch (Throwable e) { SolrException.log(log,null,e); } @@ -352,16 +388,19 @@ public class CoreContainer for (int i=0; i Map rootSolrAttribs = new HashMap(); if (libDir != null) rootSolrAttribs.put("sharedLib", libDir); @@ -895,15 +970,21 @@ public class CoreContainer // Map coresAttribs = new HashMap(); - coresAttribs.put("adminPath", adminPath); - if (adminHandler != null) coresAttribs.put("adminHandler", adminHandler); - if (shareSchema) coresAttribs.put("shareSchema", "true"); + addCoresAttrib(coresAttribs, "adminPath", this.adminPath, null); + addCoresAttrib(coresAttribs, "adminHandler", this.adminHandler, null); + addCoresAttrib(coresAttribs, "shareSchema", + Boolean.toString(this.shareSchema), + Boolean.toString(DEFAULT_SHARE_SCHEMA)); + addCoresAttrib(coresAttribs, "host", this.host, null); + if (!defaultCoreName.equals("")) coresAttribs.put("defaultCoreName", defaultCoreName); - if (host != null) coresAttribs.put("host", host); - if (hostPort != null) coresAttribs.put("hostPort", hostPort); - if (zkClientTimeout != null) coresAttribs.put("zkClientTimeout", Integer.toString(zkClientTimeout)); - if (hostContext != null) coresAttribs.put("hostContext", hostContext); + + addCoresAttrib(coresAttribs, "hostPort", this.hostPort, DEFAULT_HOST_PORT); + addCoresAttrib(coresAttribs, "zkClientTimeout", + intToString(this.zkClientTimeout), + Integer.toString(DEFAULT_ZK_CLIENT_TIMEOUT)); + addCoresAttrib(coresAttribs, "hostContext", this.hostContext, DEFAULT_HOST_CONTEXT); List solrCoreXMLDefs = new ArrayList(); @@ -911,37 +992,92 @@ public class CoreContainer for (SolrCore solrCore : cores.values()) { Map coreAttribs = new HashMap(); CoreDescriptor dcore = solrCore.getCoreDescriptor(); + + String coreName = dcore.name.equals("") ? defaultCoreName + : dcore.name; - coreAttribs.put("name", dcore.name.equals("") ? defaultCoreName - : dcore.name); - coreAttribs.put("instanceDir", dcore.getInstanceDir()); - // write config (if not default) - String opt = dcore.getConfigName(); - if (opt != null && !opt.equals(dcore.getDefaultConfigName())) { - coreAttribs.put("config", opt); + Node coreNode = null; + + if (cfg != null) { + NodeList nodes = (NodeList) cfg.evaluate("solr/cores/core", + XPathConstants.NODESET); + + String origCoreName = coreToOrigName.get(solrCore); + + if (origCoreName == null) { + origCoreName = coreName; + } + + // look for an existing node + + // first look for an exact match + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + + String name = DOMUtil.getAttr(node, "name", null); + if (origCoreName.equals(name)) { + coreNode = node; + if (coreName.equals(origCoreName)) { + coreName = name; + } + break; + } + } + + if (coreNode == null) { + // see if we match with substitution + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + String name = DOMUtil.getAttr(node, "name", null); + if (origCoreName.equals(DOMUtil.substituteProperty(name, + loader.getCoreProperties()))) { + coreNode = node; + if (coreName.equals(origCoreName)) { + coreName = name; + } + break; + } + } + } } - // write schema (if not default) - opt = dcore.getSchemaName(); - if (opt != null && !opt.equals(dcore.getDefaultSchemaName())) { - coreAttribs.put("schema", opt); - } - opt = dcore.getPropertiesName(); - if (opt != null) { - coreAttribs.put("properties", opt); - } - opt = dcore.dataDir; - if (opt != null) coreAttribs.put("dataDir", opt); + + coreAttribs.put("name", coreName); + + String instanceDir = dcore.getInstanceDir(); + addCoreProperty(coreAttribs, coreNode, "instanceDir", instanceDir, null); + + // write config + String configName = dcore.getConfigName(); + addCoreProperty(coreAttribs, coreNode, "conf", configName, dcore.getDefaultConfigName()); + + // write schema + String schema = dcore.getSchemaName(); + addCoreProperty(coreAttribs, coreNode, "schema", schema, dcore.getDefaultSchemaName()); + + String dataDir = dcore.dataDir; + addCoreProperty(coreAttribs, coreNode, "dataDir", dataDir, null); CloudDescriptor cd = dcore.getCloudDescriptor(); + String shard = null; if (cd != null) { - opt = cd.getShardId(); - if (opt != null) coreAttribs.put("shard", opt); - // only write out the collection name if it's not the default (the - // core - // name) - opt = cd.getCollectionName(); - if (opt != null && !opt.equals(dcore.name)) coreAttribs.put( - "collection", opt); + shard = cd.getShardId(); + } + addCoreProperty(coreAttribs, coreNode, "shard", shard, null); + + String collection = null; + // only write out the collection name if it's not the default (the + // core + // name) + if (cd != null) { + collection = cd.getCollectionName(); + } + + addCoreProperty(coreAttribs, coreNode, "collection", collection, dcore.name); + + // we don't try and preserve sys prop defs in these + String opt = dcore.getPropertiesName(); + if (opt != null) { + coreAttribs.put("properties", opt); } SolrCoreXMLDef solrCoreXMLDef = new SolrCoreXMLDef(); @@ -960,6 +1096,52 @@ public class CoreContainer } } + private String intToString(Integer integer) { + if (integer == null) return null; + return Integer.toString(integer); + } + + private void addCoresAttrib(Map coresAttribs, String attribName, String attribValue, String defaultValue) { + if (cfg == null) { + coresAttribs.put(attribName, attribValue); + return; + } + + if (attribValue != null) { + String rawValue = cfg.get("solr/cores/@" + attribName, null); + if (rawValue == null && defaultValue != null && attribValue.equals(defaultValue)) return; + if (attribValue.equals(DOMUtil.substituteProperty(rawValue, loader.getCoreProperties()))) { + coresAttribs.put(attribName, rawValue); + } else { + coresAttribs.put(attribName, attribValue); + } + } + } + + private void addCoreProperty(Map coreAttribs, Node node, String name, + String value, String defaultValue) { + if (node == null) { + coreAttribs.put(name, value); + return; + } + + String attribValue = null; + if (node != null) { + String rawAttribValue = DOMUtil.getAttr(node, name, null); + if (value == null) { + coreAttribs.put(name, rawAttribValue); + return; + } + if (rawAttribValue == null && defaultValue != null && value.equals(defaultValue)) return; + if (rawAttribValue != null && value.equals(DOMUtil.substituteProperty(rawAttribValue, loader.getCoreProperties()))){ + attribValue = rawAttribValue; + } + } + if (attribValue != null) { + coreAttribs.put(name, attribValue); + } + } + public String getSolrHome() { return solrHome; 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 4748ce727e5..e09bcaa67e4 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 @@ -210,12 +210,13 @@ public class TestSolrProperties extends LuceneTestCase { fis.close(); fis = new FileInputStream(new File(solrXml.getParent(), "solr-persist.xml")); String solrPersistXml = IOUtils.toString(fis); + //System.out.println("xml:" + solrPersistXml); assertTrue("\"/solr/cores[@defaultCoreName='core0']\" doesn't match in:\n" + solrPersistXml, exists("/solr/cores[@defaultCoreName='core0']", document)); assertTrue("\"/solr/cores[@host='127.0.0.1']\" doesn't match in:\n" + solrPersistXml, exists("/solr/cores[@host='127.0.0.1']", document)); - assertTrue("\"/solr/cores[@hostPort='8983']\" doesn't match in:\n" + solrPersistXml, - exists("/solr/cores[@hostPort='8983']", document)); + assertTrue("\"/solr/cores[@hostPort='${hostPort:8983}']\" doesn't match in:\n" + solrPersistXml, + exists("/solr/cores[@hostPort='${hostPort:8983}']", document)); assertTrue("\"/solr/cores[@zkClientTimeout='8000']\" doesn't match in:\n" + solrPersistXml, exists("/solr/cores[@zkClientTimeout='8000']", document)); assertTrue("\"/solr/cores[@hostContext='solr']\" doesn't match in:\n" + solrPersistXml, @@ -228,10 +229,18 @@ public class TestSolrProperties extends LuceneTestCase { CoreAdminRequest.renameCore(name, "renamed_core", coreadmin); mcr = CoreAdminRequest.persist("solr-persist.xml", getRenamedSolrAdmin()); +// fis = new FileInputStream(new File(solrXml.getParent(), "solr-persist.xml")); +// String solrPersistXml = IOUtils.toString(fis); +// System.out.println("xml:" + solrPersistXml); +// fis.close(); + fis = new FileInputStream(new File(solrXml.getParent(), "solr-persist.xml")); try { Document document = builder.parse(fis); assertTrue(exists("/solr/cores/core[@name='renamed_core']", document)); + assertTrue(exists("/solr/cores/core[@instanceDir='${theInstanceDir:./}']", document)); + assertTrue(exists("/solr/cores/core[@collection='${collection:acollection}']", document)); + } finally { fis.close(); }