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