Repository: ignite Updated Branches: refs/heads/ignite-1.6.8-hadoop 41de3ab57 -> 60c96ef3c
IGNITE-3929: New native libraries loading mechanism. This closes #1089. Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/60c96ef3 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/60c96ef3 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/60c96ef3 Branch: refs/heads/ignite-1.6.8-hadoop Commit: 60c96ef3cbe34f39ced034e05dc94317c752f3ac Parents: 41de3ab Author: vozerov-gridgain <voze...@gridgain.com> Authored: Tue Sep 20 17:38:32 2016 +0300 Committer: vozerov-gridgain <voze...@gridgain.com> Committed: Tue Sep 20 17:38:32 2016 +0300 ---------------------------------------------------------------------- .../processors/hadoop/HadoopClassLoader.java | 199 +++++++++++++++---- 1 file changed, 156 insertions(+), 43 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/60c96ef3/modules/core/src/main/java/org/apache/ignite/internal/processors/hadoop/HadoopClassLoader.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/hadoop/HadoopClassLoader.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/hadoop/HadoopClassLoader.java index 5297cea..3bf841b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/hadoop/HadoopClassLoader.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/hadoop/HadoopClassLoader.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.hadoop; import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.IgniteException; import org.apache.ignite.internal.util.ClassCache; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.S; @@ -25,12 +26,15 @@ import org.apache.ignite.internal.util.typedef.internal.U; import org.jetbrains.annotations.Nullable; import org.jsr166.ConcurrentHashMap8; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; import java.util.Map; import java.util.UUID; import java.util.Vector; @@ -43,20 +47,12 @@ import java.util.concurrent.ConcurrentMap; * unavailable for parent. */ public class HadoopClassLoader extends URLClassLoader implements ClassCache { - static { - // We are very parallel capable. - registerAsParallelCapable(); - } - /** Hadoop class name: Daemon. */ public static final String CLS_DAEMON = "org.apache.hadoop.util.Daemon"; /** Hadoop class name: ShutdownHookManager. */ public static final String CLS_SHUTDOWN_HOOK_MANAGER = "org.apache.hadoop.util.ShutdownHookManager"; - /** Hadoop class name: NativeCodeLoader. */ - public static final String CLS_NATIVE_CODE_LOADER = "org.apache.hadoop.util.NativeCodeLoader"; - /** Hadoop class name: Daemon replacement. */ public static final String CLS_DAEMON_REPLACE = "org.apache.ignite.internal.processors.hadoop.v2.HadoopDaemon"; @@ -64,15 +60,21 @@ public class HadoopClassLoader extends URLClassLoader implements ClassCache { public static final String CLS_SHUTDOWN_HOOK_MANAGER_REPLACE = "org.apache.ignite.internal.processors.hadoop.v2.HadoopShutdownHookManager"; - /** Name of libhadoop library. */ - private static final String LIBHADOOP = "hadoop."; - /** */ private static final URLClassLoader APP_CLS_LDR = (URLClassLoader)HadoopClassLoader.class.getClassLoader(); /** */ private static final Collection<URL> appJars = F.asList(APP_CLS_LDR.getURLs()); + /** Mutex for native libraries initialization. */ + private static final Object LIBS_MUX = new Object(); + + /** Predefined native libraries to load. */ + private static final Collection<String> PREDEFINED_NATIVE_LIBS; + + /** Native libraries. */ + private static Collection<Object> NATIVE_LIBS; + /** */ private static volatile Collection<URL> hadoopJars; @@ -86,12 +88,18 @@ public class HadoopClassLoader extends URLClassLoader implements ClassCache { @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) private final String name; - /** Native library names. */ - private final String[] libNames; - /** Igfs Helper. */ private final HadoopHelper helper; + static { + // We are very parallel capable. + registerAsParallelCapable(); + + PREDEFINED_NATIVE_LIBS = new HashSet<>(); + + PREDEFINED_NATIVE_LIBS.add("hadoop"); + } + /** * Gets name for Job class loader. The name is specific for local node id. * @param locNodeId The local node id. @@ -127,10 +135,9 @@ public class HadoopClassLoader extends URLClassLoader implements ClassCache { assert !(getParent() instanceof HadoopClassLoader); this.name = name; - this.libNames = libNames; this.helper = helper; - initializeNativeLibraries(); + initializeNativeLibraries(libNames); } /** @@ -145,49 +152,133 @@ public class HadoopClassLoader extends URLClassLoader implements ClassCache { * @see <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html#library_version"> * JNI specification</a> */ - private void initializeNativeLibraries() { - try { - // This must trigger native library load. - // TODO: Do not delegate to APP LDR - Class.forName(CLS_NATIVE_CODE_LOADER, true, APP_CLS_LDR); + @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") + private void initializeNativeLibraries(@Nullable String[] usrLibs) { + Collection<Object> res; + + synchronized (LIBS_MUX) { + if (NATIVE_LIBS == null) { + LinkedList<NativeLibrary> libs = new LinkedList<>(); + + for (String lib : PREDEFINED_NATIVE_LIBS) + libs.add(new NativeLibrary(lib, true)); + + if (!F.isEmpty(usrLibs)) { + for (String usrLib : usrLibs) + libs.add(new NativeLibrary(usrLib, false)); + } + + NATIVE_LIBS = initializeNativeLibraries0(libs); + } + + res = NATIVE_LIBS; + } + + // Link libraries to class loader. + Vector<Object> ldrLibs = nativeLibraries(this); + + synchronized (ldrLibs) { + ldrLibs.addAll(res); + } + } + + /** + * Initialize native libraries. + * + * @param libs Libraries to initialize. + * @return Initialized libraries. + */ + @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") + private static Collection<Object> initializeNativeLibraries0(Collection<NativeLibrary> libs) { + assert Thread.holdsLock(LIBS_MUX); + + Collection<Object> res = new HashSet<>(); + + for (NativeLibrary lib : libs) { + String libName = lib.name; + + File libFile = new File(libName); + + try { + // Load library. + if (libFile.isAbsolute()) + System.load(libName); + else + System.loadLibrary(libName); - final Vector<Object> curVector = U.field(this, "nativeLibraries"); + // Find library in class loader internals. + Object libObj = null; - // TODO: Do not delegate to APP LDR - ClassLoader ldr = APP_CLS_LDR; + ClassLoader ldr = APP_CLS_LDR; - while (ldr != null) { - Vector vector = U.field(ldr, "nativeLibraries"); + while (ldr != null) { + Vector<Object> ldrLibObjs = nativeLibraries(ldr); - for (Object lib : vector) { - String name = U.field(lib, "name"); + synchronized (ldrLibObjs) { + for (Object ldrLibObj : ldrLibObjs) { + String name = nativeLibraryName(ldrLibObj); - boolean add = name.contains(LIBHADOOP); + if (libFile.isAbsolute()) { + if (F.eq(name, libFile.getCanonicalPath())) { + libObj = ldrLibObj; - if (!add && libNames != null) { - for (String libName : libNames) { - if (libName != null && name.contains(libName)) { - add = true; + break; + } + } else { + if (name.contains(libName)) { + libObj = ldrLibObj; - break; + break; + } } } } - if (add) { - curVector.add(lib); + if (libObj != null) + break; - return; - } + ldr = ldr.getParent(); } - ldr = ldr.getParent(); + if (libObj == null) + throw new IgniteException("Failed to find loaded library: " + libName); + + res.add(libObj); + } + catch (UnsatisfiedLinkError e) { + if (!lib.optional) + throw e; + } + catch (IOException e) { + throw new IgniteException("Failed to initialize native libraries due to unexpected exception.", e); } } - catch (Exception e) { - U.quietAndWarn(null, "Failed to initialize Hadoop native library " + - "(native Hadoop methods might not work properly): " + e); - } + + return res; + } + + /** + * Get native libraries collection for the given class loader. + * + * @param ldr Class loaded. + * @return Native libraries. + */ + private static Vector<Object> nativeLibraries(ClassLoader ldr) { + assert ldr != null; + + return U.field(ldr, "nativeLibraries"); + } + + /** + * Get native library name. + * + * @param lib Library. + * @return Name. + */ + private static String nativeLibraryName(Object lib) { + assert lib != null; + + return U.field(lib, "name"); } /** {@inheritDoc} */ @@ -364,4 +455,26 @@ public class HadoopClassLoader extends URLClassLoader implements ClassCache { public String name() { return name; } + + /** + * Native library abstraction. + */ + private static class NativeLibrary { + /** Library name. */ + private final String name; + + /** Whether library is optional. */ + private final boolean optional; + + /** + * Constructor. + * + * @param name Library name. + * @param optional Optional flag. + */ + public NativeLibrary(String name, boolean optional) { + this.name = name; + this.optional = optional; + } + } } \ No newline at end of file