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

org.apache.hadoop.hbase.coprocessor.CoprocessorClassLoader Maven / Gradle / Ivy

Go to download

HBase is the &lt;a href="http://hadoop.apache.org"&rt;Hadoop</a&rt; database. Use it when you need random, realtime read/write access to your Big Data. This project's goal is the hosting of very large tables -- billions of rows X millions of columns -- atop clusters of commodity hardware.

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.hadoop.hbase.coprocessor;

import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * ClassLoader used to load Coprocessor instances.
 * 
 * This ClassLoader always tries to load classes from the Coprocessor jar first 
 * before delegating to the parent ClassLoader, thus avoiding dependency 
 * conflicts between HBase's classpath and classes in the coprocessor's jar.  
 * Certain classes are exempt from being loaded by this ClassLoader because it 
 * would prevent them from being cast to the equivalent classes in the region 
 * server.  For example, the Coprocessor interface needs to be loaded by the 
 * region server's ClassLoader to prevent a ClassCastException when casting the 
 * coprocessor implementation.
 * 
 * This ClassLoader also handles resource loading.  In most cases this 
 * ClassLoader will attempt to load resources from the coprocessor jar first 
 * before delegating to the parent.  However, like in class loading, 
 * some resources need to be handled differently.  For all of the Hadoop 
 * default configurations (e.g. hbase-default.xml) we will check the parent 
 * ClassLoader first to prevent issues such as failing the HBase default 
 * configuration version check.
 */
public class CoprocessorClassLoader extends URLClassLoader {
  private static final Log LOG = 
      LogFactory.getLog(CoprocessorClassLoader.class);
  
  /**
   * If the class being loaded starts with any of these strings, we will skip
   * trying to load it from the coprocessor jar and instead delegate 
   * directly to the parent ClassLoader.
   */
  private static final String[] CLASS_PREFIX_EXEMPTIONS = new String[] {
    // Java standard library:
    "com.sun.",
    "launcher.",
    "java.",
    "javax.",
    "org.ietf",
    "org.omg",
    "org.w3c",
    "org.xml",
    "sunw.",
    // logging
    "org.apache.commons.logging",
    "org.apache.log4j",
    "com.hadoop",
    // Hadoop/HBase/ZK:
    "org.apache.hadoop",
    "org.apache.zookeeper",
  };
  
  /**
   * If the resource being loaded matches any of these patterns, we will first 
   * attempt to load the resource with the parent ClassLoader.  Only if the 
   * resource is not found by the parent do we attempt to load it from the 
   * coprocessor jar.
   */
  private static final Pattern[] RESOURCE_LOAD_PARENT_FIRST_PATTERNS = 
      new Pattern[] {
    Pattern.compile("^[^-]+-default\\.xml$")
  };

  /**
   * Parent classloader used to load any class not matching the exemption list.
   */
  private final ClassLoader parent;

  /**
   * Creates a CoprocessorClassLoader that loads classes from the given paths.
   * @param paths paths from which to load classes.
   * @param parent the parent ClassLoader to set.
   */
  public CoprocessorClassLoader(List paths, ClassLoader parent) {
    super(paths.toArray(new URL[]{}), parent);
    this.parent = parent;
    if (parent == null) {
      throw new IllegalArgumentException("No parent classloader!");
    }
  }

  @Override
  synchronized public Class loadClass(String name) 
      throws ClassNotFoundException {
    // Delegate to the parent immediately if this class is exempt
    if (isClassExempt(name)) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Skipping exempt class " + name + 
            " - delegating directly to parent");
      }
      return parent.loadClass(name);
    }

    // Check whether the class has already been loaded:
    Class clasz = findLoadedClass(name);
    if (clasz != null) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Class " + name + " already loaded");
      }
    }
    else {
      try {
        // Try to find this class using the URLs passed to this ClassLoader, 
        // which includes the coprocessor jar
        if (LOG.isDebugEnabled()) {
          LOG.debug("Finding class: " + name);
        }
        clasz = findClass(name);
      } catch (ClassNotFoundException e) {
        // Class not found using this ClassLoader, so delegate to parent
        if (LOG.isDebugEnabled()) {
          LOG.debug("Class " + name + " not found - delegating to parent");
        }
        try {
          clasz = parent.loadClass(name);
        } catch (ClassNotFoundException e2) {
          // Class not found in this ClassLoader or in the parent ClassLoader
          // Log some debug output before rethrowing ClassNotFoundException
          if (LOG.isDebugEnabled()) {
            LOG.debug("Class " + name + " not found in parent loader");
          }
          throw e2;
        }
      }
    }
    
    return clasz;
  }
  
  @Override
  synchronized public URL getResource(String name) {
    URL resource = null;
    boolean parentLoaded = false;
    
    // Delegate to the parent first if necessary
    if (loadResourceUsingParentFirst(name)) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Checking parent first for resource " + name);
      }
      resource = super.getResource(name);
      parentLoaded = true;
    }
    
    if (resource == null) {
      // Try to find the resource in the coprocessor jar
      resource = findResource(name);
      if ((resource == null) && !parentLoaded) {
        // Not found in the coprocessor jar and we haven't attempted to load 
        // the resource in the parent yet; fall back to the parent
        resource = super.getResource(name);
      }
    }

    return resource;
  }
  
  /**
   * Determines whether the given class should be exempt from being loaded 
   * by this ClassLoader.
   * @param name the name of the class to test.
   * @return true if the class should *not* be loaded by this ClassLoader; 
   * false otherwise.
   */
  protected boolean isClassExempt(String name) {
    for (String exemptPrefix : CLASS_PREFIX_EXEMPTIONS) {
      if (name.startsWith(exemptPrefix)) {
        return true;
      }
    }
    return false;
  }
  
  /**
   * Determines whether we should attempt to load the given resource using the  
   * parent first before attempting to load the resource using this ClassLoader.
   * @param name the name of the resource to test.
   * @return true if we should attempt to load the resource using the parent 
   * first; false if we should attempt to load the resource using this 
   * ClassLoader first.
   */
  protected boolean loadResourceUsingParentFirst(String name) {
    for (Pattern resourcePattern : RESOURCE_LOAD_PARENT_FIRST_PATTERNS) {
      if (resourcePattern.matcher(name).matches()) {
        return true;
      }
    }
    return false;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy