From 2ee94b8317ec8e5009950384e3114f546ab094b5 Mon Sep 17 00:00:00 2001 From: Samuel Audet Date: Mon, 4 Jan 2021 15:14:12 +0900 Subject: [PATCH] * Prevent `Loader` from failing to find, load, or link libraries multiple times --- CHANGELOG.md | 1 + .../java/org/bytedeco/javacpp/Loader.java | 44 ++++++++++++------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de8e296f0..d1af4211f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ + * Prevent `Loader` from failing to find, load, or link libraries multiple times * Fix `Pointer.getPointer()` methods sometimes calling the wrong constructor ([issue bytedeco/javacv#1556](https://github.com/bytedeco/javacv/issues/1556)) * Prevent Android from trying to load `PointerBufferPoolMXBean` by using it via reflection ([pull #447](https://github.com/bytedeco/javacpp/pull/447)) * Fix Android build properties for NDK r22 and move legacy to `android-*-gcc.properties` ([pull #444](https://github.com/bytedeco/javacpp/pull/444)) diff --git a/src/main/java/org/bytedeco/javacpp/Loader.java b/src/main/java/org/bytedeco/javacpp/Loader.java index b439f80fd..aabda15ac 100644 --- a/src/main/java/org/bytedeco/javacpp/Loader.java +++ b/src/main/java/org/bytedeco/javacpp/Loader.java @@ -544,21 +544,21 @@ public static File cacheResource(URL resourceURL, String target) throws IOExcept File lockFile = new File(cacheDir, ".lock"); FileChannel lockChannel = null; FileLock lock = null; - if (target != null && target.length() > 0) { + if (canCreateSymbolicLink && target != null && target.length() > 0) { // ... create symbolic link to already extracted library or ... synchronized (Runtime.getRuntime()) { try { // file is already canonicalized, so normalized Path path = file.toPath(), targetPath = Paths.get(target).normalize(); if ((!file.exists() || !Files.isSymbolicLink(path) || !Files.readSymbolicLink(path).equals(targetPath)) - && targetPath.isAbsolute() && !targetPath.equals(path)) { + && targetPath.isAbsolute() && !targetPath.equals(path) && !targetPath.toRealPath().equals(path)) { if (logger.isDebugEnabled()) { logger.debug("Locking " + cacheDir + " to create symbolic link"); } lockChannel = new FileOutputStream(lockFile).getChannel(); lock = lockChannel.lock(); if ((!file.exists() || !Files.isSymbolicLink(path) || !Files.readSymbolicLink(path).equals(targetPath)) - && targetPath.isAbsolute() && !targetPath.equals(path)) { + && targetPath.isAbsolute() && !targetPath.equals(path) && !targetPath.toRealPath().equals(path)) { if (logger.isDebugEnabled()) { logger.debug("Creating symbolic link " + path + " to " + targetPath); } @@ -575,6 +575,7 @@ public static File cacheResource(URL resourceURL, String target) throws IOExcept // ... (probably an unsupported operation on Windows, but DLLs never need links, // or other (filesystem?) exception: for example, // "sun.nio.fs.UnixException: No such file or directory" on File.toPath()) ... + canCreateSymbolicLink = false; if (logger.isDebugEnabled()) { logger.debug("Failed to create symbolic link " + file + ": " + e); } @@ -589,21 +590,21 @@ public static File cacheResource(URL resourceURL, String target) throws IOExcept } } } else { - if (urlFile.exists() && reference) { + if (canCreateSymbolicLink && urlFile.exists() && reference) { // ... try to create a symbolic link to the existing file, if we can, ... synchronized (Runtime.getRuntime()) { try { // file is already canonicalized, so normalized Path path = file.toPath(), urlPath = urlFile.toPath().normalize(); if ((!file.exists() || !Files.isSymbolicLink(path) || !Files.readSymbolicLink(path).equals(urlPath)) - && urlPath.isAbsolute() && !urlPath.equals(path)) { + && urlPath.isAbsolute() && !urlPath.equals(path) && !urlPath.toRealPath().equals(path)) { if (logger.isDebugEnabled()) { logger.debug("Locking " + cacheDir + " to create symbolic link"); } lockChannel = new FileOutputStream(lockFile).getChannel(); lock = lockChannel.lock(); if ((!file.exists() || !Files.isSymbolicLink(path) || !Files.readSymbolicLink(path).equals(urlPath)) - && urlPath.isAbsolute() && !urlPath.equals(path)) { + && urlPath.isAbsolute() && !urlPath.equals(path) && !urlPath.toRealPath().equals(path)) { if (logger.isDebugEnabled()) { logger.debug("Creating symbolic link " + path + " to " + urlPath); } @@ -619,6 +620,7 @@ public static File cacheResource(URL resourceURL, String target) throws IOExcept return file; } catch (IOException | RuntimeException e) { // ... (let's try to copy the file instead, such as on Windows) ... + canCreateSymbolicLink = false; if (logger.isDebugEnabled()) { logger.debug("Could not create symbolic link " + file + ": " + e); } @@ -910,6 +912,8 @@ public static URL[] findResources(Class cls, String name, int maxLength) throws static Map foundLibraries = new HashMap(); /** Contains all the native libraries that we have loaded to avoid reloading them. */ static Map loadedLibraries = new HashMap(); + /** Will be set to false when symbolic link creation fails, such as on Windows. */ + static boolean canCreateSymbolicLink = true; static boolean pathsFirst = false; static { @@ -1226,12 +1230,14 @@ public static String load(Class cls, Properties properties, boolean pathsFirst) loadGlobally = true; preload = preload.substring(0, preload.length() - 1); } - String key = cls.getName() + "/" + preload; - URL[] urls = foundLibraries.get(key); + URL[] urls = foundLibraries.get(preload), oldUrls = urls; if (urls == null) { - foundLibraries.put(key, urls = findLibrary(cls, p, preload, pathsFirst)); + foundLibraries.put(preload, urls = findLibrary(cls, p, preload, pathsFirst)); + } + String filename = null; + if (oldUrls == null || urls.length > 0) { + filename = loadLibrary(cls, urls, preload, preloaded.toArray(new String[preloaded.size()])); } - String filename = loadLibrary(cls, urls, preload, preloaded.toArray(new String[preloaded.size()])); if (filename != null && new File(filename).exists()) { preloaded.add(filename); if (loadGlobally) { @@ -1300,12 +1306,14 @@ public static String load(Class cls, Properties properties, boolean pathsFirst) // try to load the JNI library using a different name library += "#" + library + librarySuffix; } - String key = cls.getName() + "/" + library; - URL[] urls = foundLibraries.get(key); + URL[] urls = foundLibraries.get(library), oldUrls = urls; if (urls == null) { - foundLibraries.put(key, urls = findLibrary(cls, p, library, pathsFirst)); + foundLibraries.put(library, urls = findLibrary(cls, p, library, pathsFirst)); + } + String filename = null; + if (oldUrls == null || urls.length > 0) { + filename = loadLibrary(cls, urls, library, preloaded.toArray(new String[preloaded.size()])); } - String filename = loadLibrary(cls, urls, library, preloaded.toArray(new String[preloaded.size()])); if (cacheDir != null && filename != null && filename.startsWith(cacheDir)) { createLibraryLink(filename, p, library); } @@ -1584,13 +1592,13 @@ public static synchronized String loadLibrary(Class cls, URL[] urls, String l for (String s : preloaded) { File file2 = new File(s); File dir2 = file2.getParentFile(); - if (dir2 != null && !dir2.equals(dir)) { + if (canCreateSymbolicLink && dir2 != null && !dir2.equals(dir)) { File linkFile = new File(dir, file2.getName()); try { Path linkPath = linkFile.toPath().normalize(); Path targetPath = file2.toPath().normalize(); if ((!linkFile.exists() || !Files.isSymbolicLink(linkPath) || !Files.readSymbolicLink(linkPath).equals(targetPath)) - && targetPath.isAbsolute() && !targetPath.equals(linkPath)) { + && targetPath.isAbsolute() && !targetPath.equals(linkPath) && !targetPath.toRealPath().equals(linkPath)) { if (logger.isDebugEnabled()) { logger.debug("Creating symbolic link " + linkPath + " to " + targetPath); } @@ -1601,6 +1609,7 @@ public static synchronized String loadLibrary(Class cls, URL[] urls, String l // ... (probably an unsupported operation on Windows, but DLLs never need links, // or other (filesystem?) exception: for example, // "sun.nio.fs.UnixException: No such file or directory" on File.toPath()) ... + canCreateSymbolicLink = false; if (logger.isDebugEnabled()) { logger.debug("Failed to create symbolic link " + linkFile + " to " + file2 + ": " + e); } @@ -1776,7 +1785,7 @@ public static String createLibraryLink(String filename, ClassProperties properti } } } - if (link != null && link.length() > 0) { + if (canCreateSymbolicLink && link != null && link.length() > 0) { File linkFile = new File(parent, link); try { Path linkPath = linkFile.toPath(); @@ -1813,6 +1822,7 @@ public static String createLibraryLink(String filename, ClassProperties properti // ... (probably an unsupported operation on Windows, but DLLs never need links, // or other (filesystem?) exception: for example, // "sun.nio.fs.UnixException: No such file or directory" on File.toPath()) ... + canCreateSymbolicLink = false; if (logger.isDebugEnabled()) { logger.debug("Failed to create symbolic link " + linkFile + ": " + e); }