You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sqoop.apache.org by ja...@apache.org on 2015/09/29 00:07:14 UTC

sqoop git commit: SQOOP-2577: Sqoop2: Support loading classes using custom classloader in ClassUtils

Repository: sqoop
Updated Branches:
  refs/heads/sqoop2 2ee3bdd34 -> 7c7932df4


SQOOP-2577: Sqoop2: Support loading classes using custom classloader in ClassUtils

(Dian Fu via Jarek Jarcec Cecho)


Project: http://git-wip-us.apache.org/repos/asf/sqoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/sqoop/commit/7c7932df
Tree: http://git-wip-us.apache.org/repos/asf/sqoop/tree/7c7932df
Diff: http://git-wip-us.apache.org/repos/asf/sqoop/diff/7c7932df

Branch: refs/heads/sqoop2
Commit: 7c7932df42eff29dcd1759fd8587e7a90a653df5
Parents: 2ee3bdd
Author: Jarek Jarcec Cecho <ja...@apache.org>
Authored: Mon Sep 28 15:06:48 2015 -0700
Committer: Jarek Jarcec Cecho <ja...@apache.org>
Committed: Mon Sep 28 15:06:48 2015 -0700

----------------------------------------------------------------------
 .../java/org/apache/sqoop/utils/ClassUtils.java | 111 ++++++++++++++++---
 .../org/apache/sqoop/utils/TestClassUtils.java  |  10 ++
 2 files changed, 105 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/sqoop/blob/7c7932df/common/src/main/java/org/apache/sqoop/utils/ClassUtils.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/utils/ClassUtils.java b/common/src/main/java/org/apache/sqoop/utils/ClassUtils.java
index 6262802..3c073ca 100644
--- a/common/src/main/java/org/apache/sqoop/utils/ClassUtils.java
+++ b/common/src/main/java/org/apache/sqoop/utils/ClassUtils.java
@@ -17,10 +17,14 @@
  */
 package org.apache.sqoop.utils;
 
+import java.lang.ref.WeakReference;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
 
 import org.apache.sqoop.classification.InterfaceAudience;
 import org.apache.sqoop.classification.InterfaceStability;
@@ -32,6 +36,29 @@ public final class ClassUtils {
 
   private static final Logger LOG = Logger.getLogger(ClassUtils.class);
 
+  private static final Map<ClassLoader, Map<String, WeakReference<Class<?>>>>
+    CACHE_CLASSES = new WeakHashMap<ClassLoader, Map<String, WeakReference<Class<?>>>>();
+
+  /**
+   * Sentinel value to store negative cache results in {@link #CACHE_CLASSES}.
+   */
+  private static final Class<?> NEGATIVE_CACHE_SENTINEL =
+    NegativeCacheSentinel.class;
+
+  private static ClassLoader defaultClassLoader;
+  static {
+    defaultClassLoader = Thread.currentThread().getContextClassLoader();
+    if (defaultClassLoader == null) {
+      defaultClassLoader = ClassUtils.class.getClassLoader();
+    }
+  }
+
+  /**
+   * A unique class which is used as a sentinel value in the caching
+   * for loadClass.
+   */
+  private static abstract class NegativeCacheSentinel {}
+
   /**
    * Load class by given name and return corresponding Class object.
    *
@@ -42,30 +69,58 @@ public final class ClassUtils {
    * @return Class instance or NULL
    */
   public static Class<?> loadClass(String className) {
+    return loadClassWithClassLoader(className, defaultClassLoader);
+  }
+
+  /**
+   * Load class by given name and classLoader.
+   *
+   * This method will return null in case that the class is not found, no
+   * exception will be rised.
+   *
+   * @param className Name of class
+   * @param loader classLoader to load the given class
+   * @return Class instance or NULL
+   */
+  public static Class<?> loadClassWithClassLoader(String className, ClassLoader loader) {
     if(className == null) {
       return null;
     }
 
+    Map<String, WeakReference<Class<?>>> map;
+    synchronized (CACHE_CLASSES) {
+      map = CACHE_CLASSES.get(loader);
+      if (map == null) {
+        map = Collections.synchronizedMap(
+          new WeakHashMap<String, WeakReference<Class<?>>>());
+        CACHE_CLASSES.put(loader, map);
+      }
+    }
+
     Class<?> klass = null;
-    try {
-      klass = Class.forName(className);
-    } catch (ClassNotFoundException ex) {
-      LOG.debug("Exception while loading class: " + className, ex);
+    WeakReference<Class<?>> ref = map.get(className);
+    if (ref != null) {
+      klass = ref.get();
     }
 
     if (klass == null) {
-      // Try the context class loader if one exists
-      ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader();
-      if (ctxLoader != null) {
-        try {
-          klass = ctxLoader.loadClass(className);
-        } catch (ClassNotFoundException ex) {
-          LOG.debug("Exception while load class: " + className, ex);
-        }
+      try {
+        klass = Class.forName(className, true, loader);
+      } catch (ClassNotFoundException ex) {
+        // Leave a marker that the class isn't found
+        map.put(className, new WeakReference<Class<?>>(NEGATIVE_CACHE_SENTINEL));
+        LOG.debug("Exception while loading class: " + className, ex);
+        return null;
       }
+      // two putters can race here, but they'll put the same class
+      map.put(className, new WeakReference<Class<?>>(klass));
+      return klass;
+    } else if (klass == NEGATIVE_CACHE_SENTINEL) {
+      return null; // not found
+    } else {
+      // cache hit
+      return klass;
     }
-
-    return klass;
   }
 
   /**
@@ -79,7 +134,20 @@ public final class ClassUtils {
    * @return Instance of new class or NULL in case of any error
    */
   public static Object instantiate(String className, Object ... args) {
-    return instantiate(loadClass(className), args);
+    return instantiateWithClassLoader(className, defaultClassLoader, args);
+  }
+
+  /**
+   * Create instance of given class and given parameters.
+   *
+   * @param className Class name
+   * @param loader classLoader to load the given class
+   * @param args Objects that should be passed as constructor arguments
+   * @return Instance of new class or NULL in case of any error
+   */
+  public static Object instantiateWithClassLoader(String className,
+      ClassLoader loader, Object... args) {
+    return instantiate(loadClassWithClassLoader(className, loader), args);
   }
 
   /**
@@ -123,7 +191,18 @@ public final class ClassUtils {
    * @return Path on local filesystem to jar where given jar is present
    */
   public static String jarForClass(String className) {
-    Class klass = loadClass(className);
+    return jarForClassWithClassLoader(className, defaultClassLoader);
+  }
+
+  /**
+   * Return jar path for given class.
+   *
+   * @param className Class name
+   * @param loader classLoader to load the given class
+   * @return Path on local filesystem to jar where given jar is present
+   */
+  public static String jarForClassWithClassLoader(String className, ClassLoader loader) {
+    Class klass = loadClassWithClassLoader(className, loader);
     return jarForClass(klass);
   }
 

http://git-wip-us.apache.org/repos/asf/sqoop/blob/7c7932df/common/src/test/java/org/apache/sqoop/utils/TestClassUtils.java
----------------------------------------------------------------------
diff --git a/common/src/test/java/org/apache/sqoop/utils/TestClassUtils.java b/common/src/test/java/org/apache/sqoop/utils/TestClassUtils.java
index 4126e7b..eca3505 100644
--- a/common/src/test/java/org/apache/sqoop/utils/TestClassUtils.java
+++ b/common/src/test/java/org/apache/sqoop/utils/TestClassUtils.java
@@ -17,9 +17,12 @@
  */
 package org.apache.sqoop.utils;
 
+import java.util.Arrays;
+
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 
@@ -35,6 +38,13 @@ public class TestClassUtils {
   }
 
   @Test
+  public void testLoadClassWithClassLoader() throws Exception {
+    String classpath = ClassUtils.jarForClass(A.class);
+    assertNotEquals(A.class, ClassUtils.loadClassWithClassLoader(A.class.getName(),
+        new ConnectorClassLoader(classpath, getClass().getClassLoader(), Arrays.asList("java."))));
+  }
+
+  @Test
   public void testInstantiateNull() {
     assertNull(ClassUtils.instantiate((Class) null));
   }