1616import java .awt .GraphicsEnvironment ;
1717import java .awt .HeadlessException ;
1818import java .awt .Window ;
19-
20- import java .nio .Buffer ;
21- import java .nio .ByteBuffer ;
22-
2319import java .io .File ;
24- import java .io .FilenameFilter ;
2520import java .io .FileOutputStream ;
21+ import java .io .FilenameFilter ;
2622import java .io .IOException ;
2723import java .io .InputStream ;
2824import java .io .UnsupportedEncodingException ;
3632import java .net .URI ;
3733import java .net .URISyntaxException ;
3834import java .net .URL ;
35+ import java .nio .Buffer ;
36+ import java .nio .ByteBuffer ;
3937import java .security .AccessController ;
4038import java .security .PrivilegedAction ;
4139import java .util .ArrayList ;
@@ -173,14 +171,13 @@ private static void dispose() {
173171 that introduces issues with cleaning up any extant JNA bits
174172 (e.g. Memory) which may still need use of the library before shutdown.
175173 */
176- private static boolean deleteNativeLibrary (String path ) {
177- File flib = new File (path );
178- if (flib .delete ()) {
174+ static boolean deleteLibrary (File lib ) {
175+ if (lib .delete ()) {
179176 return true ;
180177 }
181178
182179 // Couldn't delete it, mark for later deletion
183- markTemporaryFile (flib );
180+ markTemporaryFile (lib );
184181
185182 return false ;
186183 }
@@ -594,10 +591,20 @@ public static char[] toCharArray(String s) {
594591 return buf ;
595592 }
596593
594+ /** Generate a canonical String prefix based on the current OS
595+ type/arch/name.
596+ */
597+ public static String getNativeLibraryResourcePrefix () {
598+ return getNativeLibraryResourcePrefix (Platform .getOSType (), System .getProperty ("os.arch" ), System .getProperty ("os.name" ));
599+ }
600+
597601 /** Generate a canonical String prefix based on the given OS
598602 type/arch/name.
603+ @param osType from {@link Platform}
604+ @param arch from <code>os.arch</code> System property
605+ @param name from <code>os.name</code> System property
599606 */
600- static String getNativeLibraryResourcePath (int osType , String arch , String name ) {
607+ public static String getNativeLibraryResourcePrefix (int osType , String arch , String name ) {
601608 String osPrefix ;
602609 arch = arch .toLowerCase ();
603610 if ("powerpc" .equals (arch )) {
@@ -652,7 +659,7 @@ else if ("x86_64".equals(arch)) {
652659 osPrefix += "-" + arch ;
653660 break ;
654661 }
655- return "/com/sun/jna/" + osPrefix ;
662+ return osPrefix ;
656663 }
657664
658665 /**
@@ -727,28 +734,76 @@ private static void loadNativeLibrary() {
727734 throw new UnsatisfiedLinkError ("Native jnidispatch library not found" );
728735 }
729736
737+ static final String JNA_TMPLIB_PREFIX = "jna" ;
730738 /**
731739 * Attempts to load the native library resource from the filesystem,
732740 * extracting the JNA stub library from jna.jar if not already available.
733741 */
734742 private static void loadNativeLibraryFromJar () {
735- String libname = System .mapLibraryName ("jnidispatch" );
736- String arch = System .getProperty ("os.arch" );
737- String name = System .getProperty ("os.name" );
738- String resourceName = getNativeLibraryResourcePath (Platform .getOSType (), arch , name ) + "/" + libname ;
739- URL url = Native .class .getResource (resourceName );
740- boolean unpacked = false ;
741-
742- // Add an ugly hack for OpenJDK (soylatte) - JNI libs use the usual
743- // .dylib extension
744- if (url == null && Platform .isMac ()
745- && resourceName .endsWith (".dylib" )) {
746- resourceName = resourceName .substring (0 , resourceName .lastIndexOf (".dylib" )) + ".jnilib" ;
747- url = Native .class .getResource (resourceName );
743+ try {
744+ String prefix = "com/sun/jna/" + getNativeLibraryResourcePrefix ();
745+ File lib = extractFromResourcePath ("jnidispatch" , prefix , Native .class .getClassLoader ());
746+ System .load (lib .getAbsolutePath ());
747+ nativeLibraryPath = lib .getAbsolutePath ();
748+ // Attempt to delete immediately once jnidispatch is successfully
749+ // loaded. This avoids the complexity of trying to do so on "exit",
750+ // which point can vary under different circumstances (native
751+ // compilation, dynamically loaded modules, normal application, etc).
752+ if (isUnpacked (lib )) {
753+ deleteLibrary (lib );
754+ }
755+ }
756+ catch (IOException e ) {
757+ throw new UnsatisfiedLinkError (e .getMessage ());
758+ }
759+ }
760+
761+ /** Identify temporary files unpacked from classpath jar files. */
762+ static boolean isUnpacked (File file ) {
763+ return file .getName ().startsWith (JNA_TMPLIB_PREFIX );
764+ }
765+
766+ /** Attempt to extract a native library from the current resource path.
767+ * Expects native libraries to be stored under
768+ * the path returned by {@link #getNativeLibraryResourcePrefix()},
769+ * and reachable by the current thread context class loader.
770+ * @param name Base name of native library to extract
771+ * @return File indicating extracted resource on disk
772+ * @throws IOException if resource not found
773+ */
774+ static File extractFromResourcePath (String name ) throws IOException {
775+ return extractFromResourcePath (name , getNativeLibraryResourcePrefix (), Thread .currentThread ().getContextClassLoader ());
776+ }
777+
778+ /** Attempt to extract a native library from the current resource path.
779+ * Expects native libraries to be stored under
780+ * the path returned by {@link #getNativeLibraryResourcePath(int, String,
781+ * String)}.
782+ * @param name Base name of native library to extract
783+ * @param loader Class loader to use to load resources
784+ * @param resourcePrefix prefix to use when looking for the resource
785+ * @return File indicating extracted resource on disk
786+ * @throws IOException if resource not found
787+ */
788+ static File extractFromResourcePath (String name , String resourcePrefix , ClassLoader loader ) throws IOException {
789+ String libname = System .mapLibraryName (name );
790+ String resourcePath = resourcePrefix + "/" + libname ;
791+ URL url = loader .getResource (resourcePath );
792+
793+ // User libraries will have '.dylib'
794+ if (url == null && Platform .isMac ()) {
795+ if (resourcePath .endsWith (".jnilib" )) {
796+ resourcePath = resourcePath .substring (0 , resourcePath .lastIndexOf (".jnilib" )) + ".dylib" ;
797+ }
798+ // Ugly hack for OpenJDK (soylatte) - JNI libs use the usual
799+ // .dylib extension
800+ else if (resourcePath .endsWith (".dylib" )) {
801+ resourcePath = resourcePath .substring (0 , resourcePath .lastIndexOf (".dylib" )) + ".jnilib" ;
802+ }
803+ url = loader .getResource (resourcePath );
748804 }
749805 if (url == null ) {
750- throw new UnsatisfiedLinkError ("JNA native support (" + resourceName
751- + ") not found in resource path" );
806+ throw new IOException ("JNA native support (" + resourcePath + ") not found in resource path (" + System .getProperty ("java.class.path" ) + ")" );
752807 }
753808
754809 File lib = null ;
@@ -760,13 +815,13 @@ private static void loadNativeLibraryFromJar() {
760815 lib = new File (url .getPath ());
761816 }
762817 if (!lib .exists ()) {
763- throw new Error ("File URL " + url + " could not be properly decoded" );
818+ throw new IOException ("File URL " + url + " could not be properly decoded" );
764819 }
765820 }
766821 else {
767- InputStream is = Native . class . getResourceAsStream (resourceName );
822+ InputStream is = loader . getResourceAsStream (resourcePath );
768823 if (is == null ) {
769- throw new Error ("Can't obtain jnidispatch InputStream" );
824+ throw new IOException ("Can't obtain InputStream for " + resourcePath );
770825 }
771826
772827 FileOutputStream fos = null ;
@@ -775,18 +830,17 @@ private static void loadNativeLibraryFromJar() {
775830 // Let Java pick the suffix, except on windows, to avoid
776831 // problems with Web Start.
777832 File dir = getTempDir ();
778- lib = File .createTempFile ("jna" , Platform .isWindows ()?".dll" :null , dir );
833+ lib = File .createTempFile (JNA_TMPLIB_PREFIX , Platform .isWindows ()?".dll" :null , dir );
779834 lib .deleteOnExit ();
780835 fos = new FileOutputStream (lib );
781836 int count ;
782837 byte [] buf = new byte [1024 ];
783838 while ((count = is .read (buf , 0 , buf .length )) > 0 ) {
784839 fos .write (buf , 0 , count );
785840 }
786- unpacked = true ;
787841 }
788842 catch (IOException e ) {
789- throw new Error ("Failed to create temporary file for jnidispatch library" , e );
843+ throw new IOException ("Failed to create temporary file for " + name + " library" , e );
790844 }
791845 finally {
792846 try { is .close (); } catch (IOException e ) { }
@@ -795,15 +849,7 @@ private static void loadNativeLibraryFromJar() {
795849 }
796850 }
797851 }
798- System .load (lib .getAbsolutePath ());
799- nativeLibraryPath = lib .getAbsolutePath ();
800- // Attempt to delete immediately once jnidispatch is successfully
801- // loaded. This avoids the complexity of trying to do so on "exit",
802- // which point can vary under different circumstances (native
803- // compilation, dynamically loaded modules, normal application, etc).
804- if (unpacked ) {
805- deleteNativeLibrary (lib .getAbsolutePath ());
806- }
852+ return lib ;
807853 }
808854
809855 /**
@@ -966,7 +1012,7 @@ static void removeTemporaryFiles() {
9661012 File dir = getTempDir ();
9671013 FilenameFilter filter = new FilenameFilter () {
9681014 public boolean accept (File dir , String name ) {
969- return name .endsWith (".x" ) && name .indexOf ( "jna" ) != - 1 ;
1015+ return name .endsWith (".x" ) && name .startsWith ( JNA_TMPLIB_PREFIX ) ;
9701016 }
9711017 };
9721018 File [] files = dir .listFiles (filter );
0 commit comments