lucene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sh...@apache.org
Subject svn commit: r1454862 [35/49] - in /lucene/dev/branches/lucene4258: ./ dev-tools/ dev-tools/eclipse/ dev-tools/idea/.idea/ dev-tools/idea/.idea/libraries/ dev-tools/idea/lucene/demo/ dev-tools/idea/lucene/facet/ dev-tools/idea/lucene/highlighter/ dev-to...
Date Sun, 10 Mar 2013 15:05:21 GMT
Modified: lucene/dev/branches/lucene4258/solr/core/src/java/org/apache/solr/core/CoreContainer.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4258/solr/core/src/java/org/apache/solr/core/CoreContainer.java?rev=1454862&r1=1454861&r2=1454862&view=diff
==============================================================================
--- lucene/dev/branches/lucene4258/solr/core/src/java/org/apache/solr/core/CoreContainer.java (original)
+++ lucene/dev/branches/lucene4258/solr/core/src/java/org/apache/solr/core/CoreContainer.java Sun Mar 10 15:04:57 2013
@@ -21,9 +21,11 @@ import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -35,6 +37,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CompletionService;
 import java.util.concurrent.ConcurrentHashMap;
@@ -46,16 +49,8 @@ import java.util.concurrent.ThreadPoolEx
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-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.commons.lang.StringUtils;
 import org.apache.solr.cloud.CloudDescriptor;
 import org.apache.solr.cloud.CurrentCoreDescriptorProvider;
 import org.apache.solr.cloud.SolrZkServer;
@@ -66,8 +61,7 @@ import org.apache.solr.common.SolrExcept
 import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.cloud.ZooKeeperException;
 import org.apache.solr.common.util.ExecutorUtil;
-import org.apache.solr.core.SolrXMLSerializer.SolrCoreXMLDef;
-import org.apache.solr.core.SolrXMLSerializer.SolrXMLDef;
+
 import org.apache.solr.handler.admin.CollectionsHandler;
 import org.apache.solr.handler.admin.CoreAdminHandler;
 import org.apache.solr.handler.component.HttpShardHandlerFactory;
@@ -77,26 +71,20 @@ import org.apache.solr.logging.LogWatche
 import org.apache.solr.logging.jul.JulWatcher;
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.update.SolrCoreState;
-import org.apache.solr.util.AdjustableSemaphore;
-import org.apache.solr.util.DOMUtil;
 import org.apache.solr.util.DefaultSolrThreadFactory;
 import org.apache.solr.util.FileUtils;
-import org.apache.solr.util.SystemIdResolver;
+import org.apache.solr.util.PropertiesUtil;
 import org.apache.zookeeper.KeeperException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.impl.StaticLoggerBinder;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
 
 
 /**
  *
  * @since solr 1.3
  */
-public class CoreContainer 
+public class CoreContainer
 {
   private static final String LEADER_VOTE_WAIT = "180000";  // 3 minutes
   private static final int CORE_LOAD_THREADS = 3;
@@ -105,30 +93,13 @@ public class CoreContainer 
   private static final int DEFAULT_ZK_CLIENT_TIMEOUT = 15000;
   public static final String DEFAULT_DEFAULT_CORE_NAME = "collection1";
   private static final boolean DEFAULT_SHARE_SCHEMA = false;
-  
-  protected static Logger log = LoggerFactory.getLogger(CoreContainer.class);
-  
-  // solr.xml node constants
-  private static final String CORE_NAME = "name";
-  private static final String CORE_CONFIG = "config";
-  private static final String CORE_INSTDIR = "instanceDir";
-  private static final String CORE_DATADIR = "dataDir";
-  private static final String CORE_SCHEMA = "schema";
-  private static final String CORE_SHARD = "shard";
-  private static final String CORE_COLLECTION = "collection";
-  private static final String CORE_ROLES = "roles";
-  private static final String CORE_PROPERTIES = "properties";
-  private static final String CORE_LOADONSTARTUP = "loadOnStartup";
-  private static final String CORE_SWAPPABLE = "swappable";
-
 
-  protected final Map<String, SolrCore> cores = new LinkedHashMap<String, SolrCore>(); // For "permanent" cores
+  protected static Logger log = LoggerFactory.getLogger(CoreContainer.class);
 
-  protected Map<String, SolrCore> swappableCores = new LinkedHashMap<String, SolrCore>(); // For "lazily loaded" cores
 
-  protected final Map<String, CoreDescriptor> dynamicDescriptors = new LinkedHashMap<String, CoreDescriptor>();
+  private final CoreMaps coreMaps = new CoreMaps(this);
 
-  protected final Map<String,Exception> coreInitFailures = 
+  protected final Map<String,Exception> coreInitFailures =
     Collections.synchronizedMap(new LinkedHashMap<String,Exception>());
   
   protected boolean persistent = false;
@@ -150,18 +121,19 @@ public class CoreContainer 
   protected Integer zkClientTimeout;
   protected String solrHome;
   protected String defaultCoreName = null;
-  private SolrXMLSerializer solrXMLSerializer = new SolrXMLSerializer();
+
   private ZkController zkController;
   private SolrZkServer zkServer;
   private ShardHandlerFactory shardHandlerFactory;
   protected LogWatcher logging = null;
   private String zkHost;
-  private Map<SolrCore,String> coreToOrigName = new ConcurrentHashMap<SolrCore,String>();
+
   private String leaderVoteWait = LEADER_VOTE_WAIT;
   private int distribUpdateConnTimeout = 0;
   private int distribUpdateSoTimeout = 0;
-  protected int swappableCacheSize = Integer.MAX_VALUE; // Use as a flag too, if swappableCacheSize set in solr.xml this will be changed
+  protected int transientCacheSize = Integer.MAX_VALUE; // Use as a flag too, if transientCacheSize set in solr.xml this will be changed
   private int coreLoadThreads;
+  private CloserThread backgroundCloser = null;
   
   {
     log.info("New CoreContainer " + System.identityHashCode(this));
@@ -180,8 +152,7 @@ public class CoreContainer 
   /**
    * Initalize CoreContainer directly from the constructor
    */
-  public CoreContainer(String dir, File configFile)
-  {
+  public CoreContainer(String dir, File configFile) throws FileNotFoundException {
     this(dir);
     this.load(dir, configFile);
   }
@@ -250,12 +221,11 @@ public class CoreContainer 
           throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR,
               "A chroot was specified in ZkHost but the znode doesn't exist. ");
         }
-        
         zkController = new ZkController(this, zookeeperHost, zkClientTimeout,
             zkClientConnectTimeout, host, hostPort, hostContext,
             leaderVoteWait, distribUpdateConnTimeout, distribUpdateSoTimeout,
             new CurrentCoreDescriptorProvider() {
-              
+
               @Override
               public List<CoreDescriptor> getCurrentDescriptors() {
                 List<CoreDescriptor> descriptors = new ArrayList<CoreDescriptor>(
@@ -266,8 +236,8 @@ public class CoreContainer 
                 return descriptors;
               }
             });
-        
-        
+
+
         if (zkRun != null && zkServer.getServers().size() > 1 && confDir == null && boostrapConf == false) {
           // we are part of an ensemble and we are not uploading the config - pause to give the config time
           // to get up
@@ -318,24 +288,33 @@ public class CoreContainer 
 
   // Helper class to initialize the CoreContainer
   public static class Initializer {
-    protected String containerConfigFilename = null;  // normally "solr.xml"
+    protected String containerConfigFilename = null;  // normally "solr.xml" becoming solr.properties in 5.0
     protected String dataDir = null; // override datadir for single core mode
 
     // core container instantiation
-    public CoreContainer initialize() {
+    public CoreContainer initialize() throws FileNotFoundException {
       CoreContainer cores = null;
       String solrHome = SolrResourceLoader.locateSolrHome();
+      // ContainerConfigFilename could  could be a properties file
       File fconf = new File(solrHome, containerConfigFilename == null ? "solr.xml"
           : containerConfigFilename);
-      log.info("looking for solr.xml: " + fconf.getAbsolutePath());
+
+      log.info("looking for solr config file: " + fconf.getAbsolutePath());
       cores = new CoreContainer(solrHome);
+
+      if (! fconf.exists()) {
+        if (StringUtils.isBlank(containerConfigFilename) || containerConfigFilename.endsWith(".xml")) {
+          fconf = new File(solrHome, SolrProperties.SOLR_PROPERTIES_FILE);
+        }
+      }
+      // Either we have a config file or not. If it ends in .properties, assume new-style.
       
       if (fconf.exists()) {
         cores.load(solrHome, fconf);
       } else {
-        log.info("no solr.xml file found - using default");
+        log.info("no solr.xml or solr.properties file found - using default old-style solr.xml");
         try {
-          cores.load(solrHome, new InputSource(new ByteArrayInputStream(DEF_SOLR_XML.getBytes("UTF-8"))));
+          cores.load(solrHome, new ByteArrayInputStream(ConfigSolrXmlBackCompat.DEF_SOLR_XML.getBytes("UTF-8")), true, null);
         } catch (Exception e) {
           throw new SolrException(ErrorCode.SERVER_ERROR,
               "CoreContainer.Initialize failed when trying to load default solr.xml file", e);
@@ -349,44 +328,6 @@ public class CoreContainer 
     }
   }
 
-  static Properties getCoreProps(String instanceDir, String file, Properties defaults) {
-    if(file == null) file = "conf"+File.separator+ "solrcore.properties";
-    File corePropsFile = new File(file);
-    if(!corePropsFile.isAbsolute()){
-      corePropsFile = new File(instanceDir, file);
-    }
-    Properties p = defaults;
-    if (corePropsFile.exists() && corePropsFile.isFile()) {
-      p = new Properties(defaults);
-      InputStream is = null;
-      try {
-        is = new FileInputStream(corePropsFile);
-        p.load(is);
-      } catch (IOException e) {
-        log.warn("Error loading properties ",e);
-      } finally{
-        IOUtils.closeQuietly(is);        
-      }
-    }
-    return p;
-  }
-
-  // Trivial helper method for load, note it implements LRU on swappable cores
-  private void allocateLazyCores(Config cfg) {
-    swappableCacheSize = cfg.getInt("solr/cores/@swappableCacheSize", Integer.MAX_VALUE);
-    if (swappableCacheSize != Integer.MAX_VALUE) {
-      swappableCores = new LinkedHashMap<String, SolrCore>(swappableCacheSize, 0.75f, true) {
-        @Override
-        protected boolean removeEldestEntry(Map.Entry<String, SolrCore> eldest) {
-          if (size() > swappableCacheSize) {
-            eldest.getValue().close();
-            return true;
-          }
-          return false;
-        }
-      };
-    }
-  }
 
   //-------------------------------------------------------------------
   // Initialization / Cleanup
@@ -397,18 +338,25 @@ public class CoreContainer 
    * @param dir the home directory of all resources.
    * @param configFile the configuration file
    */
-  public void load(String dir, File configFile ) {
+  public void load(String dir, File configFile) throws FileNotFoundException {
     this.configFile = configFile;
-    this.load(dir, new InputSource(configFile.toURI().toASCIIString()));
+    InputStream in = new FileInputStream(configFile);
+    try {
+      this.load(dir, in, configFile.getName().endsWith(".xml"),  configFile.getName());
+    } finally {
+      IOUtils.closeQuietly(in);
+    }
   } 
 
   /**
    * Load a config file listing the available solr cores.
    * 
    * @param dir the home directory of all resources.
-   * @param cfgis the configuration file InputStream
+   * @param is the configuration file InputStream. May be a properties file or an xml file
    */
-  public void load(String dir, InputSource cfgis)  {
+
+  // Let's keep this ugly boolean out of public circulation.
+  protected void load(String dir, InputStream is, boolean isXmlFile, String fileName)  {
     ThreadPoolExecutor coreLoadExecutor = null;
     if (null == dir) {
       // don't rely on SolrResourceLoader(), determine explicitly first
@@ -418,28 +366,35 @@ public class CoreContainer 
     
     this.loader = new SolrResourceLoader(dir);
     solrHome = loader.getInstanceDir();
-    
-    Config cfg;
+
+    ConfigSolr cfg;
     
     // keep orig config for persist to consult
+    //TODO 5.0: Remove this confusing junk, the properties file is so fast to read that there's no good reason
+    //          to add this stuff. Furthermore, it would be good to persist comments when saving.....
     try {
-      cfg = new Config(loader, null, cfgis, null, false);
-      this.cfg = new Config(loader, null, copyDoc(cfg.getDocument()));
+      if (isXmlFile) {
+        cfg = new ConfigSolrXmlBackCompat(loader, null, is, null, false);
+        this.cfg = new ConfigSolrXmlBackCompat(loader, (ConfigSolrXmlBackCompat)cfg);
+      } else {
+        cfg = new SolrProperties(this, is, fileName);
+        this.cfg = new SolrProperties(this, loader, (SolrProperties)cfg);
+      }
     } catch (Exception e) {
       throw new SolrException(ErrorCode.SERVER_ERROR, "", e);
     }
     // Since the cores var is now initialized to null, let's set it up right
     // now.
     cfg.substituteProperties();
-    
-    initShardHandler(cfg);
-    
-    allocateLazyCores(cfg);
-    
+
+    shardHandlerFactory = cfg.initShardHandler();
+
+    coreMaps.allocateLazyCores(cfg, loader);
+
     // Initialize Logging
-    if (cfg.getBool("solr/logging/@enabled", true)) {
+    if (cfg.getBool(ConfigSolr.ConfLevel.SOLR_LOGGING, "enabled", true)) {
       String slf4jImpl = null;
-      String fname = cfg.get("solr/logging/watcher/@class", null);
+      String fname = cfg.get(ConfigSolr.ConfLevel.SOLR_LOGGING, "class", null);
       try {
         slf4jImpl = StaticLoggerBinder.getSingleton()
             .getLoggerFactoryClassStr();
@@ -472,8 +427,8 @@ public class CoreContainer 
         
         if (logging != null) {
           ListenerConfig v = new ListenerConfig();
-          v.size = cfg.getInt("solr/logging/watcher/@size", 50);
-          v.threshold = cfg.get("solr/logging/watcher/@threshold", null);
+          v.size = cfg.getInt(ConfigSolr.ConfLevel.SOLR_LOGGING_WATCHER, "size", 50);
+          v.threshold = cfg.get(ConfigSolr.ConfLevel.SOLR_LOGGING_WATCHER, "threshold", null);
           if (v.size > 0) {
             log.info("Registering Log Listener");
             logging.registerListener(v, this);
@@ -482,35 +437,34 @@ public class CoreContainer 
       }
     }
     
-    String dcoreName = cfg.get("solr/cores/@defaultCoreName", null);
+    String dcoreName = cfg.get(ConfigSolr.ConfLevel.SOLR_CORES, "defaultCoreName", null);
     if (dcoreName != null && !dcoreName.isEmpty()) {
       defaultCoreName = dcoreName;
     }
-    persistent = cfg.getBool("solr/@persistent", false);
-    libDir = cfg.get("solr/@sharedLib", null);
-    zkHost = cfg.get("solr/@zkHost", null);
-    coreLoadThreads = cfg.getInt("solr/@coreLoadThreads", CORE_LOAD_THREADS);
-    
-    adminPath = cfg.get("solr/cores/@adminPath", null);
-    shareSchema = cfg.getBool("solr/cores/@shareSchema", DEFAULT_SHARE_SCHEMA);
-    zkClientTimeout = cfg.getInt("solr/cores/@zkClientTimeout",
-        DEFAULT_ZK_CLIENT_TIMEOUT);
+    persistent = cfg.getBool(ConfigSolr.ConfLevel.SOLR, "persistent", false);
+    libDir = cfg.get(ConfigSolr.ConfLevel.SOLR, "sharedLib", null);
+    zkHost = cfg.get(ConfigSolr.ConfLevel.SOLR, "zkHost", null);
+    coreLoadThreads = cfg.getInt(ConfigSolr.ConfLevel.SOLR, "coreLoadThreads", CORE_LOAD_THREADS);
     
-    distribUpdateConnTimeout = cfg.getInt("solr/cores/@distribUpdateConnTimeout", 0);
-    distribUpdateSoTimeout = cfg.getInt("solr/cores/@distribUpdateSoTimeout", 0);
+    adminPath = cfg.get(ConfigSolr.ConfLevel.SOLR_CORES, "adminPath", null);
+    shareSchema = cfg.getBool(ConfigSolr.ConfLevel.SOLR_CORES, "shareSchema", DEFAULT_SHARE_SCHEMA);
+    zkClientTimeout = cfg.getInt(ConfigSolr.ConfLevel.SOLR_CORES, "zkClientTimeout", DEFAULT_ZK_CLIENT_TIMEOUT);
     
-    hostPort = cfg.get("solr/cores/@hostPort", DEFAULT_HOST_PORT);
+    distribUpdateConnTimeout = cfg.getInt(ConfigSolr.ConfLevel.SOLR_CORES, "distribUpdateConnTimeout", 0);
+    distribUpdateSoTimeout = cfg.getInt(ConfigSolr.ConfLevel.SOLR_CORES, "distribUpdateSoTimeout", 0);
     
-    hostContext = cfg.get("solr/cores/@hostContext", DEFAULT_HOST_CONTEXT);
-    host = cfg.get("solr/cores/@host", null);
+    hostPort = cfg.get(ConfigSolr.ConfLevel.SOLR_CORES, "hostPort", DEFAULT_HOST_PORT);
+
+    hostContext = cfg.get(ConfigSolr.ConfLevel.SOLR_CORES, "hostContext", DEFAULT_HOST_CONTEXT);
+    host = cfg.get(ConfigSolr.ConfLevel.SOLR_CORES, "host", null);
     
-    leaderVoteWait = cfg.get("solr/cores/@leaderVoteWait", LEADER_VOTE_WAIT);
+    leaderVoteWait = cfg.get(ConfigSolr.ConfLevel.SOLR_CORES, "leaderVoteWait", LEADER_VOTE_WAIT);
     
     if (shareSchema) {
       indexSchemaCache = new ConcurrentHashMap<String,IndexSchema>();
     }
-    adminHandler = cfg.get("solr/cores/@adminHandler", null);
-    managementPath = cfg.get("solr/cores/@managementPath", null);
+    adminHandler = cfg.get(ConfigSolr.ConfLevel.SOLR_CORES, "adminHandler", null);
+    managementPath = cfg.get(ConfigSolr.ConfLevel.SOLR_CORES, "managementPath", null);
     
     zkClientTimeout = Integer.parseInt(System.getProperty("zkClientTimeout",
         Integer.toString(zkClientTimeout)));
@@ -536,17 +490,8 @@ public class CoreContainer 
     }
     
     collectionsHandler = new CollectionsHandler(this);
-    
-    try {
-      containerProperties = readProperties(cfg, ((NodeList) cfg.evaluate(
-          DEFAULT_HOST_CONTEXT, XPathConstants.NODESET)).item(0));
-    } catch (Throwable e) {
-      SolrException.log(log, null, e);
-    }
-    
-    NodeList nodes = (NodeList) cfg.evaluate("solr/cores/core",
-        XPathConstants.NODESET);
-    
+    containerProperties = cfg.getSolrProperties(cfg, DEFAULT_HOST_CONTEXT);
+
     // setup executor to load cores in parallel
     coreLoadExecutor = new ThreadPoolExecutor(coreLoadThreads, coreLoadThreads, 1,
         TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
@@ -556,64 +501,72 @@ public class CoreContainer 
       CompletionService<SolrCore> completionService = new ExecutorCompletionService<SolrCore>(
           coreLoadExecutor);
       Set<Future<SolrCore>> pending = new HashSet<Future<SolrCore>>();
-      
-      for (int i = 0; i < nodes.getLength(); i++) {
-        Node node = nodes.item(i);
+
+      List<String> allCores = cfg.getAllCoreNames();
+
+      for (String oneCoreName : allCores) {
+
         try {
-          String rawName = DOMUtil.getAttr(node, CORE_NAME, null);
+          String rawName = cfg.getProperty(oneCoreName, CoreDescriptor.CORE_NAME, null);
+
           if (null == rawName) {
             throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
                 "Each core in solr.xml must have a 'name'");
           }
           final String name = rawName;
           final CoreDescriptor p = new CoreDescriptor(this, name,
-              DOMUtil.getAttr(node, CORE_INSTDIR, null));
+              cfg.getProperty(oneCoreName, CoreDescriptor.CORE_INSTDIR, null));
           
           // deal with optional settings
-          String opt = DOMUtil.getAttr(node, CORE_CONFIG, null);
+          String opt = cfg.getProperty(oneCoreName, CoreDescriptor.CORE_CONFIG, null);
           
           if (opt != null) {
             p.setConfigName(opt);
           }
-          opt = DOMUtil.getAttr(node, CORE_SCHEMA, null);
+          opt = cfg.getProperty(oneCoreName, CoreDescriptor.CORE_SCHEMA, null);
           if (opt != null) {
             p.setSchemaName(opt);
           }
           
           if (zkController != null) {
-            opt = DOMUtil.getAttr(node, CORE_SHARD, null);
+            opt = cfg.getProperty(oneCoreName, CoreDescriptor.CORE_SHARD, null);
             if (opt != null && opt.length() > 0) {
               p.getCloudDescriptor().setShardId(opt);
             }
-            opt = DOMUtil.getAttr(node, CORE_COLLECTION, null);
+            opt = cfg.getProperty(oneCoreName, CoreDescriptor.CORE_COLLECTION, null);
             if (opt != null) {
               p.getCloudDescriptor().setCollectionName(opt);
             }
-            opt = DOMUtil.getAttr(node, CORE_ROLES, null);
+            opt = cfg.getProperty(oneCoreName, CoreDescriptor.CORE_ROLES, null);
             if (opt != null) {
               p.getCloudDescriptor().setRoles(opt);
             }
+
+            opt = cfg.getProperty(oneCoreName, CoreDescriptor.CORE_NODE_NAME, null);
+            if (opt != null && opt.length() > 0) {
+              p.getCloudDescriptor().setCoreNodeName(opt);
+            }
           }
-          opt = DOMUtil.getAttr(node, CORE_PROPERTIES, null);
+          opt = cfg.getProperty(oneCoreName, CoreDescriptor.CORE_PROPERTIES, null);
           if (opt != null) {
             p.setPropertiesName(opt);
           }
-          opt = DOMUtil.getAttr(node, CORE_DATADIR, null);
+          opt = cfg.getProperty(oneCoreName, CoreDescriptor.CORE_DATADIR, null);
           if (opt != null) {
             p.setDataDir(opt);
           }
           
-          p.setCoreProperties(readProperties(cfg, node));
+          p.setCoreProperties(cfg.readCoreProperties(oneCoreName));
           
-          opt = DOMUtil.getAttr(node, CORE_LOADONSTARTUP, null);
+          opt = cfg.getProperty(oneCoreName, CoreDescriptor.CORE_LOADONSTARTUP, null);
           if (opt != null) {
             p.setLoadOnStartup(("true".equalsIgnoreCase(opt) || "on"
                 .equalsIgnoreCase(opt)) ? true : false);
           }
           
-          opt = DOMUtil.getAttr(node, CORE_SWAPPABLE, null);
+          opt = cfg.getProperty(oneCoreName, CoreDescriptor.CORE_TRANSIENT, null);
           if (opt != null) {
-            p.setSwappable(("true".equalsIgnoreCase(opt) || "on"
+            p.setTransient(("true".equalsIgnoreCase(opt) || "on"
                 .equalsIgnoreCase(opt)) ? true : false);
           }
           
@@ -625,12 +578,7 @@ public class CoreContainer 
                 SolrCore c = null;
                 try {
                   c = create(p);
-                  if (p.isSwappable()) {
-                    registerLazyCore(name, c, false);
-                  } else {
-                    register(name, c, false);
-                  }
-
+                  registerCore(p.isTransient(), name, c, false);
                 } catch (Throwable t) {
                   SolrException.log(log, null, t);
                   if (c != null) {
@@ -641,14 +589,12 @@ public class CoreContainer 
               }
             };
 
-
             pending.add(completionService.submit(task));
 
-            
           } else {
-            // Store it away for later use. includes non-swappable but not
+            // Store it away for later use. includes non-transient but not
             // loaded at startup cores.
-            dynamicDescriptors.put(rawName, p);
+            coreMaps.putDynamicDescriptor(rawName, p);
           }
         } catch (Throwable ex) {
           SolrException.log(log, null, ex);
@@ -657,6 +603,7 @@ public class CoreContainer 
       
       while (pending != null && pending.size() > 0) {
         try {
+
           Future<SolrCore> future = completionService.take();
           if (future == null) return;
           pending.remove(future);
@@ -665,10 +612,10 @@ public class CoreContainer 
             SolrCore c = future.get();
             // track original names
             if (c != null) {
-              coreToOrigName.put(c, c.getName());
+              coreMaps.putCoreToOrigName(c, c.getName());
             }
           } catch (ExecutionException e) {
-            SolrException.log(SolrCore.log, "error loading core", e);
+            SolrException.log(SolrCore.log, "Error loading core", e);
           }
           
         } catch (InterruptedException e) {
@@ -676,57 +623,32 @@ public class CoreContainer 
               "interrupted while loading core", e);
         }
       }
+
+      // Start the background thread
+      backgroundCloser = new CloserThread(this, coreMaps, cfg);
+      backgroundCloser.start();
+
     } finally {
       if (coreLoadExecutor != null) {
-        ExecutorUtil.shutdownAndAwaitTermination(coreLoadExecutor);
+        ExecutorUtil.shutdownNowAndAwaitTermination(coreLoadExecutor);
       }
     }
   }
 
-  protected void initShardHandler(Config cfg) {
-    PluginInfo info = null;
+  // To make this available to TestHarness.
+  protected void initShardHandler() {
     if (cfg != null) {
-      Node shfn = cfg.getNode("solr/cores/shardHandlerFactory", false);
-  
-      if (shfn != null) {
-        info = new PluginInfo(shfn, "shardHandlerFactory", false, true);
-      } else {
-        Map m = new HashMap();
-        m.put("class",HttpShardHandlerFactory.class.getName());
-        info = new PluginInfo("shardHandlerFactory", m, null, Collections.<PluginInfo>emptyList());
-      }
-    }
-
-    HttpShardHandlerFactory fac = new HttpShardHandlerFactory();
-    if (info != null) {
-      fac.init(info);
+      cfg.initShardHandler();
+    } else {
+      // Cough! Hack! But tests run this way.
+      HttpShardHandlerFactory fac = new HttpShardHandlerFactory();
+      shardHandlerFactory = fac;
     }
-    shardHandlerFactory = fac;
-  }
-
-  private Document copyDoc(Document document) throws TransformerException {
-    TransformerFactory tfactory = TransformerFactory.newInstance();
-    Transformer tx   = tfactory.newTransformer();
-    DOMSource source = new DOMSource(document);
-    DOMResult result = new DOMResult();
-    tx.transform(source,result);
-    return (Document)result.getNode();
   }
 
-  private Properties readProperties(Config cfg, Node node) throws XPathExpressionException {
-    XPath xpath = cfg.getXPath();
-    NodeList props = (NodeList) xpath.evaluate("property", node, XPathConstants.NODESET);
-    Properties properties = new Properties();
-    for (int i=0; i<props.getLength(); i++) {
-      Node prop = props.item(i);
-      properties.setProperty(DOMUtil.getAttr(prop, "name"), DOMUtil.getAttr(prop, "value"));
-    }
-    return properties;
-  }
-  
   private volatile boolean isShutDown = false;
 
-  private volatile Config cfg;
+  private volatile ConfigSolr cfg;
   
   public boolean isShutDown() {
     return isShutDown;
@@ -738,33 +660,46 @@ public class CoreContainer 
   public void shutdown() {
     log.info("Shutting down CoreContainer instance="
         + System.identityHashCode(this));
-    isShutDown = true;
     
     if (isZooKeeperAware()) {
+      try {
+        zkController.publishAndWaitForDownStates();
+      } catch (KeeperException e) {
+        log.error("", e);
+      } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+        log.warn("", e);
+      }
+    }
+    isShutDown = true;
+
+    if (isZooKeeperAware()) {
+      coreMaps.publishCoresAsDown(zkController);
       cancelCoreRecoveries();
     }
-    try {
-      synchronized (cores) {
 
-        for (SolrCore core : cores.values()) {
-          try {
-            core.close();
-          } catch (Throwable t) {
-            SolrException.log(log, "Error shutting down core", t);
-          }
-        }
-        cores.clear();
+
+    try {
+      // First allow the closer thread to drain all the pending closes it can.
+      synchronized (coreMaps.getLocker()) {
+        coreMaps.getLocker().notifyAll(); // wake up anyone waiting
       }
-      synchronized (swappableCores) {
-        for (SolrCore core : swappableCores.values()) {
-          try {
-            core.close();
-          } catch (Throwable t) {
-            SolrException.log(log, "Error shutting down core", t);
-          }
+      if (backgroundCloser != null) { // Doesn't seem right, but tests get in here without initializing the core.
+        try {
+          backgroundCloser.join();
+        } catch (InterruptedException e) {
+          ; // Don't much care if this gets interrupted
         }
-        swappableCores.clear();
       }
+      // Now clear all the cores that are being operated upon.
+      coreMaps.clearMaps(cfg);
+
+      // It's still possible that one of the pending dynamic load operation is waiting, so wake it up if so.
+      // Since all the pending operations queues have been drained, there should be nothing to do.
+      synchronized (coreMaps.getLocker()) {
+        coreMaps.getLocker().notifyAll(); // wake up the thread
+      }
+
     } finally {
       if (shardHandlerFactory != null) {
         shardHandlerFactory.close();
@@ -777,17 +712,12 @@ public class CoreContainer 
       if (zkServer != null) {
         zkServer.stop();
       }
-      
     }
   }
 
   public void cancelCoreRecoveries() {
     ArrayList<SolrCoreState> coreStates = new ArrayList<SolrCoreState>();
-    synchronized (cores) {
-      for (SolrCore core : cores.values()) {
-        coreStates.add(core.getUpdateHandler().getSolrCoreState());
-      }
-    }
+    coreMaps.addCoresToList(coreStates);
 
     // we must cancel without holding the cores sync
     // make sure we wait for any recoveries to stop
@@ -811,21 +741,7 @@ public class CoreContainer 
     }
   }
 
-  /**
-   * Registers a SolrCore descriptor in the registry using the specified name.
-   * If returnPrevNotClosed==false, the old core, if different, is closed. if true, it is returned w/o closing the core
-   *
-   * @return a previous core having the same name if it existed
-   */
-  public SolrCore register(String name, SolrCore core, boolean returnPrevNotClosed) {
-    return registerCore(cores, name, core, returnPrevNotClosed);
-  }
-
-  protected SolrCore registerLazyCore(String name, SolrCore core, boolean returnPrevNotClosed) {
-    return registerCore(swappableCores, name, core, returnPrevNotClosed);
-  }
-
-  protected SolrCore registerCore(Map<String,SolrCore> whichCores, String name, SolrCore core, boolean returnPrevNotClosed) {
+  protected SolrCore registerCore(boolean isTransientCore, String name, SolrCore core, boolean returnPrevNotClosed) {
     if( core == null ) {
       throw new RuntimeException( "Can not register a null core." );
     }
@@ -852,19 +768,26 @@ public class CoreContainer 
     }
     
     SolrCore old = null;
-    synchronized (whichCores) {
-      if (isShutDown) {
-        core.close();
-        throw new IllegalStateException("This CoreContainer has been shutdown");
-      }
-      old = whichCores.put(name, core);
-      coreInitFailures.remove(name);
+
+    if (isShutDown) {
+      core.close();
+      throw new IllegalStateException("This CoreContainer has been shutdown");
+    }
+    if (isTransientCore) {
+      old = coreMaps.putTransientCore(cfg, name, core, loader);
+    } else {
+      old = coreMaps.putCore(name, core);
+    }
       /*
       * set both the name of the descriptor and the name of the
       * core, since the descriptors name is used for persisting.
       */
-      core.setName(name);
-      core.getCoreDescriptor().name = name;
+
+    core.setName(name);
+    core.getCoreDescriptor().putProperty(CoreDescriptor.CORE_NAME, name);
+
+    synchronized (coreInitFailures) {
+      coreInitFailures.remove(name);
     }
 
     if( old == null || old == core) {
@@ -920,9 +843,14 @@ public class CoreContainer 
    * @return a previous core having the same name if it existed and returnPrev==true
    */
   public SolrCore register(SolrCore core, boolean returnPrev) {
-    return register(core.getName(), core, returnPrev);
+    return registerCore(false, core.getName(), core, returnPrev);
   }
 
+  public SolrCore register(String name, SolrCore core, boolean returnPrev) {
+    return registerCore(false, name, core, returnPrev);
+  }
+
+
   // Helper method to separate out creating a core from ZK as opposed to the "usual" way. See create()
   private SolrCore createFromZk(String instanceDir, CoreDescriptor dcore)
   {
@@ -940,10 +868,9 @@ public class CoreContainer 
         throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR,
             "Could not find config name for collection:" + collection);
       }
-      solrLoader = new ZkSolrResourceLoader(instanceDir, zkConfigName, libLoader, getCoreProps(instanceDir,
-          dcore.getPropertiesName(), dcore.getCoreProperties()), zkController);
+      solrLoader = new ZkSolrResourceLoader(instanceDir, zkConfigName, libLoader, SolrProperties.getCoreProperties(instanceDir, dcore), zkController);
       config = getSolrConfigFromZk(zkConfigName, dcore.getConfigName(), solrLoader);
-      schema = getSchemaFromZk(zkConfigName, dcore.getSchemaName(), config, solrLoader);
+      schema = getSchemaFromZk(zkConfigName, dcore.getSchemaName(), config);
       return new SolrCore(dcore.getName(), null, config, schema, dcore);
 
     } catch (KeeperException e) {
@@ -964,10 +891,11 @@ public class CoreContainer 
     SolrResourceLoader solrLoader = null;
 
     SolrConfig config = null;
-    solrLoader = new SolrResourceLoader(instanceDir, libLoader, getCoreProps(instanceDir, dcore.getPropertiesName(), dcore.getCoreProperties()));
+    solrLoader = new SolrResourceLoader(instanceDir, libLoader, SolrProperties.getCoreProperties(instanceDir, dcore));
     try {
       config = new SolrConfig(solrLoader, dcore.getConfigName(), null);
     } catch (Exception e) {
+      log.error("Failed to load file {}/{}", instanceDir, dcore.getConfigName());
       throw new SolrException(ErrorCode.SERVER_ERROR, "Could not load config for " + dcore.getConfigName(), e);
     }
 
@@ -985,11 +913,11 @@ public class CoreContainer 
             schemaFile.lastModified()));
         schema = indexSchemaCache.get(key);
         if (schema == null) {
-          log.info("creating new schema object for core: " + dcore.name);
+          log.info("creating new schema object for core: " + dcore.getProperty(CoreDescriptor.CORE_NAME));
           schema = new IndexSchema(config, dcore.getSchemaName(), null);
           indexSchemaCache.put(key, schema);
         } else {
-          log.info("re-using schema object for core: " + dcore.name);
+          log.info("re-using schema object for core: " + dcore.getProperty(CoreDescriptor.CORE_NAME));
         }
       }
     }
@@ -1021,7 +949,7 @@ public class CoreContainer 
       // Make the instanceDir relative to the cores instanceDir if not absolute
       File idir = new File(dcore.getInstanceDir());
       String instanceDir = idir.getPath();
-      log.info("Creating SolrCore '{}' using instanceDir: {}", 
+      log.info("Creating SolrCore '{}' using instanceDir: {}",
                dcore.getName(), instanceDir);
 
       // Initialize the solr config
@@ -1042,48 +970,30 @@ public class CoreContainer 
    * @return a Collection of registered SolrCores
    */
   public Collection<SolrCore> getCores() {
-    List<SolrCore> lst = new ArrayList<SolrCore>();
-    synchronized (cores) {
-      lst.addAll(this.cores.values());
-    }
-    return lst;
+    return coreMaps.getCores();
   }
 
   /**
    * @return a Collection of the names that cores are mapped to
    */
   public Collection<String> getCoreNames() {
-    List<String> lst = new ArrayList<String>();
-    synchronized (cores) {
-      lst.addAll(this.cores.keySet());
-    }
-    synchronized (swappableCores) {
-      lst.addAll(this.swappableCores.keySet());
-    }
-    return lst;
+    return coreMaps.getCoreNames();
   }
 
   /** This method is currently experimental.
    * @return a Collection of the names that a specific core is mapped to.
    */
   public Collection<String> getCoreNames(SolrCore core) {
-    List<String> lst = new ArrayList<String>();
-    synchronized (cores) {
-      for (Map.Entry<String,SolrCore> entry : cores.entrySet()) {
-        if (core == entry.getValue()) {
-          lst.add(entry.getKey());
-        }
-      }
-    }
-    synchronized (swappableCores) {
-      for (Map.Entry<String,SolrCore> entry : swappableCores.entrySet()) {
-        if (core == entry.getValue()) {
-          lst.add(entry.getKey());
-        }
-      }
-    }
+    return coreMaps.getCoreNames(core);
+  }
+
+  /**
+   * get a list of all the cores that are currently loaded
+   * @return a list of al lthe available core names in either permanent or transient core lists.
+   */
+  public Collection<String> getAllCoreNames() {
+    return coreMaps.getAllCoreNames();
 
-    return lst;
   }
 
   /**
@@ -1124,10 +1034,7 @@ public class CoreContainer 
     try {
 
       name= checkDefault(name);
-      SolrCore core;
-      synchronized(cores) {
-        core = cores.get(name);
-      }
+      SolrCore core = coreMaps.getCore(name);
       if (core == null)
         throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No such core: " + name );
 
@@ -1140,7 +1047,7 @@ public class CoreContainer 
     
       SolrResourceLoader solrLoader;
       if(zkController == null) {
-        solrLoader = new SolrResourceLoader(instanceDir.getAbsolutePath(), libLoader, getCoreProps(instanceDir.getAbsolutePath(), cd.getPropertiesName(),cd.getCoreProperties()));
+        solrLoader = new SolrResourceLoader(instanceDir.getAbsolutePath(), libLoader, SolrProperties.getCoreProperties(instanceDir.getAbsolutePath(), cd));
       } else {
         try {
           String collection = cd.getCloudDescriptor().getCollectionName();
@@ -1152,7 +1059,8 @@ public class CoreContainer 
             throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR,
                                          "Could not find config name for collection:" + collection);
           }
-          solrLoader = new ZkSolrResourceLoader(instanceDir.getAbsolutePath(), zkConfigName, libLoader, getCoreProps(instanceDir.getAbsolutePath(), cd.getPropertiesName(),cd.getCoreProperties()), zkController);
+          solrLoader = new ZkSolrResourceLoader(instanceDir.getAbsolutePath(), zkConfigName, libLoader,
+              SolrProperties.getCoreProperties(instanceDir.getAbsolutePath(), cd), zkController);
         } catch (KeeperException e) {
           log.error("", e);
           throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR,
@@ -1165,14 +1073,11 @@ public class CoreContainer 
                                        "", e);
         }
       }
-    
       SolrCore newCore = core.reload(solrLoader, core);
       // keep core to orig name link
-      String origName = coreToOrigName.remove(core);
-      if (origName != null) {
-        coreToOrigName.put(newCore, origName);
-      }
-      register(name, newCore, false);
+      coreMaps.removeCoreToOrigName(newCore, core);
+
+      registerCore(false, name, newCore, false);
 
       // :TODO: Java7...
       // http://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html
@@ -1194,22 +1099,7 @@ public class CoreContainer 
     }
     n0 = checkDefault(n0);
     n1 = checkDefault(n1);
-    synchronized( cores ) {
-      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 );
-      cores.put(n0, c1);
-      cores.put(n1, c0);
-
-      c0.setName(n1);
-      c0.getCoreDescriptor().name = n1;
-      c1.setName(n0);
-      c1.getCoreDescriptor().name = n0;
-    }
-
+    coreMaps.swap(n0, n1);
 
     log.info("swapped: "+n0 + " with " + n1);
   }
@@ -1218,13 +1108,7 @@ public class CoreContainer 
   public SolrCore remove( String name ) {
     name = checkDefault(name);    
 
-    synchronized(cores) {
-      SolrCore core = cores.remove( name );
-      if (core != null) {
-        coreToOrigName.remove(core);
-      }
-      return core;
-    }
+    return coreMaps.remove(name, true);
 
   }
 
@@ -1232,12 +1116,9 @@ public class CoreContainer 
     SolrCore core = getCore(name);
     try {
       if (core != null) {
-        register(toName, core, false);
+        registerCore(false, toName, core, false);
         name = checkDefault(name);
-        
-        synchronized (cores) {
-          cores.remove(name);
-        }
+        coreMaps.remove(name, false);
       }
     } finally {
       if (core != null) {
@@ -1245,49 +1126,40 @@ public class CoreContainer 
       }
     }
   }
-  
   /** Gets a core by name and increase its refcount.
    * @see SolrCore#close() 
    * @param name the core name
    * @return the core if found
    */
   public SolrCore getCore(String name) {
+
     name = checkDefault(name);
     // Do this in two phases since we don't want to lock access to the cores over a load.
-    SolrCore core;
+    SolrCore core = coreMaps.getCoreFromAnyList(name);
 
-    synchronized (cores) {
-      core = cores.get(name);
-      if (core != null) {
-        core.open();    // increment the ref count while still synchronized
-        return core;
-      }
+    if (core != null) return core;
+
+    // OK, it's not presently in any list, is it in the list of dynamic cores but not loaded yet? If so, load it.
+    CoreDescriptor desc = coreMaps.getDynamicDescriptor(name);
+    if (desc == null) { //Nope, no transient core with this name
+      return null;
     }
 
-    if (dynamicDescriptors.size() == 0) return null; // Nobody even tried to define any swappable cores, so we're done.
+    core = coreMaps.waitPendingCoreOps(name); // This will put an entry in pending core ops if the core isn't loaded
 
-    // Now look for already loaded swappable cores.
-    synchronized (swappableCores) {
-      core = swappableCores.get(name);
-      if (core != null) {
+    if (isShutDown) return null; // We're quitting, so stop. This needs to be after the wait above since we may come off
+                                 // the wait as a consequence of shutting down.
+
+    if (core == null) {
+      try {
+        core = create(desc); // This should throw an error if it fails.
         core.open();
-        return core;
-      }
-    }
-    CoreDescriptor desc =  dynamicDescriptors.get(name);
-    if (desc == null) { //Nope, no swappable core with this name
-      return null;
-    }
-    try {
-      core = create(desc); // This should throw an error if it fails.
-      core.open();
-      if (desc.isSwappable()) {
-        registerLazyCore(name, core, false);    // This is a swappable core
-      } else {
-        register(name, core, false); // This is a "permanent", although deferred-load core
+        registerCore(desc.isTransient(), name, core, false);
+      } catch (Exception ex) {
+        throw recordAndThrow(name, "Unable to create core: " + name, ex);
+      } finally {
+        coreMaps.releasePending(name);
       }
-    } catch (Exception ex) {
-      throw recordAndThrow(name, "Unable to create core" + name, ex);
     }
     return core;
   }
@@ -1362,12 +1234,31 @@ public class CoreContainer 
   public File getConfigFile() {
     return configFile;
   }
-  
-/** Persists the cores config file in cores.xml. */
+
+  /**
+   * Determines whether the core is already loaded or not but does NOT load the core
+   *
+   */
+  public boolean isLoaded(String name) {
+    return coreMaps.isLoaded(name);
+  }
+
+  /** Persists the cores config file in cores.xml. */
   public void persist() {
     persistFile(null);
   }
 
+  /**
+   * Gets a solr core descriptor for a core that is not loaded. Note that if the caller calls this on a
+   * loaded core, the unloaded descriptor will be returned.
+   *
+   * @param cname - name of the unloaded core descriptor to load. NOTE:
+   * @return a coreDescriptor. May return null
+   */
+  public CoreDescriptor getUnloadedCoreDescriptor(String cname) {
+    return coreMaps.getUnloadedCoreDescriptor(cname);
+  }
+
   /** Persists the cores config file in a user provided file. */
   public void persistFile(File file) {
     log.info("Persisting cores config to " + (file == null ? configFile : file));
@@ -1391,8 +1282,8 @@ public class CoreContainer 
       coresAttribs.put("defaultCoreName", defaultCoreName);
     }
 
-    if (swappableCacheSize != Integer.MAX_VALUE) {
-      coresAttribs.put("swappableCacheSize", Integer.toString(swappableCacheSize));
+    if (transientCacheSize != Integer.MAX_VALUE) {
+      coresAttribs.put("transientCacheSize", Integer.toString(transientCacheSize));
     }
     
     addCoresAttrib(coresAttribs, "hostPort", this.hostPort, DEFAULT_HOST_PORT);
@@ -1403,120 +1294,9 @@ public class CoreContainer 
     addCoresAttrib(coresAttribs, "leaderVoteWait", this.leaderVoteWait, LEADER_VOTE_WAIT);
     addCoresAttrib(coresAttribs, "coreLoadThreads", Integer.toString(this.coreLoadThreads), Integer.toString(CORE_LOAD_THREADS));
 
-    List<SolrCoreXMLDef> solrCoreXMLDefs = new ArrayList<SolrCoreXMLDef>();
-    
-    synchronized (cores) {
-      for (SolrCore solrCore : cores.values()) {
-        Map<String,String> coreAttribs = new HashMap<String,String>();
-        CoreDescriptor dcore = solrCore.getCoreDescriptor();
-
-        String coreName = dcore.name;
-        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, CORE_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, CORE_NAME, null);
-              if (origCoreName.equals(DOMUtil.substituteProperty(name,
-                  loader.getCoreProperties()))) {
-                coreNode = node;
-                if (coreName.equals(origCoreName)) {
-                  coreName = name;
-                }
-                break;
-              }
-            }
-          }
-        }
+    coreMaps.persistCores(cfg, containerProperties, rootSolrAttribs, coresAttribs, file, configFile, loader);
 
-        coreAttribs.put(CORE_NAME, coreName);
-        
-        String instanceDir = dcore.getRawInstanceDir();
-        addCoreProperty(coreAttribs, coreNode, CORE_INSTDIR, instanceDir, null);
-        
-        // write config 
-        String configName = dcore.getConfigName();
-        addCoreProperty(coreAttribs, coreNode, CORE_CONFIG, configName, dcore.getDefaultConfigName());
-        
-        // write schema
-        String schema = dcore.getSchemaName();
-        addCoreProperty(coreAttribs, coreNode, CORE_SCHEMA, schema, dcore.getDefaultSchemaName());
-        
-        String dataDir = dcore.dataDir;
-        addCoreProperty(coreAttribs, coreNode, CORE_DATADIR, dataDir, null);
-        addCoreProperty(coreAttribs, coreNode, CORE_SWAPPABLE, Boolean.toString(dcore.isSwappable()), null);
-        addCoreProperty(coreAttribs, coreNode, CORE_LOADONSTARTUP, Boolean.toString(dcore.isLoadOnStartup()), null);
-
-        CloudDescriptor cd = dcore.getCloudDescriptor();
-        String shard = null;
-        String roles = null;
-        if (cd != null) {
-          shard = cd.getShardId();
-          roles = cd.getRoles();
-        }
-        addCoreProperty(coreAttribs, coreNode, CORE_SHARD, shard, null);
-        
-        addCoreProperty(coreAttribs, coreNode, CORE_ROLES, roles, 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, CORE_COLLECTION, collection, dcore.name);
-        
-        // we don't try and preserve sys prop defs in these
-        String opt = dcore.getPropertiesName();
-        if (opt != null) {
-          coreAttribs.put(CORE_PROPERTIES, opt);
-        }
-        
-        SolrCoreXMLDef solrCoreXMLDef = new SolrCoreXMLDef();
-        solrCoreXMLDef.coreAttribs = coreAttribs;
-        solrCoreXMLDef.coreProperties = dcore.getCoreProperties();
-        solrCoreXMLDefs.add(solrCoreXMLDef);
-      }
-      
-      SolrXMLDef solrXMLDef = new SolrXMLDef();
-      solrXMLDef.coresDefs = solrCoreXMLDefs;
-      solrXMLDef.containerProperties = containerProperties;
-      solrXMLDef.solrAttribs = rootSolrAttribs;
-      solrXMLDef.coresAttribs = coresAttribs;
-      solrXMLSerializer.persistFile(file == null ? configFile : file,
-          solrXMLDef);
-    }
   }
-
   private String intToString(Integer integer) {
     if (integer == null) return null;
     return Integer.toString(integer);
@@ -1529,9 +1309,10 @@ public class CoreContainer 
     }
     
     if (attribValue != null) {
-      String rawValue = cfg.get("solr/cores/@" + attribName, null);
+      String rawValue = cfg.get(ConfigSolr.ConfLevel.SOLR_CORES, attribName, null);
       if (rawValue == null && defaultValue != null && attribValue.equals(defaultValue)) return;
-      if (attribValue.equals(DOMUtil.substituteProperty(rawValue, loader.getCoreProperties()))) {
+
+      if (attribValue.equals(PropertiesUtil.substituteProperty(rawValue, loader.getCoreProperties()))) {
         coresAttribs.put(attribName, rawValue);
       } else {
         coresAttribs.put(attribName, attribValue);
@@ -1539,32 +1320,6 @@ public class CoreContainer 
     }
   }
 
-  private void addCoreProperty(Map<String,String> coreAttribs, Node node, String name,
-      String value, String defaultValue) {
-    if (node == null) {
-      coreAttribs.put(name, value);
-      return;
-    }
-    
-    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()))){
-        coreAttribs.put(name, rawAttribValue);
-      } else {
-        coreAttribs.put(name, value);
-      }
-    }
-
-  }
-
-
   public String getSolrHome() {
     return solrHome;
   }
@@ -1589,22 +1344,8 @@ public class CoreContainer 
   private SolrConfig getSolrConfigFromZk(String zkConfigName, String solrConfigFileName,
       SolrResourceLoader resourceLoader)
   {
-    SolrConfig cfg = null;
-    try {
-      byte[] config = zkController.getConfigFileData(zkConfigName, solrConfigFileName);
-      InputSource is = new InputSource(new ByteArrayInputStream(config));
-      is.setSystemId(SystemIdResolver.createSystemIdFromResourceName(solrConfigFileName));
-      cfg = solrConfigFileName == null ? new SolrConfig(
-          resourceLoader, SolrConfig.DEFAULT_CONF_FILE, is) : new SolrConfig(
-          resourceLoader, solrConfigFileName, is);
-    } catch (Exception e) {
-      throw new SolrException(ErrorCode.SERVER_ERROR,
-          "getSolrConfigFromZK failed for " + zkConfigName + " " + solrConfigFileName, e);
-    }
-
-    return cfg;
+    return cfg.getSolrConfigFromZk(zkController, zkConfigName, solrConfigFileName, resourceLoader);
   }
-
   // Just to tidy up the code where it did this in-line.
   private SolrException recordAndThrow(String name, String msg, Exception ex) {
     synchronized (coreInitFailures) {
@@ -1614,20 +1355,590 @@ public class CoreContainer 
     log.error(msg, ex);
     return new SolrException(ErrorCode.SERVER_ERROR, msg, ex);
   }
+
   private IndexSchema getSchemaFromZk(String zkConfigName, String schemaName,
-      SolrConfig config, SolrResourceLoader resourceLoader)
+      SolrConfig config)
       throws KeeperException, InterruptedException {
-    byte[] configBytes = zkController.getConfigFileData(zkConfigName, schemaName);
-    InputSource is = new InputSource(new ByteArrayInputStream(configBytes));
-    is.setSystemId(SystemIdResolver.createSystemIdFromResourceName(schemaName));
-    IndexSchema schema = new IndexSchema(config, schemaName, is);
-    return schema;
+    return cfg.getSchemaFromZk(zkController, zkConfigName, schemaName, config);
+  }
+}
+
+
+// Introducing the two new maps (transientCores and dynamicDescriptors) introduced some locking complexities. Rather
+// than try to keep them all straight in the code, use this class you need to access any of:
+// cores
+// transientCores
+// dynamicDescriptors
+//
+
+class CoreMaps {
+
+  private static Object locker = new Object(); // for locking around manipulating any of the core maps.
+  private final Map<String, SolrCore> cores = new LinkedHashMap<String, SolrCore>(); // For "permanent" cores
+
+  //WARNING! The _only_ place you put anything into the list of transient cores is with the putTransientCore method!
+  private Map<String, SolrCore> transientCores = new LinkedHashMap<String, SolrCore>(); // For "lazily loaded" cores
+
+  private final Map<String, CoreDescriptor> dynamicDescriptors = new LinkedHashMap<String, CoreDescriptor>();
+
+  private int transientCacheSize = Integer.MAX_VALUE;
+
+  private Map<SolrCore, String> coreToOrigName = new ConcurrentHashMap<SolrCore, String>();
+
+  private final CoreContainer container;
+
+  // It's a little clumsy to have two, but closing requires a SolrCore, whereas pending loads don't have a core.
+  private static final Set<String> pendingDynamicLoads = new TreeSet<String>();
+
+  // Holds cores from the time they're removed from the transient cache until after they're closed.
+  private static final List<SolrCore> pendingDynamicCloses = new ArrayList<SolrCore>();
+
+  CoreMaps(CoreContainer container) {
+    this.container = container;
+  }
+
+  // Trivial helper method for load, note it implements LRU on transient cores. Also note, if
+  // there is no setting for max size, nothing is done and all cores go in the regular "cores" list
+  protected void allocateLazyCores(final ConfigSolr cfg, final SolrResourceLoader loader) {
+    transientCacheSize = cfg.getInt(ConfigSolr.ConfLevel.SOLR_CORES, "transientCacheSize", Integer.MAX_VALUE);
+    if (transientCacheSize != Integer.MAX_VALUE) {
+      CoreContainer.log.info("Allocating transient cache for {} transient cores", transientCacheSize);
+      transientCores = new LinkedHashMap<String, SolrCore>(transientCacheSize, 0.75f, true) {
+        @Override
+        protected boolean removeEldestEntry(Map.Entry<String, SolrCore> eldest) {
+          if (size() > transientCacheSize) {
+            synchronized (locker) {
+              SolrCore closeMe = eldest.getValue();
+              synchronized (locker) {
+                pendingDynamicCloses.add(closeMe);
+                locker.notifyAll(); // Wakes up closer thread too
+              }
+            }
+            return true;
+          }
+          return false;
+        }
+      };
+    }
+  }
+
+  protected void putDynamicDescriptor(String rawName, CoreDescriptor p) {
+    synchronized (locker) {
+      dynamicDescriptors.put(rawName, p);
+    }
+  }
+
+  // We are shutting down. We don't want to risk deadlock, so do this manipulation the expensive way. Note, I've
+  // already deadlocked with closing/opening cores while keeping locks here....
+  protected void clearMaps(ConfigSolr cfg) {
+    List<String> coreNames;
+    List<String> transientNames;
+    List<SolrCore> pendingClosers;
+    synchronized (locker) {
+      coreNames = new ArrayList(cores.keySet());
+      transientNames = new ArrayList(transientCores.keySet());
+      pendingClosers = new ArrayList(pendingDynamicCloses);
+    }
+    for (String coreName : coreNames) {
+      SolrCore core = cores.get(coreName);
+      if (core != null) {
+        try {
+          addPersistOneCore(cfg, core, container.loader);
+
+          core.close();
+        } catch (Throwable t) {
+          SolrException.log(CoreContainer.log, "Error shutting down core", t);
+        }
+      }
+    }
+    cores.clear();
+
+    for (String coreName : transientNames) {
+      SolrCore core = transientCores.get(coreName);
+      if (core != null) {
+        try {
+          core.close();
+        } catch (Throwable t) {
+          SolrException.log(CoreContainer.log, "Error shutting down core", t);
+        }
+      }
+    }
+    transientCores.clear();
+
+    // We might have some cores that we were _thinking_ about shutting down, so take care of those too.
+    for (SolrCore core : pendingClosers) {
+      core.close();
+    }
+
+  }
+
+  protected void addCoresToList(ArrayList<SolrCoreState> coreStates) {
+    List<SolrCore> addCores;
+    synchronized (locker) {
+      addCores = new ArrayList<SolrCore>(cores.values());
+    }
+    for (SolrCore core : addCores) {
+      coreStates.add(core.getUpdateHandler().getSolrCoreState());
+    }
+  }
+
+  //WARNING! This should be the _only_ place you put anything into the list of transient cores!
+  protected SolrCore putTransientCore(ConfigSolr cfg, String name, SolrCore core, SolrResourceLoader loader) {
+    SolrCore retCore;
+    CoreContainer.log.info("Opening transient core {}", name);
+    synchronized (locker) {
+      retCore = transientCores.put(name, core);
+  }
+    return retCore;
+  }
+
+  protected SolrCore putCore(String name, SolrCore core) {
+    synchronized (locker) {
+      return cores.put(name, core);
+    }
+  }
+
+  List<SolrCore> getCores() {
+    List<SolrCore> lst = new ArrayList<SolrCore>();
+
+    synchronized (locker) {
+      lst.addAll(cores.values());
+      return lst;
+    }
+  }
+
+  Set<String> getCoreNames() {
+    Set<String> set = new TreeSet<String>();
+
+    synchronized (locker) {
+      set.addAll(cores.keySet());
+      set.addAll(transientCores.keySet());
+    }
+    return set;
+  }
+
+  List<String> getCoreNames(SolrCore core) {
+    List<String> lst = new ArrayList<String>();
+
+    synchronized (locker) {
+      for (Map.Entry<String, SolrCore> entry : cores.entrySet()) {
+        if (core == entry.getValue()) {
+          lst.add(entry.getKey());
+        }
+      }
+      for (Map.Entry<String, SolrCore> entry : transientCores.entrySet()) {
+        if (core == entry.getValue()) {
+          lst.add(entry.getKey());
+        }
+      }
+    }
+    return lst;
+  }
+
+  /**
+   * Gets a list of all cores, loaded and unloaded (dynamic)
+   *
+   * @return all cores names, whether loaded or unloaded.
+   */
+  public Collection<String> getAllCoreNames() {
+    Set<String> set = new TreeSet<String>();
+    synchronized (locker) {
+      set.addAll(cores.keySet());
+      set.addAll(transientCores.keySet());
+      set.addAll(dynamicDescriptors.keySet());
+    }
+    return set;
+  }
+
+  SolrCore getCore(String name) {
+
+    synchronized (locker) {
+      return cores.get(name);
+    }
+  }
+
+  protected void swap(String n0, String n1) {
+
+    synchronized (locker) {
+      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);
+      cores.put(n0, c1);
+      cores.put(n1, c0);
+
+      c0.setName(n1);
+      c0.getCoreDescriptor().putProperty(CoreDescriptor.CORE_NAME, n1);
+      c1.setName(n0);
+      c1.getCoreDescriptor().putProperty(CoreDescriptor.CORE_NAME, n0);
+    }
+
+  }
+
+  protected SolrCore remove(String name, boolean removeOrig) {
+
+    synchronized (locker) {
+      SolrCore core = cores.remove(name);
+      if (removeOrig && core != null) {
+        coreToOrigName.remove(core);
+      }
+
+      return core;
+    }
+  }
+
+  protected void putCoreToOrigName(SolrCore c, String name) {
+
+    synchronized (locker) {
+      coreToOrigName.put(c, name);
+    }
+
+  }
+
+  protected void removeCoreToOrigName(SolrCore newCore, SolrCore core) {
+
+    synchronized (locker) {
+      String origName = coreToOrigName.remove(core);
+      if (origName != null) {
+        coreToOrigName.put(newCore, origName);
+      }
+    }
+  }
+
+  protected SolrCore getCoreFromAnyList(String name) {
+    SolrCore core;
+
+    synchronized (locker) { // This one's OK, the core.open is just an increment
+      core = cores.get(name);
+      if (core != null) {
+        core.open();    // increment the ref count while still synchronized
+        return core;
+      }
+
+      if (dynamicDescriptors.size() == 0) {
+        return null; // Nobody even tried to define any transient cores, so we're done.
+      }
+      // Now look for already loaded transient cores.
+      core = transientCores.get(name);
+      if (core != null) {
+        core.open();  // Just increments ref count, so it's ok that we're in a synch block
+        return core;
+      }
+    }
+
+    return null;
+
+  }
+
+  protected CoreDescriptor getDynamicDescriptor(String name) {
+    synchronized (locker) {
+      return dynamicDescriptors.get(name);
+    }
+  }
+
+  protected boolean isLoaded(String name) {
+    synchronized (locker) {
+      if (cores.containsKey(name)) {
+        return true;
+      }
+      if (transientCores.containsKey(name)) {
+        return true;
+      }
+    }
+    return false;
+
+  }
+
+  protected CoreDescriptor getUnloadedCoreDescriptor(String cname) {
+    synchronized (locker) {
+      CoreDescriptor desc = dynamicDescriptors.get(cname);
+      if (desc == null) {
+        return null;
+      }
+      return new CoreDescriptor(desc);
+    }
+
+  }
+
+  protected String getCoreToOrigName(SolrCore solrCore) {
+    synchronized (locker) {
+      return coreToOrigName.get(solrCore);
+    }
+  }
+
+  protected void publishCoresAsDown(ZkController zkController) {
+    synchronized (locker) {
+      for (SolrCore core : cores.values()) {
+        try {
+          zkController.publish(core.getCoreDescriptor(), ZkStateReader.DOWN);
+        } catch (KeeperException e) {
+          CoreContainer.log.error("", e);
+        } catch (InterruptedException e) {
+          CoreContainer.log.error("", e);
+        }
+      }
+      for (SolrCore core : transientCores.values()) {
+        try {
+          zkController.publish(core.getCoreDescriptor(), ZkStateReader.DOWN);
+        } catch (KeeperException e) {
+          CoreContainer.log.error("", e);
+        } catch (InterruptedException e) {
+          CoreContainer.log.error("", e);
+        }
+      }
+    }
+  }
+
+  // Irrepressably ugly bit of the transition in SOLR-4196, but there as at least one test case that follows
+  // this path, presumably it's there for a reason.
+  // This is really perverse, but all we need the here is to call a couple of static methods that for back-compat
+  // purposes
+  public void persistCores(ConfigSolr cfg, Properties containerProperties, Map<String, String> rootSolrAttribs,
+                           Map<String, String> coresAttribs, File file, File configFile, SolrResourceLoader loader) {
+    // This is expensive in the maximal case, but I think necessary. It should keep a reference open to all of the
+    // current cores while they are saved. Remember that especially the transient core can come and go.
+    //
+    // Maybe the right thing to do is keep all the core descriptors NOT in the SolrCore, but keep all of the
+    // core descriptors in SolrProperties exclusively.
+    // TODO: 5.0 move coreDescriptors out of SolrCore and keep them only once in SolrProperties
+    //
+    synchronized (locker) {
+      if (cfg == null) {
+        ConfigSolrXmlBackCompat.initPersistStatic();
+        persistCores(cfg, cores, loader);
+        persistCores(cfg, transientCores, loader);
+        ConfigSolrXmlBackCompat.addPersistAllCoresStatic(containerProperties, rootSolrAttribs, coresAttribs,
+            (file == null ? configFile : file));
+      } else {
+        cfg.initPersist();
+        persistCores(cfg, cores, loader);
+        persistCores(cfg, transientCores, loader);
+        cfg.addPersistAllCores(containerProperties, rootSolrAttribs, coresAttribs, (file == null ? configFile : file));
+      }
+    }
+  }
+  // We get here when we're being loaded, and the presumption is that we're not in the list yet.
+  protected SolrCore waitPendingCoreOps(String name) {
+
+    // Keep multiple threads from opening or closing a core at one time.
+    SolrCore ret = null;
+
+    synchronized (locker) {
+      boolean pending;
+      do { // We're either loading or unloading this core,
+        pending = pendingDynamicLoads.contains(name); // wait for the core to be loaded
+        if (! pending) {
+          // Check pending closes. This is a linear search is inefficient, but maps don't work without a lot of complexity,
+          // we'll live with it unless it proves to be a bottleneck. In the "usual" case, this list shouldn't be
+          // very long. In the stress test associated with SOLR-4196, this hovered around 0-3, occasionally spiking
+          // very briefly to around 30.
+          for (SolrCore core : pendingDynamicCloses) {
+            if (core.getName().equals(name)) {
+              pending = true;
+              break;
+            }
+          }
+        }
+
+        if (container.isShutDown()) return null; // Just stop already.
+
+        if (pending) {
+          try {
+            locker.wait();
+          } catch (InterruptedException e) {
+            return null; // Seems best not to do anything at all if the thread is interrupted
+          }
+        }
+      } while (pending);
+
+      if (!container.isShutDown()) {
+        ret = getCoreFromAnyList(name); // we might have been _unloading_ the core, so check.
+        if (ret == null) {
+          pendingDynamicLoads.add(name); // the caller is going to load us. If we happen to be shutting down, we don't care.
+        }
+      }
+    }
+
+    return ret;
+  }
+
+  // The core is loaded, remove it from the pendin gloads
+  protected void releasePending(String name) {
+    synchronized (locker) {
+      pendingDynamicLoads.remove(name);
+      locker.notifyAll();
+    }
+  }
+
+  protected void persistCores(ConfigSolr cfg, Map<String, SolrCore> whichCores, SolrResourceLoader loader) {
+    for (SolrCore solrCore : whichCores.values()) {
+      addPersistOneCore(cfg, solrCore, loader);
+    }
+  }
+
+  private void addIfNotNull(Map<String, String> coreAttribs, String key, String value) {
+    if (value == null) return;
+    coreAttribs.put(key, value);
+  }
+
+  protected void addPersistOneCore(ConfigSolr cfg, SolrCore solrCore, SolrResourceLoader loader) {
+
+    CoreDescriptor dcore = solrCore.getCoreDescriptor();
+
+    String coreName = dcore.getProperty(CoreDescriptor.CORE_NAME);
+
+    String origCoreName = null;
+
+    Map<String, String> coreAttribs = new HashMap<String, String>();
+    Properties persistProps = new Properties();
+    CloudDescriptor cd = dcore.getCloudDescriptor();
+    String collection = null;
+    if (cd  != null) collection = cd.getCollectionName();
+    String instDir = dcore.getRawInstanceDir();
+
+    if (cfg == null) {
+      addIfNotNull(coreAttribs, CoreDescriptor.CORE_NAME, coreName);
+      addIfNotNull(coreAttribs, CoreDescriptor.CORE_CONFIG, dcore.getDefaultConfigName());
+      addIfNotNull(coreAttribs, CoreDescriptor.CORE_SCHEMA, dcore.getDefaultSchemaName());
+      addIfNotNull(coreAttribs, CoreDescriptor.CORE_DATADIR, dcore.getProperty(CoreDescriptor.CORE_DATADIR));
+      addIfNotNull(coreAttribs, CoreDescriptor.CORE_ULOGDIR, dcore.getProperty(CoreDescriptor.CORE_ULOGDIR));
+      addIfNotNull(coreAttribs, CoreDescriptor.CORE_TRANSIENT, dcore.getProperty(CoreDescriptor.CORE_TRANSIENT));
+      addIfNotNull(coreAttribs, CoreDescriptor.CORE_LOADONSTARTUP, dcore.getProperty(CoreDescriptor.CORE_LOADONSTARTUP));
+      // we don't try and preserve sys prop defs in these
+
+      addIfNotNull(coreAttribs, CoreDescriptor.CORE_PROPERTIES, dcore.getPropertiesName());
+      // Add in any non-standard bits of data
+      Set<String> std = new TreeSet<String>();
+
+      Properties allProps = dcore.getCoreProperties();
+
+      std.addAll(Arrays.asList(CoreDescriptor.standardPropNames));
+
+      for (String prop : allProps.stringPropertyNames()) {
+        if (! std.contains(prop)) {
+          persistProps.put(prop, dcore.getProperty(prop));
+        }
+      }
+      if (StringUtils.isNotBlank(collection) && !collection.equals(coreName)) {
+        coreAttribs.put(CoreDescriptor.CORE_COLLECTION, collection);
+      }
+
+    } else {
+
+      origCoreName = getCoreToOrigName(solrCore);
+
+      if (origCoreName == null) {
+        origCoreName = coreName;
+      }
+      String tmp = cfg.getCoreNameFromOrig(origCoreName, loader, coreName);
+      if (tmp != null) coreName = tmp;
+
+      coreAttribs = cfg.readCoreAttributes(origCoreName);
+      persistProps = cfg.readCoreProperties(origCoreName);
+      if (coreAttribs != null) {
+        coreAttribs.put(CoreDescriptor.CORE_NAME, coreName);
+        if (coreAttribs.containsKey(CoreDescriptor.CORE_COLLECTION)) collection = coreAttribs.get(CoreDescriptor.CORE_COLLECTION);
+        if (coreAttribs.containsKey(CoreDescriptor.CORE_INSTDIR)) instDir = coreAttribs.get(CoreDescriptor.CORE_INSTDIR);
+      }
+      addIfNotNull(coreAttribs, CoreDescriptor.CORE_INSTDIR, dcore.getRawInstanceDir());
+      coreAttribs.put(CoreDescriptor.CORE_COLLECTION, StringUtils.isNotBlank(collection) ? collection : dcore.getName());
+
+    }
+
+    // Default value here is same as old code.
+    addIfNotNull(coreAttribs, CoreDescriptor.CORE_INSTDIR, instDir);
+
+    // Emulating the old code, just overwrite shard and roles if present in the cloud descriptor
+    if (cd != null) {
+      addIfNotNull(coreAttribs, CoreDescriptor.CORE_SHARD, cd.getShardId());
+      addIfNotNull(coreAttribs, CoreDescriptor.CORE_ROLES, cd.getRoles());
+    }
+    coreAttribs.put(CoreDescriptor.CORE_LOADONSTARTUP, Boolean.toString(dcore.isLoadOnStartup()));
+    coreAttribs.put(CoreDescriptor.CORE_TRANSIENT, Boolean.toString(dcore.isTransient()));
+
+    // Now add back in any implicit properties that aren't in already. These are all "attribs" in this meaning
+    Properties implicit = dcore.initImplicitProperties();
+
+    if (! coreName.equals(container.getDefaultCoreName())) {
+      for (String prop : implicit.stringPropertyNames()) {
+        if (coreAttribs.get(prop) == null) {
+          coreAttribs.put(prop, implicit.getProperty(prop));
+        }
+      }
+    }
+    if (cfg != null) {
+      cfg.addPersistCore(coreName, persistProps, coreAttribs);
+    } else {
+      // Another awkward bit for back-compat for SOLR-4196
+      ConfigSolrXmlBackCompat.addPersistCore(persistProps, coreAttribs);
+    }
+  }
+
+  protected Object getLocker() { return locker; }
+
+  // Be a little careful. We don't want to either open or close a core unless it's _not_ being opened or closed by
+  // another thread. So within this lock we'll walk along the list of pending closes until we find something NOT in
+  // the list of threads currently being opened. The "usual" case will probably return the very first one anyway..
+  protected SolrCore getCoreToClose() {
+    synchronized (locker) {
+      if (pendingDynamicCloses.size() == 0) return null; // nothing to do.
+      // Yes, a linear search but this is a pretty short list in the normal case and usually we'll take the first one.
+      for (SolrCore core : pendingDynamicCloses) {
+        if (! pendingDynamicLoads.contains(core.getName())) {  // Don't try close a core if it's being opened.
+          return core;
+        }
+      }
+    }
+    return null;
+  }
+
+  protected void removeClosedFromCloser(SolrCore core) {
+    synchronized (locker) {
+      pendingDynamicCloses.remove(core);
+      locker.notifyAll();
+    }
+  }
+}
+
+class CloserThread extends Thread {
+  CoreContainer container;
+  CoreMaps coreMaps;
+  ConfigSolr cfg;
+
+
+  CloserThread(CoreContainer container, CoreMaps coreMaps, ConfigSolr cfg) {
+    this.container = container;
+    this.coreMaps = coreMaps;
+    this.cfg = cfg;
+  }
+
+  // It's important that this be the _only_ thread removing things from pendingDynamicCloses!
+  // This is single-threaded, but I tried a multi-threaded approach and didn't see any performance gains, so
+  // there's no good justification for the complexity. I suspect that the locking on things like DefaultSolrCoreState
+  // essentially create a single-threaded process anyway.
+  @Override
+  public void run() {
+    while (! container.isShutDown()) {
+      synchronized (coreMaps.getLocker()) { // need this so we can wait and be awoken.
+        try {
+          coreMaps.getLocker().wait();
+        } catch (InterruptedException e) {
+          // Well, if we've been told to stop, we will. Otherwise, continue on and check to see if there are
+          // any cores to close.
+        }
+      }
+      for (SolrCore removeMe = coreMaps.getCoreToClose();
+           removeMe != null && !container.isShutDown();
+           removeMe = coreMaps.getCoreToClose()) {
+        try {
+          coreMaps.addPersistOneCore(cfg, removeMe, container.loader);
+          removeMe.close();
+        } finally {
+          coreMaps.removeClosedFromCloser(removeMe);
+        }
+      }
+    }
   }
-  
-  private static final String DEF_SOLR_XML ="<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
-          "<solr persistent=\"false\">\n" +
-          "  <cores adminPath=\"/admin/cores\" defaultCoreName=\"" + DEFAULT_DEFAULT_CORE_NAME + "\">\n" +
-          "    <core name=\""+ DEFAULT_DEFAULT_CORE_NAME + "\" shard=\"${shard:}\" instanceDir=\"collection1\" />\n" +
-          "  </cores>\n" +
-          "</solr>";
 }

Modified: lucene/dev/branches/lucene4258/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4258/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java?rev=1454862&r1=1454861&r2=1454862&view=diff
==============================================================================
--- lucene/dev/branches/lucene4258/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java (original)
+++ lucene/dev/branches/lucene4258/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java Sun Mar 10 15:04:57 2013
@@ -20,6 +20,7 @@ package org.apache.solr.core;
 import java.util.Properties;
 import java.io.File;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.solr.cloud.CloudDescriptor;
 
 /**
@@ -28,29 +29,93 @@ import org.apache.solr.cloud.CloudDescri
  * @since solr 1.3
  */
 public class CoreDescriptor {
-  protected String name;
-  protected String instanceDir;
-  protected String dataDir;
-  protected String ulogDir;
-  protected String configName;
-  protected String propertiesName;
-  protected String schemaName;
-  private final CoreContainer coreContainer;
-  private Properties coreProperties;
-  private boolean loadOnStartup = true;
-  private boolean swappable = false;
 
+  // Properties file name constants
+  public static final String CORE_NAME = "name";
+  public static final String CORE_CONFIG = "config";
+  public static final String CORE_INSTDIR = "instanceDir";
+  public static final String CORE_DATADIR = "dataDir";
+  public static final String CORE_ULOGDIR = "ulogDir";
+  public static final String CORE_SCHEMA = "schema";
+  public static final String CORE_SHARD = "shard";
+  public static final String CORE_COLLECTION = "collection";
+  public static final String CORE_ROLES = "roles";
+  public static final String CORE_PROPERTIES = "properties";
+  public static final String CORE_LOADONSTARTUP = "loadOnStartup";
+  public static final String CORE_TRANSIENT = "transient";
+  public static final String CORE_NODE_NAME = "coreNodeName";
+
+  static final String[] standardPropNames = {
+      CORE_NAME,
+      CORE_CONFIG,
+      CORE_INSTDIR,
+      CORE_DATADIR,
+      CORE_ULOGDIR,
+      CORE_SCHEMA,
+      CORE_SHARD,
+      CORE_COLLECTION,
+      CORE_ROLES,
+      CORE_PROPERTIES,
+      CORE_LOADONSTARTUP,
+      CORE_TRANSIENT
+  };
+
+  // As part of moving away from solr.xml (see SOLR-4196), it's _much_ easier to keep these as properties than set
+  // them individually.
+  private Properties coreProperties = new Properties();
+
+  private final CoreContainer coreContainer;
 
   private CloudDescriptor cloudDesc;
 
-  public CoreDescriptor(CoreContainer coreContainer, String name, String instanceDir) {
-    this.coreContainer = coreContainer;
-    this.name = name;
-    
+  private CoreDescriptor(CoreContainer cont) {
+    // Just a place to put initialization since it's a pain to add to the descriptor in every c'tor.
+    this.coreContainer = cont;
+    coreProperties.put(CORE_LOADONSTARTUP, "true");
+    coreProperties.put(CORE_TRANSIENT, "false");
+
+  }
+  public CoreDescriptor(CoreContainer container, String name, String instanceDir) {
+    this(container);
+    doInit(name, instanceDir);
+  }
+
+
+  public CoreDescriptor(CoreDescriptor descr) {
+    this(descr.coreContainer);
+    coreProperties.put(CORE_INSTDIR, descr.getInstanceDir());
+    coreProperties.put(CORE_CONFIG, descr.getConfigName());
+    coreProperties.put(CORE_SCHEMA, descr.getSchemaName());
+    coreProperties.put(CORE_NAME, descr.getName());
+    coreProperties.put(CORE_DATADIR, descr.getDataDir());
+  }
+
+  /**
+   * CoreDescriptor - create a core descriptor given default properties from a core.properties file. This will be
+   * used in the "solr.xml-less (See SOLR-4196) world where there are no &lt;core&gt; &lt;/core&gt; tags at all, thus  much
+   * of the initialization that used to be done when reading solr.xml needs to be done here instead, particularly
+   * setting any defaults (e.g. schema.xml, directories, whatever).
+   *
+   * @param container - the CoreContainer that holds all the information about our cores, loaded, lazy etc.
+   * @param propsIn - A properties structure "core.properties" found while walking the file tree to discover cores.
+   *                  Any properties set in this param will overwrite the any defaults.
+   */
+  public CoreDescriptor(CoreContainer container, Properties propsIn) {
+    this(container);
+
+    // Set some default, normalize a directory or two
+    doInit(propsIn.getProperty(CORE_NAME), propsIn.getProperty(CORE_INSTDIR));
+
+    coreProperties.putAll(propsIn);
+  }
+
+  private void doInit(String name, String instanceDir) {
     if (name == null) {
       throw new RuntimeException("Core needs a name");
     }
-    
+
+    coreProperties.put(CORE_NAME, name);
+
     if(coreContainer != null && coreContainer.getZkController() != null) {
       this.cloudDesc = new CloudDescriptor();
       // cloud collection defaults to core name
@@ -61,27 +126,18 @@ public class CoreDescriptor {
       throw new NullPointerException("Missing required \'instanceDir\'");
     }
     instanceDir = SolrResourceLoader.normalizeDir(instanceDir);
-    this.instanceDir = instanceDir;
-    this.configName = getDefaultConfigName();
-    this.schemaName = getDefaultSchemaName();
-  }
-
-  public CoreDescriptor(CoreDescriptor descr) {
-    this.instanceDir = descr.instanceDir;
-    this.configName = descr.configName;
-    this.schemaName = descr.schemaName;
-    this.name = descr.name;
-    this.dataDir = descr.dataDir;
-    coreContainer = descr.coreContainer;
+    coreProperties.put(CORE_INSTDIR, instanceDir);
+    coreProperties.put(CORE_CONFIG, getDefaultConfigName());
+    coreProperties.put(CORE_SCHEMA, getDefaultSchemaName());
   }
 
-  private Properties initImplicitProperties() {
+  public Properties initImplicitProperties() {
     Properties implicitProperties = new Properties(coreContainer.getContainerProperties());
-    implicitProperties.setProperty("solr.core.name", name);
-    implicitProperties.setProperty("solr.core.instanceDir", instanceDir);
-    implicitProperties.setProperty("solr.core.dataDir", getDataDir());
-    implicitProperties.setProperty("solr.core.configName", configName);
-    implicitProperties.setProperty("solr.core.schemaName", schemaName);
+    implicitProperties.setProperty(CORE_NAME, getName());
+    implicitProperties.setProperty(CORE_INSTDIR, getInstanceDir());
+    implicitProperties.setProperty(CORE_DATADIR, getDataDir());
+    implicitProperties.setProperty(CORE_CONFIG, getConfigName());
+    implicitProperties.setProperty(CORE_SCHEMA, getSchemaName());
     return implicitProperties;
   }
 
@@ -101,41 +157,47 @@ public class CoreDescriptor {
   }
 
   public String getPropertiesName() {
-    return propertiesName;
+    return coreProperties.getProperty(CORE_PROPERTIES);
   }
 
   public void setPropertiesName(String propertiesName) {
-    this.propertiesName = propertiesName;
+    coreProperties.put(CORE_PROPERTIES, propertiesName);
   }
 
   public String getDataDir() {
-    String dataDir = this.dataDir;
-    if (dataDir == null) dataDir = getDefaultDataDir();
+    String dataDir = coreProperties.getProperty(CORE_DATADIR);
+    if (dataDir == null) {
+      dataDir = getDefaultDataDir();
+    }
     if (new File(dataDir).isAbsolute()) {
       return dataDir;
     } else {
-      if (new File(instanceDir).isAbsolute()) {
-        return SolrResourceLoader.normalizeDir(SolrResourceLoader.normalizeDir(instanceDir) + dataDir);
+      if (new File(getInstanceDir()).isAbsolute()) {
+        return SolrResourceLoader.normalizeDir(SolrResourceLoader.normalizeDir(getInstanceDir()) + dataDir);
       } else  {
         return SolrResourceLoader.normalizeDir(coreContainer.getSolrHome() +
-                SolrResourceLoader.normalizeDir(instanceDir) + dataDir);
+                SolrResourceLoader.normalizeDir(getRawInstanceDir()) + dataDir);
       }
     }
   }
 
   public void setDataDir(String s) {
-    dataDir = s;
     // normalize zero length to null.
-    if (dataDir != null && dataDir.length()==0) dataDir=null;
+    if (StringUtils.isBlank(s)) {
+      coreProperties.remove(s);
+    } else {
+      coreProperties.put(CORE_DATADIR, s);
+    }
   }
   
   public boolean usingDefaultDataDir() {
-    return this.dataDir == null;
+    // DO NOT use the getDataDir method here since it'll assign something regardless.
+    return coreProperties.getProperty(CORE_DATADIR) == null;
   }
 
   /**@return the core instance directory. */
   public String getRawInstanceDir() {
-    return this.instanceDir;
+    return coreProperties.getProperty(CORE_INSTDIR);
   }
 
   /**
@@ -143,42 +205,44 @@ public class CoreDescriptor {
    * @return the core instance directory, prepended with solr_home if not an absolute path.
    */
   public String getInstanceDir() {
-    String instDir = this.instanceDir;
+    String instDir = coreProperties.getProperty(CORE_INSTDIR);
     if (instDir == null) return null; // No worse than before.
 
     if (new File(instDir).isAbsolute()) {
-      return SolrResourceLoader.normalizeDir(SolrResourceLoader.normalizeDir(instanceDir));
+      return SolrResourceLoader.normalizeDir(
+          SolrResourceLoader.normalizeDir(instDir));
     }
     return SolrResourceLoader.normalizeDir(coreContainer.getSolrHome() +
         SolrResourceLoader.normalizeDir(instDir));
   }
+
   /**Sets the core configuration resource name. */
   public void setConfigName(String name) {
     if (name == null || name.length() == 0)
       throw new IllegalArgumentException("name can not be null or empty");
-    this.configName = name;
+    coreProperties.put(CORE_CONFIG, name);
   }
 
   /**@return the core configuration resource name. */
   public String getConfigName() {
-    return this.configName;
+    return coreProperties.getProperty(CORE_CONFIG);
   }
 
   /**Sets the core schema resource name. */
   public void setSchemaName(String name) {
     if (name == null || name.length() == 0)
       throw new IllegalArgumentException("name can not be null or empty");
-    this.schemaName = name;
+    coreProperties.put(CORE_SCHEMA, name);
   }
 
   /**@return the core schema resource name. */
   public String getSchemaName() {
-    return this.schemaName;
+    return coreProperties.getProperty(CORE_SCHEMA);
   }
 
   /**@return the initial core name */
   public String getName() {
-    return this.name;
+    return coreProperties.getProperty(CORE_NAME);
   }
 
   public CoreContainer getCoreContainer() {
@@ -192,15 +256,19 @@ public class CoreDescriptor {
   /**
    * Set this core's properties. Please note that some implicit values will be added to the
    * Properties instance passed into this method. This means that the Properties instance
-   * set to this method will have different (less) key/value pairs than the Properties
+   * sent to this method will have different (less) key/value pairs than the Properties
    * instance returned by #getCoreProperties method.
+   *
+   * Under any circumstance, the properties passed in will override any already present.Merge
    */
   public void setCoreProperties(Properties coreProperties) {
     if (this.coreProperties == null) {
       Properties p = initImplicitProperties();
       this.coreProperties = new Properties(p);
-      if(coreProperties != null)
-        this.coreProperties.putAll(coreProperties);
+    }
+    // The caller presumably wants whatever properties passed in to override the current core props, so just add them.
+    if(coreProperties != null) {
+      this.coreProperties.putAll(coreProperties);
     }
   }
 
@@ -212,26 +280,59 @@ public class CoreDescriptor {
     this.cloudDesc = cloudDesc;
   }
   public boolean isLoadOnStartup() {
-    return loadOnStartup;
+    String tmp = coreProperties.getProperty(CORE_LOADONSTARTUP, "false");
+    return Boolean.parseBoolean(tmp);
   }
 
   public void setLoadOnStartup(boolean loadOnStartup) {
-    this.loadOnStartup = loadOnStartup;
+    coreProperties.put(CORE_LOADONSTARTUP, Boolean.toString(loadOnStartup));
   }
 
-  public boolean isSwappable() {
-    return swappable;
+  public boolean isTransient() {
+    String tmp = coreProperties.getProperty(CORE_TRANSIENT, "false");
+    return (Boolean.parseBoolean(tmp));
   }
 
-  public void setSwappable(boolean swappable) {
-    this.swappable = swappable;
+  public void setTransient(boolean isTransient) {
+    coreProperties.put(CORE_TRANSIENT, Boolean.toString(isTransient));
   }
 
   public String getUlogDir() {
-    return ulogDir;
+    return coreProperties.getProperty(CORE_ULOGDIR);
   }
 
   public void setUlogDir(String ulogDir) {
-    this.ulogDir = ulogDir;
+    coreProperties.put(CORE_ULOGDIR, ulogDir);
+  }
+
+  /**
+   * Reads a property defined in the core.properties file that's replacing solr.xml (if present).
+   * @param prop    - value to read from the properties structure.
+   * @param defVal  - return if no property found.
+   * @return associated string. May be null.
+   */
+  public String getProperty(String prop, String defVal) {
+    return coreProperties.getProperty(prop, defVal);
+  }
+
+  /**
+   * gReads a property defined in the core.properties file that's replacing solr.xml (if present).
+   * @param prop  value to read from the properties structure.
+   * @return associated string. May be null.
+   */
+  public String getProperty(String prop) {
+    return coreProperties.getProperty(prop);
+  }
+  /**
+   * This will eventually replace _all_ of the setters. Puts a value in the "new" (obsoleting solr.xml JIRAs) properties
+   * structures.
+   *
+   * Will replace any currently-existing property with the key "prop".
+   *
+   * @param prop - property name
+   * @param val  - property value
+   */
+  public void putProperty(String prop, String val) {
+    coreProperties.put(prop, val);
   }
 }



Mime
View raw message