From 5305155265572250d8dfd13c8d38c45b46650d83 Mon Sep 17 00:00:00 2001
From: Hannes Wellmann <44067969+HannesWell@users.noreply.github.com>
Date: Tue, 17 Aug 2021 15:17:29 +0200
Subject: [PATCH 1/5] Support directory extraction in modular OSGi runtimes
(issue #506)
---
pom.xml | 7 ++
.../java/org/bytedeco/javacpp/Loader.java | 103 +++++++++++++++---
2 files changed, 93 insertions(+), 17 deletions(-)
diff --git a/pom.xml b/pom.xml
index e5c1ae2d2..7afc8645f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -105,6 +105,13 @@
7.0.0
provided
+
+ org.osgi
+ osgi.core
+ 7.0.0
+ provided
+
+
diff --git a/src/main/java/org/bytedeco/javacpp/Loader.java b/src/main/java/org/bytedeco/javacpp/Loader.java
index 9015b9e33..411167a2c 100644
--- a/src/main/java/org/bytedeco/javacpp/Loader.java
+++ b/src/main/java/org/bytedeco/javacpp/Loader.java
@@ -55,12 +55,16 @@
import java.util.WeakHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
+
import org.bytedeco.javacpp.annotation.Cast;
import org.bytedeco.javacpp.annotation.Name;
import org.bytedeco.javacpp.annotation.Platform;
import org.bytedeco.javacpp.annotation.Raw;
import org.bytedeco.javacpp.tools.Builder;
import org.bytedeco.javacpp.tools.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.Version;
/**
* The Loader contains functionality to load native libraries, but also has a bit
@@ -89,6 +93,18 @@ public class Loader {
}
};
+ private static final boolean IS_OSGI_RUNTIME;
+ static {
+ boolean isOSGI;
+ try {
+ Bundle.class.getName();
+ isOSGI = true;
+ } catch (NoClassDefFoundError e) {
+ isOSGI = false;
+ }
+ IS_OSGI_RUNTIME = isOSGI;
+ }
+
public static class Detector {
/**
* Returns either the value of the "org.bytedeco.javacpp.platform"
@@ -414,7 +430,7 @@ public static File cacheResource(String name) throws IOException {
*/
public static File cacheResource(Class cls, String name) throws IOException {
URL u = findResource(cls, name);
- return u != null ? cacheResource(u) : null;
+ return u != null ? cacheResource(cls, u, null) : null;
}
/**
@@ -440,14 +456,14 @@ public static File[] cacheResources(Class cls, String name) throws IOException {
URL[] urls = findResources(cls, name);
File[] files = new File[urls.length];
for (int i = 0; i < urls.length; i++) {
- files[i] = cacheResource(urls[i]);
+ files[i] = cacheResource(cls, urls[i], null);
}
return files;
}
/** Returns {@code cacheResource(resourceUrl, null)} */
public static File cacheResource(URL resourceURL) throws IOException {
- return cacheResource(resourceURL, null);
+ return cacheResource(null, resourceURL, null);
}
/**
* Extracts a resource, if the size or last modified timestamp differs from what is in cache,
@@ -462,6 +478,10 @@ public static File cacheResource(URL resourceURL) throws IOException {
* @see #cacheDir
*/
public static File cacheResource(URL resourceURL, String target) throws IOException {
+ return cacheResource(null, resourceURL, target);
+ }
+
+ private static File cacheResource(Class> cls, URL resourceURL, String target) throws IOException {
// Find appropriate subdirectory in cache for the resource ...
File urlFile;
String[] splitURL = resourceURL.toString().split("#");
@@ -522,6 +542,21 @@ public static File cacheResource(URL resourceURL, String target) throws IOExcept
if (!noSubdir) {
cacheSubdir = new File(cacheSubdir, urlFile.getParentFile().getName());
}
+ } else if (IS_OSGI_RUNTIME && cls != null) {
+ size = urlConnection.getContentLengthLong();
+ timestamp = urlConnection.getLastModified();
+
+ Bundle bundle = FrameworkUtil.getBundle(cls);
+ if (bundle != null) {
+ Version v = bundle.getVersion();
+ String version = v.getMajor() + "." + v.getMinor() + "." + v.getMicro(); // skip qualifier
+ String subdirName = bundle.getSymbolicName() + "_" + version;
+ String parentName = urlFile.getParentFile().toString();
+ if (parentName != null) {
+ subdirName = subdirName + File.separator + parentName;
+ }
+ cacheSubdir = new File(cacheSubdir, subdirName);
+ }
} else {
if (urlFile.exists()) {
size = urlFile.length();
@@ -654,7 +689,7 @@ public static File cacheResource(URL resourceURL, String target) throws IOExcept
logger.debug("Extracting " + resourceURL);
}
file.delete();
- extractResource(resourceURL, file, null, null, true);
+ extractResource(cls, resourceURL, file, null, null, true);
file.setLastModified(timestamp);
}
} finally {
@@ -692,7 +727,7 @@ public static File extractResource(String name, File directory,
public static File extractResource(Class cls, String name, File directory,
String prefix, String suffix) throws IOException {
URL u = findResource(cls, name);
- return u != null ? extractResource(u, directory, prefix, suffix) : null;
+ return u != null ? extractResource(cls, u, directory, prefix, suffix, false) : null;
}
/**
@@ -718,7 +753,7 @@ public static File[] extractResources(Class cls, String name, File directory,
URL[] urls = findResources(cls, name);
File[] files = new File[urls.length];
for (int i = 0; i < urls.length; i++) {
- files[i] = extractResource(urls[i], directory, prefix, suffix);
+ files[i] = extractResource(cls, urls[i], directory, prefix, suffix, false);
}
return files;
}
@@ -726,7 +761,7 @@ public static File[] extractResources(Class cls, String name, File directory,
/** Returns {@code extractResource(resourceURL, directoryOrFile, prefix, suffix, false)}. */
public static File extractResource(URL resourceURL, File directoryOrFile,
String prefix, String suffix) throws IOException {
- return extractResource(resourceURL, directoryOrFile, prefix, suffix, false);
+ return extractResource(null, resourceURL, directoryOrFile, prefix, suffix, false);
}
/**
@@ -744,11 +779,16 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
*/
public static File extractResource(URL resourceURL, File directoryOrFile,
String prefix, String suffix, boolean cacheDirectory) throws IOException {
+ return extractResource(null, directoryOrFile, prefix, suffix, cacheDirectory);
+ }
+
+ private static File extractResource(Class cls, URL resourceURL, File directoryOrFile, String prefix, String suffix,
+ boolean cacheDirectory) throws IOException {
URLConnection urlConnection = resourceURL != null ? resourceURL.openConnection() : null;
+ String resourceURLStr = resourceURL.toString();
if (urlConnection instanceof JarURLConnection) {
JarFile jarFile = ((JarURLConnection)urlConnection).getJarFile();
JarEntry jarEntry = ((JarURLConnection)urlConnection).getJarEntry();
- String jarFileName = jarFile.getName();
String jarEntryName = jarEntry.getName();
if (!jarEntryName.endsWith("/")) {
jarEntryName += "/";
@@ -759,9 +799,9 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
- long entrySize = entry.getSize();
- long entryTimestamp = entry.getTime();
if (entryName.startsWith(jarEntryName)) {
+ long entrySize = entry.getSize();
+ long entryTimestamp = entry.getTime();
File file = new File(directoryOrFile, entryName.substring(jarEntryName.length()));
if (entry.isDirectory()) {
file.mkdirs();
@@ -769,9 +809,8 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
|| file.lastModified() != entryTimestamp || !file.equals(file.getCanonicalFile())) {
// ... extract it from our resources ...
file.delete();
- String s = resourceURL.toString();
- URL u = new URL(s.substring(0, s.indexOf("!/") + 2) + entryName);
- file = extractResource(u, file, prefix, suffix);
+ URL u = new URL(resourceURLStr.substring(0, resourceURLStr.indexOf("!/") + 2) + entryName);
+ file = extractResource(cls, u, file, prefix, suffix, false);
}
file.setLastModified(entryTimestamp);
}
@@ -779,6 +818,36 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
return directoryOrFile;
}
}
+ if (IS_OSGI_RUNTIME && cls != null) {
+ // TODO: check if the URL is connected to the given class, respectively obtain
+ // the bundle from the URL.
+ Bundle bundle = FrameworkUtil.getBundle(cls);
+ if (bundle != null) {
+ String path = resourceURL.getPath();
+ Enumeration entries = bundle.findEntries(path, null, true);
+ if (entries != null && entries.hasMoreElements()) { // a not empty directory
+ while (entries.hasMoreElements()) {
+ URL entry = entries.nextElement();
+ String entryPath = entry.getPath();
+ URLConnection entryConnection = entry.openConnection();
+ long entrySize = entryConnection.getContentLengthLong();
+ long entryTimestamp = entryConnection.getLastModified();
+ File file = new File(directoryOrFile, entryPath.substring(path.length()));
+ if (entryPath.endsWith("/")) { // is directory
+ file.mkdirs();
+ } else if (!cacheDirectory || !file.exists() || file.length() != entrySize
+ || file.lastModified() != entryTimestamp || !file.equals(file.getCanonicalFile())) {
+ // ... extract it from our resources ...
+ file.delete();
+ // optimization: pass null-class to not check for directories again
+ extractResource(null, entry, file, prefix, suffix, false);
+ }
+ file.setLastModified(entryTimestamp);
+ }
+ return directoryOrFile;
+ }
+ }
+ }
InputStream is = urlConnection != null ? urlConnection.getInputStream() : null;
OutputStream os = null;
if (is == null) {
@@ -795,7 +864,7 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
if (directoryOrFile.isDirectory()) {
directory = directoryOrFile;
try {
- file = new File(directoryOrFile, new File(new URI(resourceURL.toString().split("#")[0])).getName());
+ file = new File(directoryOrFile, new File(new URI(resourceURLStr.split("#")[0])).getName());
} catch (IllegalArgumentException | URISyntaxException ex) {
file = new File(directoryOrFile, new File(resourceURL.getPath()).getName());
}
@@ -1283,7 +1352,7 @@ public static String load(Class cls, Properties properties, boolean pathsFirst,
for (String preload : preloads) {
URL[] urls = findLibrary(cls, p, preload, true);
for (URL url : urls) {
- File f = cacheResource(url);
+ File f = cacheResource(cls, url, null);
if (f != null) {
f.setExecutable(true);
break;
@@ -1309,7 +1378,7 @@ public static String load(Class cls, Properties properties, boolean pathsFirst,
f.set(u, filename2);
}
}
- File f = cacheResource(u);
+ File f = cacheResource(cls, u, null);
if (f != null) {
f.setExecutable(true);
executablePaths.put(e, f.getAbsolutePath());
@@ -1596,7 +1665,7 @@ public static synchronized String loadLibrary(Class> cls, URL[] urls, String l
file = new File(uri);
} catch (Exception exc) {
// ... extract it from resources into the cache, if necessary ...
- File f = cacheResource(url, filename);
+ File f = cacheResource(cls, url, filename);
try {
if (f != null) {
file = f;
From e5399d1bdfb8d461b055f27401af6f163bd168ef Mon Sep 17 00:00:00 2001
From: Hannes Wellmann <44067969+HannesWell@users.noreply.github.com>
Date: Wed, 18 Aug 2021 14:22:52 +0200
Subject: [PATCH 2/5] [SQUASH] extract bundle from URL and move OSGi related
code to own class
- fixes extraction paths and OSGi related code into own class
- prevents NoClassDefFoundError in non-OSGi environments
---
.../java/org/bytedeco/javacpp/Loader.java | 147 +++++++--------
.../tools/OSGiBundleResourceLoader.java | 178 ++++++++++++++++++
2 files changed, 241 insertions(+), 84 deletions(-)
create mode 100644 src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
diff --git a/src/main/java/org/bytedeco/javacpp/Loader.java b/src/main/java/org/bytedeco/javacpp/Loader.java
index 411167a2c..b5070a0d5 100644
--- a/src/main/java/org/bytedeco/javacpp/Loader.java
+++ b/src/main/java/org/bytedeco/javacpp/Loader.java
@@ -62,9 +62,7 @@
import org.bytedeco.javacpp.annotation.Raw;
import org.bytedeco.javacpp.tools.Builder;
import org.bytedeco.javacpp.tools.Logger;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.Version;
+import org.bytedeco.javacpp.tools.OSGiBundleResourceLoader;
/**
* The Loader contains functionality to load native libraries, but also has a bit
@@ -93,18 +91,6 @@ public class Loader {
}
};
- private static final boolean IS_OSGI_RUNTIME;
- static {
- boolean isOSGI;
- try {
- Bundle.class.getName();
- isOSGI = true;
- } catch (NoClassDefFoundError e) {
- isOSGI = false;
- }
- IS_OSGI_RUNTIME = isOSGI;
- }
-
public static class Detector {
/**
* Returns either the value of the "org.bytedeco.javacpp.platform"
@@ -430,7 +416,7 @@ public static File cacheResource(String name) throws IOException {
*/
public static File cacheResource(Class cls, String name) throws IOException {
URL u = findResource(cls, name);
- return u != null ? cacheResource(cls, u, null) : null;
+ return u != null ? cacheResource(u) : null;
}
/**
@@ -456,14 +442,14 @@ public static File[] cacheResources(Class cls, String name) throws IOException {
URL[] urls = findResources(cls, name);
File[] files = new File[urls.length];
for (int i = 0; i < urls.length; i++) {
- files[i] = cacheResource(cls, urls[i], null);
+ files[i] = cacheResource(urls[i]);
}
return files;
}
/** Returns {@code cacheResource(resourceUrl, null)} */
public static File cacheResource(URL resourceURL) throws IOException {
- return cacheResource(null, resourceURL, null);
+ return cacheResource(resourceURL, null);
}
/**
* Extracts a resource, if the size or last modified timestamp differs from what is in cache,
@@ -478,10 +464,6 @@ public static File cacheResource(URL resourceURL) throws IOException {
* @see #cacheDir
*/
public static File cacheResource(URL resourceURL, String target) throws IOException {
- return cacheResource(null, resourceURL, target);
- }
-
- private static File cacheResource(Class> cls, URL resourceURL, String target) throws IOException {
// Find appropriate subdirectory in cache for the resource ...
File urlFile;
String[] splitURL = resourceURL.toString().split("#");
@@ -542,22 +524,22 @@ private static File cacheResource(Class> cls, URL resourceURL, String target)
if (!noSubdir) {
cacheSubdir = new File(cacheSubdir, urlFile.getParentFile().getName());
}
- } else if (IS_OSGI_RUNTIME && cls != null) {
- size = urlConnection.getContentLengthLong();
- timestamp = urlConnection.getLastModified();
-
- Bundle bundle = FrameworkUtil.getBundle(cls);
- if (bundle != null) {
- Version v = bundle.getVersion();
- String version = v.getMajor() + "." + v.getMinor() + "." + v.getMicro(); // skip qualifier
- String subdirName = bundle.getSymbolicName() + "_" + version;
- String parentName = urlFile.getParentFile().toString();
- if (parentName != null) {
- subdirName = subdirName + File.separator + parentName;
- }
- cacheSubdir = new File(cacheSubdir, subdirName);
- }
- } else {
+ } else if (OSGiBundleResourceLoader.isOSGiRuntime()) {
+ // TODO: what happens if this is another URL in a OSGi environment?
+ // I think it is unlikely that is called with URL-schema?!
+ if (!noSubdir) {
+ String subdirName = OSGiBundleResourceLoader.getContainerBundleName(resourceURL);
+ if (subdirName != null) {
+ String parentName = urlFile.getParentFile().toString();
+ if (parentName != null) {
+ subdirName = subdirName + File.separator + parentName;
+ }
+ cacheSubdir = new File(cacheSubdir, subdirName);
+ }
+ size = urlConnection.getContentLengthLong();
+ timestamp = urlConnection.getLastModified();
+ }
+ } else {
if (urlFile.exists()) {
size = urlFile.length();
timestamp = urlFile.lastModified();
@@ -689,7 +671,7 @@ private static File cacheResource(Class> cls, URL resourceURL, String target)
logger.debug("Extracting " + resourceURL);
}
file.delete();
- extractResource(cls, resourceURL, file, null, null, true);
+ extractResource(resourceURL, file, null, null, true);
file.setLastModified(timestamp);
}
} finally {
@@ -727,7 +709,7 @@ public static File extractResource(String name, File directory,
public static File extractResource(Class cls, String name, File directory,
String prefix, String suffix) throws IOException {
URL u = findResource(cls, name);
- return u != null ? extractResource(cls, u, directory, prefix, suffix, false) : null;
+ return u != null ? extractResource(u, directory, prefix, suffix) : null;
}
/**
@@ -753,7 +735,7 @@ public static File[] extractResources(Class cls, String name, File directory,
URL[] urls = findResources(cls, name);
File[] files = new File[urls.length];
for (int i = 0; i < urls.length; i++) {
- files[i] = extractResource(cls, urls[i], directory, prefix, suffix, false);
+ files[i] = extractResource(urls[i], directory, prefix, suffix);
}
return files;
}
@@ -761,7 +743,7 @@ public static File[] extractResources(Class cls, String name, File directory,
/** Returns {@code extractResource(resourceURL, directoryOrFile, prefix, suffix, false)}. */
public static File extractResource(URL resourceURL, File directoryOrFile,
String prefix, String suffix) throws IOException {
- return extractResource(null, resourceURL, directoryOrFile, prefix, suffix, false);
+ return extractResource(resourceURL, directoryOrFile, prefix, suffix, false);
}
/**
@@ -779,16 +761,12 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
*/
public static File extractResource(URL resourceURL, File directoryOrFile,
String prefix, String suffix, boolean cacheDirectory) throws IOException {
- return extractResource(null, directoryOrFile, prefix, suffix, cacheDirectory);
- }
-
- private static File extractResource(Class cls, URL resourceURL, File directoryOrFile, String prefix, String suffix,
- boolean cacheDirectory) throws IOException {
URLConnection urlConnection = resourceURL != null ? resourceURL.openConnection() : null;
- String resourceURLStr = resourceURL.toString();
+ long start = System.currentTimeMillis();
if (urlConnection instanceof JarURLConnection) {
- JarFile jarFile = ((JarURLConnection)urlConnection).getJarFile();
- JarEntry jarEntry = ((JarURLConnection)urlConnection).getJarEntry();
+ JarFile jarFile = ((JarURLConnection) urlConnection).getJarFile();
+ JarEntry jarEntry = ((JarURLConnection) urlConnection).getJarEntry();
+ String jarFileName = jarFile.getName();
String jarEntryName = jarEntry.getName();
if (!jarEntryName.endsWith("/")) {
jarEntryName += "/";
@@ -799,9 +777,9 @@ private static File extractResource(Class cls, URL resourceURL, File directoryOr
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
+ long entrySize = entry.getSize();
+ long entryTimestamp = entry.getTime();
if (entryName.startsWith(jarEntryName)) {
- long entrySize = entry.getSize();
- long entryTimestamp = entry.getTime();
File file = new File(directoryOrFile, entryName.substring(jarEntryName.length()));
if (entry.isDirectory()) {
file.mkdirs();
@@ -809,45 +787,45 @@ private static File extractResource(Class cls, URL resourceURL, File directoryOr
|| file.lastModified() != entryTimestamp || !file.equals(file.getCanonicalFile())) {
// ... extract it from our resources ...
file.delete();
- URL u = new URL(resourceURLStr.substring(0, resourceURLStr.indexOf("!/") + 2) + entryName);
- file = extractResource(cls, u, file, prefix, suffix, false);
+ String s = resourceURL.toString();
+ URL u = new URL(s.substring(0, s.indexOf("!/") + 2) + entryName);
+ // FIXME: check if directories have to be extracted again?!
+ file = extractResource(u, file, prefix, suffix);
}
file.setLastModified(entryTimestamp);
}
}
+ System.out.println("Extract took " + (System.currentTimeMillis() - start) + "ms, for:" + resourceURL);
return directoryOrFile;
}
}
- if (IS_OSGI_RUNTIME && cls != null) {
- // TODO: check if the URL is connected to the given class, respectively obtain
- // the bundle from the URL.
- Bundle bundle = FrameworkUtil.getBundle(cls);
- if (bundle != null) {
- String path = resourceURL.getPath();
- Enumeration entries = bundle.findEntries(path, null, true);
- if (entries != null && entries.hasMoreElements()) { // a not empty directory
- while (entries.hasMoreElements()) {
- URL entry = entries.nextElement();
- String entryPath = entry.getPath();
- URLConnection entryConnection = entry.openConnection();
- long entrySize = entryConnection.getContentLengthLong();
- long entryTimestamp = entryConnection.getLastModified();
- File file = new File(directoryOrFile, entryPath.substring(path.length()));
- if (entryPath.endsWith("/")) { // is directory
- file.mkdirs();
- } else if (!cacheDirectory || !file.exists() || file.length() != entrySize
- || file.lastModified() != entryTimestamp || !file.equals(file.getCanonicalFile())) {
- // ... extract it from our resources ...
- file.delete();
- // optimization: pass null-class to not check for directories again
- extractResource(null, entry, file, prefix, suffix, false);
- }
- file.setLastModified(entryTimestamp);
+ if (OSGiBundleResourceLoader.isOSGiRuntime()) {
+ Enumeration directoryEntries = OSGiBundleResourceLoader.getBundleDirectoryContent(resourceURL);
+ if (directoryEntries != null && directoryEntries.hasMoreElements()) { // a not empty directory
+ String directoryName = resourceURL.getPath();
+ while (directoryEntries.hasMoreElements()) {
+ URL entry = directoryEntries.nextElement();
+ String entryName = entry.getPath();
+ URLConnection entryConnection = entry.openConnection();
+ long entrySize = entryConnection.getContentLengthLong();
+ long entryTimestamp = entryConnection.getLastModified();
+ File file = new File(directoryOrFile, entryName.substring(directoryName.length()));
+ if (entryName.endsWith("/")) { // is directory
+ file.mkdirs();
+ } else if (!cacheDirectory || !file.exists() || file.length() != entrySize
+ || file.lastModified() != entryTimestamp || !file.equals(file.getCanonicalFile())) {
+ // ... extract it from our resources ...
+ file.delete();
+ // FIXME: check if directories have to be extracted again?!
+ file = extractResource(entry, file, prefix, suffix);
}
- return directoryOrFile;
+ file.setLastModified(entryTimestamp);
}
+ System.out.println("Extract took " + (System.currentTimeMillis() - start) + "ms, for:" + resourceURL);
+ return directoryOrFile;
}
}
+
InputStream is = urlConnection != null ? urlConnection.getInputStream() : null;
OutputStream os = null;
if (is == null) {
@@ -864,7 +842,7 @@ private static File extractResource(Class cls, URL resourceURL, File directoryOr
if (directoryOrFile.isDirectory()) {
directory = directoryOrFile;
try {
- file = new File(directoryOrFile, new File(new URI(resourceURLStr.split("#")[0])).getName());
+ file = new File(directoryOrFile, new File(new URI(resourceURL.toString().split("#")[0])).getName());
} catch (IllegalArgumentException | URISyntaxException ex) {
file = new File(directoryOrFile, new File(resourceURL.getPath()).getName());
}
@@ -879,6 +857,7 @@ private static File extractResource(Class cls, URL resourceURL, File directoryOr
} else {
file = File.createTempFile(prefix, suffix, directoryOrFile);
}
+ System.out.println("Extract resource (" + resourceURL + ") to " + file);
file.delete();
os = new FileOutputStream(file);
byte[] buffer = new byte[64 * 1024];
@@ -1352,7 +1331,7 @@ public static String load(Class cls, Properties properties, boolean pathsFirst,
for (String preload : preloads) {
URL[] urls = findLibrary(cls, p, preload, true);
for (URL url : urls) {
- File f = cacheResource(cls, url, null);
+ File f = cacheResource(url);
if (f != null) {
f.setExecutable(true);
break;
@@ -1378,7 +1357,7 @@ public static String load(Class cls, Properties properties, boolean pathsFirst,
f.set(u, filename2);
}
}
- File f = cacheResource(cls, u, null);
+ File f = cacheResource(u);
if (f != null) {
f.setExecutable(true);
executablePaths.put(e, f.getAbsolutePath());
@@ -1665,7 +1644,7 @@ public static synchronized String loadLibrary(Class> cls, URL[] urls, String l
file = new File(uri);
} catch (Exception exc) {
// ... extract it from resources into the cache, if necessary ...
- File f = cacheResource(cls, url, filename);
+ File f = cacheResource(url, filename);
try {
if (f != null) {
file = f;
diff --git a/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java b/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
new file mode 100644
index 000000000..96bd726af
--- /dev/null
+++ b/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021-2021 Hannes Wellmann
+ *
+ * Licensed either under the Apache License, Version 2.0, or (at your option)
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (subject to the "Classpath" exception),
+ * either version 2, or any later version (collectively, 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
+ * http://www.gnu.org/licenses/
+ * http://www.gnu.org/software/classpath/license.html
+ *
+ * or as provided in the LICENSE.txt file that accompanied this code.
+ * 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.bytedeco.javacpp.tools;
+
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.Version;
+
+public class OSGiBundleResourceLoader {
+
+ private OSGiBundleResourceLoader() { // static use only
+ }
+
+ public static boolean isOSGiRuntime() {
+ return IS_OSGI_RUNTIME;
+ }
+
+ public static String getContainerBundleName(URL resourceURL) {
+ requireOSGi();
+ return OSGiEnvironmentLoader.getContainerBundleName(resourceURL);
+ }
+
+ public static Enumeration getBundleDirectoryContent(URL resourceURL) {
+ requireOSGi();
+ return OSGiEnvironmentLoader.getBundleDirectoryContent(resourceURL);
+ }
+
+ private static void requireOSGi() {
+ if (!IS_OSGI_RUNTIME) {
+ throw new IllegalStateException(
+ OSGiBundleResourceLoader.class.getSimpleName() + " must only be used within a OSGi runtime");
+ }
+ }
+
+ private static final Map HOST_2_BUNDLE = new ConcurrentHashMap();
+ private static final boolean IS_OSGI_RUNTIME;
+
+ static {
+ boolean isOSGI;
+ try {
+ Bundle.class.getName();
+ isOSGI = true;
+ } catch (NoClassDefFoundError e) {
+ isOSGI = false;
+ }
+ IS_OSGI_RUNTIME = isOSGI;
+ if (IS_OSGI_RUNTIME) {
+ OSGiEnvironmentLoader.initialize();
+ }
+ }
+
+ private static class OSGiEnvironmentLoader {
+ // Code using OSGi APIs has to be encapsulated into own class
+ // to prevent NoClassDefFoundErrors in OSGi environments
+
+ private static void initialize() {
+ BundleContext context = getBundleContext();
+ if (context != null) {
+ indexAllBundles(context);
+ context.addBundleListener(new BundleListener() {
+ @Override
+ public void bundleChanged(BundleEvent event) {
+ Bundle bundle = event.getBundle();
+ switch (event.getType()) {
+ case BundleEvent.RESOLVED:
+ HOST_2_BUNDLE.put(getBundleURLHost(bundle), bundle.getBundleId());
+ break;
+ case BundleEvent.UNRESOLVED:
+ HOST_2_BUNDLE.remove(getBundleURLHost(bundle));
+ break;
+ default:
+ break;
+ }
+ }
+ });
+ context.addFrameworkListener(new FrameworkListener() {
+ @Override
+ public void frameworkEvent(FrameworkEvent event) {
+ if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
+ HOST_2_BUNDLE.clear();
+ indexAllBundles(getBundleContext());
+ // don't keep a reference on the BundleContext
+ }
+ }
+ });
+ }
+ }
+
+ private static BundleContext getBundleContext() {
+ Bundle bundle = FrameworkUtil.getBundle(OSGiEnvironmentLoader.class);
+ if (bundle != null) {
+ int state = bundle.getState();
+ if (state != Bundle.ACTIVE
+ && (state == Bundle.INSTALLED || state == Bundle.RESOLVED || state == Bundle.STARTING)) {
+ try {
+ bundle.start();
+ } catch (BundleException e) { // ignore
+ }
+ }
+ if (bundle.getState() == Bundle.ACTIVE) {
+ return bundle.getBundleContext();
+ }
+ }
+ return null;
+ }
+
+ private static void indexAllBundles(BundleContext context) {
+ if (context != null) {
+ for (Bundle bundle : context.getBundles()) {
+ HOST_2_BUNDLE.put(getBundleURLHost(bundle), bundle.getBundleId());
+ }
+ }
+ }
+
+ private static String getBundleURLHost(Bundle bundle) {
+ return bundle.getEntry("/").getHost();
+ }
+
+ private static Bundle getContainerBundle(URL url) {
+ Long bundleId = HOST_2_BUNDLE.get(url.getHost());
+ if (bundleId != null) {
+ BundleContext context = getBundleContext();
+ if (context != null) {
+ return context.getBundle(bundleId);
+ }
+ }
+ return null;
+ }
+
+ public static String getContainerBundleName(URL resourceURL) {
+ Bundle bundle = getContainerBundle(resourceURL);
+ if (bundle != null) {
+ Version v = bundle.getVersion();
+ String version = v.getMajor() + "." + v.getMinor() + "." + v.getMicro(); // skip qualifier
+ return bundle.getSymbolicName() + "_" + version;
+ }
+ return null;
+ }
+
+ public static Enumeration getBundleDirectoryContent(URL resourceURL) {
+ Bundle bundle = getContainerBundle(resourceURL);
+ if (bundle != null) {
+ return bundle.findEntries(resourceURL.getPath(), null, true);
+ }
+ return null;
+ }
+ }
+}
From caed1607fb53aa6cb761100466153d00bdc391ad Mon Sep 17 00:00:00 2001
From: Hannes Wellmann <44067969+HannesWell@users.noreply.github.com>
Date: Fri, 20 Aug 2021 18:26:34 +0200
Subject: [PATCH 3/5] [FIXUP] apply javacpp formatting
---
.../java/org/bytedeco/javacpp/Loader.java | 32 +--
.../tools/OSGiBundleResourceLoader.java | 272 +++++++++---------
2 files changed, 151 insertions(+), 153 deletions(-)
diff --git a/src/main/java/org/bytedeco/javacpp/Loader.java b/src/main/java/org/bytedeco/javacpp/Loader.java
index b5070a0d5..dc02dd146 100644
--- a/src/main/java/org/bytedeco/javacpp/Loader.java
+++ b/src/main/java/org/bytedeco/javacpp/Loader.java
@@ -524,22 +524,22 @@ public static File cacheResource(URL resourceURL, String target) throws IOExcept
if (!noSubdir) {
cacheSubdir = new File(cacheSubdir, urlFile.getParentFile().getName());
}
- } else if (OSGiBundleResourceLoader.isOSGiRuntime()) {
- // TODO: what happens if this is another URL in a OSGi environment?
- // I think it is unlikely that is called with URL-schema?!
- if (!noSubdir) {
- String subdirName = OSGiBundleResourceLoader.getContainerBundleName(resourceURL);
- if (subdirName != null) {
- String parentName = urlFile.getParentFile().toString();
- if (parentName != null) {
- subdirName = subdirName + File.separator + parentName;
- }
- cacheSubdir = new File(cacheSubdir, subdirName);
- }
- size = urlConnection.getContentLengthLong();
- timestamp = urlConnection.getLastModified();
- }
- } else {
+ } else if (OSGiBundleResourceLoader.isOSGiRuntime()) {
+ // TODO: what happens if this is another URL in a OSGi environment?
+ // I think it is unlikely that is called with URL-schema?!
+ if (!noSubdir) {
+ String subdirName = OSGiBundleResourceLoader.getContainerBundleName(resourceURL);
+ if (subdirName != null) {
+ String parentName = urlFile.getParentFile().toString();
+ if (parentName != null) {
+ subdirName = subdirName + File.separator + parentName;
+ }
+ cacheSubdir = new File(cacheSubdir, subdirName);
+ }
+ size = urlConnection.getContentLengthLong();
+ timestamp = urlConnection.getLastModified();
+ }
+ } else {
if (urlFile.exists()) {
size = urlFile.length();
timestamp = urlFile.lastModified();
diff --git a/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java b/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
index 96bd726af..f89ba0fb3 100644
--- a/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
+++ b/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
@@ -38,141 +38,139 @@
public class OSGiBundleResourceLoader {
- private OSGiBundleResourceLoader() { // static use only
- }
-
- public static boolean isOSGiRuntime() {
- return IS_OSGI_RUNTIME;
- }
-
- public static String getContainerBundleName(URL resourceURL) {
- requireOSGi();
- return OSGiEnvironmentLoader.getContainerBundleName(resourceURL);
- }
-
- public static Enumeration getBundleDirectoryContent(URL resourceURL) {
- requireOSGi();
- return OSGiEnvironmentLoader.getBundleDirectoryContent(resourceURL);
- }
-
- private static void requireOSGi() {
- if (!IS_OSGI_RUNTIME) {
- throw new IllegalStateException(
- OSGiBundleResourceLoader.class.getSimpleName() + " must only be used within a OSGi runtime");
- }
- }
-
- private static final Map HOST_2_BUNDLE = new ConcurrentHashMap();
- private static final boolean IS_OSGI_RUNTIME;
-
- static {
- boolean isOSGI;
- try {
- Bundle.class.getName();
- isOSGI = true;
- } catch (NoClassDefFoundError e) {
- isOSGI = false;
- }
- IS_OSGI_RUNTIME = isOSGI;
- if (IS_OSGI_RUNTIME) {
- OSGiEnvironmentLoader.initialize();
- }
- }
-
- private static class OSGiEnvironmentLoader {
- // Code using OSGi APIs has to be encapsulated into own class
- // to prevent NoClassDefFoundErrors in OSGi environments
-
- private static void initialize() {
- BundleContext context = getBundleContext();
- if (context != null) {
- indexAllBundles(context);
- context.addBundleListener(new BundleListener() {
- @Override
- public void bundleChanged(BundleEvent event) {
- Bundle bundle = event.getBundle();
- switch (event.getType()) {
- case BundleEvent.RESOLVED:
- HOST_2_BUNDLE.put(getBundleURLHost(bundle), bundle.getBundleId());
- break;
- case BundleEvent.UNRESOLVED:
- HOST_2_BUNDLE.remove(getBundleURLHost(bundle));
- break;
- default:
- break;
- }
- }
- });
- context.addFrameworkListener(new FrameworkListener() {
- @Override
- public void frameworkEvent(FrameworkEvent event) {
- if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
- HOST_2_BUNDLE.clear();
- indexAllBundles(getBundleContext());
- // don't keep a reference on the BundleContext
- }
- }
- });
- }
- }
-
- private static BundleContext getBundleContext() {
- Bundle bundle = FrameworkUtil.getBundle(OSGiEnvironmentLoader.class);
- if (bundle != null) {
- int state = bundle.getState();
- if (state != Bundle.ACTIVE
- && (state == Bundle.INSTALLED || state == Bundle.RESOLVED || state == Bundle.STARTING)) {
- try {
- bundle.start();
- } catch (BundleException e) { // ignore
- }
- }
- if (bundle.getState() == Bundle.ACTIVE) {
- return bundle.getBundleContext();
- }
- }
- return null;
- }
-
- private static void indexAllBundles(BundleContext context) {
- if (context != null) {
- for (Bundle bundle : context.getBundles()) {
- HOST_2_BUNDLE.put(getBundleURLHost(bundle), bundle.getBundleId());
- }
- }
- }
-
- private static String getBundleURLHost(Bundle bundle) {
- return bundle.getEntry("/").getHost();
- }
-
- private static Bundle getContainerBundle(URL url) {
- Long bundleId = HOST_2_BUNDLE.get(url.getHost());
- if (bundleId != null) {
- BundleContext context = getBundleContext();
- if (context != null) {
- return context.getBundle(bundleId);
- }
- }
- return null;
- }
-
- public static String getContainerBundleName(URL resourceURL) {
- Bundle bundle = getContainerBundle(resourceURL);
- if (bundle != null) {
- Version v = bundle.getVersion();
- String version = v.getMajor() + "." + v.getMinor() + "." + v.getMicro(); // skip qualifier
- return bundle.getSymbolicName() + "_" + version;
- }
- return null;
- }
-
- public static Enumeration getBundleDirectoryContent(URL resourceURL) {
- Bundle bundle = getContainerBundle(resourceURL);
- if (bundle != null) {
- return bundle.findEntries(resourceURL.getPath(), null, true);
- }
- return null;
- }
- }
+ private OSGiBundleResourceLoader() { // static use only
+ }
+
+ public static boolean isOSGiRuntime() {
+ return IS_OSGI_RUNTIME;
+ }
+
+ public static String getContainerBundleName(URL resourceURL) {
+ requireOSGi();
+ return OSGiEnvironmentLoader.getContainerBundleName(resourceURL);
+ }
+
+ public static Enumeration getBundleDirectoryContent(URL resourceURL) {
+ requireOSGi();
+ return OSGiEnvironmentLoader.getBundleDirectoryContent(resourceURL);
+ }
+
+ private static void requireOSGi() {
+ if (!IS_OSGI_RUNTIME) {
+ throw new IllegalStateException(OSGiBundleResourceLoader.class.getSimpleName() + " must only be used within a OSGi runtime");
+ }
+ }
+
+ private static final Map HOST_2_BUNDLE = new ConcurrentHashMap();
+ private static final boolean IS_OSGI_RUNTIME;
+
+ static {
+ boolean isOSGI;
+ try {
+ Bundle.class.getName();
+ isOSGI = true;
+ } catch (NoClassDefFoundError e) {
+ isOSGI = false;
+ }
+ IS_OSGI_RUNTIME = isOSGI;
+ if (IS_OSGI_RUNTIME) {
+ OSGiEnvironmentLoader.initialize();
+ }
+ }
+
+ private static class OSGiEnvironmentLoader {
+ // Code using OSGi APIs has to be encapsulated into own class
+ // to prevent NoClassDefFoundErrors in OSGi environments
+
+ private static void initialize() {
+ BundleContext context = getBundleContext();
+ if (context != null) {
+ indexAllBundles(context);
+ context.addBundleListener(new BundleListener() {
+ @Override
+ public void bundleChanged(BundleEvent event) {
+ Bundle bundle = event.getBundle();
+ switch (event.getType()) {
+ case BundleEvent.RESOLVED:
+ HOST_2_BUNDLE.put(getBundleURLHost(bundle), bundle.getBundleId());
+ break;
+ case BundleEvent.UNRESOLVED:
+ HOST_2_BUNDLE.remove(getBundleURLHost(bundle));
+ break;
+ default:
+ break;
+ }
+ }
+ });
+ context.addFrameworkListener(new FrameworkListener() {
+ @Override
+ public void frameworkEvent(FrameworkEvent event) {
+ if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
+ HOST_2_BUNDLE.clear();
+ indexAllBundles(getBundleContext());
+ // don't keep a reference on the BundleContext
+ }
+ }
+ });
+ }
+ }
+
+ private static BundleContext getBundleContext() {
+ Bundle bundle = FrameworkUtil.getBundle(OSGiEnvironmentLoader.class);
+ if (bundle != null) {
+ int state = bundle.getState();
+ if (state != Bundle.ACTIVE && (state == Bundle.INSTALLED || state == Bundle.RESOLVED || state == Bundle.STARTING)) {
+ try {
+ bundle.start();
+ } catch (BundleException e) { // ignore
+ }
+ }
+ if (bundle.getState() == Bundle.ACTIVE) {
+ return bundle.getBundleContext();
+ }
+ }
+ return null;
+ }
+
+ private static void indexAllBundles(BundleContext context) {
+ if (context != null) {
+ for (Bundle bundle : context.getBundles()) {
+ HOST_2_BUNDLE.put(getBundleURLHost(bundle), bundle.getBundleId());
+ }
+ }
+ }
+
+ private static String getBundleURLHost(Bundle bundle) {
+ return bundle.getEntry("/").getHost();
+ }
+
+ private static Bundle getContainerBundle(URL url) {
+ Long bundleId = HOST_2_BUNDLE.get(url.getHost());
+ if (bundleId != null) {
+ BundleContext context = getBundleContext();
+ if (context != null) {
+ return context.getBundle(bundleId);
+ }
+ }
+ return null;
+ }
+
+ public static String getContainerBundleName(URL resourceURL) {
+ Bundle bundle = getContainerBundle(resourceURL);
+ if (bundle != null) {
+ Version v = bundle.getVersion();
+ String version = v.getMajor() + "." + v.getMinor() + "." + v.getMicro(); // skip qualifier
+ return bundle.getSymbolicName() + "_" + version;
+ }
+ return null;
+ }
+
+ public static Enumeration getBundleDirectoryContent(URL resourceURL) {
+ Bundle bundle = getContainerBundle(resourceURL);
+ if (bundle != null) {
+ return bundle.findEntries(resourceURL.getPath(), null, true);
+ }
+ return null;
+ }
+ }
}
From 10f20205694b160ddcbefa371c036336494b6b79 Mon Sep 17 00:00:00 2001
From: Hannes Wellmann <44067969+HannesWell@users.noreply.github.com>
Date: Sun, 29 Aug 2021 16:04:47 +0200
Subject: [PATCH 4/5] [SQUASH] use BundleTracker
---
.../java/org/bytedeco/javacpp/Loader.java | 12 +-
.../tools/OSGiBundleResourceLoader.java | 121 ++++++++++++++----
2 files changed, 103 insertions(+), 30 deletions(-)
diff --git a/src/main/java/org/bytedeco/javacpp/Loader.java b/src/main/java/org/bytedeco/javacpp/Loader.java
index dc02dd146..06123a33e 100644
--- a/src/main/java/org/bytedeco/javacpp/Loader.java
+++ b/src/main/java/org/bytedeco/javacpp/Loader.java
@@ -766,7 +766,6 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
if (urlConnection instanceof JarURLConnection) {
JarFile jarFile = ((JarURLConnection) urlConnection).getJarFile();
JarEntry jarEntry = ((JarURLConnection) urlConnection).getJarEntry();
- String jarFileName = jarFile.getName();
String jarEntryName = jarEntry.getName();
if (!jarEntryName.endsWith("/")) {
jarEntryName += "/";
@@ -783,8 +782,7 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
File file = new File(directoryOrFile, entryName.substring(jarEntryName.length()));
if (entry.isDirectory()) {
file.mkdirs();
- } else if (!cacheDirectory || !file.exists() || file.length() != entrySize
- || file.lastModified() != entryTimestamp || !file.equals(file.getCanonicalFile())) {
+ } else if (!cacheDirectory || isCacheFileCurrent(file, entrySize, entryTimestamp)) {
// ... extract it from our resources ...
file.delete();
String s = resourceURL.toString();
@@ -812,8 +810,7 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
File file = new File(directoryOrFile, entryName.substring(directoryName.length()));
if (entryName.endsWith("/")) { // is directory
file.mkdirs();
- } else if (!cacheDirectory || !file.exists() || file.length() != entrySize
- || file.lastModified() != entryTimestamp || !file.equals(file.getCanonicalFile())) {
+ } else if (!cacheDirectory || isCacheFileCurrent(file, entrySize, entryTimestamp)) {
// ... extract it from our resources ...
file.delete();
// FIXME: check if directories have to be extracted again?!
@@ -879,6 +876,11 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
return file;
}
+ private static boolean isCacheFileCurrent(File file, long entrySize, long entryTimestamp) throws IOException {
+ return !file.exists() || file.length() != entrySize || file.lastModified() != entryTimestamp
+ || !file.equals(file.getCanonicalFile());
+ }
+
/** Returns {@code findResources(cls, name, 1)[0]} or null if none. */
public static URL findResource(Class cls, String name) throws IOException {
URL[] url = findResources(cls, name, 1);
diff --git a/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java b/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
index f89ba0fb3..e4d4523ba 100644
--- a/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
+++ b/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
@@ -22,19 +22,33 @@
package org.bytedeco.javacpp.tools;
import java.net.URL;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
-import org.osgi.framework.BundleListener;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.util.tracker.BundleTracker;
public class OSGiBundleResourceLoader {
@@ -61,7 +75,6 @@ private static void requireOSGi() {
}
}
- private static final Map HOST_2_BUNDLE = new ConcurrentHashMap();
private static final boolean IS_OSGI_RUNTIME;
static {
@@ -82,33 +95,72 @@ private static class OSGiEnvironmentLoader {
// Code using OSGi APIs has to be encapsulated into own class
// to prevent NoClassDefFoundErrors in OSGi environments
+ private static final Map HOST_2_BUNDLE_ID = new HashMap<>();
+ private static final ReadWriteLock LOCK = new ReentrantReadWriteLock();
+
private static void initialize() {
BundleContext context = getBundleContext();
if (context != null) {
- indexAllBundles(context);
- context.addBundleListener(new BundleListener() {
+ final BundleTracker bundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.RESOLVED | Bundle.STARTING,
+ null) {
@Override
- public void bundleChanged(BundleEvent event) {
- Bundle bundle = event.getBundle();
- switch (event.getType()) {
- case BundleEvent.RESOLVED:
- HOST_2_BUNDLE.put(getBundleURLHost(bundle), bundle.getBundleId());
- break;
- case BundleEvent.UNRESOLVED:
- HOST_2_BUNDLE.remove(getBundleURLHost(bundle));
- break;
- default:
- break;
+ public Bundle addingBundle(Bundle bundle, BundleEvent event) {
+ Bundle javaCppBundle = context.getBundle();
+ if (requires(bundle, javaCppBundle)) {
+ String bundleURLHost = getBundleURLHost(bundle);
+ Long bundleId = bundle.getBundleId();
+ LOCK.writeLock().lock();
+ try {
+ HOST_2_BUNDLE_ID.put(bundleURLHost, bundleId);
+ } finally {
+ LOCK.writeLock().unlock();
+ }
+ return bundle;
}
+ return null;
}
- });
+
+ @Override
+ public void removedBundle(Bundle bundle, BundleEvent event, Bundle object) {
+ String bundleURLHost = getBundleURLHost(bundle);
+ LOCK.writeLock().lock();
+ try {
+ HOST_2_BUNDLE_ID.remove(bundleURLHost);
+ } finally {
+ LOCK.writeLock().unlock();
+ }
+ }
+
+ };
+ bundleTracker.open();
context.addFrameworkListener(new FrameworkListener() {
@Override
public void frameworkEvent(FrameworkEvent event) {
if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
- HOST_2_BUNDLE.clear();
- indexAllBundles(getBundleContext());
- // don't keep a reference on the BundleContext
+ BundleContext bundleContext = getBundleContext(); // don't keep a reference on the BundleContext
+ if (bundleContext != null) {
+ LOCK.writeLock().lock();
+ try {
+ List> toAdd = new ArrayList<>();
+ for (Iterator> iterator = HOST_2_BUNDLE_ID.entrySet().iterator(); iterator.hasNext();) {
+ Entry entry = iterator.next();
+ Long bundleId = entry.getValue();
+ Bundle bundle = bundleContext.getBundle(bundleId);
+ String bundleURLHost = getBundleURLHost(bundle);
+ if (bundleURLHost.equals(entry.getKey())) {
+ iterator.remove();
+ toAdd.add(new SimpleImmutableEntry<>(bundleURLHost, bundleId));
+ }
+ }
+ for (Entry entry : toAdd) {
+ HOST_2_BUNDLE_ID.put(entry.getKey(), entry.getValue());
+ }
+ } finally {
+ LOCK.writeLock().unlock();
+ }
+ }
+ } else if (event.getType() == FrameworkEvent.STOPPED) {
+ bundleTracker.close();
}
}
});
@@ -132,12 +184,25 @@ private static BundleContext getBundleContext() {
return null;
}
- private static void indexAllBundles(BundleContext context) {
- if (context != null) {
- for (Bundle bundle : context.getBundles()) {
- HOST_2_BUNDLE.put(getBundleURLHost(bundle), bundle.getBundleId());
+ private static boolean requires(Bundle source, Bundle target) {
+ BundleWiring sourceWiring = source.adapt(BundleWiring.class);
+ Queue pending = new ArrayDeque<>(Collections.singleton(sourceWiring));
+ Set visited = new HashSet<>(Collections.singleton(sourceWiring));
+
+ while (!pending.isEmpty()) { // perform iterative bfs
+ BundleWiring wiring = pending.remove();
+ if (wiring.getBundle().equals(target)) {
+ return true;
+ }
+ List requiredWires = wiring.getRequiredWires(null);
+ for (BundleWire requiredWire : requiredWires) {
+ BundleWiring provider = requiredWire.getProviderWiring();
+ if (visited.add(provider)) {
+ pending.add(provider);
+ }
}
}
+ return false;
}
private static String getBundleURLHost(Bundle bundle) {
@@ -145,7 +210,13 @@ private static String getBundleURLHost(Bundle bundle) {
}
private static Bundle getContainerBundle(URL url) {
- Long bundleId = HOST_2_BUNDLE.get(url.getHost());
+ LOCK.readLock().lock();
+ Long bundleId;
+ try {
+ bundleId = HOST_2_BUNDLE_ID.get(url.getHost());
+ } finally {
+ LOCK.readLock().unlock();
+ }
if (bundleId != null) {
BundleContext context = getBundleContext();
if (context != null) {
From 7f428b1daf501435608c708d72c641d767c396b7 Mon Sep 17 00:00:00 2001
From: Hannes Wellmann <44067969+HannesWell@users.noreply.github.com>
Date: Sun, 29 Aug 2021 16:20:47 +0200
Subject: [PATCH 5/5] [SQUASH] store URL and targeted bundle in a WeakHashMap
---
pom.xml | 6 +-
.../java/org/bytedeco/javacpp/Loader.java | 18 +-
.../tools/OSGiBundleResourceLoader.java | 223 ++++++------------
3 files changed, 83 insertions(+), 164 deletions(-)
diff --git a/pom.xml b/pom.xml
index 7afc8645f..e9ecc1151 100644
--- a/pom.xml
+++ b/pom.xml
@@ -96,19 +96,19 @@
true
org.osgi
osgi.annotation
- 7.0.0
+ 8.0.0
provided
org.osgi
osgi.core
- 7.0.0
+ 8.0.0
provided
diff --git a/src/main/java/org/bytedeco/javacpp/Loader.java b/src/main/java/org/bytedeco/javacpp/Loader.java
index 06123a33e..cd8ce94ea 100644
--- a/src/main/java/org/bytedeco/javacpp/Loader.java
+++ b/src/main/java/org/bytedeco/javacpp/Loader.java
@@ -22,6 +22,10 @@
package org.bytedeco.javacpp;
+import static org.bytedeco.javacpp.tools.OSGiBundleResourceLoader.getOSGiClassLoaderResources;
+import static org.bytedeco.javacpp.tools.OSGiBundleResourceLoader.getOSGiClassResource;
+import static org.bytedeco.javacpp.tools.OSGiBundleResourceLoader.isOSGiRuntime;
+
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -524,11 +528,11 @@ public static File cacheResource(URL resourceURL, String target) throws IOExcept
if (!noSubdir) {
cacheSubdir = new File(cacheSubdir, urlFile.getParentFile().getName());
}
- } else if (OSGiBundleResourceLoader.isOSGiRuntime()) {
+ } else if (isOSGiRuntime()) {
// TODO: what happens if this is another URL in a OSGi environment?
// I think it is unlikely that is called with URL-schema?!
if (!noSubdir) {
- String subdirName = OSGiBundleResourceLoader.getContainerBundleName(resourceURL);
+ String subdirName = OSGiBundleResourceLoader.getOSGiContainerBundleName(resourceURL);
if (subdirName != null) {
String parentName = urlFile.getParentFile().toString();
if (parentName != null) {
@@ -797,8 +801,8 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
return directoryOrFile;
}
}
- if (OSGiBundleResourceLoader.isOSGiRuntime()) {
- Enumeration directoryEntries = OSGiBundleResourceLoader.getBundleDirectoryContent(resourceURL);
+ if (isOSGiRuntime()) {
+ Enumeration directoryEntries = OSGiBundleResourceLoader.getOSGiBundleDirectoryContent(resourceURL);
if (directoryEntries != null && directoryEntries.hasMoreElements()) { // a not empty directory
String directoryName = resourceURL.getPath();
while (directoryEntries.hasMoreElements()) {
@@ -912,7 +916,7 @@ public static URL[] findResources(Class cls, String name, int maxLength) throws
}
// Under JPMS, Class.getResource() and ClassLoader.getResources() do not return the same URLs
- URL url = cls.getResource(name);
+ URL url = !isOSGiRuntime() ? cls.getResource(name) : getOSGiClassResource(cls, name);
if (url != null && maxLength == 1) {
return new URL[] {url};
}
@@ -932,7 +936,7 @@ public static URL[] findResources(Class cls, String name, int maxLength) throws
// This is the bootstrap class loader, let's try the system class loader instead
classLoader = ClassLoader.getSystemClassLoader();
}
- Enumeration urls = classLoader.getResources(path + name);
+ Enumeration urls = !isOSGiRuntime() ? classLoader.getResources(path + name) : getOSGiClassLoaderResources(classLoader, path + name);
ArrayList array = new ArrayList();
if (url != null) {
array.add(url);
@@ -944,7 +948,7 @@ public static URL[] findResources(Class cls, String name, int maxLength) throws
} else {
path = "";
}
- urls = classLoader.getResources(path + name);
+ urls = !isOSGiRuntime() ? classLoader.getResources(path + name) : getOSGiClassLoaderResources(classLoader, path + name);
}
while (urls.hasMoreElements() && (maxLength < 0 || array.size() < maxLength)) {
url = urls.nextElement();
diff --git a/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java b/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
index e4d4523ba..a451fce37 100644
--- a/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
+++ b/src/main/java/org/bytedeco/javacpp/tools/OSGiBundleResourceLoader.java
@@ -21,73 +21,66 @@
*/
package org.bytedeco.javacpp.tools;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.net.URL;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Queue;
-import java.util.Set;
+import java.util.Optional;
+import java.util.WeakHashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.FrameworkEvent;
-import org.osgi.framework.FrameworkListener;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.Version;
-import org.osgi.framework.wiring.BundleWire;
-import org.osgi.framework.wiring.BundleWiring;
-import org.osgi.util.tracker.BundleTracker;
public class OSGiBundleResourceLoader {
private OSGiBundleResourceLoader() { // static use only
}
+ private static final boolean IS_OSGI_RUNTIME;
+
+ static {
+ boolean isOSGI;
+ try {
+ Bundle.class.getName();
+ isOSGI = true;
+ } catch (NoClassDefFoundError e) {
+ isOSGI = false;
+ }
+ IS_OSGI_RUNTIME = isOSGI;
+ }
+
public static boolean isOSGiRuntime() {
return IS_OSGI_RUNTIME;
}
- public static String getContainerBundleName(URL resourceURL) {
+ public static String getOSGiContainerBundleName(URL resourceURL) {
requireOSGi();
return OSGiEnvironmentLoader.getContainerBundleName(resourceURL);
}
- public static Enumeration getBundleDirectoryContent(URL resourceURL) {
+ public static Enumeration getOSGiBundleDirectoryContent(URL resourceURL) {
requireOSGi();
return OSGiEnvironmentLoader.getBundleDirectoryContent(resourceURL);
}
- private static void requireOSGi() {
- if (!IS_OSGI_RUNTIME) {
- throw new IllegalStateException(OSGiBundleResourceLoader.class.getSimpleName() + " must only be used within a OSGi runtime");
- }
+ public static URL getOSGiClassResource(Class> c, String name) {
+ requireOSGi();
+ return OSGiEnvironmentLoader.getClassResource(c, name);
}
- private static final boolean IS_OSGI_RUNTIME;
+ public static Enumeration getOSGiClassLoaderResources(ClassLoader cl, String name) throws IOException {
+ requireOSGi();
+ return OSGiEnvironmentLoader.getClassLoaderResources(cl, name);
+ }
- static {
- boolean isOSGI;
- try {
- Bundle.class.getName();
- isOSGI = true;
- } catch (NoClassDefFoundError e) {
- isOSGI = false;
- }
- IS_OSGI_RUNTIME = isOSGI;
- if (IS_OSGI_RUNTIME) {
- OSGiEnvironmentLoader.initialize();
+ private static void requireOSGi() {
+ if (!IS_OSGI_RUNTIME) {
+ throw new IllegalStateException(OSGiBundleResourceLoader.class.getSimpleName() + " must only be used within a OSGi runtime");
}
}
@@ -95,138 +88,60 @@ private static class OSGiEnvironmentLoader {
// Code using OSGi APIs has to be encapsulated into own class
// to prevent NoClassDefFoundErrors in OSGi environments
- private static final Map HOST_2_BUNDLE_ID = new HashMap<>();
private static final ReadWriteLock LOCK = new ReentrantReadWriteLock();
+ private static final WeakHashMap> URL_TO_BUNDLE = new WeakHashMap<>();
- private static void initialize() {
- BundleContext context = getBundleContext();
- if (context != null) {
- final BundleTracker bundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.RESOLVED | Bundle.STARTING,
- null) {
- @Override
- public Bundle addingBundle(Bundle bundle, BundleEvent event) {
- Bundle javaCppBundle = context.getBundle();
- if (requires(bundle, javaCppBundle)) {
- String bundleURLHost = getBundleURLHost(bundle);
- Long bundleId = bundle.getBundleId();
- LOCK.writeLock().lock();
- try {
- HOST_2_BUNDLE_ID.put(bundleURLHost, bundleId);
- } finally {
- LOCK.writeLock().unlock();
- }
- return bundle;
- }
- return null;
- }
-
- @Override
- public void removedBundle(Bundle bundle, BundleEvent event, Bundle object) {
- String bundleURLHost = getBundleURLHost(bundle);
- LOCK.writeLock().lock();
- try {
- HOST_2_BUNDLE_ID.remove(bundleURLHost);
- } finally {
- LOCK.writeLock().unlock();
- }
- }
-
- };
- bundleTracker.open();
- context.addFrameworkListener(new FrameworkListener() {
- @Override
- public void frameworkEvent(FrameworkEvent event) {
- if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
- BundleContext bundleContext = getBundleContext(); // don't keep a reference on the BundleContext
- if (bundleContext != null) {
- LOCK.writeLock().lock();
- try {
- List> toAdd = new ArrayList<>();
- for (Iterator> iterator = HOST_2_BUNDLE_ID.entrySet().iterator(); iterator.hasNext();) {
- Entry entry = iterator.next();
- Long bundleId = entry.getValue();
- Bundle bundle = bundleContext.getBundle(bundleId);
- String bundleURLHost = getBundleURLHost(bundle);
- if (bundleURLHost.equals(entry.getKey())) {
- iterator.remove();
- toAdd.add(new SimpleImmutableEntry<>(bundleURLHost, bundleId));
- }
- }
- for (Entry entry : toAdd) {
- HOST_2_BUNDLE_ID.put(entry.getKey(), entry.getValue());
- }
- } finally {
- LOCK.writeLock().unlock();
- }
- }
- } else if (event.getType() == FrameworkEvent.STOPPED) {
- bundleTracker.close();
- }
- }
- });
- }
+ private static void associateURLtoBundle(URL resource, Bundle bundle) {
+ URL_TO_BUNDLE.put(resource, new WeakReference<>(bundle));
}
- private static BundleContext getBundleContext() {
- Bundle bundle = FrameworkUtil.getBundle(OSGiEnvironmentLoader.class);
- if (bundle != null) {
- int state = bundle.getState();
- if (state != Bundle.ACTIVE && (state == Bundle.INSTALLED || state == Bundle.RESOLVED || state == Bundle.STARTING)) {
- try {
- bundle.start();
- } catch (BundleException e) { // ignore
- }
- }
- if (bundle.getState() == Bundle.ACTIVE) {
- return bundle.getBundleContext();
- }
+ private static Bundle getContainerBundle(URL url) {
+ WeakReference bundle;
+ LOCK.readLock().lock();
+ try {
+ bundle = URL_TO_BUNDLE.get(url);
+ } finally {
+ LOCK.readLock().unlock();
}
- return null;
+ return bundle != null ? bundle.get() : null;
}
- private static boolean requires(Bundle source, Bundle target) {
- BundleWiring sourceWiring = source.adapt(BundleWiring.class);
- Queue pending = new ArrayDeque<>(Collections.singleton(sourceWiring));
- Set visited = new HashSet<>(Collections.singleton(sourceWiring));
-
- while (!pending.isEmpty()) { // perform iterative bfs
- BundleWiring wiring = pending.remove();
- if (wiring.getBundle().equals(target)) {
- return true;
- }
- List requiredWires = wiring.getRequiredWires(null);
- for (BundleWire requiredWire : requiredWires) {
- BundleWiring provider = requiredWire.getProviderWiring();
- if (visited.add(provider)) {
- pending.add(provider);
- }
+ private static URL getClassResource(Class> c, String name) {
+ URL resource = c.getResource(name);
+ if (resource != null) {
+ Bundle bundle = FrameworkUtil.getBundle(c);
+ LOCK.writeLock().lock();
+ try {
+ associateURLtoBundle(resource, bundle);
+ } finally {
+ LOCK.writeLock().unlock();
}
}
- return false;
+ return resource;
}
- private static String getBundleURLHost(Bundle bundle) {
- return bundle.getEntry("/").getHost();
- }
-
- private static Bundle getContainerBundle(URL url) {
- LOCK.readLock().lock();
- Long bundleId;
- try {
- bundleId = HOST_2_BUNDLE_ID.get(url.getHost());
- } finally {
- LOCK.readLock().unlock();
- }
- if (bundleId != null) {
- BundleContext context = getBundleContext();
- if (context != null) {
- return context.getBundle(bundleId);
+ private static Enumeration getClassLoaderResources(ClassLoader cl, String name) throws IOException {
+ Enumeration resources = cl.getResources(name);
+ if (resources != null && resources.hasMoreElements()) {
+ Optional bundleOpt = FrameworkUtil.getBundle(cl);
+ if (bundleOpt.isPresent()) {
+ Bundle bundle = bundleOpt.get();
+ List resourcesList = Collections.list(resources);
+ LOCK.writeLock().lock();
+ try {
+ for (URL url : resourcesList) {
+ associateURLtoBundle(url, bundle);
+ }
+ } finally {
+ LOCK.writeLock().unlock();
+ }
+ return Collections.enumeration(resourcesList);
}
}
- return null;
+ return resources;
}
- public static String getContainerBundleName(URL resourceURL) {
+ private static String getContainerBundleName(URL resourceURL) {
Bundle bundle = getContainerBundle(resourceURL);
if (bundle != null) {
Version v = bundle.getVersion();
@@ -236,7 +151,7 @@ public static String getContainerBundleName(URL resourceURL) {
return null;
}
- public static Enumeration getBundleDirectoryContent(URL resourceURL) {
+ private static Enumeration getBundleDirectoryContent(URL resourceURL) {
Bundle bundle = getContainerBundle(resourceURL);
if (bundle != null) {
return bundle.findEntries(resourceURL.getPath(), null, true);