001/* 002 * BridJ - Dynamic and blazing-fast native interop for Java. 003 * http://bridj.googlecode.com/ 004 * 005 * Copyright (c) 2010-2013, Olivier Chafik (http://ochafik.com/) 006 * All rights reserved. 007 * 008 * Redistribution and use in source and binary forms, with or without 009 * modification, are permitted provided that the following conditions are met: 010 * 011 * * Redistributions of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * * Redistributions in binary form must reproduce the above copyright 014 * notice, this list of conditions and the following disclaimer in the 015 * documentation and/or other materials provided with the distribution. 016 * * Neither the name of Olivier Chafik nor the 017 * names of its contributors may be used to endorse or promote products 018 * derived from this software without specific prior written permission. 019 * 020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY 021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 030 */ 031package org.bridj; 032 033import org.bridj.util.ProcessUtils; 034 035import java.security.AccessController; 036import java.security.PrivilegedAction; 037import java.util.Set; 038import java.util.HashSet; 039import java.util.regex.Pattern; 040import java.io.*; 041import java.net.URL; 042 043import java.util.List; 044import java.util.Collections; 045import java.util.Collection; 046import java.util.ArrayList; 047import java.net.MalformedURLException; 048import java.net.URLClassLoader; 049import java.util.Arrays; 050import java.util.Iterator; 051import java.util.LinkedHashSet; 052import java.util.LinkedList; 053import org.bridj.util.StringUtils; 054 055/** 056 * Information about the execution platform (OS, architecture, native sizes...) 057 * and platform-specific actions. 058 * <ul> 059 * <li>To know if the JVM platform is 32 bits or 64 bits, use 060 * {@link Platform#is64Bits()} 061 * </li><li>To know if the OS is an Unix-like system, use 062 * {@link Platform#isUnix()} 063 * </li><li>To open files and URLs in a platform-specific way, use 064 * {@link Platform#open(File)}, {@link Platform#open(URL)}, {@link Platform#show(File)} 065 * </li></ul> 066 * 067 * @author ochafik 068 */ 069public class Platform { 070 071 static final String osName = System.getProperty("os.name", ""); 072 private static boolean inited; 073 static final String BridJLibraryName = "bridj"; 074 public static final int POINTER_SIZE, 075 WCHAR_T_SIZE, 076 SIZE_T_SIZE, 077 TIME_T_SIZE, 078 CLONG_SIZE; 079 /*interface FunInt { 080 int apply(); 081 } 082 static int tryInt(FunInt f, int defaultValue) { 083 try { 084 return f.apply(); 085 } catch (Throwable th) { 086 return defaultValue; 087 } 088 }*/ 089 static final ClassLoader systemClassLoader; 090 091 public static ClassLoader getClassLoader() { 092 return getClassLoader(BridJ.class); 093 } 094 095 public static ClassLoader getClassLoader(Class<?> cl) { 096 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); 097 if (contextClassLoader != null) { 098 return contextClassLoader; 099 } 100 ClassLoader classLoader = cl == null ? null : cl.getClassLoader(); 101 return classLoader == null ? systemClassLoader : classLoader; 102 } 103 104 public static InputStream getResourceAsStream(String path) { 105 URL url = getResource(path); 106 try { 107 return url != null ? url.openStream() : null; 108 } catch (IOException ex) { 109 if (BridJ.verbose) { 110 BridJ.warning("Failed to get resource '" + path + "'", ex); 111 } 112 return null; 113 } 114 } 115 116 public static URL getResource(String path) { 117 if (!path.startsWith("/")) { 118 path = "/" + path; 119 } 120 121 URL in = BridJ.class.getResource(path); 122 if (in != null) { 123 return in; 124 } 125 126 ClassLoader[] cls = { 127 BridJ.class.getClassLoader(), 128 Thread.currentThread().getContextClassLoader(), 129 systemClassLoader 130 }; 131 for (ClassLoader cl : cls) { 132 if (cl != null && (in = cl.getResource(path)) != null) { 133 return in; 134 } 135 } 136 return null; 137 } 138 /* 139 public static class utsname { 140 public final String sysname, nodename, release, version, machine; 141 public utsname(String sysname, String nodename, String release, String version, String machine) { 142 this.sysname = sysname; 143 this.nodename = nodename; 144 this.release = release; 145 this.version = version; 146 this.machine = machine; 147 } 148 public String toString() { 149 StringBuilder b = new StringBuilder("{\n"); 150 b.append("\tsysname: \"").append(sysname).append("\",\n"); 151 b.append("\tnodename: \"").append(nodename).append("\",\n"); 152 b.append("\trelease: \"").append(release).append("\",\n"); 153 b.append("\tversion: \"").append(version).append("\",\n"); 154 b.append("\tmachine: \"").append(machine).append("\"\n"); 155 return b.append("}").toString(); 156 } 157 } 158 public static native utsname uname(); 159 */ 160 static final List<String> embeddedLibraryResourceRoots = new ArrayList<String>(); 161 162 /** 163 * BridJ is able to automatically extract native binaries bundled in the 164 * application's JARs, using a customizable root path and a predefined 165 * architecture-dependent subpath. This method adds an alternative root path 166 * to the search list.<br> 167 * For instance, if you want to load library "mylib" and call 168 * <code>addEmbeddedLibraryResourceRoot("my/company/lib/")</code>, BridJ 169 * will look for library in the following locations : 170 * <ul> 171 * <li>"my/company/lib/darwin_universal/libmylib.dylib" on MacOS X (or 172 * darwin_x86, darwin_x64, darwin_ppc if the binary is not universal)</li> 173 * <li>"my/company/lib/win32/mylib.dll" on Windows (use win64 on 64 bits 174 * architectures)</li> 175 * <li>"my/company/lib/linux_x86/libmylib.so" on Linux (use linux_x64 on 64 176 * bits architectures)</li> 177 * <li>"my/company/lib/sunos_x86/libmylib.so" on Solaris (use sunos_x64 / 178 * sunos_sparc on other architectures)</li> 179 * <li>"lib/armeabi/libmylib.so" on Android (for Android-specific reasons, 180 * only the "lib" sub-path can effectively contain loadable binaries)</li> 181 * </ul> 182 * For other platforms or for an updated list of supported platforms, please 183 * have a look at BridJ's JAR contents (under "org/bridj/lib") and/or to its 184 * source tree, browsable online. 185 */ 186 public static synchronized void addEmbeddedLibraryResourceRoot(String root) { 187 embeddedLibraryResourceRoots.add(0, root); 188 } 189 static Set<File> temporaryExtractedLibraryCanonicalFiles = Collections.synchronizedSet(new LinkedHashSet<File>()); 190 191 static void addTemporaryExtractedLibraryFileToDeleteOnExit(File file) throws IOException { 192 File canonicalFile = file.getCanonicalFile(); 193 194 // Give a chance to NativeLibrary.release() to delete the file : 195 temporaryExtractedLibraryCanonicalFiles.add(canonicalFile); 196 197 // Ask Java to delete the file upon exit if it still exists 198 canonicalFile.deleteOnExit(); 199 } 200 private static final String arch; 201 private static boolean is64Bits; 202 private static File extractedLibrariesTempDir; 203 204 static { 205 arch = System.getProperty("os.arch"); 206 { 207 String dataModel = System.getProperty("sun.arch.data.model", System.getProperty("com.ibm.vm.bitmode")); 208 if ("32".equals(dataModel)) { 209 is64Bits = false; 210 } else if ("64".equals(dataModel)) { 211 is64Bits = true; 212 } else { 213 is64Bits = 214 arch.contains("64") 215 || arch.equalsIgnoreCase("sparcv9"); 216 } 217 } 218 systemClassLoader = createClassLoader(); 219 220 addEmbeddedLibraryResourceRoot("libs/"); 221 if (!isAndroid()) { 222 addEmbeddedLibraryResourceRoot("lib/"); 223 addEmbeddedLibraryResourceRoot("org/bridj/lib/"); 224 if (!Version.VERSION_SPECIFIC_SUB_PACKAGE.equals("")) { 225 addEmbeddedLibraryResourceRoot("org/bridj/" + Version.VERSION_SPECIFIC_SUB_PACKAGE + "/lib/"); 226 } 227 } 228 229 try { 230 extractedLibrariesTempDir = createTempDir("BridJExtractedLibraries"); 231 initLibrary(); 232 } catch (Throwable th) { 233 th.printStackTrace(); 234 } 235 POINTER_SIZE = sizeOf_ptrdiff_t(); 236 WCHAR_T_SIZE = sizeOf_wchar_t(); 237 SIZE_T_SIZE = sizeOf_size_t(); 238 TIME_T_SIZE = sizeOf_time_t(); 239 CLONG_SIZE = sizeOf_long(); 240 241 is64Bits = POINTER_SIZE == 8; 242 243 Runtime.getRuntime().addShutdownHook(new Thread() { 244 public void run() { 245 shutdown(); 246 } 247 }); 248 } 249 private static List<NativeLibrary> nativeLibraries = new ArrayList<NativeLibrary>(); 250 251 static void addNativeLibrary(NativeLibrary library) { 252 synchronized (nativeLibraries) { 253 nativeLibraries.add(library); 254 } 255 } 256 257 private static void shutdown() { 258 //releaseNativeLibraries(); 259 deleteTemporaryExtractedLibraryFiles(); 260 } 261 262 private static void releaseNativeLibraries() { 263 synchronized (nativeLibraries) { 264 // Release libraries in reverse order : 265 for (int iLibrary = nativeLibraries.size(); iLibrary-- != 0;) { 266 NativeLibrary lib = nativeLibraries.get(iLibrary); 267 try { 268 lib.release(); 269 } catch (Throwable th) { 270 BridJ.error("Failed to release library '" + lib.path + "' : " + th, th); 271 } 272 } 273 } 274 } 275 276 private static void deleteTemporaryExtractedLibraryFiles() { 277 synchronized (temporaryExtractedLibraryCanonicalFiles) { 278 temporaryExtractedLibraryCanonicalFiles.add(extractedLibrariesTempDir); 279 280 // Release libraries in reverse order : 281 List<File> filesToDeleteAfterExit = new ArrayList<File>(); 282 for (File tempFile : Platform.temporaryExtractedLibraryCanonicalFiles) { 283 if (tempFile.delete()) { 284 if (BridJ.verbose) { 285 BridJ.info("Deleted temporary library file '" + tempFile + "'"); 286 } 287 } else { 288 filesToDeleteAfterExit.add(tempFile); 289 } 290 } 291 if (!filesToDeleteAfterExit.isEmpty()) { 292 if (BridJ.verbose) { 293 BridJ.info("Attempting to delete " + filesToDeleteAfterExit.size() + " files after JVM exit : " + StringUtils.implode(filesToDeleteAfterExit, ", ")); 294 } 295 296 try { 297 ProcessUtils.startJavaProcess(DeleteFiles.class, filesToDeleteAfterExit); 298 } catch (Throwable ex) { 299 BridJ.error("Failed to launch process to delete files after JVM exit : " + ex, ex); 300 } 301 } 302 } 303 } 304 305 public static class DeleteFiles { 306 307 static boolean delete(List<File> files) { 308 for (Iterator<File> it = files.iterator(); it.hasNext();) { 309 File file = it.next(); 310 if (file.delete()) { 311 it.remove(); 312 } 313 } 314 return files.isEmpty(); 315 } 316 final static long TRY_DELETE_EVERY_MILLIS = 50, 317 FAIL_AFTER_MILLIS = 10000; 318 319 public static void main(String[] args) { 320 try { 321 List<File> files = new LinkedList<File>(); 322 for (String arg : args) { 323 files.add(new File(arg)); 324 } 325 326 long start = System.currentTimeMillis(); 327 while (!delete(files)) { 328 long elapsed = System.currentTimeMillis() - start; 329 if (elapsed > FAIL_AFTER_MILLIS) { 330 BridJ.error("Failed to delete the following files : " + StringUtils.implode(files)); 331 System.exit(1); 332 } 333 334 Thread.sleep(TRY_DELETE_EVERY_MILLIS); 335 } 336 } catch (Throwable th) { 337 th.printStackTrace(); 338 } finally { 339 System.exit(0); 340 } 341 } 342 } 343 344 static ClassLoader createClassLoader() { 345 List<URL> urls = new ArrayList<URL>(); 346 for (String propName : new String[]{"java.class.path", "sun.boot.class.path"}) { 347 String prop = System.getProperty(propName); 348 if (prop == null) { 349 continue; 350 } 351 352 for (String path : prop.split(File.pathSeparator)) { 353 path = path.trim(); 354 if (path.length() == 0) { 355 continue; 356 } 357 358 URL url; 359 try { 360 url = new URL(path); 361 } catch (MalformedURLException ex) { 362 try { 363 url = new File(path).toURI().toURL(); 364 } catch (MalformedURLException ex2) { 365 url = null; 366 } 367 } 368 if (url != null) { 369 urls.add(url); 370 } 371 } 372 } 373 //System.out.println("URLs for synthetic class loader :"); 374 //for (URL url : urls) 375 // System.out.println("\t" + url); 376 return new URLClassLoader(urls.toArray(new URL[urls.size()])); 377 } 378 379 static String getenvOrProperty(String envName, String javaName, String defaultValue) { 380 String value = System.getenv(envName); 381 if (value == null) { 382 value = System.getProperty(javaName); 383 } 384 if (value == null) { 385 value = defaultValue; 386 } 387 return value; 388 } 389 390 public static synchronized void initLibrary() { 391 if (inited) { 392 return; 393 } 394 inited = true; 395 396 try { 397 boolean loaded = false; 398 399 String forceLibFile = getenvOrProperty("BRIDJ_LIBRARY", "bridj.library", null); 400 401 String lib = null; 402 403 if (forceLibFile != null) { 404 try { 405 System.load(lib = forceLibFile); 406 loaded = true; 407 } catch (Throwable ex) { 408 BridJ.error("Failed to load forced library " + forceLibFile, ex); 409 } 410 } 411 412 if (!loaded) { 413 if (!Platform.isAndroid()) { 414 try { 415 File libFile = extractEmbeddedLibraryResource(BridJLibraryName); 416 if (libFile == null) { 417 throw new FileNotFoundException("Failed to extract embedded library '" + BridJLibraryName + "' (could be a classloader issue, or missing binary in resource path " + StringUtils.implode(embeddedLibraryResourceRoots, ", ") + ")"); 418 } 419 420 if (BridJ.veryVerbose) { 421 BridJ.info("Loading library " + libFile); 422 } 423 System.load(lib = libFile.toString()); 424 BridJ.setNativeLibraryFile(BridJLibraryName, libFile); 425 loaded = true; 426 } catch (IOException ex) { 427 BridJ.error("Failed to load '" + BridJLibraryName + "'", ex); 428 } 429 } 430 if (!loaded) { 431 System.loadLibrary("bridj"); 432 } 433 } 434 if (BridJ.veryVerbose) { 435 BridJ.info("Loaded library " + lib); 436 } 437 438 init(); 439 440 //if (BridJ.protectedMode) 441 // BridJ.info("Protected mode enabled"); 442 if (BridJ.logCalls) { 443 BridJ.info("Calls logs enabled"); 444 } 445 446 } catch (Throwable ex) { 447 throw new RuntimeException("Failed to initialize " + BridJ.class.getSimpleName() + " (" + ex + ")", ex); 448 } 449 } 450 451 private static native void init(); 452 453 public static boolean isLinux() { 454 return isUnix() && osName.toLowerCase().contains("linux"); 455 } 456 457 public static boolean isMacOSX() { 458 return isUnix() && (osName.startsWith("Mac") || osName.startsWith("Darwin")); 459 } 460 461 public static boolean isSolaris() { 462 return isUnix() && (osName.startsWith("SunOS") || osName.startsWith("Solaris")); 463 } 464 465 public static boolean isBSD() { 466 return isUnix() && (osName.contains("BSD") || isMacOSX()); 467 } 468 469 public static boolean isUnix() { 470 return File.separatorChar == '/'; 471 } 472 473 public static boolean isWindows() { 474 return File.separatorChar == '\\'; 475 } 476 477 public static boolean isWindows7() { 478 return osName.equals("Windows 7"); 479 } 480 /** 481 * Whether to use Unicode versions of Windows APIs rather than ANSI versions 482 * (for functions that haven't been bound yet : has no effect on functions 483 * that have already been bound).<br> 484 * Some Windows APIs such as SendMessage have two versions : 485 * <ul> 486 * <li>one that uses single-byte character strings (SendMessageA, with 'A' 487 * for ANSI strings)</li> 488 * <li>one that uses unicode character strings (SendMessageW, with 'W' for 489 * Wide strings).</li> 490 * </ul> 491 * <br> 492 * In a C/C++ program, this behaviour is controlled by the UNICODE macro 493 * definition.<br> 494 * By default, BridJ will use the Unicode versions. Set this field to false, 495 * set the bridj.useUnicodeVersionOfWindowsAPIs property to "false" or the 496 * BRIDJ_USE_UNICODE_VERSION_OF_WINDOWS_APIS environment variable to "0" to 497 * use the ANSI string version instead. 498 */ 499 public static boolean useUnicodeVersionOfWindowsAPIs = !("false".equals(System.getProperty("bridj.useUnicodeVersionOfWindowsAPIs")) 500 || "0".equals(System.getenv("BRIDJ_USE_UNICODE_VERSION_OF_WINDOWS_APIS"))); 501 502 private static String getArch() { 503 return arch; 504 } 505 506 /** 507 * Machine (as returned by `uname -m`, except for i686 which is actually 508 * i386), adjusted to the JVM platform (32 or 64 bits) 509 */ 510 public static String getMachine() { 511 String arch = getArch(); 512 if (arch.equals("amd64") || arch.equals("x86_64")) { 513 if (is64Bits()) { 514 return "x86_64"; 515 } else { 516 return "i386"; // we are running a 32 bits JVM on a 64 bits platform 517 } 518 } 519 return arch; 520 } 521 522 public static boolean isAndroid() { 523 return "dalvik".equalsIgnoreCase(System.getProperty("java.vm.name")) && isLinux(); 524 } 525 526 public static boolean isArm() { 527 String arch = getArch(); 528 return "arm".equals(arch); 529 } 530 531 public static boolean isSparc() { 532 String arch = getArch(); 533 return "sparc".equals(arch) 534 || "sparcv9".equals(arch); 535 } 536 537 public static boolean is64Bits() { 538 return is64Bits; 539 } 540 541 public static boolean isAmd64Arch() { 542 String arch = getArch(); 543 return arch.equals("x86_64"); 544 } 545 546 static List<String> getPossibleFileNames(String name) { 547 List<String> fileNames = new ArrayList<String>(1); 548 if (isWindows()) { 549 fileNames.add(name + ".dll"); 550 fileNames.add(name + ".drv"); 551 } else { 552 String jniName = "lib" + name + ".jnilib"; 553 if (isMacOSX()) { 554 fileNames.add("lib" + name + ".dylib"); 555 fileNames.add(jniName); 556 } else { 557 fileNames.add("lib" + name + ".so"); 558 fileNames.add(name + ".so"); 559 fileNames.add(jniName); 560 } 561 } 562 563 assert !fileNames.isEmpty(); 564 if (name.contains(".")) { 565 fileNames.add(name); 566 } 567 return fileNames; 568 } 569 570 static synchronized List<String> getEmbeddedLibraryPaths(String name) { 571 List<String> paths = new ArrayList<String>(embeddedLibraryResourceRoots.size()); 572 for (String root : embeddedLibraryResourceRoots) { 573 if (root == null) { 574 continue; 575 } 576 577 if (isWindows()) { 578 paths.add(root + (is64Bits() ? "win64/" : "win32/")); 579 } else if (isMacOSX()) { 580 if (isArm()) { 581 paths.add(root + "iphoneos_arm32_arm/"); 582 } else { 583 paths.add(root + "darwin_universal/"); 584 if (isAmd64Arch()) { 585 paths.add(root + "darwin_x64/"); 586 } 587 } 588 } else { 589 if (isAndroid()) { 590 assert root.equals("libs/"); 591 paths.add(root + "armeabi/"); // Android SDK + NDK-style .so embedding = lib/armeabi/libTest.so 592 } else if (isLinux()) { 593 if (isArm()) { 594 //TODO: ARM support is still broken! 595 //There are 2 ABIs: ARMEL and ARMHF 596 //ARMEL should work ok 597 //ARMHF has two (largely) incompatible calling conventions: softfp and hard/vfp. 598 //The differences are largely down to which registers are used for passing double/float arguments; 599 //in hard/vfp mode, the vfp registers are used, whereas in softfp the normal cpu registers 600 //are used. Calling a function with the wrong convention can lead to stack corruption (although I think 601 //you are probably safe if you stay away from floats and doubles)... 602 //At the time of writing, dyncall only supports softfp, but the common 603 //linux distributions are using hard/vfp. 604 //In the future we need to make bridj support both conventions. 605 606 paths.add(root + getARMLinuxLibDir()); 607 //for compatibility with the older OpenIMAJ forks supporting ARM 608 paths.add(root + getARMLinuxLibDir().replace("_arm", "_arm32_arm")); 609 } else { 610 paths.add(root + (is64Bits() ? "linux_x64/" : "linux_x86/")); 611 } 612 } else if (isSolaris()) { 613 if (isSparc()) { 614 paths.add(root + (is64Bits() ? "sunos_sparc64/" : "sunos_sparc/")); 615 } else { 616 paths.add(root + (is64Bits() ? "sunos_x64/" : "sunos_x86/")); 617 } 618 } 619 } 620 } 621 622 if (paths.isEmpty()) { 623 throw new RuntimeException("Platform not supported ! (os.name='" + osName + "', os.arch='" + System.getProperty("os.arch") + "')"); 624 } 625 return paths; 626 } 627 628 static synchronized List<String> getEmbeddedLibraryResource(String name) { 629 List<String> paths = getEmbeddedLibraryPaths(name); 630 List<String> fileNames = getPossibleFileNames(name); 631 List<String> ret = new ArrayList<String>(paths.size() * fileNames.size()); 632 for (String path : paths) { 633 for (String fileName : fileNames) { 634 ret.add(path + fileName); 635 } 636 } 637 638 if (BridJ.veryVerbose) { 639 BridJ.info("Embedded resource paths for library '" + name + "': " + ret); 640 } 641 return ret; 642 } 643 644 static void tryDeleteFilesInSameDirectory(final File legitFile, final Pattern fileNamePattern, long atLeastOlderThanMillis) { 645 final long maxModifiedDateForDeletion = System.currentTimeMillis() - atLeastOlderThanMillis; 646 new Thread(new Runnable() { 647 public void run() { 648 File dir = legitFile.getParentFile(); 649 String legitFileName = legitFile.getName(); 650 try { 651 for (String name : dir.list()) { 652 if (name.equals(legitFileName)) { 653 continue; 654 } 655 656 if (!fileNamePattern.matcher(name).matches()) { 657 continue; 658 } 659 660 File file = new File(dir, name); 661 if (file.lastModified() > maxModifiedDateForDeletion) { 662 continue; 663 } 664 665 if (file.delete() && BridJ.verbose) { 666 BridJ.info("Deleted old binary file '" + file + "'"); 667 } 668 } 669 } catch (SecurityException ex) { 670 // no right to delete files in that directory 671 BridJ.warning("Failed to delete files matching '" + fileNamePattern + "' in directory '" + dir + "'", ex); 672 } catch (Throwable ex) { 673 BridJ.error("Unexpected error : " + ex, ex); 674 } 675 } 676 }).start(); 677 } 678 static final long DELETE_OLD_BINARIES_AFTER_MILLIS = 24 * 60 * 60 * 1000; // 24 hours 679 680 static File extractEmbeddedLibraryResource(String name) throws IOException { 681 String firstLibraryResource = null; 682 683 List<String> libraryResources = getEmbeddedLibraryResource(name); 684 if (BridJ.veryVerbose) { 685 BridJ.info("Library resources for " + name + ": " + libraryResources); 686 } 687 for (String libraryResource : libraryResources) { 688 if (firstLibraryResource == null) { 689 firstLibraryResource = libraryResource; 690 } 691 int i = libraryResource.lastIndexOf('.'); 692 int len; 693 byte[] b = new byte[8196]; 694 InputStream in = getResourceAsStream(libraryResource); 695 if (in == null) { 696 File f = new File(libraryResource); 697 if (!f.exists()) { 698 f = new File(f.getName()); 699 } 700 if (f.exists()) { 701 return f.getCanonicalFile(); 702 } 703 continue; 704 } 705 String fileName = new File(libraryResource).getName(); 706 File libFile = new File(extractedLibrariesTempDir, fileName); 707 OutputStream out = new BufferedOutputStream(new FileOutputStream(libFile)); 708 while ((len = in.read(b)) > 0) { 709 out.write(b, 0, len); 710 } 711 out.close(); 712 in.close(); 713 714 addTemporaryExtractedLibraryFileToDeleteOnExit(libFile); 715 addTemporaryExtractedLibraryFileToDeleteOnExit(libFile.getParentFile()); 716 717 return libFile; 718 } 719 return null; 720 } 721 static final int maxTempFileAttempts = 20; 722 723 static File createTempDir(String prefix) throws IOException { 724 File dir; 725 for (int i = 0; i < maxTempFileAttempts; i++) { 726 dir = File.createTempFile(prefix, ""); 727 if (dir.delete() && dir.mkdirs()) { 728 return dir; 729 } 730 } 731 throw new RuntimeException("Failed to create temp dir with prefix '" + prefix + "' despite " + maxTempFileAttempts + " attempts!"); 732 } 733 734 /** 735 * Opens an URL with the default system action. 736 * 737 * @param url url to open 738 * @throws NoSuchMethodException if opening an URL on the current platform 739 * is not supported 740 */ 741 public static final void open(URL url) throws NoSuchMethodException { 742 if (url.getProtocol().equals("file")) { 743 open(new File(url.getFile())); 744 } else { 745 if (Platform.isMacOSX()) { 746 execArgs("open", url.toString()); 747 } else if (Platform.isWindows()) { 748 execArgs("rundll32", "url.dll,FileProtocolHandler", url.toString()); 749 } else if (Platform.isUnix() && hasUnixCommand("gnome-open")) { 750 execArgs("gnome-open", url.toString()); 751 } else if (Platform.isUnix() && hasUnixCommand("konqueror")) { 752 execArgs("konqueror", url.toString()); 753 } else if (Platform.isUnix() && hasUnixCommand("mozilla")) { 754 execArgs("mozilla", url.toString()); 755 } else { 756 throw new NoSuchMethodException("Cannot open urls on this platform"); 757 } 758 } 759 } 760 761 /** 762 * Opens a file with the default system action. 763 * 764 * @param file file to open 765 * @throws NoSuchMethodException if opening a file on the current platform 766 * is not supported 767 */ 768 public static final void open(File file) throws NoSuchMethodException { 769 if (Platform.isMacOSX()) { 770 execArgs("open", file.getAbsolutePath()); 771 } else if (Platform.isWindows()) { 772 if (file.isDirectory()) { 773 execArgs("explorer", file.getAbsolutePath()); 774 } else { 775 execArgs("start", file.getAbsolutePath()); 776 } 777 } else if (Platform.isUnix() && hasUnixCommand("gnome-open")) { 778 execArgs("gnome-open", file.toString()); 779 } else if (Platform.isUnix() && hasUnixCommand("konqueror")) { 780 execArgs("konqueror", file.toString()); 781 } else if (Platform.isSolaris() && file.isDirectory()) { 782 execArgs("/usr/dt/bin/dtfile", "-folder", file.getAbsolutePath()); 783 } else { 784 throw new NoSuchMethodException("Cannot open files on this platform"); 785 } 786 } 787 788 /** 789 * Show a file in its parent directory, if possible selecting the file (not 790 * possible on all platforms). 791 * 792 * @param file file to show in the system's default file navigator 793 * @throws NoSuchMethodException if showing a file on the current platform 794 * is not supported 795 */ 796 public static final void show(File file) throws NoSuchMethodException, IOException { 797 if (Platform.isWindows()) { 798 exec("explorer /e,/select,\"" + file.getCanonicalPath() + "\""); 799 } else { 800 open(file.getAbsoluteFile().getParentFile()); 801 } 802 } 803 804 static final void execArgs(String... cmd) throws NoSuchMethodException { 805 try { 806 Runtime.getRuntime().exec(cmd); 807 } catch (Exception ex) { 808 ex.printStackTrace(); 809 throw new NoSuchMethodException(ex.toString()); 810 } 811 } 812 813 static final void exec(String cmd) throws NoSuchMethodException { 814 try { 815 Runtime.getRuntime().exec(cmd).waitFor(); 816 } catch (Exception ex) { 817 ex.printStackTrace(); 818 throw new NoSuchMethodException(ex.toString()); 819 } 820 } 821 822 static final boolean hasUnixCommand(String name) { 823 try { 824 Process p = Runtime.getRuntime().exec(new String[]{"which", name}); 825 return p.waitFor() == 0; 826 } catch (Exception ex) { 827 ex.printStackTrace(); 828 return false; 829 } 830 } 831 832 static native int sizeOf_size_t(); 833 834 static native int sizeOf_time_t(); 835 836 static native int sizeOf_wchar_t(); 837 838 static native int sizeOf_ptrdiff_t(); 839 840 static native int sizeOf_long(); 841 842 static native int getMaxDirectMappingArgCount(); 843 844 private static final boolean contains(String data, String[] search) { 845 if (null != data && null != search) { 846 for (int i = 0; i < search.length; i++) { 847 if (data.indexOf(search[i]) >= 0) { 848 return true; 849 } 850 } 851 } 852 return false; 853 } 854 855 /* 856 * [From https://github.com/Pi4J/pi4j/blob/develop/pi4j-core/src/main/java/com/pi4j/system/SystemInfo.java] 857 * <p> 858 * this method will to obtain the version info string from the 'bash' program 859 * (this method is used to help determine the HARD-FLOAT / SOFT-FLOAT ABI of the system) 860 */ 861 private static String getBashVersionInfo() { 862 String versionInfo = ""; 863 try { 864 865 String cmd = "bash --version"; 866 Process p = Runtime.getRuntime().exec(cmd); 867 p.waitFor(); 868 BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); 869 String line = reader.readLine(); 870 if(p.exitValue() == 0) { 871 while(line != null) { 872 if(!line.isEmpty()) { 873 versionInfo = line; // return only first output line of version info 874 break; 875 } 876 line = reader.readLine(); 877 } 878 } 879 } 880 catch (IOException ioe) { ioe.printStackTrace(); } 881 catch (InterruptedException ie) { ie.printStackTrace(); } 882 return versionInfo; 883 } 884 885 /* 886 * [From https://github.com/Pi4J/pi4j/blob/develop/pi4j-core/src/main/java/com/pi4j/system/SystemInfo.java] 887 * <p> 888 * this method will determine if a specified tag exists from the elf info in the '/proc/self/exe' program 889 * (this method is used to help determine the HARD-FLOAT / SOFT-FLOAT ABI of the system) 890 */ 891 private static boolean hasReadElfTag(String tag) { 892 String tagValue = getReadElfTag(tag); 893 if(tagValue != null && !tagValue.isEmpty()) 894 return true; 895 return false; 896 } 897 898 /* 899 * [From https://github.com/Pi4J/pi4j/blob/develop/pi4j-core/src/main/java/com/pi4j/system/SystemInfo.java] 900 * <p> 901 * this method will obtain a specified tag value from the elf info in the '/proc/self/exe' program 902 * (this method is used to help determine the HARD-FLOAT / SOFT-FLOAT ABI of the system) 903 */ 904 private static String getReadElfTag(String tag) { 905 String tagValue = null; 906 try { 907 String cmd = "/usr/bin/readelf -A /proc/self/exe"; 908 Process p = Runtime.getRuntime().exec(cmd); 909 p.waitFor(); 910 if(p.exitValue() == 0) { 911 BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); 912 String line = reader.readLine(); 913 while(line != null) { 914 line = line.trim(); 915 if (line.startsWith(tag) && line.contains(":")) { 916 String lineParts[] = line.split(":", 2); 917 if(lineParts.length > 1) 918 tagValue = lineParts[1].trim(); 919 break; 920 } 921 line = reader.readLine(); 922 } 923 } 924 } 925 catch (IOException ioe) { ioe.printStackTrace(); } 926 catch (InterruptedException ie) { ie.printStackTrace(); } 927 return tagValue; 928 } 929 930 /** 931 * ARM processors have two incompatible ABIs - one for use with the floating 932 * point unit (HardFP - armhf), the other without (SoftFP - armel). This 933 * generates the correct search path depending on the ABI in use by the JVM. 934 * Unfortunately, there isn't currently a standard property that describes 935 * the abi, so we have to "guess". 936 * <p> 937 * The implementation is derived from code in the PI4J project: 938 * https://github.com/Pi4J/pi4j/blob/develop/pi4j-core/src/main/java/com/pi4j/system/SystemInfo.java 939 * 940 * @return the library directory 941 */ 942 private static final String getARMLinuxLibDir() { 943 944 final boolean isHF = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 945 private final String[] gnueabihf = new String[] { "gnueabihf", "armhf" }; 946 public Boolean run() { 947 if ( contains(System.getProperty("sun.boot.library.path"), gnueabihf) || 948 contains(System.getProperty("java.library.path"), gnueabihf) || 949 contains(System.getProperty("java.home"), gnueabihf) || 950 getBashVersionInfo().contains("gnueabihf") || 951 hasReadElfTag("Tag_ABI_HardFP_use")) { 952 return true; // 953 } 954 return false; 955 } } ); 956 957 return "linux_arm" + (isHF ? "hf" : "el") + "/"; 958 } 959}