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

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

There is a newer version: 9.7.0
Show 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 javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.NoInitialContextException;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.lucene.analysis.WordlistLoader;
import org.apache.lucene.analysis.util.CharFilterFactory;
import org.apache.lucene.analysis.util.ResourceLoader;
import org.apache.lucene.analysis.util.ResourceLoaderAware;
import org.apache.lucene.analysis.util.TokenFilterFactory;
import org.apache.lucene.analysis.util.TokenizerFactory;
import org.apache.lucene.codecs.Codec;
import org.apache.lucene.codecs.DocValuesFormat;
import org.apache.lucene.codecs.PostingsFormat;
import org.apache.lucene.util.IOUtils;
import org.apache.solr.common.SolrException;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.handler.component.SearchComponent;
import org.apache.solr.handler.component.ShardHandlerFactory;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.QueryResponseWriter;
import org.apache.solr.rest.RestManager;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.ManagedIndexSchemaFactory;
import org.apache.solr.schema.SimilarityFactory;
import org.apache.solr.search.QParserPlugin;
import org.apache.solr.update.processor.UpdateRequestProcessorFactory;
import org.apache.solr.util.plugin.SolrCoreAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @since solr 1.3
 */ 
public class SolrResourceLoader implements ResourceLoader,Closeable
{
  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

  static final String project = "solr";
  static final String base = "org.apache" + "." + project;
  static final String[] packages = {
      "", "analysis.", "schema.", "handler.", "handler.tagger.", "search.", "update.", "core.", "response.", "request.",
      "update.processor.", "util.", "spelling.", "handler.component.", "handler.dataimport.",
      "spelling.suggest.", "spelling.suggest.fst.", "rest.schema.analysis.", "security.", "handler.admin.",
      "cloud.autoscaling."
  };
  private static final java.lang.String SOLR_CORE_NAME = "solr.core.name";
  private static Set loggedOnce = new ConcurrentSkipListSet<>();
  private static final Charset UTF_8 = StandardCharsets.UTF_8;


  private String name = "";
  protected URLClassLoader classLoader;
  private final Path instanceDir;
  private String dataDir;
  
  private final List waitingForCore = Collections.synchronizedList(new ArrayList());
  private final List infoMBeans = Collections.synchronizedList(new ArrayList());
  private final List waitingForResources = Collections.synchronizedList(new ArrayList());

  private final Properties coreProperties;

  private volatile boolean live;
  
  // Provide a registry so that managed resources can register themselves while the XML configuration
  // documents are being parsed ... after all are registered, they are asked by the RestManager to
  // initialize themselves. This two-step process is required because not all resources are available
  // (such as the SolrZkClient) when XML docs are being parsed.    
  private RestManager.Registry managedResourceRegistry;
  
  public synchronized RestManager.Registry getManagedResourceRegistry() {
    if (managedResourceRegistry == null) {
      managedResourceRegistry = new RestManager.Registry();      
    }
    return managedResourceRegistry; 
  }

  public SolrResourceLoader() {
    this(SolrResourceLoader.locateSolrHome(), null, null);
  }

  /**
   * 

* This loader will delegate to the context classloader when possible, * otherwise it will attempt to resolve resources using any jar files * found in the "lib/" directory in the specified instance directory. * If the instance directory is not specified (=null), SolrResourceLoader#locateInstanceDir will provide one. */ public SolrResourceLoader(Path instanceDir, ClassLoader parent) { this(instanceDir, parent, null); } public SolrResourceLoader(String name, List classpath, Path instanceDir, ClassLoader parent) throws MalformedURLException { this(instanceDir, parent); this.name = name; for (Path path : classpath) { addToClassLoader(path.toUri().normalize().toURL()); } } public SolrResourceLoader(Path instanceDir) { this(instanceDir, null, null); } /** *

* This loader will delegate to Solr's classloader when possible, * otherwise it will attempt to resolve resources using any jar files * found in the "lib/" directory in the specified instance directory. *

* * @param instanceDir - base directory for this resource loader, if null locateSolrHome() will be used. * @see #locateSolrHome */ public SolrResourceLoader(Path instanceDir, ClassLoader parent, Properties coreProperties) { if (instanceDir == null) { this.instanceDir = SolrResourceLoader.locateSolrHome().toAbsolutePath().normalize(); log.debug("new SolrResourceLoader for deduced Solr Home: '{}'", this.instanceDir); } else{ this.instanceDir = instanceDir.toAbsolutePath().normalize(); log.debug("new SolrResourceLoader for directory: '{}'", this.instanceDir); } if (parent == null) { parent = getClass().getClassLoader(); } this.classLoader = URLClassLoader.newInstance(new URL[0], parent); /* * Skip the lib subdirectory when we are loading from the solr home. * Otherwise load it, so core lib directories still get loaded. * The default sharedLib will pick this up later, and if the user has * changed sharedLib, then we don't want to load that location anyway. */ if (!this.instanceDir.equals(SolrResourceLoader.locateSolrHome())) { Path libDir = this.instanceDir.resolve("lib"); if (Files.exists(libDir)) { try { addToClassLoader(getURLs(libDir)); } catch (IOException e) { log.warn("Couldn't add files from {} to classpath: {}", libDir, e.getMessage()); } reloadLuceneSPI(); } } this.coreProperties = coreProperties; } /** * Adds URLs to the ResourceLoader's internal classloader. This method MUST * only be called prior to using this ResourceLoader to get any resources, otherwise * its behavior will be non-deterministic. You also have to {link @reloadLuceneSPI} * before using this ResourceLoader. * * @param urls the URLs of files to add */ void addToClassLoader(List urls) { URLClassLoader newLoader = addURLsToClassLoader(classLoader, urls); if (newLoader != classLoader) { this.classLoader = newLoader; } log.info("[{}] Added {} libs to classloader, from paths: {}", getCoreName("null"), urls.size(), urls.stream() .map(u -> u.getPath().substring(0,u.getPath().lastIndexOf("/"))) .sorted() .distinct() .collect(Collectors.toList())); } private String getCoreName(String defaultVal) { if (getCoreProperties() != null) { return getCoreProperties().getProperty(SOLR_CORE_NAME, defaultVal); } else { return defaultVal; } } /** * Adds URLs to the ResourceLoader's internal classloader. This method MUST * only be called prior to using this ResourceLoader to get any resources, otherwise * its behavior will be non-deterministic. You also have to {link @reloadLuceneSPI} * before using this ResourceLoader. * * @param urls the URLs of files to add */ void addToClassLoader(URL... urls) { addToClassLoader(Arrays.asList(urls)); } /** * Reloads all Lucene SPI implementations using the new classloader. * This method must be called after {@link #addToClassLoader(List)} * and before using this ResourceLoader. */ void reloadLuceneSPI() { // Codecs: PostingsFormat.reloadPostingsFormats(this.classLoader); DocValuesFormat.reloadDocValuesFormats(this.classLoader); Codec.reloadCodecs(this.classLoader); // Analysis: CharFilterFactory.reloadCharFilters(this.classLoader); TokenFilterFactory.reloadTokenFilters(this.classLoader); TokenizerFactory.reloadTokenizers(this.classLoader); } private static URLClassLoader addURLsToClassLoader(final URLClassLoader oldLoader, List urls) { if (urls.size() == 0) { return oldLoader; } List allURLs = new ArrayList<>(); allURLs.addAll(Arrays.asList(oldLoader.getURLs())); allURLs.addAll(urls); for (URL url : urls) { log.debug("Adding '{}' to classloader", url.toString()); } ClassLoader oldParent = oldLoader.getParent(); IOUtils.closeWhileHandlingException(oldLoader); return URLClassLoader.newInstance(allURLs.toArray(new URL[allURLs.size()]), oldParent); } /** * Utility method to get the URLs of all paths under a given directory that match a filter * @param libDir the root directory * @param filter the filter * @return all matching URLs * @throws IOException on error */ public static List getURLs(Path libDir, DirectoryStream.Filter filter) throws IOException { List urls = new ArrayList<>(); try (DirectoryStream directory = Files.newDirectoryStream(libDir, filter)) { for (Path element : directory) { urls.add(element.toUri().normalize().toURL()); } } return urls; } /** * Utility method to get the URLs of all paths under a given directory * @param libDir the root directory * @return all subdirectories as URLs * @throws IOException on error */ public static List getURLs(Path libDir) throws IOException { return getURLs(libDir, new DirectoryStream.Filter() { @Override public boolean accept(Path entry) throws IOException { return true; } }); } /** * Utility method to get the URLs of all paths under a given directory that match a regex * @param libDir the root directory * @param regex the regex as a String * @return all matching URLs * @throws IOException on error */ public static List getFilteredURLs(Path libDir, String regex) throws IOException { final PathMatcher matcher = libDir.getFileSystem().getPathMatcher("regex:" + regex); return getURLs(libDir, new DirectoryStream.Filter() { @Override public boolean accept(Path entry) throws IOException { return matcher.matches(entry.getFileName()); } }); } /** Ensures a directory name always ends with a '/'. */ public static String normalizeDir(String path) { return ( path != null && (!(path.endsWith("/") || path.endsWith("\\"))) )? path + File.separator : path; } public String[] listConfigDir() { File configdir = new File(getConfigDir()); if( configdir.exists() && configdir.isDirectory() ) { return configdir.list(); } else { return new String[0]; } } public String getConfigDir() { return instanceDir.resolve("conf").toString(); } public String getDataDir() { return dataDir; } public Properties getCoreProperties() { return coreProperties; } /** * EXPERT *

* The underlying class loader. Most applications will not need to use this. * @return The {@link ClassLoader} */ public ClassLoader getClassLoader() { return classLoader; } /** Opens a schema resource by its name. * Override this method to customize loading schema resources. *@return the stream for the named schema */ public InputStream openSchema(String name) throws IOException { return openResource(name); } /** Opens a config resource by its name. * Override this method to customize loading config resources. *@return the stream for the named configuration */ public InputStream openConfig(String name) throws IOException { return openResource(name); } private Path checkPathIsSafe(Path pathToCheck) throws IOException { if (Boolean.getBoolean("solr.allow.unsafe.resourceloading")) return pathToCheck; pathToCheck = pathToCheck.normalize(); if (pathToCheck.startsWith(instanceDir)) return pathToCheck; throw new IOException("File " + pathToCheck + " is outside resource loader dir " + instanceDir + "; set -Dsolr.allow.unsafe.resourceloading=true to allow unsafe loading"); } /** Opens any resource by its name. * By default, this will look in multiple locations to load the resource: * $configDir/$resource (if resource is not absolute) * $CWD/$resource * otherwise, it will look for it in any jar accessible through the class loader. * Override this method to customize loading resources. *@return the stream for the named resource */ @Override public InputStream openResource(String resource) throws IOException { Path inConfigDir = getInstancePath().resolve("conf").resolve(resource); if (Files.exists(inConfigDir) && Files.isReadable(inConfigDir)) { return Files.newInputStream(checkPathIsSafe(inConfigDir)); } Path inInstanceDir = getInstancePath().resolve(resource); if (Files.exists(inInstanceDir) && Files.isReadable(inInstanceDir)) { return Files.newInputStream(checkPathIsSafe(inInstanceDir)); } // Delegate to the class loader (looking into $INSTANCE_DIR/lib jars). // We need a ClassLoader-compatible (forward-slashes) path here! InputStream is = classLoader.getResourceAsStream(resource.replace(File.separatorChar, '/')); // This is a hack just for tests (it is not done in ZKResourceLoader)! // TODO can we nuke this? if (is == null && System.getProperty("jetty.testMode") != null) { is = classLoader.getResourceAsStream(("conf/" + resource).replace(File.separatorChar, '/')); } if (is == null) { throw new SolrResourceNotFoundException("Can't find resource '" + resource + "' in classpath or '" + instanceDir + "'"); } return is; } /** * Report the location of a resource found by the resource loader */ public String resourceLocation(String resource) { Path inConfigDir = getInstancePath().resolve("conf").resolve(resource); if (Files.exists(inConfigDir) && Files.isReadable(inConfigDir)) return inConfigDir.toAbsolutePath().normalize().toString(); Path inInstanceDir = getInstancePath().resolve(resource); if (Files.exists(inInstanceDir) && Files.isReadable(inInstanceDir)) return inInstanceDir.toAbsolutePath().normalize().toString(); try (InputStream is = classLoader.getResourceAsStream(resource.replace(File.separatorChar, '/'))) { if (is != null) return "classpath:" + resource; } catch (IOException e) { // ignore } return resource; } /** * Accesses a resource by name and returns the (non comment) lines * containing data. * *

* A comment line is any line that starts with the character "#" *

* * @return a list of non-blank non-comment lines with whitespace trimmed * from front and back. * @throws IOException If there is a low-level I/O error. */ public List getLines(String resource) throws IOException { return getLines(resource, UTF_8); } /** * Accesses a resource by name and returns the (non comment) lines containing * data using the given character encoding. * *

* A comment line is any line that starts with the character "#" *

* * @param resource the file to be read * @return a list of non-blank non-comment lines with whitespace trimmed * @throws IOException If there is a low-level I/O error. */ public List getLines(String resource, String encoding) throws IOException { return getLines(resource, Charset.forName(encoding)); } public List getLines(String resource, Charset charset) throws IOException{ try { return WordlistLoader.getLines(openResource(resource), charset); } catch (CharacterCodingException ex) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error loading resource (wrong encoding?): " + resource, ex); } } /* * A static map of short class name to fully qualified class name */ private static final Map classNameCache = new ConcurrentHashMap<>(); // Using this pattern, legacy analysis components from previous Solr versions are identified and delegated to SPI loader: private static final Pattern legacyAnalysisPattern = Pattern.compile("((\\Q"+base+".analysis.\\E)|(\\Q"+project+".\\E))([\\p{L}_$][\\p{L}\\p{N}_$]+?)(TokenFilter|Filter|Tokenizer|CharFilter)Factory"); @Override public Class findClass(String cname, Class expectedType) { return findClass(cname, expectedType, empty); } /** * This method loads a class either with its FQN or a short-name (solr.class-simplename or class-simplename). * It tries to load the class with the name that is given first and if it fails, it tries all the known * solr packages. This method caches the FQN of a short-name in a static map in-order to make subsequent lookups * for the same class faster. The caching is done only if the class is loaded by the webapp classloader and it * is loaded using a shortname. * * @param cname The name or the short name of the class. * @param subpackages the packages to be tried if the cname starts with solr. * @return the loaded class. An exception is thrown if it fails */ public Class findClass(String cname, Class expectedType, String... subpackages) { if (subpackages == null || subpackages.length == 0 || subpackages == packages) { subpackages = packages; String c = classNameCache.get(cname); if (c != null) { try { return Class.forName(c, true, classLoader).asSubclass(expectedType); } catch (ClassNotFoundException | ClassCastException e) { // this can happen if the legacyAnalysisPattern below caches the wrong thing log.warn( name + " Unable to load cached class, attempting lookup. name={} shortname={} reason={}", c, cname, e); classNameCache.remove(cname); } } } Class clazz = null; try { // first try legacy analysis patterns, now replaced by Lucene's Analysis package: final Matcher m = legacyAnalysisPattern.matcher(cname); if (m.matches()) { final String name = m.group(4); log.trace("Trying to load class from analysis SPI using name='{}'", name); try { if (CharFilterFactory.class.isAssignableFrom(expectedType)) { return clazz = CharFilterFactory.lookupClass(name).asSubclass(expectedType); } else if (TokenizerFactory.class.isAssignableFrom(expectedType)) { return clazz = TokenizerFactory.lookupClass(name).asSubclass(expectedType); } else if (TokenFilterFactory.class.isAssignableFrom(expectedType)) { return clazz = TokenFilterFactory.lookupClass(name).asSubclass(expectedType); } else { log.warn("'{}' looks like an analysis factory, but caller requested different class type: {}", cname, expectedType.getName()); } } catch (IllegalArgumentException ex) { // ok, we fall back to legacy loading } } // first try cname == full name try { return clazz = Class.forName(cname, true, classLoader).asSubclass(expectedType); } catch (ClassNotFoundException e) { String newName=cname; if (newName.startsWith(project)) { newName = cname.substring(project.length()+1); } for (String subpackage : subpackages) { try { String name = base + '.' + subpackage + newName; log.trace("Trying class name " + name); return clazz = Class.forName(name,true,classLoader).asSubclass(expectedType); } catch (ClassNotFoundException e1) { // ignore... assume first exception is best. } } throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "Error loading class '" + cname + "'", e); } } finally { if (clazz != null) { //cache the shortname vs FQN if it is loaded by the webapp classloader and it is loaded // using a shortname if (clazz.getClassLoader() == SolrResourceLoader.class.getClassLoader() && !cname.equals(clazz.getName()) && (subpackages.length == 0 || subpackages == packages)) { //store in the cache classNameCache.put(cname, clazz.getName()); } // print warning if class is deprecated if (clazz.isAnnotationPresent(Deprecated.class)) { log.warn("Solr loaded a deprecated plugin/analysis class [{}]. Please consult documentation how to replace it accordingly.", cname); } } } } static final String empty[] = new String[0]; @Override public T newInstance(String name, Class expectedType) { return newInstance(name, expectedType, empty); } private static final Class[] NO_CLASSES = new Class[0]; private static final Object[] NO_OBJECTS = new Object[0]; public T newInstance(String cname, Class expectedType, String ... subpackages) { return newInstance(cname, expectedType, subpackages, NO_CLASSES, NO_OBJECTS); } public CoreAdminHandler newAdminHandlerInstance(final CoreContainer coreContainer, String cname, String ... subpackages) { Class clazz = findClass(cname, CoreAdminHandler.class, subpackages); if( clazz == null ) { throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "Can not find class: "+cname + " in " + classLoader); } CoreAdminHandler obj = null; try { Constructor ctor = clazz.getConstructor(CoreContainer.class); obj = ctor.newInstance(coreContainer); } catch (Exception e) { throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "Error instantiating class: '" + clazz.getName()+"'", e); } if (!live) { //TODO: Does SolrCoreAware make sense here since in a multi-core context // which core are we talking about ? if( obj instanceof ResourceLoaderAware ) { assertAwareCompatibility( ResourceLoaderAware.class, obj ); waitingForResources.add( (ResourceLoaderAware)obj ); } } return obj; } public T newInstance(String cName, Class expectedType, String [] subPackages, Class[] params, Object[] args){ Class clazz = findClass(cName, expectedType, subPackages); if( clazz == null ) { throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "Can not find class: "+cName + " in " + classLoader); } T obj = null; try { Constructor constructor = null; try { constructor = clazz.getConstructor(params); obj = constructor.newInstance(args); } catch (NoSuchMethodException e) { //look for a zero arg constructor if the constructor args do not match try { constructor = clazz.getConstructor(); obj = constructor.newInstance(); } catch (NoSuchMethodException e1) { throw e; } } } catch (Error err) { log.error("Loading Class " + cName + " ("+clazz.getName() + ") triggered serious java error: " + err.getClass().getName(), err); throw err; } catch (Exception e) { throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "Error instantiating class: '" + clazz.getName()+"'", e); } if (!live) { if( obj instanceof SolrCoreAware ) { assertAwareCompatibility( SolrCoreAware.class, obj ); waitingForCore.add( (SolrCoreAware)obj ); } if( obj instanceof ResourceLoaderAware ) { assertAwareCompatibility( ResourceLoaderAware.class, obj ); waitingForResources.add( (ResourceLoaderAware)obj ); } if (obj instanceof SolrInfoBean){ //TODO: Assert here? infoMBeans.add((SolrInfoBean) obj); } } return obj; } /** * Tell all {@link SolrCoreAware} instances about the SolrCore */ public void inform(SolrCore core) { this.dataDir = core.getDataDir(); // make a copy to avoid potential deadlock of a callback calling newInstance and trying to // add something to waitingForCore. SolrCoreAware[] arr; while (waitingForCore.size() > 0) { synchronized (waitingForCore) { arr = waitingForCore.toArray(new SolrCoreAware[waitingForCore.size()]); waitingForCore.clear(); } for( SolrCoreAware aware : arr) { aware.inform( core ); } } // this is the last method to be called in SolrCore before the latch is released. live = true; } /** * Tell all {@link ResourceLoaderAware} instances about the loader */ public void inform( ResourceLoader loader ) throws IOException { // make a copy to avoid potential deadlock of a callback adding to the list ResourceLoaderAware[] arr; while (waitingForResources.size() > 0) { synchronized (waitingForResources) { arr = waitingForResources.toArray(new ResourceLoaderAware[waitingForResources.size()]); waitingForResources.clear(); } for( ResourceLoaderAware aware : arr) { aware.inform(loader); } } } /** * Register any {@link SolrInfoBean}s * @param infoRegistry The Info Registry */ public void inform(Map infoRegistry) { // this can currently happen concurrently with requests starting and lazy components // loading. Make sure infoMBeans doesn't change. SolrInfoBean[] arr; synchronized (infoMBeans) { arr = infoMBeans.toArray(new SolrInfoBean[infoMBeans.size()]); waitingForResources.clear(); } for (SolrInfoBean bean : arr) { // Too slow? I suspect not, but we may need // to start tracking this in a Set. if (!infoRegistry.containsValue(bean)) { try { infoRegistry.put(bean.getName(), bean); } catch (Exception e) { log.warn("could not register MBean '" + bean.getName() + "'.", e); } } } } /** * Determines the solrhome from the environment. * Tries JNDI (java:comp/env/solr/home) then system property (solr.solr.home); * if both fail, defaults to solr/ * @return the instance directory name */ /** * Finds the solrhome based on looking up the value in one of three places: *
    *
  1. JNDI: via java:comp/env/solr/home
  2. *
  3. The system property solr.solr.home
  4. *
  5. Look in the current working directory for a solr/ directory
  6. *
* * The return value is normalized. Normalization essentially means it ends in a trailing slash. * @return A normalized solrhome * @see #normalizeDir(String) */ public static Path locateSolrHome() { String home = null; // Try JNDI try { Context c = new InitialContext(); home = (String)c.lookup("java:comp/env/"+project+"/home"); logOnceInfo("home_using_jndi", "Using JNDI solr.home: "+home ); } catch (NoInitialContextException e) { log.debug("JNDI not configured for "+project+" (NoInitialContextEx)"); } catch (NamingException e) { log.debug("No /"+project+"/home in JNDI"); } catch( RuntimeException ex ) { log.warn("Odd RuntimeException while testing for JNDI: " + ex.getMessage()); } // Now try system property if( home == null ) { String prop = project + ".solr.home"; home = System.getProperty(prop); if( home != null ) { logOnceInfo("home_using_sysprop", "Using system property "+prop+": " + home ); } } // if all else fails, try if( home == null ) { home = project + '/'; logOnceInfo("home_default", project + " home defaulted to '" + home + "' (could not find system property or JNDI)"); } return Paths.get(home); } /** * Solr allows users to store arbitrary files in a special directory located directly under SOLR_HOME. * * This directory is generally created by each node on startup. Files located in this directory can then be * manipulated using select Solr features (e.g. streaming expressions). */ public static final String USER_FILES_DIRECTORY = "userfiles"; public static void ensureUserFilesDataDir(Path solrHome) { final Path userFilesPath = getUserFilesPath(solrHome); final File userFilesDirectory = new File(userFilesPath.toString()); if (! userFilesDirectory.exists()) { try { final boolean created = userFilesDirectory.mkdir(); if (! created) { log.warn("Unable to create [{}] directory in SOLR_HOME [{}]. Features requiring this directory may fail.", USER_FILES_DIRECTORY, solrHome); } } catch (Exception e) { log.warn("Unable to create [" + USER_FILES_DIRECTORY + "] directory in SOLR_HOME [" + solrHome + "]. Features requiring this directory may fail.", e); } } } public static Path getUserFilesPath(Path solrHome) { return Paths.get(solrHome.toAbsolutePath().toString(), USER_FILES_DIRECTORY).toAbsolutePath(); } // Logs a message only once per startup private static void logOnceInfo(String key, String msg) { if (!loggedOnce.contains(key)) { loggedOnce.add(key); log.info(msg); } } /** * @return the instance path for this resource loader */ public Path getInstancePath() { return instanceDir; } /** * Keep a list of classes that are allowed to implement each 'Aware' interface */ private static final Map awareCompatibility; static { awareCompatibility = new HashMap<>(); awareCompatibility.put( SolrCoreAware.class, new Class[] { // DO NOT ADD THINGS TO THIS LIST -- ESPECIALLY THINGS THAT CAN BE CREATED DYNAMICALLY // VIA RUNTIME APIS -- UNTILL CAREFULLY CONSIDERING THE ISSUES MENTIONED IN SOLR-8311 CodecFactory.class, DirectoryFactory.class, ManagedIndexSchemaFactory.class, QueryResponseWriter.class, SearchComponent.class, ShardHandlerFactory.class, SimilarityFactory.class, SolrRequestHandler.class, UpdateRequestProcessorFactory.class } ); awareCompatibility.put( ResourceLoaderAware.class, new Class[] { // DO NOT ADD THINGS TO THIS LIST -- ESPECIALLY THINGS THAT CAN BE CREATED DYNAMICALLY // VIA RUNTIME APIS -- UNTILL CAREFULLY CONSIDERING THE ISSUES MENTIONED IN SOLR-8311 CharFilterFactory.class, TokenFilterFactory.class, TokenizerFactory.class, QParserPlugin.class, FieldType.class } ); } /** * Utility function to throw an exception if the class is invalid */ static void assertAwareCompatibility( Class aware, Object obj ) { Class[] valid = awareCompatibility.get( aware ); if( valid == null ) { throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "Unknown Aware interface: "+aware ); } for( Class v : valid ) { if( v.isInstance( obj ) ) { return; } } StringBuilder builder = new StringBuilder(); builder.append( "Invalid 'Aware' object: " ).append( obj ); builder.append( " -- ").append( aware.getName() ); builder.append( " must be an instance of: " ); for( Class v : valid ) { builder.append( "[" ).append( v.getName() ).append( "] ") ; } throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, builder.toString() ); } @Override public void close() throws IOException { IOUtils.close(classLoader); } public List getInfoMBeans(){ return Collections.unmodifiableList(infoMBeans); } public static void persistConfLocally(SolrResourceLoader loader, String resourceName, byte[] content) { // Persist locally File confFile = new File(loader.getConfigDir(), resourceName); try { File parentDir = confFile.getParentFile(); if ( ! parentDir.isDirectory()) { if ( ! parentDir.mkdirs()) { final String msg = "Can't create managed schema directory " + parentDir.getAbsolutePath(); log.error(msg); throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, msg); } } try (OutputStream out = new FileOutputStream(confFile);) { out.write(content); } log.info("Written confile " + resourceName); } catch (IOException e) { final String msg = "Error persisting conf file " + resourceName; log.error(msg, e); throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, msg, e); } finally { try { IOUtils.fsync(confFile.toPath(), false); } catch (IOException e) { final String msg = "Error syncing conf file " + resourceName; log.error(msg, e); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy