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

Java tutorial

Introduction

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

Source

package org.apache.solr.core;

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

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.xpath.XPathExpressionException;

import org.apache.commons.lang.StringUtils;
import org.apache.solr.cloud.CloudDescriptor;
import org.apache.solr.common.SolrException;
import org.apache.solr.core.SolrXMLSerializer.SolrCoreXMLDef;
import org.apache.solr.util.DOMUtil;
import org.w3c.dom.Node;

class SolrCores {
    private static SolrXMLSerializer SOLR_XML_SERIALIZER = new SolrXMLSerializer();
    private static Object modifyLock = 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 final Map<String, SolrCore> createdCores = new LinkedHashMap<String, SolrCore>();

    private Map<SolrCore, String> coreToOrigName = new ConcurrentHashMap<SolrCore, String>();

    private final CoreContainer container;

    // This map will hold objects that are being currently operated on. The core (value) may be null in the case of
    // initial load. The rule is, never to any operation on a core that is currently being operated upon.
    private static final Set<String> pendingCoreOps = new HashSet<String>();

    // Due to the fact that closes happen potentially whenever anything is _added_ to the transient core list, we need
    // to essentially queue them up to be handled via pendingCoreOps.
    private static final List<SolrCore> pendingCloses = new ArrayList<SolrCore>();

    SolrCores(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) {
        final int transientCacheSize = cfg.getInt(ConfigSolr.CfgProp.SOLR_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 (modifyLock) {
                            pendingCloses.add(eldest.getValue()); // Essentially just queue this core up for closing.
                            modifyLock.notifyAll(); // Wakes up closer thread too
                        }
                        return true;
                    }
                    return false;
                }
            };
        }
    }

    protected void putDynamicDescriptor(String rawName, CoreDescriptor p) {
        synchronized (modifyLock) {
            dynamicDescriptors.put(rawName, p);
        }
    }

    // We are shutting down. You can't hold the lock on the various lists of cores while they shut down, so we need to
    // make a temporary copy of the names and shut them down outside the lock.
    protected void close() {
        List<String> coreNames;
        List<String> transientNames;
        List<SolrCore> pendingToClose;

        // It might be possible for one of the cores to move from one list to another while we're closing them. So
        // loop through the lists until they're all empty. In particular, the core could have moved from the transient
        // list to the pendingCloses list.

        while (true) {
            synchronized (modifyLock) {
                coreNames = new ArrayList<String>(cores.keySet());
                transientNames = new ArrayList<String>(transientCores.keySet());
                pendingToClose = new ArrayList<SolrCore>(pendingCloses);
            }

            if (coreNames.size() == 0 && transientNames.size() == 0 && pendingToClose.size() == 0)
                break;

            for (String coreName : coreNames) {
                SolrCore core = cores.get(coreName);
                if (core == null) {
                    CoreContainer.log.info("Core " + coreName + " moved from core container list before closing.");
                } else {
                    try {
                        core.close();
                    } catch (Throwable t) {
                        SolrException.log(CoreContainer.log, "Error shutting down core", t);
                    } finally {
                        synchronized (modifyLock) {
                            cores.remove(coreName);
                        }
                    }
                }
            }

            for (String coreName : transientNames) {
                SolrCore core = transientCores.get(coreName);
                if (core == null) {
                    CoreContainer.log
                            .info("Core " + coreName + " moved from transient core container list before closing.");
                } else {
                    try {
                        core.close();
                    } catch (Throwable t) {
                        SolrException.log(CoreContainer.log, "Error shutting down core", t);
                    } finally {
                        synchronized (modifyLock) {
                            transientCores.remove(coreName);
                        }
                    }
                }
            }

            // We might have some cores that we were _thinking_ about shutting down, so take care of those too.
            for (SolrCore core : pendingToClose) {
                try {
                    core.close();
                } catch (Throwable t) {
                    SolrException.log(CoreContainer.log, "Error shutting down core", t);
                } finally {
                    synchronized (modifyLock) {
                        pendingCloses.remove(core);
                    }
                }
            }
        }
    }

    //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 (modifyLock) {
            retCore = transientCores.put(name, core);
        }
        return retCore;
    }

    protected SolrCore putCore(String name, SolrCore core) {
        synchronized (modifyLock) {
            return cores.put(name, core);
        }
    }

    List<SolrCore> getCores() {
        List<SolrCore> lst = new ArrayList<SolrCore>();

        synchronized (modifyLock) {
            lst.addAll(cores.values());
            return lst;
        }
    }

    Set<String> getCoreNames() {
        Set<String> set = new TreeSet<String>();

        synchronized (modifyLock) {
            set.addAll(cores.keySet());
            set.addAll(transientCores.keySet());
        }
        return set;
    }

    List<String> getCoreNames(SolrCore core) {
        List<String> lst = new ArrayList<String>();

        synchronized (modifyLock) {
            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 (modifyLock) {
            set.addAll(cores.keySet());
            set.addAll(transientCores.keySet());
            set.addAll(dynamicDescriptors.keySet());
            set.addAll(createdCores.keySet());
        }
        return set;
    }

    SolrCore getCore(String name) {

        synchronized (modifyLock) {
            return cores.get(name);
        }
    }

    protected void swap(String n0, String n1) {

        synchronized (modifyLock) {
            SolrCore c0 = cores.get(n0);
            SolrCore c1 = cores.get(n1);
            if (c0 == null)
                throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n0);
            if (c1 == null)
                throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n1);
            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 (modifyLock) {
            SolrCore tmp = cores.remove(name);
            SolrCore ret = null;
            if (removeOrig && tmp != null) {
                coreToOrigName.remove(tmp);
            }
            ret = (ret == null) ? tmp : ret;
            // It could have been a newly-created core. It could have been a transient core. The newly-created cores
            // in particular should be checked. It could have been a dynamic core.
            tmp = transientCores.remove(name);
            ret = (ret == null) ? tmp : ret;
            tmp = createdCores.remove(name);
            ret = (ret == null) ? tmp : ret;
            dynamicDescriptors.remove(name);
            return ret;
        }
    }

    protected void putCoreToOrigName(SolrCore c, String name) {

        synchronized (modifyLock) {
            coreToOrigName.put(c, name);
        }

    }

    protected void removeCoreToOrigName(SolrCore newCore, SolrCore core) {

        synchronized (modifyLock) {
            String origName = coreToOrigName.remove(core);
            if (origName != null) {
                coreToOrigName.put(newCore, origName);
            }
        }
    }

    protected SolrCore getCoreFromAnyList(String name) {
        SolrCore core;

        synchronized (modifyLock) {
            core = cores.get(name);
            if (core != null) {
                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.
            return transientCores.get(name);
        }
    }

    protected CoreDescriptor getDynamicDescriptor(String name) {
        synchronized (modifyLock) {
            return dynamicDescriptors.get(name);
        }
    }

    protected boolean isLoaded(String name) {
        synchronized (modifyLock) {
            if (cores.containsKey(name)) {
                return true;
            }
            if (transientCores.containsKey(name)) {
                return true;
            }
        }
        return false;

    }

    protected CoreDescriptor getUnloadedCoreDescriptor(String cname) {
        synchronized (modifyLock) {
            CoreDescriptor desc = dynamicDescriptors.get(cname);
            if (desc == null) {
                return null;
            }
            return new CoreDescriptor(desc);
        }

    }

    protected String getCoreToOrigName(SolrCore solrCore) {
        synchronized (modifyLock) {
            return coreToOrigName.get(solrCore);
        }
    }

    public void persistCores(Config cfg, Properties containerProperties, Map<String, String> rootSolrAttribs,
            Map<String, String> coresAttribs, File file, File configFile, SolrResourceLoader loader)
            throws XPathExpressionException {

        List<SolrXMLSerializer.SolrCoreXMLDef> solrCoreXMLDefs = new ArrayList<SolrXMLSerializer.SolrCoreXMLDef>();
        synchronized (modifyLock) {

            persistCores(cfg, cores, loader, solrCoreXMLDefs);
            persistCores(cfg, transientCores, loader, solrCoreXMLDefs);
            // add back all the cores that aren't loaded, either in cores or transient
            // cores
            for (Map.Entry<String, CoreDescriptor> ent : dynamicDescriptors.entrySet()) {
                if (!cores.containsKey(ent.getKey()) && !transientCores.containsKey(ent.getKey())) {
                    addCoreToPersistList(cfg, loader, ent.getValue(), null, solrCoreXMLDefs);
                }
            }
            for (Map.Entry<String, SolrCore> ent : createdCores.entrySet()) {
                if (!cores.containsKey(ent.getKey()) && !transientCores.containsKey(ent.getKey())
                        && !dynamicDescriptors.containsKey(ent.getKey())) {
                    addCoreToPersistList(cfg, loader, ent.getValue().getCoreDescriptor(), null, solrCoreXMLDefs);
                }
            }

            SolrXMLSerializer.SolrXMLDef solrXMLDef = new SolrXMLSerializer.SolrXMLDef();
            solrXMLDef.coresDefs = solrCoreXMLDefs;
            solrXMLDef.containerProperties = containerProperties;
            solrXMLDef.solrAttribs = rootSolrAttribs;
            solrXMLDef.coresAttribs = coresAttribs;
            SOLR_XML_SERIALIZER.persistFile(file, solrXMLDef);
        }

    }

    // Wait here until any pending operations (load, unload or reload) are completed on this core.
    protected SolrCore waitAddPendingCoreOps(String name) {

        // Keep multiple threads from operating on a core at one time.
        synchronized (modifyLock) {
            boolean pending;
            do { // Are we currently doing anything to this core? Loading, unloading, reloading?
                pending = pendingCoreOps.contains(name); // wait for the core to be done being operated upon
                if (!pending) { // Linear list, but shouldn't be too long
                    for (SolrCore core : pendingCloses) {
                        if (core.getName().equals(name)) {
                            pending = true;
                            break;
                        }
                    }
                }
                if (container.isShutDown())
                    return null; // Just stop already.

                if (pending) {
                    try {
                        modifyLock.wait();
                    } catch (InterruptedException e) {
                        return null; // Seems best not to do anything at all if the thread is interrupted
                    }
                }
            } while (pending);
            // We _really_ need to do this within the synchronized block!
            if (!container.isShutDown()) {
                if (!pendingCoreOps.add(name)) {
                    CoreContainer.log.warn("Replaced an entry in pendingCoreOps {}, we should not be doing this",
                            name);
                }
                return getCoreFromAnyList(name); // we might have been _unloading_ the core, so return the core if it was loaded.
            }
        }
        return null;
    }

    // We should always be removing the first thing in the list with our name! The idea here is to NOT do anything n
    // any core while some other operation is working on that core.
    protected void removeFromPendingOps(String name) {
        synchronized (modifyLock) {
            if (!pendingCoreOps.remove(name)) {
                CoreContainer.log.warn("Tried to remove core {} from pendingCoreOps and it wasn't there. ", name);
            }
            modifyLock.notifyAll();
        }
    }

    protected void persistCores(Config cfg, Map<String, SolrCore> whichCores, SolrResourceLoader loader,
            List<SolrCoreXMLDef> solrCoreXMLDefs) throws XPathExpressionException {
        for (SolrCore solrCore : whichCores.values()) {
            addCoreToPersistList(cfg, loader, solrCore.getCoreDescriptor(), getCoreToOrigName(solrCore),
                    solrCoreXMLDefs);
        }
    }

    private void addCoreProperty(Map<String, String> coreAttribs, SolrResourceLoader loader, 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);
            }
        }

    }

    protected void addCoreToPersistList(Config cfg, SolrResourceLoader loader, CoreDescriptor dcore,
            String origCoreName, List<SolrCoreXMLDef> solrCoreXMLDefs) throws XPathExpressionException {

        String coreName = dcore.getProperty(CoreDescriptor.CORE_NAME);

        Map<String, String> coreAttribs = new HashMap<String, String>();

        CloudDescriptor cd = dcore.getCloudDescriptor();
        String collection = null;
        if (cd != null)
            collection = cd.getCollectionName();

        if (origCoreName == null) {
            origCoreName = coreName;
        }

        Properties properties = dcore.getCoreProperties();
        Node node = null;
        if (cfg != null) {
            node = cfg.getNode("/solr/cores/core[@name='" + origCoreName + "']", false);
        }

        coreAttribs.put(CoreDescriptor.CORE_NAME, coreName);

        addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_INSTDIR, dcore.getRawInstanceDir(), null);

        coreAttribs.put(CoreDescriptor.CORE_COLLECTION,
                StringUtils.isNotBlank(collection) ? collection : dcore.getName());

        addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_DATADIR, dcore.getDataDir(), null);
        addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_ULOGDIR, dcore.getUlogDir(), null);
        addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_TRANSIENT,
                Boolean.toString(dcore.isTransient()), null);
        addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_LOADONSTARTUP,
                Boolean.toString(dcore.isLoadOnStartup()), null);

        addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_COLLECTION, collection, dcore.getName());

        String shard = null;
        String roles = null;
        if (cd != null) {
            shard = cd.getShardId();
            roles = cd.getRoles();
        }
        addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_SHARD, shard, null);

        addCoreProperty(coreAttribs, loader, node, CoreDescriptor.CORE_ROLES, roles, null);

        coreAttribs.put(CoreDescriptor.CORE_LOADONSTARTUP, Boolean.toString(dcore.isLoadOnStartup()));
        coreAttribs.put(CoreDescriptor.CORE_TRANSIENT, Boolean.toString(dcore.isTransient()));

        SolrXMLSerializer.SolrCoreXMLDef solrCoreXMLDef = new SolrXMLSerializer.SolrCoreXMLDef();
        solrCoreXMLDef.coreAttribs = coreAttribs;
        solrCoreXMLDef.coreProperties = properties;
        solrCoreXMLDefs.add(solrCoreXMLDef);

    }

    protected Object getModifyLock() {
        return modifyLock;
    }

    // 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 loaded or reloaded. The "usual" case will probably return the very first
    // one anyway..
    protected SolrCore getCoreToClose() {
        synchronized (modifyLock) {
            for (SolrCore core : pendingCloses) {
                if (!pendingCoreOps.contains(core.getName())) {
                    pendingCoreOps.add(core.getName());
                    pendingCloses.remove(core);
                    return core;
                }
            }
        }
        return null;
    }

    protected void addCreated(SolrCore core) {
        synchronized (modifyLock) {
            createdCores.put(core.getName(), core);
        }
    }
}