All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bridj.Platform Maven / Gradle / Ivy

The newest version!
/*
 * BridJ - Dynamic and blazing-fast native interop for Java.
 * http://bridj.googlecode.com/
 *
 * Copyright (c) 2010-2013, Olivier Chafik (http://ochafik.com/)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Olivier Chafik nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.bridj;

import org.bridj.util.ProcessUtils;
import java.util.Set;
import java.util.HashSet;
import java.util.regex.Pattern;
import java.io.*;
import java.net.URL;

import java.util.List;
import java.util.Collections;
import java.util.Collection;
import java.util.ArrayList;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import org.bridj.util.StringUtils;

/**
 * Information about the execution platform (OS, architecture, native sizes...)
 * and platform-specific actions.
 * 
    *
  • To know if the JVM platform is 32 bits or 64 bits, use * {@link Platform#is64Bits()} *
  • To know if the OS is an Unix-like system, use * {@link Platform#isUnix()} *
  • To open files and URLs in a platform-specific way, use * {@link Platform#open(File)}, {@link Platform#open(URL)}, {@link Platform#show(File)} *
* * @author ochafik */ public class Platform { static final String osName = System.getProperty("os.name", ""); private static boolean inited; static final String BridJLibraryName = "bridj"; public static final int POINTER_SIZE, WCHAR_T_SIZE, SIZE_T_SIZE, TIME_T_SIZE, CLONG_SIZE; /*interface FunInt { int apply(); } static int tryInt(FunInt f, int defaultValue) { try { return f.apply(); } catch (Throwable th) { return defaultValue; } }*/ static final ClassLoader systemClassLoader; public static ClassLoader getClassLoader() { return getClassLoader(BridJ.class); } public static ClassLoader getClassLoader(Class cl) { ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); if (contextClassLoader != null) { return contextClassLoader; } ClassLoader classLoader = cl == null ? null : cl.getClassLoader(); return classLoader == null ? systemClassLoader : classLoader; } public static InputStream getResourceAsStream(String path) { URL url = getResource(path); try { return url != null ? url.openStream() : null; } catch (IOException ex) { if (BridJ.verbose) { BridJ.warning("Failed to get resource '" + path + "'", ex); } return null; } } public static URL getResource(String path) { if (!path.startsWith("/")) { path = "/" + path; } URL in = BridJ.class.getResource(path); if (in != null) { return in; } ClassLoader[] cls = { BridJ.class.getClassLoader(), Thread.currentThread().getContextClassLoader(), systemClassLoader }; for (ClassLoader cl : cls) { if (cl != null && (in = cl.getResource(path)) != null) { return in; } } return null; } /* public static class utsname { public final String sysname, nodename, release, version, machine; public utsname(String sysname, String nodename, String release, String version, String machine) { this.sysname = sysname; this.nodename = nodename; this.release = release; this.version = version; this.machine = machine; } public String toString() { StringBuilder b = new StringBuilder("{\n"); b.append("\tsysname: \"").append(sysname).append("\",\n"); b.append("\tnodename: \"").append(nodename).append("\",\n"); b.append("\trelease: \"").append(release).append("\",\n"); b.append("\tversion: \"").append(version).append("\",\n"); b.append("\tmachine: \"").append(machine).append("\"\n"); return b.append("}").toString(); } } public static native utsname uname(); */ static final List embeddedLibraryResourceRoots = new ArrayList(); /** * BridJ is able to automatically extract native binaries bundled in the * application's JARs, using a customizable root path and a predefined * architecture-dependent subpath. This method adds an alternative root path * to the search list.
* For instance, if you want to load library "mylib" and call * addEmbeddedLibraryResourceRoot("my/company/lib/"), BridJ * will look for library in the following locations : *
    *
  • "my/company/lib/darwin_universal/libmylib.dylib" on MacOS X (or * darwin_x86, darwin_x64, darwin_ppc if the binary is not universal)
  • *
  • "my/company/lib/win32/mylib.dll" on Windows (use win64 on 64 bits * architectures)
  • *
  • "my/company/lib/linux_x86/libmylib.so" on Linux (use linux_x64 on 64 * bits architectures)
  • *
  • "my/company/lib/sunos_x86/libmylib.so" on Solaris (use sunos_x64 / * sunos_sparc on other architectures)
  • *
  • "lib/armeabi/libmylib.so" on Android (for Android-specific reasons, * only the "lib" sub-path can effectively contain loadable binaries)
  • *
* For other platforms or for an updated list of supported platforms, please * have a look at BridJ's JAR contents (under "org/bridj/lib") and/or to its * source tree, browsable online. */ public static synchronized void addEmbeddedLibraryResourceRoot(String root) { embeddedLibraryResourceRoots.add(0, root); } static Set temporaryExtractedLibraryCanonicalFiles = Collections.synchronizedSet(new LinkedHashSet()); static void addTemporaryExtractedLibraryFileToDeleteOnExit(File file) throws IOException { File canonicalFile = file.getCanonicalFile(); // Give a chance to NativeLibrary.release() to delete the file : temporaryExtractedLibraryCanonicalFiles.add(canonicalFile); // Ask Java to delete the file upon exit if it still exists canonicalFile.deleteOnExit(); } private static final String arch; private static boolean is64Bits; private static File extractedLibrariesTempDir; static { arch = System.getProperty("os.arch"); { String dataModel = System.getProperty("sun.arch.data.model", System.getProperty("com.ibm.vm.bitmode")); if ("32".equals(dataModel)) { is64Bits = false; } else if ("64".equals(dataModel)) { is64Bits = true; } else { is64Bits = arch.contains("64") || arch.equalsIgnoreCase("sparcv9"); } } systemClassLoader = createClassLoader(); addEmbeddedLibraryResourceRoot("libs/"); if (!isAndroid()) { addEmbeddedLibraryResourceRoot("lib/"); addEmbeddedLibraryResourceRoot("org/bridj/lib/"); if (!Version.VERSION_SPECIFIC_SUB_PACKAGE.equals("")) { addEmbeddedLibraryResourceRoot("org/bridj/" + Version.VERSION_SPECIFIC_SUB_PACKAGE + "/lib/"); } } try { extractedLibrariesTempDir = createTempDir("BridJExtractedLibraries"); initLibrary(); } catch (Throwable th) { th.printStackTrace(); } POINTER_SIZE = sizeOf_ptrdiff_t(); WCHAR_T_SIZE = sizeOf_wchar_t(); SIZE_T_SIZE = sizeOf_size_t(); TIME_T_SIZE = sizeOf_time_t(); CLONG_SIZE = sizeOf_long(); is64Bits = POINTER_SIZE == 8; Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { shutdown(); } }); } private static List nativeLibraries = new ArrayList(); static void addNativeLibrary(NativeLibrary library) { synchronized (nativeLibraries) { nativeLibraries.add(library); } } private static void shutdown() { //releaseNativeLibraries(); deleteTemporaryExtractedLibraryFiles(); } private static void releaseNativeLibraries() { synchronized (nativeLibraries) { // Release libraries in reverse order : for (int iLibrary = nativeLibraries.size(); iLibrary-- != 0;) { NativeLibrary lib = nativeLibraries.get(iLibrary); try { lib.release(); } catch (Throwable th) { BridJ.error("Failed to release library '" + lib.path + "' : " + th, th); } } } } private static void deleteTemporaryExtractedLibraryFiles() { synchronized (temporaryExtractedLibraryCanonicalFiles) { temporaryExtractedLibraryCanonicalFiles.add(extractedLibrariesTempDir); // Release libraries in reverse order : List filesToDeleteAfterExit = new ArrayList(); for (File tempFile : Platform.temporaryExtractedLibraryCanonicalFiles) { if (tempFile.delete()) { if (BridJ.verbose) { BridJ.info("Deleted temporary library file '" + tempFile + "'"); } } else { filesToDeleteAfterExit.add(tempFile); } } if (!filesToDeleteAfterExit.isEmpty()) { if (BridJ.verbose) { BridJ.info("Attempting to delete " + filesToDeleteAfterExit.size() + " files after JVM exit : " + StringUtils.implode(filesToDeleteAfterExit, ", ")); } try { ProcessUtils.startJavaProcess(DeleteFiles.class, filesToDeleteAfterExit); } catch (Throwable ex) { BridJ.error("Failed to launch process to delete files after JVM exit : " + ex, ex); } } } } public static class DeleteFiles { static boolean delete(List files) { for (Iterator it = files.iterator(); it.hasNext();) { File file = it.next(); if (file.delete()) { it.remove(); } } return files.isEmpty(); } final static long TRY_DELETE_EVERY_MILLIS = 50, FAIL_AFTER_MILLIS = 10000; public static void main(String[] args) { try { List files = new LinkedList(); for (String arg : args) { files.add(new File(arg)); } long start = System.currentTimeMillis(); while (!delete(files)) { long elapsed = System.currentTimeMillis() - start; if (elapsed > FAIL_AFTER_MILLIS) { BridJ.error("Failed to delete the following files : " + StringUtils.implode(files)); System.exit(1); } Thread.sleep(TRY_DELETE_EVERY_MILLIS); } } catch (Throwable th) { th.printStackTrace(); } finally { System.exit(0); } } } static ClassLoader createClassLoader() { List urls = new ArrayList(); for (String propName : new String[]{"java.class.path", "sun.boot.class.path"}) { String prop = System.getProperty(propName); if (prop == null) { continue; } for (String path : prop.split(File.pathSeparator)) { path = path.trim(); if (path.length() == 0) { continue; } URL url; try { url = new URL(path); } catch (MalformedURLException ex) { try { url = new File(path).toURI().toURL(); } catch (MalformedURLException ex2) { url = null; } } if (url != null) { urls.add(url); } } } //System.out.println("URLs for synthetic class loader :"); //for (URL url : urls) // System.out.println("\t" + url); return new URLClassLoader(urls.toArray(new URL[urls.size()])); } static String getenvOrProperty(String envName, String javaName, String defaultValue) { String value = System.getenv(envName); if (value == null) { value = System.getProperty(javaName); } if (value == null) { value = defaultValue; } return value; } public static synchronized void initLibrary() { if (inited) { return; } inited = true; try { boolean loaded = false; String forceLibFile = getenvOrProperty("BRIDJ_LIBRARY", "bridj.library", null); String lib = null; if (forceLibFile != null) { try { System.load(lib = forceLibFile); loaded = true; } catch (Throwable ex) { BridJ.error("Failed to load forced library " + forceLibFile, ex); } } if (!loaded) { if (!Platform.isAndroid()) { try { File libFile = extractEmbeddedLibraryResource(BridJLibraryName); if (libFile == null) { throw new FileNotFoundException("Failed to extract embedded library '" + BridJLibraryName + "' (could be a classloader issue, or missing binary in resource path " + StringUtils.implode(embeddedLibraryResourceRoots, ", ") + ")"); } if (BridJ.veryVerbose) { BridJ.info("Loading library " + libFile); } System.load(lib = libFile.toString()); BridJ.setNativeLibraryFile(BridJLibraryName, libFile); loaded = true; } catch (IOException ex) { BridJ.error("Failed to load '" + BridJLibraryName + "'", ex); } } if (!loaded) { System.loadLibrary("bridj"); } } if (BridJ.veryVerbose) { BridJ.info("Loaded library " + lib); } init(); //if (BridJ.protectedMode) // BridJ.info("Protected mode enabled"); if (BridJ.logCalls) { BridJ.info("Calls logs enabled"); } } catch (Throwable ex) { throw new RuntimeException("Failed to initialize " + BridJ.class.getSimpleName() + " (" + ex + ")", ex); } } private static native void init(); public static boolean isLinux() { return isUnix() && osName.toLowerCase().contains("linux"); } public static boolean isMacOSX() { return isUnix() && (osName.startsWith("Mac") || osName.startsWith("Darwin")); } public static boolean isSolaris() { return isUnix() && (osName.startsWith("SunOS") || osName.startsWith("Solaris")); } public static boolean isBSD() { return isUnix() && (osName.contains("BSD") || isMacOSX()); } public static boolean isUnix() { return File.separatorChar == '/'; } public static boolean isWindows() { return File.separatorChar == '\\'; } public static boolean isWindows7() { return osName.equals("Windows 7"); } /** * Whether to use Unicode versions of Windows APIs rather than ANSI versions * (for functions that haven't been bound yet : has no effect on functions * that have already been bound).
* Some Windows APIs such as SendMessage have two versions : *
    *
  • one that uses single-byte character strings (SendMessageA, with 'A' * for ANSI strings)
  • *
  • one that uses unicode character strings (SendMessageW, with 'W' for * Wide strings).
  • *
*
* In a C/C++ program, this behaviour is controlled by the UNICODE macro * definition.
* By default, BridJ will use the Unicode versions. Set this field to false, * set the bridj.useUnicodeVersionOfWindowsAPIs property to "false" or the * BRIDJ_USE_UNICODE_VERSION_OF_WINDOWS_APIS environment variable to "0" to * use the ANSI string version instead. */ public static boolean useUnicodeVersionOfWindowsAPIs = !("false".equals(System.getProperty("bridj.useUnicodeVersionOfWindowsAPIs")) || "0".equals(System.getenv("BRIDJ_USE_UNICODE_VERSION_OF_WINDOWS_APIS"))); private static String getArch() { return arch; } /** * Machine (as returned by `uname -m`, except for i686 which is actually * i386), adjusted to the JVM platform (32 or 64 bits) */ public static String getMachine() { String arch = getArch(); if (arch.equals("amd64") || arch.equals("x86_64")) { if (is64Bits()) { return "x86_64"; } else { return "i386"; // we are running a 32 bits JVM on a 64 bits platform } } return arch; } public static boolean isAndroid() { return "dalvik".equalsIgnoreCase(System.getProperty("java.vm.name")) && isLinux(); } public static boolean isArm() { String arch = getArch(); return "arm".equals(arch); } public static boolean isSparc() { String arch = getArch(); return "sparc".equals(arch) || "sparcv9".equals(arch); } public static boolean is64Bits() { return is64Bits; } public static boolean isAmd64Arch() { String arch = getArch(); return arch.equals("x86_64"); } static List getPossibleFileNames(String name) { List fileNames = new ArrayList(1); if (isWindows()) { fileNames.add(name + ".dll"); fileNames.add(name + ".drv"); } else { String jniName = "lib" + name + ".jnilib"; if (isMacOSX()) { fileNames.add("lib" + name + ".dylib"); fileNames.add(jniName); } else { fileNames.add("lib" + name + ".so"); fileNames.add(name + ".so"); fileNames.add(jniName); } } assert !fileNames.isEmpty(); if (name.contains(".")) { fileNames.add(name); } return fileNames; } static synchronized List getEmbeddedLibraryPaths(String name) { List paths = new ArrayList(embeddedLibraryResourceRoots.size()); for (String root : embeddedLibraryResourceRoots) { if (root == null) { continue; } if (isWindows()) { paths.add(root + (is64Bits() ? "win64/" : "win32/")); } else if (isMacOSX()) { if (isArm()) { paths.add(root + "iphoneos_arm32_arm/"); } else { paths.add(root + "darwin_universal/"); if (isAmd64Arch()) { paths.add(root + "darwin_x64/"); } } } else { if (isAndroid()) { assert root.equals("libs/"); paths.add(root + "armeabi/"); // Android SDK + NDK-style .so embedding = lib/armeabi/libTest.so } else if (isLinux()) { if (isArm()) { paths.add(root + "linux_armhf/"); // To discriminate between hard-float and soft-float, we used to test new File("/lib/arm-linux-gnueabihf").isDirectory(). } else { paths.add(root + (is64Bits() ? "linux_x64/" : "linux_x86/")); } } else if (isSolaris()) { if (isSparc()) { paths.add(root + (is64Bits() ? "sunos_sparc64/" : "sunos_sparc/")); } else { paths.add(root + (is64Bits() ? "sunos_x64/" : "sunos_x86/")); } } } } if (paths.isEmpty()) { throw new RuntimeException("Platform not supported ! (os.name='" + osName + "', os.arch='" + System.getProperty("os.arch") + "')"); } return paths; } static synchronized List getEmbeddedLibraryResource(String name) { List paths = getEmbeddedLibraryPaths(name); List fileNames = getPossibleFileNames(name); List ret = new ArrayList(paths.size() * fileNames.size()); for (String path : paths) { for (String fileName : fileNames) { ret.add(path + fileName); } } if (BridJ.veryVerbose) { BridJ.info("Embedded resource paths for library '" + name + "': " + ret); } return ret; } static void tryDeleteFilesInSameDirectory(final File legitFile, final Pattern fileNamePattern, long atLeastOlderThanMillis) { final long maxModifiedDateForDeletion = System.currentTimeMillis() - atLeastOlderThanMillis; new Thread(new Runnable() { public void run() { File dir = legitFile.getParentFile(); String legitFileName = legitFile.getName(); try { for (String name : dir.list()) { if (name.equals(legitFileName)) { continue; } if (!fileNamePattern.matcher(name).matches()) { continue; } File file = new File(dir, name); if (file.lastModified() > maxModifiedDateForDeletion) { continue; } if (file.delete() && BridJ.verbose) { BridJ.info("Deleted old binary file '" + file + "'"); } } } catch (SecurityException ex) { // no right to delete files in that directory BridJ.warning("Failed to delete files matching '" + fileNamePattern + "' in directory '" + dir + "'", ex); } catch (Throwable ex) { BridJ.error("Unexpected error : " + ex, ex); } } }).start(); } static final long DELETE_OLD_BINARIES_AFTER_MILLIS = 24 * 60 * 60 * 1000; // 24 hours static File extractEmbeddedLibraryResource(String name) throws IOException { String firstLibraryResource = null; List libraryResources = getEmbeddedLibraryResource(name); if (BridJ.veryVerbose) { BridJ.info("Library resources for " + name + ": " + libraryResources); } for (String libraryResource : libraryResources) { if (firstLibraryResource == null) { firstLibraryResource = libraryResource; } int i = libraryResource.lastIndexOf('.'); int len; byte[] b = new byte[8196]; InputStream in = getResourceAsStream(libraryResource); if (in == null) { File f = new File(libraryResource); if (!f.exists()) { f = new File(f.getName()); } if (f.exists()) { return f.getCanonicalFile(); } continue; } String fileName = new File(libraryResource).getName(); File libFile = new File(extractedLibrariesTempDir, fileName); OutputStream out = new BufferedOutputStream(new FileOutputStream(libFile)); while ((len = in.read(b)) > 0) { out.write(b, 0, len); } out.close(); in.close(); addTemporaryExtractedLibraryFileToDeleteOnExit(libFile); addTemporaryExtractedLibraryFileToDeleteOnExit(libFile.getParentFile()); return libFile; } return null; } static final int maxTempFileAttempts = 20; static File createTempDir(String prefix) throws IOException { File dir; for (int i = 0; i < maxTempFileAttempts; i++) { dir = File.createTempFile(prefix, ""); if (dir.delete() && dir.mkdirs()) { return dir; } } throw new RuntimeException("Failed to create temp dir with prefix '" + prefix + "' despite " + maxTempFileAttempts + " attempts!"); } /** * Opens an URL with the default system action. * * @param url url to open * @throws NoSuchMethodException if opening an URL on the current platform * is not supported */ public static final void open(URL url) throws NoSuchMethodException { if (url.getProtocol().equals("file")) { open(new File(url.getFile())); } else { if (Platform.isMacOSX()) { execArgs("open", url.toString()); } else if (Platform.isWindows()) { execArgs("rundll32", "url.dll,FileProtocolHandler", url.toString()); } else if (Platform.isUnix() && hasUnixCommand("gnome-open")) { execArgs("gnome-open", url.toString()); } else if (Platform.isUnix() && hasUnixCommand("konqueror")) { execArgs("konqueror", url.toString()); } else if (Platform.isUnix() && hasUnixCommand("mozilla")) { execArgs("mozilla", url.toString()); } else { throw new NoSuchMethodException("Cannot open urls on this platform"); } } } /** * Opens a file with the default system action. * * @param file file to open * @throws NoSuchMethodException if opening a file on the current platform * is not supported */ public static final void open(File file) throws NoSuchMethodException { if (Platform.isMacOSX()) { execArgs("open", file.getAbsolutePath()); } else if (Platform.isWindows()) { if (file.isDirectory()) { execArgs("explorer", file.getAbsolutePath()); } else { execArgs("start", file.getAbsolutePath()); } } else if (Platform.isUnix() && hasUnixCommand("gnome-open")) { execArgs("gnome-open", file.toString()); } else if (Platform.isUnix() && hasUnixCommand("konqueror")) { execArgs("konqueror", file.toString()); } else if (Platform.isSolaris() && file.isDirectory()) { execArgs("/usr/dt/bin/dtfile", "-folder", file.getAbsolutePath()); } else { throw new NoSuchMethodException("Cannot open files on this platform"); } } /** * Show a file in its parent directory, if possible selecting the file (not * possible on all platforms). * * @param file file to show in the system's default file navigator * @throws NoSuchMethodException if showing a file on the current platform * is not supported */ public static final void show(File file) throws NoSuchMethodException, IOException { if (Platform.isWindows()) { exec("explorer /e,/select,\"" + file.getCanonicalPath() + "\""); } else { open(file.getAbsoluteFile().getParentFile()); } } static final void execArgs(String... cmd) throws NoSuchMethodException { try { Runtime.getRuntime().exec(cmd); } catch (Exception ex) { ex.printStackTrace(); throw new NoSuchMethodException(ex.toString()); } } static final void exec(String cmd) throws NoSuchMethodException { try { Runtime.getRuntime().exec(cmd).waitFor(); } catch (Exception ex) { ex.printStackTrace(); throw new NoSuchMethodException(ex.toString()); } } static final boolean hasUnixCommand(String name) { try { Process p = Runtime.getRuntime().exec(new String[]{"which", name}); return p.waitFor() == 0; } catch (Exception ex) { ex.printStackTrace(); return false; } } static native int sizeOf_size_t(); static native int sizeOf_time_t(); static native int sizeOf_wchar_t(); static native int sizeOf_ptrdiff_t(); static native int sizeOf_long(); static native int getMaxDirectMappingArgCount(); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy