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));
}