org.bytedeco.javacpp.Loader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javacpp Show documentation
Show all versions of javacpp Show documentation
The missing bridge between Java and native C++
/*
* Copyright (C) 2011-2016 Samuel Audet
*
* 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;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.WeakHashMap;
import org.bytedeco.javacpp.annotation.Platform;
import org.bytedeco.javacpp.tools.Builder;
import org.bytedeco.javacpp.tools.Logger;
/**
* The Loader contains functionality to load native libraries, but also has a bit
* of everything that does not fit anywhere else. In addition to its library loading
* features, it also has utility methods to get the platform name, to load properties
* from Java resources and from Class annotations, to extract file resources to the
* temporary directory, and to get the {@code offsetof()} or {@code sizeof()} a native
* {@code struct}, {@code class}, or {@code union} with its {@link Pointer} peer class
* and a {@link HashMap} initialized by the native libraries inside {@code JNI_OnLoad()}.
*
* @author Samuel Audet
*/
public class Loader {
private static final Logger logger = Logger.create(Loader.class);
/** Value created out of "java.vm.name", "os.name", and "os.arch" system properties.
* Returned by {@link #getPlatform()} as default. */
private static final String PLATFORM;
/** Default platform properties loaded and returned by {@link #loadProperties()}. */
private static Properties platformProperties = null;
static {
String jvmName = System.getProperty("java.vm.name", "").toLowerCase();
String osName = System.getProperty("os.name", "").toLowerCase();
String osArch = System.getProperty("os.arch", "").toLowerCase();
String abiType = System.getProperty("sun.arch.abi", "").toLowerCase();
if (jvmName.startsWith("dalvik") && osName.startsWith("linux")) {
osName = "android";
} else if (jvmName.startsWith("robovm") && osName.startsWith("darwin")) {
osName = "ios";
osArch = "arm";
} else if (osName.startsWith("mac os x") || osName.startsWith("darwin")) {
osName = "macosx";
} else {
int spaceIndex = osName.indexOf(' ');
if (spaceIndex > 0) {
osName = osName.substring(0, spaceIndex);
}
}
if (osArch.equals("i386") || osArch.equals("i486") || osArch.equals("i586") || osArch.equals("i686")) {
osArch = "x86";
} else if (osArch.equals("amd64") || osArch.equals("x86-64") || osArch.equals("x64")) {
osArch = "x86_64";
} else if (osArch.startsWith("aarch64") || osArch.startsWith("armv8") || osArch.startsWith("arm64")) {
osArch = "arm64";
} else if ((osArch.startsWith("arm")) && abiType.equals("gnueabihf")) {
osArch = "armhf";
} else if (osArch.startsWith("arm")) {
osArch = "arm";
}
PLATFORM = osName + "-" + osArch;
}
/**
* Returns either the value of the "org.bytedeco.javacpp.platform"
* system property, or {@link #PLATFORM} when the former is not set.
*
* @return {@code System.getProperty("org.bytedeco.javacpp.platform", platform)}
* @see #PLATFORM
*/
public static String getPlatform() {
return System.getProperty("org.bytedeco.javacpp.platform", PLATFORM);
}
/**
* Loads the {@link Properties} associated with the default {@link #getPlatform()}.
*
* @return {@code loadProperties(getPlatform(), null)}
* @see #loadProperties(String, String)
*/
public static Properties loadProperties() {
String name = getPlatform();
if (platformProperties != null && name.equals(platformProperties.getProperty("platform"))) {
return platformProperties;
}
return platformProperties = loadProperties(name, null);
}
/**
* Loads from resources the default {@link Properties} of the specified platform name.
* The resource must be at {@code "org/bytedeco/javacpp/properties/" + name + ".properties"}.
*
* @param name the platform name
* @param defaults the fallback platform name (null == "generic")
* @return the Properties from resources
*/
public static Properties loadProperties(String name, String defaults) {
if (defaults == null) {
defaults = "generic";
}
Properties p = new Properties();
p.put("platform", name);
p.put("platform.path.separator", File.pathSeparator);
String s = System.mapLibraryName("/");
int i = s.indexOf('/');
p.put("platform.library.prefix", s.substring(0, i));
p.put("platform.library.suffix", s.substring(i + 1));
name = "properties/" + name + ".properties";
InputStream is = Loader.class.getResourceAsStream(name);
try {
try {
p.load(new InputStreamReader(is));
} catch (NoSuchMethodError e) {
p.load(is);
}
} catch (Exception e) {
name = "properties/" + defaults + ".properties";
InputStream is2 = Loader.class.getResourceAsStream(name);
try {
try {
p.load(new InputStreamReader(is2));
} catch (NoSuchMethodError e2) {
p.load(is2);
}
} catch (Exception e2) {
// give up and return defaults
} finally {
try {
if (is2 != null) {
is2.close();
}
} catch (IOException ex) {
logger.error("Unable to close resource : " + ex.getMessage());
}
}
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException ex) {
logger.error("Unable to close resource : " + ex.getMessage());
}
}
return p;
}
/**
* If annotated with properties, returns the argument as "enclosing Class".
* If no properties are found on the Class, makes a search for the first Class
* with properties that we can use, and returns it as the enclosing Class found.
*
* @param cls the Class to start the search from
* @return the enclosing Class
* @see org.bytedeco.javacpp.annotation.Platform
* @see org.bytedeco.javacpp.annotation.Properties
*/
public static Class getEnclosingClass(Class cls) {
Class> c = cls;
// Find first enclosing declaring class with some properties to use
while (c.getDeclaringClass() != null) {
if (c.isAnnotationPresent(org.bytedeco.javacpp.annotation.Properties.class)) {
break;
}
if (c.isAnnotationPresent(Platform.class)) {
Platform p = c.getAnnotation(Platform.class);
if (p.pragma().length > 0 || p.define().length > 0 || p.include().length > 0 || p.cinclude().length > 0 ||
p.includepath().length > 0 || p.compiler().length > 0 || p.linkpath().length > 0 ||
p.link().length > 0 || p.frameworkpath().length > 0 || p.framework().length > 0 ||
p.preloadpath().length > 0 || p.preload().length > 0 || p.library().length() > 0) {
break;
}
}
c = c.getDeclaringClass();
}
return c;
}
/**
* For all the classes, loads all properties from each Class annotations for the given platform.
* @see #loadProperties(Class, java.util.Properties, boolean)
*/
public static ClassProperties loadProperties(Class[] cls, Properties properties, boolean inherit) {
ClassProperties cp = new ClassProperties(properties);
for (Class c : cls) {
cp.load(c, inherit);
}
return cp;
}
/**
* Loads all properties from Class annotations for the given platform. The platform
* of interest needs to be specified as the value of the "platform" key in the
* properties argument. It is also possible to indicate whether to load all the classes
* specified in the {@link org.bytedeco.javacpp.annotation.Properties#inherit()}
* annotation recursively via the inherit argument.
*
* @param cls the Class of which to return Properties
* @param properties the platform Properties to inherit
* @param inherit indicates whether or not to inherit properties from other classes
* @return all the properties associated with the Class for the given platform
*/
public static ClassProperties loadProperties(Class cls, Properties properties, boolean inherit) {
ClassProperties cp = new ClassProperties(properties);
cp.load(cls, inherit);
return cp;
}
/**
* Returns the {@link Class} object that contains a caller's method.
*
* @param i the offset on the call stack of the method of interest
* @return the Class found from the calling context, or {@code null} if not found
*/
public static Class getCallerClass(int i) {
Class[] classContext = null;
try {
new SecurityManager() {
@Override public Class[] getClassContext() {
return super.getClassContext();
}
}.getClassContext();
} catch (NoSuchMethodError e) {
logger.error("No definition of this method : " + e.getMessage());
}
if (classContext != null) {
for (int j = 0; j < classContext.length; j++) {
if (classContext[j] == Loader.class) {
return classContext[i+j];
}
}
} else {
// SecurityManager.getClassContext() returns null on Android 4.0
try {
StackTraceElement[] classNames = Thread.currentThread().getStackTrace();
for (int j = 0; j < classNames.length; j++) {
if (Class.forName(classNames[j].getClassName()) == Loader.class) {
return Class.forName(classNames[i+j].getClassName());
}
}
} catch (ClassNotFoundException e) {
logger.error("No definition for the class found : " + e.getMessage());
}
}
return null;
}
/**
* Extracts by name a resource using the {@link ClassLoader} of the caller.
*
* @param name the name of the resource passed to {@link Class#getResource(String)}
* @see #extractResource(URL, File, String, String)
*/
public static File extractResource(String name, File directory,
String prefix, String suffix) throws IOException {
Class cls = getCallerClass(2);
return extractResource(cls, name, directory, prefix, suffix);
}
/**
* Extracts by name a resource using the {@link ClassLoader} of the specified {@link Class}.
*
* @param cls the Class from which to load resources
* @param name the name of the resource passed to {@link Class#getResource(String)}
* @see #extractResource(URL, File, String, String)
*/
public static File extractResource(Class cls, String name, File directory,
String prefix, String suffix) throws IOException {
return extractResource(cls.getResource(name), directory, prefix, suffix);
}
/**
* Extracts a resource into the specified directory and with the specified
* prefix and suffix for the filename. If both prefix and suffix are {@code null},
* the original filename is used, so directoryOrFile must not be {@code null}.
*
* @param resourceURL the URL of the resource to extract
* @param directoryOrFile the output directory or file ({@code null == System.getProperty("java.io.tmpdir")})
* @param prefix the prefix of the temporary filename to use
* @param suffix the suffix of the temporary filename to use
* @return the File object representing the extracted file
* @throws IOException if fails to extract resource properly
*/
public static File extractResource(URL resourceURL, File directoryOrFile,
String prefix, String suffix) throws IOException {
InputStream is = resourceURL != null ? resourceURL.openStream() : null;
OutputStream os = null;
if (is == null) {
return null;
}
File file = null;
boolean fileExisted = false;
try {
if (prefix == null && suffix == null) {
if (directoryOrFile == null) {
directoryOrFile = new File(System.getProperty("java.io.tmpdir"));
}
if (directoryOrFile.isDirectory()) {
file = new File(directoryOrFile, new File(resourceURL.getPath()).getName());
} else {
file = directoryOrFile;
}
fileExisted = file.exists();
} else {
file = File.createTempFile(prefix, suffix, directoryOrFile);
}
os = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) != -1) {
os.write(buffer, 0, length);
}
is.close();
os.close();
} catch (IOException e) {
if (file != null && !fileExisted) {
file.delete();
}
throw e;
} finally {
is.close();
if (os != null) {
os.close();
}
}
return file;
}
/** User-specified cache directory set and returned by {@link #getCacheDir()}. */
static File cacheDir = null;
/** Temporary directory set and returned by {@link #getTempDir()}. */
static File tempDir = null;
/** Contains all the native libraries that we have loaded to avoid reloading them. */
static Map loadedLibraries = Collections.synchronizedMap(new HashMap());
/**
* Creates and returns {@code System.getProperty("org.bytedeco.javacpp.cachedir")}, or null when not set.
*
* @return {@link #cacheDir}
*/
public static File getCacheDir() {
if (cacheDir == null) {
String dirName = System.getProperty("org.bytedeco.javacpp.cachedir", null);
if (dirName != null) {
File f = new File(dirName);
if (f.exists() || f.mkdirs()) {
cacheDir = f;
}
}
}
return cacheDir;
}
/**
* Creates a unique name for {@link #tempDir} out of
* {@code System.getProperty("java.io.tmpdir")} and {@code System.nanoTime()}.
*
* @return {@link #tempDir}
*/
public static File getTempDir() {
if (tempDir == null) {
File tmpdir = new File(System.getProperty("java.io.tmpdir"));
File f;
for (int i = 0; i < 1000; i++) {
f = new File(tmpdir, "javacpp" + System.nanoTime());
if (f.mkdir()) {
tempDir = f;
tempDir.deleteOnExit();
break;
}
}
}
return tempDir;
}
/** Returns {@code System.getProperty("org.bytedeco.javacpp.loadlibraries")}.
* Flag set by the {@link Builder} to tell us not to try to load anything. */
public static boolean isLoadLibraries() {
String s = System.getProperty("org.bytedeco.javacpp.loadlibraries", "true").toLowerCase();
return s.equals("true") || s.equals("t") || s.equals("");
}
/** Returns {@code load(getCallerClass(2), loadProperties(), false)}. */
public static String load() {
return load(getCallerClass(2), loadProperties(), false);
}
/**
* Loads native libraries associated with the {@link Class} of the caller.
*
* @param pathsFirst search the paths first before bundled resources
* @return {@code load(getCallerClass(2), loadProperties(), pathsFirst) }
* @see #getCallerClass(int)
* @see #load(Class, Properties, boolean)
*/
public static String load(boolean pathsFirst) {
Class cls = getCallerClass(2);
return load(cls, loadProperties(), pathsFirst);
}
/** Returns {@code load(cls, loadProperties(), false)}. */
public static String load(Class cls) {
return load(cls, loadProperties(), false);
}
/**
* Loads native libraries associated with the given {@link Class}.
*
* @param cls the Class to get native library information from
* @param properties the platform Properties to inherit
* @param pathsFirst search the paths first before bundled resources
* @return the full path to the main file loaded, or the library name if unknown
* (but {@code if (!isLoadLibraries() || cls == null) { return null; }})
* @throws NoClassDefFoundError on Class initialization failure
* @throws UnsatisfiedLinkError on native library loading failure
* @see #findLibrary(Class, ClassProperties, String, boolean)
* @see #loadLibrary(URL[], String)
*/
public static String load(Class cls, Properties properties, boolean pathsFirst) {
if (!isLoadLibraries() || cls == null) {
return null;
}
// Find the top enclosing class, to match the library filename
cls = getEnclosingClass(cls);
ClassProperties p = loadProperties(cls, properties, true);
// Force initialization of all the target classes in case they need it
List targets = p.get("target");
if (targets.isEmpty()) {
if (p.getInheritedClasses() != null) {
for (Class c : p.getInheritedClasses()) {
targets.add(c.getName());
}
}
targets.add(cls.getName());
}
for (String s : targets) {
try {
if (logger.isDebugEnabled()) {
logger.debug("Loading class " + s);
}
Class.forName(s, true, cls.getClassLoader());
} catch (ClassNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to load class " + s + ": " + ex);
}
Error e = new NoClassDefFoundError(ex.toString());
e.initCause(ex);
throw e;
}
}
// Preload native libraries desired by our class
List preloads = new ArrayList();
preloads.addAll(p.get("platform.preload"));
preloads.addAll(p.get("platform.link"));
UnsatisfiedLinkError preloadError = null;
for (String preload : preloads) {
try {
URL[] urls = findLibrary(cls, p, preload, pathsFirst);
loadLibrary(urls, preload);
} catch (UnsatisfiedLinkError e) {
preloadError = e;
}
}
try {
String library = p.getProperty("platform.library");
URL[] urls = findLibrary(cls, p, library, pathsFirst);
return loadLibrary(urls, library);
} catch (UnsatisfiedLinkError e) {
if (preloadError != null && e.getCause() == null) {
e.initCause(preloadError);
}
throw e;
}
}
/**
* Finds from where the library may be extracted and loaded among the {@link Class}
* resources. But in case that fails, and depending on the value of {@code pathsFirst},
* either as a fallback or in priority over bundled resources, also searches the paths
* found in the "platform.preloadpath" and "platform.linkpath" class properties as well as
* the "java.library.path" system property, in that order.
*
* @param cls the Class whose package name and {@link ClassLoader} are used to extract from resources
* @param properties contains the directories to scan for if we fail to extract the library from resources
* @param libnameversion the name of the library + "@" + optional version tag
* + "#" + a second optional name used at extraction
* @param pathsFirst search the paths first before bundled resources
* @return URLs that point to potential locations of the library
*/
public static URL[] findLibrary(Class cls, ClassProperties properties, String libnameversion, boolean pathsFirst) {
String[] split = libnameversion.split("#");
String libnameversion2 = libnameversion = split[0];
if (split.length > 1) {
libnameversion2 = split[1];
}
String[] s = libnameversion.split("@");
String[] s2 = libnameversion2.split("@");
String libname = s[0];
String libname2 = s2[0];
String version = s.length > 1 ? s[s.length-1] : "";
String version2 = s2.length > 1 ? s2[s2.length-1] : "";
// If we do not already have the native library file ...
String filename = loadedLibraries.get(libnameversion);
if (filename != null) {
try {
return new URL[] { new File(filename).toURI().toURL() };
} catch (IOException ex) {
return new URL[] { };
}
}
String subdir = properties.getProperty("platform") + '/';
String prefix = properties.getProperty("platform.library.prefix", "");
String suffix = properties.getProperty("platform.library.suffix", "");
String[] styles = {
prefix + libname + suffix + version, // Linux style
prefix + libname + version + suffix, // Mac OS X style
prefix + libname + suffix // without version
};
String[] styles2 = {
prefix + libname2 + suffix + version2, // Linux style
prefix + libname2 + version2 + suffix, // Mac OS X style
prefix + libname2 + suffix // without version
};
String[] suffixes = properties.get("platform.library.suffix").toArray(new String[0]);
if (suffixes.length > 1) {
styles = new String[3 * suffixes.length];
styles2 = new String[3 * suffixes.length];
for (int i = 0; i < suffixes.length; i++) {
styles[3 * i ] = prefix + libname + suffixes[i] + version; // Linux style
styles[3 * i + 1] = prefix + libname + version + suffixes[i]; // Mac OS X style
styles[3 * i + 2] = prefix + libname + suffixes[i]; // without version
styles2[3 * i ] = prefix + libname2 + suffixes[i] + version2; // Linux style
styles2[3 * i + 1] = prefix + libname2 + version2 + suffixes[i]; // Mac OS X style
styles2[3 * i + 2] = prefix + libname2 + suffixes[i]; // without version
}
}
List paths = new ArrayList();
paths.addAll(properties.get("platform.preloadpath"));
paths.addAll(properties.get("platform.linkpath"));
String libpath = System.getProperty("java.library.path", "");
if (libpath.length() > 0) {
paths.addAll(Arrays.asList(libpath.split(File.pathSeparator)));
}
ArrayList urls = new ArrayList(styles.length * (1 + paths.size()));
for (int i = 0; cls != null && i < styles.length; i++) {
// ... then find it from in our resources ...
URL u = cls.getResource(subdir + styles[i]);
if (u != null) {
if (!styles[i].equals(styles2[i])) {
try {
u = new URL(u + "#" + styles2[i]);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
urls.add(u);
}
}
// ... and in case of bad resources search the paths last, or first on user request.
int k = pathsFirst ? 0 : urls.size();
for (int i = 0; paths.size() > 0 && i < styles.length; i++) {
for (String path : paths) {
File file = new File(path, styles[i]);
if (file.exists()) {
try {
URL u = file.toURI().toURL();
if (!styles[i].equals(styles2[i])) {
u = new URL(u + "#" + styles2[i]);
}
urls.add(k++, u);
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
}
}
}
return urls.toArray(new URL[urls.size()]);
}
/**
* Tries to load the library from the URLs in order, extracting resources as necessary.
* Finally, if all fails, falls back on {@link System#loadLibrary(String)}.
*
* @param urls the URLs to try loading the library from
* @param libnameversion the name of the library + "@" + optional version tag
* + "#" + a second optional name used at extraction
* @return the full path of the file loaded, or the library name if unknown
* (but {@code if (!isLoadLibraries) { return null; }})
* @throws UnsatisfiedLinkError on failure
*/
public static String loadLibrary(URL[] urls, String libnameversion) {
if (!isLoadLibraries()) {
return null;
}
// If we do not already have the native library file ...
String filename = loadedLibraries.get(libnameversion);
if (filename != null) {
return filename;
}
File tempFile = null;
UnsatisfiedLinkError loadError = null;
try {
for (URL url : urls) {
File file;
try {
// ... and if the URL is not already a file without fragments, etc ...
file = new File(url.toURI());
} catch (Exception e) {
String name = new File(url.getPath()).getName();
if (url.getRef() != null) {
// ... get the URL fragment to let users rename library files ...
name = url.getRef();
}
// ... then check if it has not already been extracted, and if not ...
file = new File(getCacheDir() != null ? getCacheDir() : getTempDir(), name);
if (!file.exists()) {
if (tempFile != null && tempFile.exists()) {
tempFile.deleteOnExit();
}
// ... then extract it from our resources ...
if (logger.isDebugEnabled()) {
logger.debug("Extracting " + url);
}
extractResource(url, file, null, null);
if (getCacheDir() == null) {
tempFile = file;
}
} else while (System.currentTimeMillis() - file.lastModified() < 1000) {
// ... else wait until the file is at least 1 second old ...
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
// ... reset interrupt to be nice ...
Thread.currentThread().interrupt();
}
}
}
if (file != null && file.exists()) {
filename = file.getAbsolutePath();
try {
// ... and load it!
if (logger.isDebugEnabled()) {
logger.debug("Loading " + filename);
}
loadedLibraries.put(libnameversion, filename);
System.load(filename);
return filename;
} catch (UnsatisfiedLinkError e) {
loadError = e;
loadedLibraries.remove(libnameversion);
if (logger.isDebugEnabled()) {
logger.debug("Failed to load " + filename + ": " + e);
}
}
}
}
// ... or as last resort, try to load it via the system.
String libname = libnameversion.split("#")[0].split("@")[0];
if (logger.isDebugEnabled()) {
logger.debug("Loading library " + libname);
}
loadedLibraries.put(libnameversion, libname);
System.loadLibrary(libname);
return libname;
} catch (UnsatisfiedLinkError e) {
loadedLibraries.remove(libnameversion);
if (loadError != null && e.getCause() == null) {
e.initCause(loadError);
}
if (logger.isDebugEnabled()) {
logger.debug("Failed to load for " + libnameversion + ": " + e);
}
throw e;
} catch (IOException ex) {
loadedLibraries.remove(libnameversion);
if (loadError != null && ex.getCause() == null) {
ex.initCause(loadError);
}
Error e = new UnsatisfiedLinkError(ex.toString());
e.initCause(ex);
if (logger.isDebugEnabled()) {
logger.debug("Failed to extract for " + libnameversion + ": " + e);
}
throw e;
} finally {
if (tempFile != null && tempFile.exists()) {
tempFile.deleteOnExit();
}
// But under Windows, it won't get deleted!
}
}
// So, let's use a shutdown hook...
static {
if (getPlatform().startsWith("windows")) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override public void run() {
if (tempDir == null) {
return;
}
try {
// ... to launch a separate process ...
List command = new ArrayList();
command.add(System.getProperty("java.home") + "/bin/java");
command.add("-classpath");
command.add((new File(Loader.class.getProtectionDomain().getCodeSource().getLocation().toURI())).toString());
command.add(Loader.class.getName());
command.add(tempDir.getAbsolutePath());
new ProcessBuilder(command).start();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
});
}
}
// ... that makes sure to delete all our files.
public static void main(String[] args) throws InterruptedException {
File tmpdir = new File(System.getProperty("java.io.tmpdir"));
File tempDir = new File(args[0]);
if (!tmpdir.equals(tempDir.getParentFile()) ||
!tempDir.getName().startsWith("javacpp")) {
// Someone is trying to break us ... ?
return;
}
for (File file : tempDir.listFiles()) {
while (file.exists() && !file.delete()) {
Thread.sleep(100);
}
}
tempDir.delete();
}
/**
* Contains {@code offsetof()} and {@code sizeof()} values of native types
* of {@code struct}, {@code class}, and {@code union}. A {@link WeakHashMap}
* is used to prevent the Loader from hanging onto Class objects the user may
* be trying to unload.
*/
static WeakHashMap,HashMap> memberOffsets =
new WeakHashMap,HashMap>();
/**
* Called by native libraries to put {@code offsetof()} and {@code sizeof()} values in {@link #memberOffsets}.
* Tries to load the Class object for typeName using the {@link ClassLoader} of the Loader.
*
* @param typeName the name of the peer Class acting as interface to the native type
* @param member the name of the native member variable (can be null to retrieve the Class object only)
* @param offset the value of {@code offsetof()} (or {@code sizeof()} when {@code member.equals("sizeof")})
* @return {@code Class.forName(typeName, false)}
* @throws ClassNotFoundException on Class initialization failure
*/
static Class putMemberOffset(String typeName, String member, int offset) throws ClassNotFoundException {
Class> c = Class.forName(typeName.replace('/', '.'), false, Loader.class.getClassLoader());
if (member != null) {
putMemberOffset(c.asSubclass(Pointer.class), member, offset);
}
return c;
}
/**
* Called by native libraries to put {@code offsetof()} and {@code sizeof()} values in {@link #memberOffsets}.
*
* @param type the peer Class acting as interface to the native type
* @param member the name of the native member variable
* @param offset the value of {@code offsetof()} (or {@code sizeof()} when {@code member.equals("sizeof")})
*/
static synchronized void putMemberOffset(Class extends Pointer> type, String member, int offset) {
HashMap offsets = memberOffsets.get(type);
if (offsets == null) {
memberOffsets.put(type, offsets = new HashMap());
}
offsets.put(member, offset);
}
/**
* Gets {@code offsetof()} values from {@link #memberOffsets} filled by native libraries.
*
* @param type the peer Class acting as interface to the native type
* @param member the name of the native member variable
* @return {@code memberOffsets.get(type).get(member)}
*/
public static int offsetof(Class extends Pointer> type, String member) {
// Should we synchronize that?
return memberOffsets.get(type).get(member);
}
/**
* Gets {@code sizeof()} values from {@link #memberOffsets} filled by native libraries.
*
* @param type the peer Class acting as interface to the native type
* @return {@code memberOffsets.get(type).get("sizeof")}
*/
public static int sizeof(Class extends Pointer> type) {
// Should we synchronize that?
return memberOffsets.get(type).get("sizeof");
}
}