atom feed4 messages in net.java.dev.jna.usersJnidispatch Library Issue and Suggestion
FromSent OnAttachments
Charles SmithFeb 4, 2008 2:55 pm 
Timothy WallFeb 5, 2008 6:35 am 
Charles SmithFeb 5, 2008 2:46 pm 
Timothy WallFeb 12, 2008 5:50 am 
Subject:Jnidispatch Library Issue and Suggestion
From:Charles Smith (csm@chenomx.com)
Date:Feb 4, 2008 2:55:58 pm
List:net.java.dev.jna.users

Hello,

Recently I have been integrating JNA into our java application to give us access to the Recylce Bin and other niceties in Windows. When I was finished I noticed that sometimes the jnidispatch.dll that JNA extracts to my temp folder on Windows is left behind even though the VM exits cleanly. I tested it further and noticed that it worked fine when running in my IDE but not when packed up in a jar (we use one-jar.sourceforge.net for our jar).

After looking into the handling of the native lib extraction in the Native class and DeleteNativeLibrary class I realized that it might be a timing issue when the VM is shutting down. Perhaps in certain cases one second is not a long enough time for the previous VM to release its lock on the tempfile and so the DeleteNativeLibrary main fails to do its job...

Since the DeleteNativeLibrary class is not an ideal solution I've been pondering whether it might be better to also provide JNA users the ability to specify their own file to extract the native library to through a System Property. The simplest way JNA users could make use of this property would be to set it to a static file in their own application's directory in the user's profile (user.home). In this way JNA users can maintain an easy distribution mechanism (a single jar with no requirement to alter java.library.path) and also ensure that no stray files are being left behind on the user's system.

I altered the Native.loadNativeLibary() method to acheive this while still failing back to the temp dir extraction:

private static void loadNativeLibrary() { String libname = System.mapLibraryName("jnidispatch"); String resourceName = getNativeLibraryResourcePath() + "/" + libname; URL url = Native.class.getResource(resourceName);

// Add an ugly hack for OpenJDK (soylatte) - JNI libs use the usual .dylib extension if (url == null && Platform.isMac() && resourceName.endsWith(".dylib")) { resourceName = resourceName.substring(0, resourceName.lastIndexOf(".dylib")) + ".jnilib"; url = Native.class.getResource(resourceName); } if (url == null) { throw new UnsatisfiedLinkError("jnidispatch (" + resourceName + ") not found in resource path"); }

File lib = null; if (url.getProtocol().toLowerCase().equals("file")) { // NOTE: use older API for 1.3 compatibility lib = new File(URLDecoder.decode(url.getPath())); } else { InputStream is = Native.class.getResourceAsStream(resourceName); if (is == null) { throw new Error("Can't obtain jnidispatch InputStream"); }

FileOutputStream fos = null; try {

* // See if the jna.native.extraction.path System property has // been set to an existing directory and if so extract to that // directory rather than a temp file. String altExtractionPath = System.getProperty("jna.native.extraction.path"); if ( altExtractionPath != null ) { lib = new File(altExtractionPath); if ( !lib.exists() ) { File parent = lib.getParentFile(); if ( parent != null && parent.exists() == false ) { parent.mkdirs(); } try { lib.createNewFile(); } catch (Throwable t) { if ( !lib.exists() ) { // fallback to extracting to a temp file lib = null; } } } } * if ( lib == null ) { // Suffix is required on windows, or library fails to load // Let Java pick the suffix lib = File.createTempFile("jna", null); lib.deleteOnExit(); if (Platform.deleteNativeLibraryAfterVMExit()) { Runtime.getRuntime().addShutdownHook(new DeleteNativeLibrary(lib)); } }

fos = new FileOutputStream(lib); int count; byte[] buf = new byte[1024]; while ((count = is.read(buf, 0, buf.length)) > 0) { fos.write(buf, 0, count); }

} catch(IOException e) { throw new Error("Failed to create temporary file for jnidispatch library: " + e); } finally { try { is.close(); } catch(IOException e) { } if (fos != null) { try { fos.close(); } catch(IOException e) { } } } } System.load(lib.getAbsolutePath()); }

Is this a change you might consider including in your codebase?

BTW - great work on this very useful java library! I was very happy when I was able to remove all our JNI code in favour of JNA.

Thank-you,