org.apache.solr.core.CoreContainer.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.core.CoreContainer.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.solr.core;

import java.io.*;
import java.nio.channels.FileChannel;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;
import java.text.SimpleDateFormat;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;

import org.apache.solr.cloud.CloudDescriptor;
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.cloud.ZooKeeperException;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.util.DOMUtil;
import org.apache.solr.common.util.XML;
import org.apache.solr.common.util.FileUtils;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.schema.IndexSchema;
import org.apache.zookeeper.KeeperException;
import org.apache.commons.io.IOUtils;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * @version $Id: CoreContainer.java 1054157 2010-12-31 18:36:14Z yonik $
 * @since solr 1.3
 */
public class CoreContainer {
    private static final String DEFAULT_DEFAULT_CORE_NAME = "collection1";

    protected static Logger log = LoggerFactory.getLogger(CoreContainer.class);

    protected final Map<String, SolrCore> cores = new LinkedHashMap<String, SolrCore>();
    protected boolean persistent = false;
    protected String adminPath = null;
    protected String managementPath = null;
    protected String hostPort;
    protected String hostContext;
    protected String host;
    protected CoreAdminHandler coreAdminHandler = null;
    protected File configFile = null;
    protected String libDir = null;
    protected ClassLoader libLoader = null;
    protected SolrResourceLoader loader = null;
    protected Properties containerProperties;
    protected Map<String, IndexSchema> indexSchemaCache;
    protected String adminHandler;
    protected boolean shareSchema;
    protected String solrHome;
    protected String defaultCoreName = "";
    private ZkController zkController;
    private SolrZkServer zkServer;

    private String zkHost;

    {
        log.info("New CoreContainer " + System.identityHashCode(this));
    }

    public CoreContainer() {
        solrHome = SolrResourceLoader.locateSolrHome();
    }

    /**
     * Initalize CoreContainer directly from the constructor
     *
     * @param dir
     * @param configFile
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public CoreContainer(String dir, File configFile)
            throws ParserConfigurationException, IOException, SAXException {
        this.load(dir, configFile);
    }

    /**
     * Minimal CoreContainer constructor.
     * @param loader the CoreContainer resource loader
     */
    public CoreContainer(SolrResourceLoader loader) {
        this.loader = loader;
        this.solrHome = loader.getInstanceDir();
    }

    public CoreContainer(String solrHome) {
        this.solrHome = solrHome;
    }

    protected void initZooKeeper(String zkHost, int zkClientTimeout) {
        // if zkHost sys property is not set, we are not using ZooKeeper
        String zookeeperHost;
        if (zkHost == null) {
            zookeeperHost = System.getProperty("zkHost");
        } else {
            zookeeperHost = zkHost;
        }

        String zkRun = System.getProperty("zkRun");

        if (zkRun == null && zookeeperHost == null)
            return; // not in zk mode

        zkServer = new SolrZkServer(zkRun, zookeeperHost, solrHome, hostPort);
        zkServer.parseConfig();
        zkServer.start();

        // set client from server config if not already set
        if (zookeeperHost == null) {
            zookeeperHost = zkServer.getClientString();
        }

        int zkClientConnectTimeout = 5000;

        if (zookeeperHost != null) {
            // we are ZooKeeper enabled
            try {
                // If this is an ensemble, allow for a long connect time for other servers to come up
                if (zkRun != null && zkServer.getServers().size() > 1) {
                    zkClientConnectTimeout = 24 * 60 * 60 * 1000; // 1 day for embedded ensemble
                    log.info("Zookeeper client=" + zookeeperHost + "  Waiting for a quorum.");
                } else {
                    log.info("Zookeeper client=" + zookeeperHost);
                }
                zkController = new ZkController(zookeeperHost, zkClientTimeout, zkClientConnectTimeout, host,
                        hostPort, hostContext);

                String confDir = System.getProperty("bootstrap_confdir");
                if (confDir != null) {
                    File dir = new File(confDir);
                    if (!dir.isDirectory()) {
                        throw new IllegalArgumentException(
                                "bootstrap_confdir must be a directory of configuration files");
                    }
                    String confName = System.getProperty(
                            ZkController.COLLECTION_PARAM_PREFIX + ZkController.CONFIGNAME_PROP, "configuration1");
                    zkController.uploadConfigDir(dir, confName);
                }
            } catch (InterruptedException e) {
                // Restore the interrupted status
                Thread.currentThread().interrupt();
                log.error("", e);
                throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            } catch (TimeoutException e) {
                log.error("Could not connect to ZooKeeper", e);
                throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            } catch (IOException e) {
                log.error("", e);
                throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            } catch (KeeperException e) {
                log.error("", e);
                throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            }
        }

    }

    public Properties getContainerProperties() {
        return containerProperties;
    }

    // Helper class to initialize the CoreContainer
    public static class Initializer {
        protected String solrConfigFilename = null;
        protected String dataDir = null; // override datadir for single core mode

        /**
         * @deprecated all cores now abort on configuration error regardless of configuration
         */
        @Deprecated
        public boolean isAbortOnConfigurationError() {
            return true;
        }

        /**
         * @exception generates an error if you attempt to set this value to false
         * @deprecated all cores now abort on configuration error regardless of configuration
         */
        @Deprecated
        public void setAbortOnConfigurationError(boolean abortOnConfigurationError) {
            if (false == abortOnConfigurationError)
                throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
                        "Setting abortOnConfigurationError==false is no longer supported");
        }

        // core container instantiation
        public CoreContainer initialize() throws IOException, ParserConfigurationException, SAXException {
            CoreContainer cores = null;
            String solrHome = SolrResourceLoader.locateSolrHome();
            // TODO : fix broken logic confusing solr.xml with solrconfig.xml
            File fconf = new File(solrHome, solrConfigFilename == null ? "solr.xml" : solrConfigFilename);
            log.info("looking for solr.xml: " + fconf.getAbsolutePath());
            cores = new CoreContainer();

            if (fconf.exists()) {
                cores.load(solrHome, fconf);
            } else {
                log.info("no solr.xml file found - using default");
                cores.load(solrHome, new ByteArrayInputStream(DEF_SOLR_XML.getBytes()));
                cores.configFile = fconf;
            }

            solrConfigFilename = cores.getConfigFile().getName();

            return cores;
        }
    }

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

    //-------------------------------------------------------------------
    // Initialization / Cleanup
    //-------------------------------------------------------------------

    /**
     * Load a config file listing the available solr cores.
     * @param dir the home directory of all resources.
     * @param configFile the configuration file
     * @throws javax.xml.parsers.ParserConfigurationException
     * @throws java.io.IOException
     * @throws org.xml.sax.SAXException
     */
    public void load(String dir, File configFile) throws ParserConfigurationException, IOException, SAXException {
        this.configFile = configFile;
        this.load(dir, new FileInputStream(configFile));
    }

    /**
     * Load a config file listing the available solr cores.
     * 
     * @param dir the home directory of all resources.
     * @param cfgis the configuration file InputStream
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public void load(String dir, InputStream cfgis) throws ParserConfigurationException, IOException, SAXException {
        this.loader = new SolrResourceLoader(dir);
        solrHome = loader.getInstanceDir();
        try {
            Config cfg = new Config(loader, null, cfgis, null);
            String dcoreName = cfg.get("solr/cores/@defaultCoreName", null);
            if (dcoreName != null) {
                defaultCoreName = dcoreName;
            }
            persistent = cfg.getBool("solr/@persistent", false);
            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);
            int zkClientTimeout = cfg.getInt("solr/cores/@zkClientTimeout", 10000);

            hostPort = System.getProperty("hostPort");
            if (hostPort == null) {
                hostPort = cfg.get("solr/cores/@hostPort", "8983");
            }

            hostContext = cfg.get("solr/cores/@hostContext", "solr");
            host = cfg.get("solr/cores/@host", null);

            if (shareSchema) {
                indexSchemaCache = new ConcurrentHashMap<String, IndexSchema>();
            }
            adminHandler = cfg.get("solr/cores/@adminHandler", null);
            managementPath = cfg.get("solr/cores/@managementPath", null);

            zkClientTimeout = Integer
                    .parseInt(System.getProperty("zkClientTimeout", Integer.toString(zkClientTimeout)));
            initZooKeeper(zkHost, zkClientTimeout);

            if (libDir != null) {
                File f = FileUtils.resolvePath(new File(dir), libDir);
                log.info("loading shared library: " + f.getAbsolutePath());
                libLoader = SolrResourceLoader.createClassLoader(f, null);
            }

            if (adminPath != null) {
                if (adminHandler == null) {
                    coreAdminHandler = new CoreAdminHandler(this);
                } else {
                    coreAdminHandler = this.createMultiCoreHandler(adminHandler);
                }
            }

            try {
                containerProperties = readProperties(cfg,
                        ((NodeList) cfg.evaluate("solr", XPathConstants.NODESET)).item(0));
            } catch (Throwable e) {
                SolrConfig.severeErrors.add(e);
                SolrException.logOnce(log, null, e);
            }

            NodeList nodes = (NodeList) cfg.evaluate("solr/cores/core", XPathConstants.NODESET);
            boolean defaultCoreFound = false;
            for (int i = 0; i < nodes.getLength(); i++) {
                Node node = nodes.item(i);
                try {
                    String name = DOMUtil.getAttr(node, "name", null);
                    if (null == name) {
                        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
                                "Each core in solr.xml must have a 'name'");
                    }
                    if (name.equals(defaultCoreName)) {
                        // for the default core we use a blank name,
                        // later on attempts to access it by it's full name will 
                        // be mapped to this.
                        name = "";
                    }
                    CoreDescriptor p = new CoreDescriptor(this, name, DOMUtil.getAttr(node, "instanceDir", null));

                    // deal with optional settings
                    String opt = DOMUtil.getAttr(node, "config", null);

                    if (opt != null) {
                        p.setConfigName(opt);
                    }
                    opt = DOMUtil.getAttr(node, "schema", null);
                    if (opt != null) {
                        p.setSchemaName(opt);
                    }
                    if (zkController != null) {
                        opt = DOMUtil.getAttr(node, "shard", null);
                        if (opt != null && opt.length() > 0) {
                            p.getCloudDescriptor().setShardId(opt);
                        }
                        opt = DOMUtil.getAttr(node, "collection", null);
                        if (opt != null) {
                            p.getCloudDescriptor().setCollectionName(opt);
                        }
                    }
                    opt = DOMUtil.getAttr(node, "properties", null);
                    if (opt != null) {
                        p.setPropertiesName(opt);
                    }
                    opt = DOMUtil.getAttr(node, CoreAdminParams.DATA_DIR, null);
                    if (opt != null) {
                        p.setDataDir(opt);
                    }

                    p.setCoreProperties(readProperties(cfg, node));

                    SolrCore core = create(p);
                    register(name, core, false);
                } catch (Throwable ex) {
                    SolrConfig.severeErrors.add(ex);
                    SolrException.logOnce(log, null, ex);
                }
            }
        } finally {
            if (cfgis != null) {
                try {
                    cfgis.close();
                } catch (Exception xany) {
                }
            }
        }

        if (zkController != null) {
            try {
                synchronized (zkController.getZkStateReader().getUpdateLock()) {
                    zkController.getZkStateReader().makeShardZkNodeWatches(false);
                    zkController.getZkStateReader().updateCloudState(true);
                }
            } catch (InterruptedException e) {
                // Restore the interrupted status
                Thread.currentThread().interrupt();
                log.error("", e);
                throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            } catch (KeeperException e) {
                log.error("", e);
                throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            } catch (IOException e) {
                log.error("", e);
                throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            }
        }
    }

    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 boolean isShutDown = false;

    /**
     * Stops all cores.
     */
    public void shutdown() {
        log.info("Shutting down CoreContainer instance=" + System.identityHashCode(this));
        synchronized (cores) {
            try {
                for (SolrCore core : cores.values()) {
                    core.close();
                }
                cores.clear();
            } finally {
                if (zkController != null) {
                    zkController.close();
                }
                if (zkServer != null) {
                    zkServer.stop();
                }
                isShutDown = true;
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            if (!isShutDown) {
                log.error(
                        "CoreContainer was not shutdown prior to finalize(), indicates a bug -- POSSIBLE RESOURCE LEAK!!!  instance="
                                + System.identityHashCode(this));
                shutdown();
            }
        } finally {
            super.finalize();
        }
    }

    /**
     * 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) {
        if (core == null) {
            throw new RuntimeException("Can not register a null core.");
        }
        if (name == null || name.indexOf('/') >= 0 || name.indexOf('\\') >= 0) {
            throw new RuntimeException("Invalid core name: " + name);
        }

        SolrCore old = null;
        synchronized (cores) {
            old = cores.put(name, core);
            core.setName(name);
        }

        if (zkController != null) {
            try {
                zkController.register(core.getName(), core.getCoreDescriptor().getCloudDescriptor(), true);
            } catch (InterruptedException e) {
                // Restore the interrupted status
                Thread.currentThread().interrupt();
                log.error("", e);
                throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            } catch (KeeperException e) {
                log.error("", e);
                throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            } catch (IOException e) {
                log.error("", e);
                throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            }
        }

        if (old == null || old == core) {
            log.info("registering core: " + name);
            return null;
        } else {
            log.info("replacing core: " + name);
            if (!returnPrevNotClosed) {
                old.close();
            }
            return old;
        }
    }

    /**
     * Registers a SolrCore descriptor in the registry using the core's name.
     * If returnPrev==false, the old core, if different, is closed.
     * @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);
    }

    /**
     * Creates a new core based on a descriptor but does not register it.
     *
     * @param dcore a core descriptor
     * @return the newly created core
     * @throws javax.xml.parsers.ParserConfigurationException
     * @throws java.io.IOException
     * @throws org.xml.sax.SAXException
     */
    public SolrCore create(CoreDescriptor dcore) throws ParserConfigurationException, IOException, SAXException {
        // Make the instanceDir relative to the cores instanceDir if not absolute
        File idir = new File(dcore.getInstanceDir());
        if (!idir.isAbsolute()) {
            idir = new File(solrHome, dcore.getInstanceDir());
        }
        String instanceDir = idir.getPath();

        // Initialize the solr config
        SolrResourceLoader solrLoader = null;

        SolrConfig config = null;
        String zkConfigName = null;
        if (zkController == null) {
            solrLoader = new SolrResourceLoader(instanceDir, libLoader,
                    getCoreProps(instanceDir, dcore.getPropertiesName(), dcore.getCoreProperties()));
            config = new SolrConfig(solrLoader, dcore.getConfigName(), null);
        } else {
            try {
                String collection = dcore.getCloudDescriptor().getCollectionName();
                zkController.createCollectionZkNode(dcore.getCloudDescriptor());
                // zkController.createCollectionZkNode(collection);
                zkConfigName = zkController.readConfigName(collection);
                if (zkConfigName == null) {
                    log.error("Could not find config name for collection:" + collection);
                    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);
                config = getSolrConfigFromZk(zkConfigName, dcore.getConfigName(), solrLoader);
            } catch (KeeperException e) {
                log.error("", e);
                throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            } catch (InterruptedException e) {
                // Restore the interrupted status
                Thread.currentThread().interrupt();
                log.error("", e);
                throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
            }
        }

        IndexSchema schema = null;
        if (indexSchemaCache != null) {
            if (zkController != null) {
                File schemaFile = new File(dcore.getSchemaName());
                if (!schemaFile.isAbsolute()) {
                    schemaFile = new File(
                            solrLoader.getInstanceDir() + "conf" + File.separator + dcore.getSchemaName());
                }
                if (schemaFile.exists()) {
                    String key = schemaFile.getAbsolutePath() + ":"
                            + new SimpleDateFormat("yyyyMMddHHmmss", Locale.US)
                                    .format(new Date(schemaFile.lastModified()));
                    schema = indexSchemaCache.get(key);
                    if (schema == null) {
                        log.info("creating new schema object for core: " + dcore.name);
                        schema = new IndexSchema(config, dcore.getSchemaName(), null);
                        indexSchemaCache.put(key, schema);
                    } else {
                        log.info("re-using schema object for core: " + dcore.name);
                    }
                }
            } else {
                // TODO: handle caching from ZooKeeper - perhaps using ZooKeepers versioning
                // Don't like this cache though - how does it empty as last modified changes?
            }
        }
        if (schema == null) {
            if (zkController != null) {
                try {
                    schema = getSchemaFromZk(zkConfigName, dcore.getSchemaName(), config, solrLoader);
                } catch (KeeperException e) {
                    log.error("", e);
                    throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
                } catch (InterruptedException e) {
                    // Restore the interrupted status
                    Thread.currentThread().interrupt();
                    log.error("", e);
                    throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
                }
            } else {
                schema = new IndexSchema(config, dcore.getSchemaName(), null);
            }
        }
        String dataDir = null;

        SolrCore core = new SolrCore(dcore.getName(), dataDir, config, schema, dcore);
        return core;
    }

    /**
     * @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 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());
        }
        return lst;
    }

    /** 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());
                }
            }
        }
        return lst;
    }

    // ---------------- Core name related methods --------------- 
    /**
     * Recreates a SolrCore.
     * While the new core is loading, requests will continue to be dispatched to
     * and processed by the old core
     * 
     * @param name the name of the SolrCore to reload
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */

    public void reload(String name) throws ParserConfigurationException, IOException, SAXException {
        name = checkDefault(name);
        SolrCore core;
        synchronized (cores) {
            core = cores.get(name);
        }
        if (core == null)
            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No such core: " + name);

        SolrCore newCore = create(core.getCoreDescriptor());
        register(name, newCore, false);
    }

    private String checkDefault(String name) {
        return name.length() == 0 || defaultCoreName.equals(name) || name.trim().length() == 0 ? "" : name;
    }

    /**
     * Swaps two SolrCore descriptors.
     * @param n0
     * @param n1
     */
    public void swap(String n0, String n1) {
        if (n0 == null || n1 == null) {
            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Can not swap unnamed cores.");
        }
        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;
        }

        log.info("swaped: " + n0 + " with " + n1);
    }

    /** Removes and returns registered core w/o decrementing it's reference count */
    public SolrCore remove(String name) {
        name = checkDefault(name);
        synchronized (cores) {
            return cores.remove(name);
        }
    }

    /** Gets a core by name and increase its refcount.
     * @see SolrCore#open() 
     * @see SolrCore#close() 
     * @param name the core name
     * @return the core if found
     */
    public SolrCore getCore(String name) {
        name = checkDefault(name);
        synchronized (cores) {
            SolrCore core = cores.get(name);
            if (core != null)
                core.open(); // increment the ref count while still synchronized
            return core;
        }
    }

    // ---------------- Multicore self related methods ---------------
    /** 
     * Creates a CoreAdminHandler for this MultiCore.
     * @return a CoreAdminHandler
     */
    protected CoreAdminHandler createMultiCoreHandler(final String adminHandlerClass) {
        SolrResourceLoader loader = new SolrResourceLoader(null, libLoader, null);
        Object obj = loader.newAdminHandlerInstance(CoreContainer.this, adminHandlerClass);
        if (!(obj instanceof CoreAdminHandler)) {
            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
                    "adminHandlerClass is not of type " + CoreAdminHandler.class);

        }
        return (CoreAdminHandler) obj;
    }

    public CoreAdminHandler getMultiCoreHandler() {
        return coreAdminHandler;
    }

    public String getDefaultCoreName() {
        return defaultCoreName;
    }

    // all of the following properties aren't synchronized
    // but this should be OK since they normally won't be changed rapidly
    public boolean isPersistent() {
        return persistent;
    }

    public void setPersistent(boolean persistent) {
        this.persistent = persistent;
    }

    public String getAdminPath() {
        return adminPath;
    }

    public void setAdminPath(String adminPath) {
        this.adminPath = adminPath;
    }

    public String getManagementPath() {
        return managementPath;
    }

    /**
     * Sets the alternate path for multicore handling:
     * This is used in case there is a registered unnamed core (aka name is "") to
     * declare an alternate way of accessing named cores.
     * This can also be used in a pseudo single-core environment so admins can prepare
     * a new version before swapping.
     * @param path
     */
    public void setManagementPath(String path) {
        this.managementPath = path;
    }

    public File getConfigFile() {
        return configFile;
    }

    /** Persists the cores config file in cores.xml. */
    public void persist() {
        persistFile(null);
    }

    /** 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));

        File tmpFile = null;
        try {
            // write in temp first
            if (file == null) {
                file = tmpFile = File.createTempFile("solr", ".xml", configFile.getParentFile());
            }
            java.io.FileOutputStream out = new java.io.FileOutputStream(file);
            Writer writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
            persist(writer);
            writer.flush();
            writer.close();
            out.close();
            // rename over origin or copy it this fails
            if (tmpFile != null) {
                if (tmpFile.renameTo(configFile))
                    tmpFile = null;
                else
                    fileCopy(tmpFile, configFile);
            }
        } catch (java.io.FileNotFoundException xnf) {
            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, xnf);
        } catch (java.io.IOException xio) {
            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, xio);
        } finally {
            if (tmpFile != null) {
                if (!tmpFile.delete())
                    tmpFile.deleteOnExit();
            }
        }
    }

    /** Write the cores configuration through a writer.*/
    void persist(Writer w) throws IOException {
        w.write("<?xml version='1.0' encoding='UTF-8'?>");
        w.write("<solr");
        if (this.libDir != null) {
            writeAttribute(w, "sharedLib", libDir);
        }
        writeAttribute(w, "persistent", isPersistent());
        w.write(">\n");

        if (containerProperties != null && !containerProperties.isEmpty()) {
            writeProperties(w, containerProperties);
        }
        w.write("<cores");
        writeAttribute(w, "adminPath", adminPath);
        if (adminHandler != null)
            writeAttribute(w, "adminHandler", adminHandler);
        if (shareSchema)
            writeAttribute(w, "shareSchema", "true");
        if (!defaultCoreName.equals(""))
            writeAttribute(w, "defaultCoreName", defaultCoreName);
        w.write(">\n");

        synchronized (cores) {
            for (SolrCore solrCore : cores.values()) {
                persist(w, solrCore.getCoreDescriptor());
            }
        }

        w.write("</cores>\n");
        w.write("</solr>\n");
    }

    private void writeAttribute(Writer w, String name, Object value) throws IOException {
        if (value == null)
            return;
        w.write(" ");
        w.write(name);
        w.write("=\"");
        XML.escapeAttributeValue(value.toString(), w);
        w.write("\"");
    }

    /** Writes the cores configuration node for a given core. */
    void persist(Writer w, CoreDescriptor dcore) throws IOException {
        w.write("  <core");
        writeAttribute(w, "name", dcore.name);
        writeAttribute(w, "instanceDir", dcore.getInstanceDir());
        //write config (if not default)
        String opt = dcore.getConfigName();
        if (opt != null && !opt.equals(dcore.getDefaultConfigName())) {
            writeAttribute(w, "config", opt);
        }
        //write schema (if not default)
        opt = dcore.getSchemaName();
        if (opt != null && !opt.equals(dcore.getDefaultSchemaName())) {
            writeAttribute(w, "schema", opt);
        }
        opt = dcore.getPropertiesName();
        if (opt != null) {
            writeAttribute(w, "properties", opt);
        }
        opt = dcore.dataDir;
        if (opt != null)
            writeAttribute(w, "dataDir", opt);

        CloudDescriptor cd = dcore.getCloudDescriptor();
        if (cd != null) {
            opt = cd.getShardId();
            if (opt != null)
                writeAttribute(w, "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))
                writeAttribute(w, "collection", opt);
        }

        if (dcore.getCoreProperties() == null || dcore.getCoreProperties().isEmpty())
            w.write("/>\n"); // core
        else {
            w.write(">\n");
            writeProperties(w, dcore.getCoreProperties());
            w.write("</core>");
        }
    }

    private void writeProperties(Writer w, Properties props) throws IOException {
        for (Map.Entry<Object, Object> entry : props.entrySet()) {
            w.write("<property");
            writeAttribute(w, "name", entry.getKey());
            writeAttribute(w, "value", entry.getValue());
            w.write("/>\n");
        }
    }

    /** Copies a src file to a dest file:
     *  used to circumvent the platform discrepancies regarding renaming files.
     */
    public static void fileCopy(File src, File dest) throws IOException {
        IOException xforward = null;
        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileChannel fcin = null;
        FileChannel fcout = null;
        try {
            fis = new FileInputStream(src);
            fos = new FileOutputStream(dest);
            fcin = fis.getChannel();
            fcout = fos.getChannel();
            // do the file copy 32Mb at a time
            final int MB32 = 32 * 1024 * 1024;
            long size = fcin.size();
            long position = 0;
            while (position < size) {
                position += fcin.transferTo(position, MB32, fcout);
            }
        } catch (IOException xio) {
            xforward = xio;
        } finally {
            if (fis != null)
                try {
                    fis.close();
                    fis = null;
                } catch (IOException xio) {
                }
            if (fos != null)
                try {
                    fos.close();
                    fos = null;
                } catch (IOException xio) {
                }
            if (fcin != null && fcin.isOpen())
                try {
                    fcin.close();
                    fcin = null;
                } catch (IOException xio) {
                }
            if (fcout != null && fcout.isOpen())
                try {
                    fcout.close();
                    fcout = null;
                } catch (IOException xio) {
                }
        }
        if (xforward != null) {
            throw xforward;
        }
    }

    public String getSolrHome() {
        return solrHome;
    }

    public boolean isZooKeeperAware() {
        return zkController != null;
    }

    public ZkController getZkController() {
        return zkController;
    }

    private SolrConfig getSolrConfigFromZk(String zkConfigName, String solrConfigFileName,
            SolrResourceLoader resourceLoader)
            throws IOException, ParserConfigurationException, SAXException, KeeperException, InterruptedException {
        byte[] config = zkController.getConfigFileData(zkConfigName, solrConfigFileName);
        InputStream is = new ByteArrayInputStream(config);
        SolrConfig cfg = solrConfigFileName == null
                ? new SolrConfig(resourceLoader, SolrConfig.DEFAULT_CONF_FILE, is)
                : new SolrConfig(resourceLoader, solrConfigFileName, is);

        return cfg;
    }

    private IndexSchema getSchemaFromZk(String zkConfigName, String schemaName, SolrConfig config,
            SolrResourceLoader resourceLoader) throws KeeperException, InterruptedException {
        byte[] configBytes = zkController.getConfigFileData(zkConfigName, schemaName);
        InputStream is = new ByteArrayInputStream(configBytes);
        IndexSchema schema = new IndexSchema(config, schemaName, is);
        return schema;
    }

    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
            + "\" instanceDir=\".\" />\n" + "  </cores>\n" + "</solr>";
}