Project: datasalt-utils
Code Examples
package org.apache.solr.core;
 
 
 
import java.io.BufferedReader; 
import java.io.File; 
import java.io.FileFilter; 
import java.io.FileInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.InputStreamReader; 
import java.net.MalformedURLException; 
import java.net.URL; 
import java.net.URLClassLoader; 
import java.util.*; 
import java.util.concurrent.ConcurrentHashMap; 
 
import org.apache.solr.handler.component.ShardHandlerFactory; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
 
import java.nio.charset.CharacterCodingException; 
import java.nio.charset.Charset; 
import java.nio.charset.CodingErrorAction; 
import java.lang.reflect.Constructor; 
 
import javax.naming.Context; 
import javax.naming.InitialContext; 
import javax.naming.NamingException; 
import javax.naming.NoInitialContextException; 
 
import org.apache.solr.analysis.CharFilterFactory; 
import org.apache.solr.analysis.TokenFilterFactory; 
import org.apache.solr.analysis.TokenizerFactory; 
import org.apache.solr.common.util.FileUtils; 
import org.apache.solr.common.ResourceLoader; 
import org.apache.solr.common.SolrException; 
import org.apache.solr.core.CoreContainer; 
import org.apache.solr.core.SolrCore; 
import org.apache.solr.core.SolrInfoMBean; 
import org.apache.solr.handler.component.SearchComponent; 
import org.apache.solr.request.SolrRequestHandler; 
import org.apache.solr.response.QueryResponseWriter; 
import org.apache.solr.schema.FieldType; 
import org.apache.solr.update.processor.UpdateRequestProcessorFactory; 
import org.apache.solr.util.plugin.ResourceLoaderAware; 
import org.apache.solr.util.plugin.SolrCoreAware; 
import org.apache.solr.search.QParserPlugin; 
 
/**
 * @since solr 1.3 
 */
  
public class SolrResourceLoader implements ResourceLoader 
  public static final Logger log = LoggerFactory.getLogger(SolrResourceLoader.class); 
 
  static final String project = "solr"
  static final String base = "org.apache" + "." + project; 
  static final String[] packages = {"","analysis.","schema.","handler.","search.","update.","core.","response.","request.","update.processor.","util.""spelling.""handler.component.""handler.dataimport." }; 
 
  protected URLClassLoader classLoader; 
  private final String instanceDir; 
  private String dataDir; 
   
  private final List<SolrCoreAware> waitingForCore = Collections.synchronizedList(new ArrayList<SolrCoreAware>()); 
  private final List<SolrInfoMBean> infoMBeans = Collections.synchronizedList(new ArrayList<SolrInfoMBean>()); 
  private final List<ResourceLoaderAware> waitingForResources = Collections.synchronizedList(new ArrayList<ResourceLoaderAware>()); 
  private static final Charset UTF_8 = Charset.forName("UTF-8"); 
 
  private final Properties coreProperties; 
 
  private volatile boolean live; 
 
  /**
   * <p> 
   * 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. 
   * <p> 
   */
 
  public SolrResourceLoader( String instanceDir, ClassLoader parent, Properties coreProperties ) 
  { 
    if( instanceDir == null ) { 
      this.instanceDir = SolrResourceLoader.locateSolrHome(); 
    } else
      this.instanceDir = normalizeDir(instanceDir); 
    } 
    log.info("Solr home set to '" + this.instanceDir + "'"); 
     
    this.classLoader = createClassLoader(null, parent); 
    addToClassLoader("./lib/"null); 
     
    this.coreProperties = coreProperties; 
  } 
 
  /**
   * <p> 
   * 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. 
   * <p> 
   */
 
  public SolrResourceLoader( String instanceDir, ClassLoader parent ) 
  { 
    this(instanceDir, parent, null); 
  } 
 
  /**
   * Adds every file/dir found in the baseDir which passes the specified Filter 
   * to the ClassLoader used by this ResourceLoader.  This method <b>MUST</b> 
   * only be called prior to using this ResourceLoader to get any resources, otherwise 
   * it's behavior will be non-deterministic. 
   * 
   * @param baseDir base directory whose children (either jars or directories of 
   *                classes) will be in the classpath, will be resolved relative 
   *                the instance dir. 
   * @param filter The filter files must satisfy, if null all files will be accepted. 
   */
 
  void addToClassLoader(final String baseDir, final FileFilter filter) { 
    File base = FileUtils.resolvePath(new File(getInstanceDir()), baseDir); 
    this.classLoader = replaceClassLoader(classLoader, base, filter); 
  } 
   
  /**
   * Adds the specific file/dir specified to the ClassLoader used by this 
   * ResourceLoader.  This method <b>MUST</b> 
   * only be called prior to using this ResourceLoader to get any resources, otherwise 
   * it's behavior will be non-deterministic. 
   * 
   * @param path A jar file (or directory of classes) to be added to the classpath, 
   *             will be resolved relative the instance dir. 
   */
 
  void addToClassLoader(final String path) { 
    final File file = FileUtils.resolvePath(new File(getInstanceDir()), path); 
    if (file.canRead()) { 
      this.classLoader = replaceClassLoader(classLoader, file.getParentFile(), 
                                            new FileFilter() { 
                                              public boolean accept(File pathname) { 
                                                return pathname.equals(file); 
                                              } 
                                            }); 
    } else { 
      log.error("Can't find (or read) file to add to classloader: " + file); 
    } 
  } 
   
  private static URLClassLoader replaceClassLoader(final URLClassLoader oldLoader, 
                                                   final File base, 
                                                   final FileFilter filter) { 
    if (null != base && base.canRead() && base.isDirectory()) { 
      File[] files = base.listFiles(filter); 
       
      if (null == files || 0 == files.length) return oldLoader; 
       
      URL[] oldElements = oldLoader.getURLs(); 
      URL[] elements = new URL[oldElements.length + files.length]; 
      System.arraycopy(oldElements, 0, elements, 0, oldElements.length); 
       
      for (int j = 0; j < files.length; j++) { 
        try { 
          URL element = files[j].toURI().normalize().toURL(); 
          log.info("Adding '" + element.toString() + "' to classloader"); 
          elements[oldElements.length + j] = element; 
        } catch (MalformedURLException e) { 
          SolrException.log(log, "Can't add element to classloader: " + files[j], e); 
        } 
      } 
      return URLClassLoader.newInstance(elements, oldLoader.getParent()); 
    } 
    // are we still here? 
    return oldLoader; 
  } 
   
  /**
   * Convenience method for getting a new ClassLoader using all files found 
   * in the specified lib directory. 
   */
 
  static URLClassLoader createClassLoader(final File libDir, ClassLoader parent) { 
    if ( null == parent ) { 
      parent = Thread.currentThread().getContextClassLoader(); 
    } 
    return replaceClassLoader(URLClassLoader.newInstance(new URL[0], parent), 
                              libDir, null); 
  } 
   
  public SolrResourceLoader( String instanceDir ) 
  { 
    this( instanceDir, nullnull ); 
  } 
   
  /** 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 + "conf/"
  } 
   
  public String getDataDir()    { 
    return dataDir; 
  } 
 
  public Properties getCoreProperties() { 
    return coreProperties; 
  } 
 
  /**
   * EXPERT 
   * <p/> 
   * 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) { 
    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) { 
    return openResource(name); 
  } 
   
  /** 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 
   */
 
  public InputStream openResource(String resource) { 
    InputStream is=null
    try { 
      File f0 = new File(resource); 
      File f = f0; 
      if (!f.isAbsolute()) { 
        // try $CWD/$configDir/$resource 
        f = new File(getConfigDir() + resource); 
      } 
      if (f.isFile() && f.canRead()) { 
        return new FileInputStream(f); 
      } else if (f != f0) { // no success with $CWD/$configDir/$resource 
        if (f0.isFile() && f0.canRead()) 
          return new FileInputStream(f0); 
      } 
      // delegate to the class loader (looking into $INSTANCE_DIR/lib jars) 
      is = classLoader.getResourceAsStream(resource); 
      if (is == null
        is = classLoader.getResourceAsStream(getConfigDir() + resource); 
    } catch (Exception e) { 
      throw new RuntimeException("Error opening " + resource, e); 
    } 
    if (is==null) { 
      throw new RuntimeException("Can't find resource '" + resource + "' in classpath or '" + getConfigDir() + "', cwd="+System.getProperty("user.dir")); 
    } 
    return is; 
  } 
 
  /**
   * Accesses a resource by name and returns the (non comment) lines 
   * containing data. 
   * 
   * <p> 
   * A comment line is any line that starts with the character "#" 
   * </p> 
   * 
   * @param resource 
   * @return a list of non-blank non-comment lines with whitespace trimmed 
   * from front and back. 
   * @throws IOException 
   */
 
  public List<String> 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. 
   * 
   * <p> 
   * A comment line is any line that starts with the character "#" 
   * </p> 
   * 
   * @param resource the file to be read 
   * @param encoding 
   * @return a list of non-blank non-comment lines with whitespace trimmed 
   * @throws IOException 
   */
 
  public List<String> getLines(String resource, 
      String encoding) throws IOException { 
    return getLines(resource, Charset.forName(encoding)); 
  } 
 
 
  public List<String> getLines(String resource, Charset charset) throws IOException{ 
    BufferedReader input = null
    ArrayList<String> lines; 
    try { 
      input = new BufferedReader(new InputStreamReader(openResource(resource), 
          charset.newDecoder() 
          .onMalformedInput(CodingErrorAction.REPORT) 
          .onUnmappableCharacter(CodingErrorAction.REPORT))); 
 
      lines = new ArrayList<String>(); 
      for (String word=null; (word=input.readLine())!=null;) { 
        // skip initial bom marker 
        if (lines.isEmpty() && word.length() > 0 && word.charAt(0) == '\uFEFF') 
          word = word.substring(1); 
        // skip comments 
        if (word.startsWith("#")) continue
        word=word.trim(); 
        // skip blank lines 
        if (word.length()==0continue
        lines.add(word); 
      } 
    } catch (CharacterCodingException ex) { 
      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,  
          "Error loading resource (wrong encoding?): " + resource, ex); 
    } finally { 
      if (input != null
        input.close(); 
    } 
    return lines; 
  } 
 
  /*
   * A static map of short class name to fully qualified class name  
   */
 
  private static Map<String, String> classNameCache = new ConcurrentHashMap<String, String>(); 
 
  /**
   * This method loads a class either with it's 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 cnams starts with solr. 
   * @return the loaded class. An exception is thrown if it fails 
   */
 
  public Class findClass(String cname, 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); 
        } catch (ClassNotFoundException e) { 
          //this is unlikely 
          log.error("Unable to load cached class-name :  "+ c +" for shortname : "+cname + e); 
        } 
 
      } 
    } 
    Class clazz = null
    // first try cname == full name 
    try { 
      return Class.forName(cname, true, classLoader); 
    } 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); 
        } catch (ClassNotFoundException e1) { 
          // ignore... assume first exception is best. 
        } 
      } 
   
      throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "Error loading class '" + cname + "'", e, false); 
    }finally
      //cache the shortname vs FQN if it is loaded by the webapp classloader  and it is loaded 
      // using a shortname 
      if ( clazz != null && 
              clazz.getClassLoader() == SolrResourceLoader.class.getClassLoader() && 
              !cname.equals(clazz.getName()) && 
              (subpackages.length == 0 || subpackages == packages)) { 
        //store in the cache 
        classNameCache.put(cname, clazz.getName()); 
      } 
    } 
  } 
 
  public Object newInstance(String cname, String ... subpackages) { 
    Class clazz = findClass(cname,subpackages); 
    if( clazz == null ) { 
      throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, 
          "Can not find class: "+cname + " in " + classLoader, false); 
    } 
     
    Object obj = null
    try { 
      obj = clazz.newInstance(); 
    }  
    catch (Exception e) { 
      throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, 
          "Error instantiating class: '" + clazz.getName()+"'", e, false ); 
    } 
 
    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 SolrInfoMBean){ 
        //TODO: Assert here? 
        infoMBeans.add((SolrInfoMBean) obj); 
      } 
    } 
    return obj; 
  } 
 
  public Object newAdminHandlerInstance(final CoreContainer coreContainer, String cname, String ... subpackages) { 
    Class clazz = findClass(cname,subpackages); 
    if( clazz == null ) { 
      throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, 
          "Can not find class: "+cname + " in " + classLoader, false); 
    } 
     
    Object 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, false ); 
    } 
 
    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 Object newInstance(String cName, String [] subPackages, Class[] params, Object[] args){ 
    Class clazz = findClass(cName,subPackages); 
    if( clazz == null ) { 
      throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, 
          "Can not find class: "+cName + " in " + classLoader, false); 
    } 
 
    Object obj = null
    try { 
 
      Constructor constructor = clazz.getConstructor(params); 
      obj = constructor.newInstance(args); 
    } 
    catch (Exception e) { 
      throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, 
          "Error instantiating class: '" + clazz.getName()+"'", e, false ); 
    } 
 
    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 SolrInfoMBean){ 
        //TODO: Assert here? 
        infoMBeans.add((SolrInfoMBean) 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 )  
  { 
 
     // 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 org.apache.solr.core.SolrInfoMBean}s 
   * @param infoRegistry The Info Registry 
   */
 
  public void inform(Map<String, SolrInfoMBean> infoRegistry) { 
    // this can currently happen concurrently with requests starting and lazy components 
    // loading.  Make sure infoMBeans doesn't change. 
 
    SolrInfoMBean[] arr; 
    synchronized (infoMBeans) { 
      arr = infoMBeans.toArray(new SolrInfoMBean[infoMBeans.size()]); 
      waitingForResources.clear(); 
    } 
 
 
    for (SolrInfoMBean bean : arr) { 
      infoRegistry.put(bean.getName(), bean); 
    } 
  } 
   
  /**
   * 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: 
   * <ol> 
   *  <li>JNDI: via java:comp/env/solr/home</li> 
   *  <li>The system property solr.solr.home</li> 
   *  <li>Look in the current working directory for a solr/ directory</li>  
   * </ol> 
   * 
   * The return value is normalized.  Normalization essentially means it ends in a trailing slash. 
   * @return A normalized solrhome 
   * @see #normalizeDir(String) 
   */
 
  public static String locateSolrHome() { 
    String home = null
    // Try JNDI 
    try { 
      Context c = new InitialContext(); 
      home = (String)c.lookup("java:comp/env/"+project+"/home"); 
      log.info("Using JNDI solr.home: "+home ); 
    } catch (NoInitialContextException e) { 
      log.info("JNDI not configured for "+project+" (NoInitialContextEx)"); 
    } catch (NamingException e) { 
      log.info("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 ) { 
        log.info("using system property "+prop+": " + home ); 
      } 
    } 
     
    // if all else fails, try  
    if( home == null ) { 
      home = project + '/'; 
      log.info(project + " home defaulted to '" + home + "' (could not find system property or JNDI)"); 
    } 
    return normalizeDir( home ); 
  } 
 
 
  public String getInstanceDir() { 
    return instanceDir; 
  } 
   
  /**
   * Keep a list of classes that are allowed to implement each 'Aware' interface 
   */
 
  private static final Map<Class, Class[]> awareCompatibility; 
  static { 
    awareCompatibility = new HashMap<Class, Class[]>(); 
    awareCompatibility.put(  
      SolrCoreAware.classnew Class[] { 
        SolrRequestHandler.class
        QueryResponseWriter.class
        SearchComponent.class
        UpdateRequestProcessorFactory.class
        ShardHandlerFactory.class 
      } 
    ); 
 
    awareCompatibility.put( 
      ResourceLoaderAware.classnew Class[] { 
        CharFilterFactory.class
        TokenFilterFactory.class
        TokenizerFactory.class
        QParserPlugin.class
        FieldType.class 
      } 
    ); 
  } 
 
  /**
   * Utility function to throw an exception if the class is invalid 
   */
 
  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() ); 
  } 
}