content: /**
   * 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.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 java.util.concurrent.atomic.AtomicLong;
  
  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.lucene.util.cache.Cache;
  import org.apache.lucene.util.cache.SimpleLRUCache;
  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.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;
  
  import com.alimama.mdrill.utils.UniqConfig;
  
  /**
   * @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." };
  
    private 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, null, null );
    }
    
    /** 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 getConfigDir() {
      return instanceDir + "conf/";
    }
    
    public String getDataDir()    {
      return dataDir;
    }
  
    public Properties getCoreProperties() {
      return coreProperties;
    }
  
    /** 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()==0) continue;
          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)
     */
    
    private static String solrhome=null;
    public static void SetSolrHome(String solrHome)
    {
  	  SolrResourceLoader.solrhome=solrHome;
    }
    
    
    private static AtomicLong cacheFlushKey=new AtomicLong(System.currentTimeMillis());
    
    
    
    public static class PartionKey{
  	public String partion;
  
  	public String tablename;
  	public PartionKey( String tablename,String partion) {
  		super();
  		this.partion = partion;
  		this.tablename = tablename;
  	}
  	  @Override
  	public int hashCode() {
  		final int prime = 31;
  		int result = 1;
  		result = prime * result + ((partion == null) ? 0 : partion.hashCode());
  		result = prime * result
  				+ ((tablename == null) ? 0 : tablename.hashCode());
  		return result;
  	}
  	@Override
  	public boolean equals(Object obj) {
  		if (this == obj)
  			return true;
  		if (obj == null)
  			return false;
  		if (getClass() != obj.getClass())
  			return false;
  		PartionKey other = (PartionKey) obj;
  		if (partion == null) {
  			if (other.partion != null)
  				return false;
  		} else if (!partion.equals(other.partion))
  			return false;
  		if (tablename == null) {
  			if (other.tablename != null)
  				return false;
  		} else if (!tablename.equals(other.tablename))
  			return false;
  		return true;
  	}
  	
  	@Override
  	public String toString() {
  		return "PartionKey [partion=" + partion + ", tablename=" + tablename
  				+ "]";
  	}
    }
  	private static Cache<PartionKey,Long> cacheFlushKeyMap = Cache.synchronizedCache(new SimpleLRUCache<PartionKey,Long>(1024));
  
   
  
    public static void SetCacheFlushKey(PartionKey p,long key)
    {
  	  log.info("higolog SetCacheFlushKey "+String.valueOf(p)+" "+key);
  	  SolrResourceLoader.cacheFlushKey.set(key);
  	  if(p!=null)
  	  {
  		  cacheFlushKeyMap.put(p, key);
  	  }
  	  
    }
    
    public static void DropCacheFlushKey(PartionKey p)
    {
  	  log.info("higolog DropCacheFlushKey "+String.valueOf(p));
  	  if(p!=null)
  	  {
  		  cacheFlushKeyMap.remove(p);
  	  }
  	  
    }
    
    public static long getCacheFlushKey(PartionKey p)
    {
  	  if(p!=null)
  	  {
  		  Long rtn=cacheFlushKeyMap.get(p);
  		  if(rtn!=null)
  		  {
  			  return rtn;
  		  }
  	  }
  	  long rtn= SolrResourceLoader.cacheFlushKey.get();
  	  
  	  if(p!=null)
  	  {
  		  cacheFlushKeyMap.put(p, rtn);
  	  }
  	  return rtn;
    }
    
    
    private static String schemaHome=null;
    public static void SetSchemaHome(String schemaHome)
    {
  	  SolrResourceLoader.schemaHome=schemaHome;
    }
    
    public static String GetSchemaHome()
    {
  	  return SolrResourceLoader.schemaHome;
    }
    
    public static String locateSolrHome() {
      String home = SolrResourceLoader.solrhome;
      if( 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 );
    }
    @Deprecated
    public static String locateInstanceDir() {
      return locateSolrHome();
    }
  
    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.class, new Class[] {
          SolrRequestHandler.class,
          QueryResponseWriter.class,
          SearchComponent.class,
          UpdateRequestProcessorFactory.class
        }
      );
  
      awareCompatibility.put(
        ResourceLoaderAware.class, new 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() );
    }
    /**
    +   * EXPERT
    +   * <p/>
    +   * The underlying class loader.  Most applications will not need to use this.
    +   * @return The {@link ClassLoader}
    +   */
    public ClassLoader getClassLoader() {
        return classLoader;
      }
  }
  