All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.core.CoreContainer Maven / Gradle / Ivy

The newest version!
/**
 * 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.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
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.common.SolrException;
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.StrUtils;
import org.apache.solr.common.util.FileUtils;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.schema.IndexSchema;
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 830977 2009-10-29 14:50:29Z shalin $
 * @since solr 1.3
 */
public class CoreContainer 
{
  protected static Logger log = LoggerFactory.getLogger(CoreContainer.class);
  
  protected final Map cores = new LinkedHashMap();
  protected boolean persistent = false;
  protected String adminPath = null;
  protected String managementPath = null;
  protected CoreAdminHandler coreAdminHandler = null;
  protected File configFile = null;
  protected String libDir = null;
  protected ClassLoader libLoader = null;
  protected SolrResourceLoader loader = null;
  protected java.lang.ref.WeakReference adminCore = null;
  protected Properties containerProperties;
  protected Map indexSchemaCache;
  protected String adminHandler;
  protected boolean shareSchema;
  protected String solrHome;

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

  public Properties getContainerProperties() {
    return containerProperties;
  }

  // Helper class to initialize the CoreContainer
  public static class Initializer {
    protected String solrConfigFilename = null;
    protected boolean abortOnConfigurationError = true;

    public boolean isAbortOnConfigurationError() {
      return abortOnConfigurationError;
    }

    public void setAbortOnConfigurationError(boolean abortOnConfigurationError) {
      this.abortOnConfigurationError = abortOnConfigurationError;
    }

    public String getSolrConfigFilename() {
      return solrConfigFilename;
    }

    public void setSolrConfigFilename(String solrConfigFilename) {
      this.solrConfigFilename = solrConfigFilename;
    }

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

      if (fconf.exists()) {
        cores = new CoreContainer();
        cores.load(solrHome, fconf);
        abortOnConfigurationError = false;
        // if any core aborts on startup, then abort
        for (SolrCore c : cores.getCores()) {
          if (c.getSolrConfig().getBool("abortOnConfigurationError", false)) {
            abortOnConfigurationError = true;
            break;
          }
        }
        solrConfigFilename = cores.getConfigFile().getName();
      } else {
        // perform compatibility init
        cores = new CoreContainer(solrHome);
        CoreDescriptor dcore = new CoreDescriptor(cores, "", ".");
        dcore.setCoreProperties(null);
        SolrResourceLoader resourceLoader = new SolrResourceLoader(solrHome, null, getCoreProps(solrHome, null,dcore.getCoreProperties()));
        cores.loader = resourceLoader;
        SolrConfig cfg = solrConfigFilename == null ?
                new SolrConfig(resourceLoader, SolrConfig.DEFAULT_CONF_FILE,null) :
                new SolrConfig(resourceLoader, solrConfigFilename,null);
        SolrCore singlecore = new SolrCore(null, null, cfg, null, dcore);
        abortOnConfigurationError = cfg.getBool(
                "abortOnConfigurationError", abortOnConfigurationError);
        cores.register("", singlecore, false);
        cores.setPersistent(false);
        solrConfigFilename = cfg.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;
  }

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

  //-------------------------------------------------------------------
  // 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.loader = new SolrResourceLoader(dir);
    solrHome = loader.getInstanceDir();
    FileInputStream cfgis = new FileInputStream(configFile);
    try {
      Config cfg = new Config(loader, null, cfgis, null);

      persistent = cfg.getBool( "solr/@persistent", false );
      libDir     = cfg.get(     "solr/@sharedLib", null);
      adminPath  = cfg.get(     "solr/cores/@adminPath", null );
      shareSchema = cfg.getBool("solr/cores/@shareSchema", false );
      if(shareSchema){
        indexSchemaCache = new ConcurrentHashMap();
      }
      adminHandler  = cfg.get("solr/cores/@adminHandler", null );
      managementPath  = cfg.get("solr/cores/@managementPath", null );

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

      for (int i=0; i aliases = StrUtils.splitSmart(names,',');
          String name = aliases.get(0);
          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);
          }
          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);

          for (int a=1; a= 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( 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 = new SolrResourceLoader(instanceDir, libLoader, getCoreProps(instanceDir, dcore.getPropertiesName(),dcore.getCoreProperties()));
    SolrConfig config = new SolrConfig(solrLoader, dcore.getConfigName(), null);
    IndexSchema schema = null;
    if(indexSchemaCache != null){
      //schema sharing is enabled. so check if it already is loaded
      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").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);
        }
      }
    }
    if(schema == null){
      schema = new IndexSchema(config, dcore.getSchemaName(), null);
    }
    SolrCore core = new SolrCore(dcore.getName(), null, config, schema, dcore);
    return core;
  }
    
  /**
   * @return a Collection of registered SolrCores
   */
  public Collection getCores() {
    List lst = new ArrayList();
    synchronized (cores) {
      lst.addAll(this.cores.values());
    }
    return lst;
  }

  /**
   * @return a Collection of the names that cores are mapped to
   */
  public Collection getCoreNames() {
    List lst = new ArrayList();
    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 getCoreNames(SolrCore core) {
    List lst = new ArrayList();
    synchronized (cores) {
      for (Map.Entry 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 {
    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());

    // point all aliases to the reloaded core
    for (String alias : getCoreNames(core)) {
      if (!name.equals(alias)) {
        newCore.open();
        register(alias, newCore, false);
      }
    }

    register(name, newCore, false);
  }
    
  
  /**
   * 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." );
    }
    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);
      c1.setName(n0);
    }


    log.info("swaped: "+n0 + " with " + n1);
  }
  
  /** Removes and returns registered core w/o decrementing it's reference count */
  public SolrCore remove( String 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) {
    synchronized(cores) {
      SolrCore core = cores.get(name);
      if (core != null)
        core.open();  // increment the ref count while still synchronized
      return core;
    }
  }

  /**
   * Sets the preferred core used to handle MultiCore admin tasks.
   */
  @Deprecated
  public void setAdminCore(SolrCore core) {
    synchronized (cores) {
      adminCore = new java.lang.ref.WeakReference(core);
    }
  }

  /**
   * Ensures there is a valid core to handle MultiCore admin taks and
   * increase its refcount.
   * @see SolrCore#close() 
   * @return the acquired admin core, null if no core is available
   */
  @Deprecated
  public SolrCore getAdminCore() {
    synchronized (cores) {
      SolrCore core = adminCore != null ? adminCore.get() : null;
      if (core != null && !core.isClosed()) {
        core.open();
      } else {
        for (SolrCore c : cores.values()) {
          if (c != null) {
            core = c;
            core.open();
            setAdminCore(core);
            break;
          }
        }
      }
      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;
  }
  
  // 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("");
    w.write("\n");

    if (containerProperties != null && !containerProperties.isEmpty())  {
      writeProperties(w, containerProperties);
    }
    w.write("\n");

    Map> aliases = new HashMap>();

    synchronized(cores) {
      for (Map.Entry entry : cores.entrySet()) {
        String name = entry.getKey();
        LinkedList a = aliases.get(entry.getValue());
        if (a==null) a = new LinkedList();
        if (name.equals(entry.getValue().getName())) {
          a.addFirst(name);
        } else {
          a.addLast(name);
        }
        aliases.put(entry.getValue(), a);
      }
    }

    for (Map.Entry> entry : aliases.entrySet()) {
      persist(w, entry.getValue(), entry.getKey().getCoreDescriptor());
    }

    w.write("\n");
    w.write("\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, List aliases, CoreDescriptor dcore) throws IOException {
    w.write("  \n"); // core
    else  {
      w.write(">\n");
      writeProperties(w, dcore.getCoreProperties());
      w.write("");
    }
  }

  private void writeProperties(Writer w, Properties props) throws IOException {
    for (Map.Entry entry : props.entrySet()) {
      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;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy