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; 034import java.util.Set; 035import java.util.HashSet; 036import java.util.regex.Pattern; 037import java.io.*; 038import java.net.URL; 039 040import java.util.List; 041import java.util.Collections; 042import java.util.Collection; 043import java.util.ArrayList; 044import java.net.MalformedURLException; 045import java.net.URLClassLoader; 046import java.util.Arrays; 047import java.util.Iterator; 048import java.util.LinkedHashSet; 049import java.util.LinkedList; 050import org.bridj.util.StringUtils; 051 052/** 053 * Information about the execution platform (OS, architecture, native sizes...) 054 * and platform-specific actions. 055 * <ul> 056 * <li>To know if the JVM platform is 32 bits or 64 bits, use 057 * {@link Platform#is64Bits()} 058 * </li><li>To know if the OS is an Unix-like system, use 059 * {@link Platform#isUnix()} 060 * </li><li>To open files and URLs in a platform-specific way, use 061 * {@link Platform#open(File)}, {@link Platform#open(URL)}, {@link Platform#show(File)} 062 * </li></ul> 063 * 064 * @author ochafik 065 */ 066public class Platform { 067 068 static final String osName = System.getProperty("os.name", ""); 069 private static boolean inited; 070 static final String BridJLibraryName = "bridj"; 071 public static final int POINTER_SIZE, 072 WCHAR_T_SIZE, 073 SIZE_T_SIZE, 074 TIME_T_SIZE, 075 CLONG_SIZE; 076 /*interface FunInt { 077 int apply(); 078 } 079 static int tryInt(FunInt f, int defaultValue) { 080 try { 081 return f.apply(); 082 } catch (Throwable th) { 083 return defaultValue; 084 } 085 }*/ 086 static final ClassLoader systemClassLoader; 087 088 public static ClassLoader getClassLoader() { 089 return getClassLoader(BridJ.class); 090 } 091 092 public static ClassLoader getClassLoader(Class<?> cl) { 093 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); 094 if (contextClassLoader != null) { 095 return contextClassLoader; 096 } 097 ClassLoader classLoader = cl == null ? null : cl.getClassLoader(); 098 return classLoader == null ? systemClassLoader : classLoader; 099 } 100 101 public static InputStream getResourceAsStream(String path) { 102 URL url = getResource(path); 103 try { 104 return url != null ? url.openStream() : null; 105 } catch (IOException ex) { 106 if (BridJ.verbose) { 107 BridJ.warning("Failed to get resource '" + path + "'", ex); 108 } 109 return null; 110 } 111 } 112 113 public static URL getResource(String path) { 114 if (!path.startsWith("/")) { 115 path = "/" + path; 116 } 117 118 URL in = BridJ.class.getResource(path); 119 if (in != null) { 120 return in; 121 } 122 123 ClassLoader[] cls = { 124 BridJ.class.getClassLoader(), 125 Thread.currentThread().getContextClassLoader(), 126 systemClassLoader 127 }; 128 for (ClassLoader cl : cls) { 129 if (cl != null && (in = cl.getResource(path)) != null) { 130 return in; 131 } 132 } 133 return null; 134 } 135 /* 136 public static class utsname { 137 public final String sysname, nodename, release, version, machine; 138 public utsname(String sysname, String nodename, String release, String version, String machine) { 139 this.sysname = sysname; 140 this.nodename = nodename; 141 this.release = release; 142 this.version = version; 143 this.machine = machine; 144 } 145 public String toString() { 146 StringBuilder b = new StringBuilder("{\n"); 147 b.append("\tsysname: \"").append(sysname).append("\",\n"); 148 b.append("\tnodename: \"").append(nodename).append("\",\n"); 149 b.append("\trelease: \"").append(release).append("\",\n"); 150 b.append("\tversion: \"").append(version).append("\",\n"); 151 b.append("\tmachine: \"").append(machine).append("\"\n"); 152 return b.append("}").toString(); 153 } 154 } 155 public static native utsname uname(); 156 */ 157 static final List<String> embeddedLibraryResourceRoots = new ArrayList<String>(); 158 159 /** 160 * BridJ is able to automatically extract native binaries bundled in the 161 * application's JARs, using a customizable root path and a predefined 162 * architecture-dependent subpath. This method adds an alternative root path 163 * to the search list.<br> 164 * For instance, if you want to load library "mylib" and call 165 * <code>addEmbeddedLibraryResourceRoot("my/company/lib/")</code>, BridJ 166 * will look for library in the following locations : 167 * <ul> 168 * <li>"my/company/lib/darwin_universal/libmylib.dylib" on MacOS X (or 169 * darwin_x86, darwin_x64, darwin_ppc if the binary is not universal)</li> 170 * <li>"my/company/lib/win32/mylib.dll" on Windows (use win64 on 64 bits 171 * architectures)</li> 172 * <li>"my/company/lib/linux_x86/libmylib.so" on Linux (use linux_x64 on 64 173 * bits architectures)</li> 174 * <li>"my/company/lib/sunos_x86/libmylib.so" on Solaris (use sunos_x64 / 175 * sunos_sparc on other architectures)</li> 176 * <li>"lib/armeabi/libmylib.so" on Android (for Android-specific reasons, 177 * only the "lib" sub-path can effectively contain loadable binaries)</li> 178 * </ul> 179 * For other platforms or for an updated list of supported platforms, please 180 * have a look at BridJ's JAR contents (under "org/bridj/lib") and/or to its 181 * source tree, browsable online. 182 */ 183 public static synchronized void addEmbeddedLibraryResourceRoot(String root) { 184 embeddedLibraryResourceRoots.add(0, root); 185 } 186 static Set<File> temporaryExtractedLibraryCanonicalFiles = Collections.synchronizedSet(new LinkedHashSet<File>()); 187 188 static void addTemporaryExtractedLibraryFileToDeleteOnExit(File file) throws IOException { 189 File canonicalFile = file.getCanonicalFile(); 190 191 // Give a chance to NativeLibrary.release() to delete the file : 192 temporaryExtractedLibraryCanonicalFiles.add(canonicalFile); 193 194 // Ask Java to delete the file upon exit if it still exists 195 canonicalFile.deleteOnExit(); 196 } 197 private static final String arch; 198 private static boolean is64Bits; 199 private static File extractedLibrariesTempDir; 200 201 static { 202 arch = System.getProperty("os.arch"); 203 { 204 String dataModel = System.getProperty("sun.arch.data.model", System.getProperty("com.ibm.vm.bitmode")); 205 if ("32".equals(dataModel)) { 206 is64Bits = false; 207 } else if ("64".equals(dataModel)) { 208 is64Bits = true; 209 } else { 210 is64Bits = 211 arch.contains("64") 212 || arch.equalsIgnoreCase("sparcv9"); 213 } 214 } 215 systemClassLoader = createClassLoader(); 216 217 addEmbeddedLibraryResourceRoot("libs/"); 218 if (!isAndroid()) { 219 addEmbeddedLibraryResourceRoot("lib/"); 220 addEmbeddedLibraryResourceRoot("org/bridj/lib/"); 221 if (!Version.VERSION_SPECIFIC_SUB_PACKAGE.equals("")) { 222 addEmbeddedLibraryResourceRoot("org/bridj/" + Version.VERSION_SPECIFIC_SUB_PACKAGE + "/lib/"); 223 } 224 } 225 226 try { 227 extractedLibrariesTempDir = createTempDir("BridJExtractedLibraries"); 228 initLibrary(); 229 } catch (Throwable th) { 230 th.printStackTrace(); 231 } 232 POINTER_SIZE = sizeOf_ptrdiff_t(); 233 WCHAR_T_SIZE = sizeOf_wchar_t(); 234 SIZE_T_SIZE = sizeOf_size_t(); 235 TIME_T_SIZE = sizeOf_time_t(); 236 CLONG_SIZE = sizeOf_long(); 237 238 is64Bits = POINTER_SIZE == 8; 239 240 Runtime.getRuntime().addShutdownHook(new Thread() { 241 public void run() { 242 shutdown(); 243 } 244 }); 245 } 246 private static List<NativeLibrary> nativeLibraries = new ArrayList<NativeLibrary>(); 247 248 static void addNativeLibrary(NativeLibrary library) { 249 synchronized (nativeLibraries) { 250 nativeLibraries.add(library); 251 } 252 } 253 254 private static void shutdown() { 255 //releaseNativeLibraries(); 256 deleteTemporaryExtractedLibraryFiles(); 257 } 258 259 private static void releaseNativeLibraries() { 260 synchronized (nativeLibraries) { 261 // Release libraries in reverse order : 262 for (int iLibrary = nativeLibraries.size(); iLibrary-- != 0;) { 263 NativeLibrary lib = nativeLibraries.get(iLibrary); 264 try { 265 lib.release(); 266 } catch (Throwable th) { 267 BridJ.error("Failed to release library '" + lib.path + "' : " + th, th); 268 } 269 } 270 } 271 } 272 273 private static void deleteTemporaryExtractedLibraryFiles() { 274 synchronized (temporaryExtractedLibraryCanonicalFiles) { 275 temporaryExtractedLibraryCanonicalFiles.add(extractedLibrariesTempDir); 276 277 // Release libraries in reverse order : 278 List<File> filesToDeleteAfterExit = new ArrayList<File>(); 279 for (File tempFile : Platform.temporaryExtractedLibraryCanonicalFiles) { 280 if (tempFile.delete()) { 281 if (BridJ.verbose) { 282 BridJ.info("Deleted temporary library file '" + tempFile + "'"); 283 } 284 } else { 285 filesToDeleteAfterExit.add(tempFile); 286 } 287 } 288 if (!filesToDeleteAfterExit.isEmpty()) { 289 if (BridJ.verbose) { 290 BridJ.info("Attempting to delete " + filesToDeleteAfterExit.size() + " files after JVM exit : " + StringUtils.implode(filesToDeleteAfterExit, ", ")); 291 } 292 293 try { 294 ProcessUtils.startJavaProcess(DeleteFiles.class, filesToDeleteAfterExit); 295 } catch (Throwable ex) { 296 BridJ.error("Failed to launch process to delete files after JVM exit : " + ex, ex); 297 } 298 } 299 } 300 } 301 302 public static class DeleteFiles { 303 304 static boolean delete(List<File> files) { 305 for (Iterator<File> it = files.iterator(); it.hasNext();) { 306 File file = it.next(); 307 if (file.delete()) { 308 it.remove(); 309 } 310 } 311 return files.isEmpty(); 312 } 313 final static long TRY_DELETE_EVERY_MILLIS = 50, 314 FAIL_AFTER_MILLIS = 10000; 315 316 public static void main(String[] args) { 317 try { 318 List<File> files = new LinkedList<File>(); 319 for (String arg : args) { 320 files.add(new File(arg)); 321 } 322 323 long start = System.currentTimeMillis(); 324 while (!delete(files)) { 325 long elapsed = System.currentTimeMillis() - start; 326 if (elapsed > FAIL_AFTER_MILLIS) { 327 BridJ.error("Failed to delete the following files : " + StringUtils.implode(files)); 328 System.exit(1); 329 } 330 331 Thread.sleep(TRY_DELETE_EVERY_MILLIS); 332 } 333 } catch (Throwable th) { 334 th.printStackTrace(); 335 } finally { 336 System.exit(0); 337 } 338 } 339 } 340 341 static ClassLoader createClassLoader() { 342 List<URL> urls = new ArrayList<URL>(); 343 for (String propName : new String[]{"java.class.path", "sun.boot.class.path"}) { 344 String prop = System.getProperty(propName); 345 if (prop == null) { 346 continue; 347 } 348 349 for (String path : prop.split(File.pathSeparator)) { 350 path = path.trim(); 351 if (path.length() == 0) { 352 continue; 353 } 354 355 URL url; 356 try { 357 url = new URL(path); 358 } catch (MalformedURLException ex) { 359 try { 360 url = new File(path).toURI().toURL(); 361 } catch (MalformedURLException ex2) { 362 url = null; 363 } 364 } 365 if (url != null) { 366 urls.add(url); 367 } 368 } 369 } 370 //System.out.println("URLs for synthetic class loader :"); 371 //for (URL url : urls) 372 // System.out.println("\t" + url); 373 return new URLClassLoader(urls.toArray(new URL[urls.size()])); 374 } 375 376 static String getenvOrProperty(String envName, String javaName, String defaultValue) { 377 String value = System.getenv(envName); 378 if (value == null) { 379 value = System.getProperty(javaName); 380 } 381 if (value == null) { 382 value = defaultValue; 383 } 384 return value; 385 } 386 387 public static synchronized void initLibrary() { 388 if (inited) { 389 return; 390 } 391 inited = true; 392 393 try { 394 boolean loaded = false; 395 396 String forceLibFile = getenvOrProperty("BRIDJ_LIBRARY", "bridj.library", null); 397 398 String lib = null; 399 400 if (forceLibFile != null) { 401 try { 402 System.load(lib = forceLibFile); 403 loaded = true; 404 } catch (Throwable ex) { 405 BridJ.error("Failed to load forced library " + forceLibFile, ex); 406 } 407 } 408 409 if (!loaded) { 410 if (!Platform.isAndroid()) { 411 try { 412 File libFile = extractEmbeddedLibraryResource(BridJLibraryName); 413 if (libFile == null) { 414 throw new FileNotFoundException("Failed to extract embedded library '" + BridJLibraryName + "' (could be a classloader issue, or missing binary in resource path " + StringUtils.implode(embeddedLibraryResourceRoots, ", ") + ")"); 415 } 416 417 if (BridJ.veryVerbose) { 418 BridJ.info("Loading library " + libFile); 419 } 420 System.load(lib = libFile.toString()); 421 BridJ.setNativeLibraryFile(BridJLibraryName, libFile); 422 loaded = true; 423 } catch (IOException ex) { 424 BridJ.error("Failed to load '" + BridJLibraryName + "'", ex); 425 } 426 } 427 if (!loaded) { 428 System.loadLibrary("bridj"); 429 } 430 } 431 if (BridJ.veryVerbose) { 432 BridJ.info("Loaded library " + lib); 433 } 434 435 init(); 436 437 //if (BridJ.protectedMode) 438 // BridJ.info("Protected mode enabled"); 439 if (BridJ.logCalls) { 440 BridJ.info("Calls logs enabled"); 441 } 442 443 } catch (Throwable ex) { 444 throw new RuntimeException("Failed to initialize " + BridJ.class.getSimpleName() + " (" + ex + ")", ex); 445 } 446 } 447 448 private static native void init(); 449 450 public static boolean isLinux() { 451 return isUnix() && osName.toLowerCase().contains("linux"); 452 } 453 454 public static boolean isMacOSX() { 455 return isUnix() && (osName.startsWith("Mac") || osName.startsWith("Darwin")); 456 } 457 458 public static boolean isSolaris() { 459 return isUnix() && (osName.startsWith("SunOS") || osName.startsWith("Solaris")); 460 } 461 462 public static boolean isBSD() { 463 return isUnix() && (osName.contains("BSD") || isMacOSX()); 464 } 465 466 public static boolean isUnix() { 467 return File.separatorChar == '/'; 468 } 469 470 public static boolean isWindows() { 471 return File.separatorChar == '\\'; 472 } 473 474 public static boolean isWindows7() { 475 return osName.equals("Windows 7"); 476 } 477 /** 478 * Whether to use Unicode versions of Windows APIs rather than ANSI versions 479 * (for functions that haven't been bound yet : has no effect on functions 480 * that have already been bound).<br> 481 * Some Windows APIs such as SendMessage have two versions : 482 * <ul> 483 * <li>one that uses single-byte character strings (SendMessageA, with 'A' 484 * for ANSI strings)</li> 485 * <li>one that uses unicode character strings (SendMessageW, with 'W' for 486 * Wide strings).</li> 487 * </ul> 488 * <br> 489 * In a C/C++ program, this behaviour is controlled by the UNICODE macro 490 * definition.<br> 491 * By default, BridJ will use the Unicode versions. Set this field to false, 492 * set the bridj.useUnicodeVersionOfWindowsAPIs property to "false" or the 493 * BRIDJ_USE_UNICODE_VERSION_OF_WINDOWS_APIS environment variable to "0" to 494 * use the ANSI string version instead. 495 */ 496 public static boolean useUnicodeVersionOfWindowsAPIs = !("false".equals(System.getProperty("bridj.useUnicodeVersionOfWindowsAPIs")) 497 || "0".equals(System.getenv("BRIDJ_USE_UNICODE_VERSION_OF_WINDOWS_APIS"))); 498 499 private static String getArch() { 500 return arch; 501 } 502 503 /** 504 * Machine (as returned by `uname -m`, except for i686 which is actually 505 * i386), adjusted to the JVM platform (32 or 64 bits) 506 */ 507 public static String getMachine() { 508 String arch = getArch(); 509 if (arch.equals("amd64") || arch.equals("x86_64")) { 510 if (is64Bits()) { 511 return "x86_64"; 512 } else { 513 return "i386"; // we are running a 32 bits JVM on a 64 bits platform 514 } 515 } 516 return arch; 517 } 518 519 public static boolean isAndroid() { 520 return "dalvik".equalsIgnoreCase(System.getProperty("java.vm.name")) && isLinux(); 521 } 522 523 public static boolean isArm() { 524 String arch = getArch(); 525 return "arm".equals(arch); 526 } 527 528 public static boolean isSparc() { 529 String arch = getArch(); 530 return "sparc".equals(arch) 531 || "sparcv9".equals(arch); 532 } 533 534 public static boolean is64Bits() { 535 return is64Bits; 536 } 537 538 public static boolean isAmd64Arch() { 539 String arch = getArch(); 540 return arch.equals("x86_64"); 541 } 542 543 static List<String> getPossibleFileNames(String name) { 544 List<String> fileNames = new ArrayList<String>(1); 545 if (isWindows()) { 546 fileNames.add(name + ".dll"); 547 fileNames.add(name + ".drv"); 548 } else { 549 String jniName = "lib" + name + ".jnilib"; 550 if (isMacOSX()) { 551 fileNames.add("lib" + name + ".dylib"); 552 fileNames.add(jniName); 553 } else { 554 fileNames.add("lib" + name + ".so"); 555 fileNames.add(name + ".so"); 556 fileNames.add(jniName); 557 } 558 } 559 560 assert !fileNames.isEmpty(); 561 if (name.contains(".")) { 562 fileNames.add(name); 563 } 564 return fileNames; 565 } 566 567 static synchronized List<String> getEmbeddedLibraryPaths(String name) { 568 List<String> paths = new ArrayList<String>(embeddedLibraryResourceRoots.size()); 569 for (String root : embeddedLibraryResourceRoots) { 570 if (root == null) { 571 continue; 572 } 573 574 if (isWindows()) { 575 paths.add(root + (is64Bits() ? "win64/" : "win32/")); 576 } else if (isMacOSX()) { 577 if (isArm()) { 578 paths.add(root + "iphoneos_arm32_arm/"); 579 } else { 580 paths.add(root + "darwin_universal/"); 581 if (isAmd64Arch()) { 582 paths.add(root + "darwin_x64/"); 583 } 584 } 585 } else { 586 if (isAndroid()) { 587 assert root.equals("libs/"); 588 paths.add(root + "armeabi/"); // Android SDK + NDK-style .so embedding = lib/armeabi/libTest.so 589 } else if (isLinux()) { 590 if (isArm()) { 591 paths.add(root + "linux_armhf/"); 592 // To discriminate between hard-float and soft-float, we used to test new File("/lib/arm-linux-gnueabihf").isDirectory(). 593 } else { 594 paths.add(root + (is64Bits() ? "linux_x64/" : "linux_x86/")); 595 } 596 } else if (isSolaris()) { 597 if (isSparc()) { 598 paths.add(root + (is64Bits() ? "sunos_sparc64/" : "sunos_sparc/")); 599 } else { 600 paths.add(root + (is64Bits() ? "sunos_x64/" : "sunos_x86/")); 601 } 602 } 603 } 604 } 605 606 if (paths.isEmpty()) { 607 throw new RuntimeException("Platform not supported ! (os.name='" + osName + "', os.arch='" + System.getProperty("os.arch") + "')"); 608 } 609 return paths; 610 } 611 612 static synchronized List<String> getEmbeddedLibraryResource(String name) { 613 List<String> paths = getEmbeddedLibraryPaths(name); 614 List<String> fileNames = getPossibleFileNames(name); 615 List<String> ret = new ArrayList<String>(paths.size() * fileNames.size()); 616 for (String path : paths) { 617 for (String fileName : fileNames) { 618 ret.add(path + fileName); 619 } 620 } 621 622 if (BridJ.veryVerbose) { 623 BridJ.info("Embedded resource paths for library '" + name + "': " + ret); 624 } 625 return ret; 626 } 627 628 static void tryDeleteFilesInSameDirectory(final File legitFile, final Pattern fileNamePattern, long atLeastOlderThanMillis) { 629 final long maxModifiedDateForDeletion = System.currentTimeMillis() - atLeastOlderThanMillis; 630 new Thread(new Runnable() { 631 public void run() { 632 File dir = legitFile.getParentFile(); 633 String legitFileName = legitFile.getName(); 634 try { 635 for (String name : dir.list()) { 636 if (name.equals(legitFileName)) { 637 continue; 638 } 639 640 if (!fileNamePattern.matcher(name).matches()) { 641 continue; 642 } 643 644 File file = new File(dir, name); 645 if (file.lastModified() > maxModifiedDateForDeletion) { 646 continue; 647 } 648 649 if (file.delete() && BridJ.verbose) { 650 BridJ.info("Deleted old binary file '" + file + "'"); 651 } 652 } 653 } catch (SecurityException ex) { 654 // no right to delete files in that directory 655 BridJ.warning("Failed to delete files matching '" + fileNamePattern + "' in directory '" + dir + "'", ex); 656 } catch (Throwable ex) { 657 BridJ.error("Unexpected error : " + ex, ex); 658 } 659 } 660 }).start(); 661 } 662 static final long DELETE_OLD_BINARIES_AFTER_MILLIS = 24 * 60 * 60 * 1000; // 24 hours 663 664 static File extractEmbeddedLibraryResource(String name) throws IOException { 665 String firstLibraryResource = null; 666 667 List<String> libraryResources = getEmbeddedLibraryResource(name); 668 if (BridJ.veryVerbose) { 669 BridJ.info("Library resources for " + name + ": " + libraryResources); 670 } 671 for (String libraryResource : libraryResources) { 672 if (firstLibraryResource == null) { 673 firstLibraryResource = libraryResource; 674 } 675 int i = libraryResource.lastIndexOf('.'); 676 int len; 677 byte[] b = new byte[8196]; 678 InputStream in = getResourceAsStream(libraryResource); 679 if (in == null) { 680 File f = new File(libraryResource); 681 if (!f.exists()) { 682 f = new File(f.getName()); 683 } 684 if (f.exists()) { 685 return f.getCanonicalFile(); 686 } 687 continue; 688 } 689 String fileName = new File(libraryResource).getName(); 690 File libFile = new File(extractedLibrariesTempDir, fileName); 691 OutputStream out = new BufferedOutputStream(new FileOutputStream(libFile)); 692 while ((len = in.read(b)) > 0) { 693 out.write(b, 0, len); 694 } 695 out.close(); 696 in.close(); 697 698 addTemporaryExtractedLibraryFileToDeleteOnExit(libFile); 699 addTemporaryExtractedLibraryFileToDeleteOnExit(libFile.getParentFile()); 700 701 return libFile; 702 } 703 return null; 704 } 705 static final int maxTempFileAttempts = 20; 706 707 static File createTempDir(String prefix) throws IOException { 708 File dir; 709 for (int i = 0; i < maxTempFileAttempts; i++) { 710 dir = File.createTempFile(prefix, ""); 711 if (dir.delete() && dir.mkdirs()) { 712 return dir; 713 } 714 } 715 throw new RuntimeException("Failed to create temp dir with prefix '" + prefix + "' despite " + maxTempFileAttempts + " attempts!"); 716 } 717 718 /** 719 * Opens an URL with the default system action. 720 * 721 * @param url url to open 722 * @throws NoSuchMethodException if opening an URL on the current platform 723 * is not supported 724 */ 725 public static final void open(URL url) throws NoSuchMethodException { 726 if (url.getProtocol().equals("file")) { 727 open(new File(url.getFile())); 728 } else { 729 if (Platform.isMacOSX()) { 730 execArgs("open", url.toString()); 731 } else if (Platform.isWindows()) { 732 execArgs("rundll32", "url.dll,FileProtocolHandler", url.toString()); 733 } else if (Platform.isUnix() && hasUnixCommand("gnome-open")) { 734 execArgs("gnome-open", url.toString()); 735 } else if (Platform.isUnix() && hasUnixCommand("konqueror")) { 736 execArgs("konqueror", url.toString()); 737 } else if (Platform.isUnix() && hasUnixCommand("mozilla")) { 738 execArgs("mozilla", url.toString()); 739 } else { 740 throw new NoSuchMethodException("Cannot open urls on this platform"); 741 } 742 } 743 } 744 745 /** 746 * Opens a file with the default system action. 747 * 748 * @param file file to open 749 * @throws NoSuchMethodException if opening a file on the current platform 750 * is not supported 751 */ 752 public static final void open(File file) throws NoSuchMethodException { 753 if (Platform.isMacOSX()) { 754 execArgs("open", file.getAbsolutePath()); 755 } else if (Platform.isWindows()) { 756 if (file.isDirectory()) { 757 execArgs("explorer", file.getAbsolutePath()); 758 } else { 759 execArgs("start", file.getAbsolutePath()); 760 } 761 } else if (Platform.isUnix() && hasUnixCommand("gnome-open")) { 762 execArgs("gnome-open", file.toString()); 763 } else if (Platform.isUnix() && hasUnixCommand("konqueror")) { 764 execArgs("konqueror", file.toString()); 765 } else if (Platform.isSolaris() && file.isDirectory()) { 766 execArgs("/usr/dt/bin/dtfile", "-folder", file.getAbsolutePath()); 767 } else { 768 throw new NoSuchMethodException("Cannot open files on this platform"); 769 } 770 } 771 772 /** 773 * Show a file in its parent directory, if possible selecting the file (not 774 * possible on all platforms). 775 * 776 * @param file file to show in the system's default file navigator 777 * @throws NoSuchMethodException if showing a file on the current platform 778 * is not supported 779 */ 780 public static final void show(File file) throws NoSuchMethodException, IOException { 781 if (Platform.isWindows()) { 782 exec("explorer /e,/select,\"" + file.getCanonicalPath() + "\""); 783 } else { 784 open(file.getAbsoluteFile().getParentFile()); 785 } 786 } 787 788 static final void execArgs(String... cmd) throws NoSuchMethodException { 789 try { 790 Runtime.getRuntime().exec(cmd); 791 } catch (Exception ex) { 792 ex.printStackTrace(); 793 throw new NoSuchMethodException(ex.toString()); 794 } 795 } 796 797 static final void exec(String cmd) throws NoSuchMethodException { 798 try { 799 Runtime.getRuntime().exec(cmd).waitFor(); 800 } catch (Exception ex) { 801 ex.printStackTrace(); 802 throw new NoSuchMethodException(ex.toString()); 803 } 804 } 805 806 static final boolean hasUnixCommand(String name) { 807 try { 808 Process p = Runtime.getRuntime().exec(new String[]{"which", name}); 809 return p.waitFor() == 0; 810 } catch (Exception ex) { 811 ex.printStackTrace(); 812 return false; 813 } 814 } 815 816 static native int sizeOf_size_t(); 817 818 static native int sizeOf_time_t(); 819 820 static native int sizeOf_wchar_t(); 821 822 static native int sizeOf_ptrdiff_t(); 823 824 static native int sizeOf_long(); 825 826 static native int getMaxDirectMappingArgCount(); 827}