/* Copyright (c) 2007-2009 Timothy Wall, All Rights Reserved * * The contents of this file is dual-licensed under 2 * alternative Open Source/Free licenses: LGPL 2.1 or later and * Apache License 2.0. (starting with JNA version 4.0.0). * * You can freely decide which license you want to apply to * the project. * * You may obtain a copy of the LGPL License at: * * http://www.gnu.org/licenses/licenses.html * * A copy is also included in the downloadable source code package * containing JNA, in file "LGPL2.1". * * You may obtain a copy of the Apache License at: * * http://www.apache.org/licenses/ * * A copy is also included in the downloadable source code package * containing JNA, in file "AL2.0". */ package com.sun.jna; import java.io.File; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.Properties; import junit.framework.TestCase; /** Test loading and unloading native support from various locations. Note * that no JNI classes are directly referenced in these tests. */ public class JNALoadTest extends TestCase implements Paths, GCWaits { private class TestLoader extends URLClassLoader { public TestLoader(boolean fromJar) throws MalformedURLException { super(new URL[] { Platform.isWindowsCE() ? new File("/Storage Card/" + (fromJar ? "jna.jar" : "test.jar")).toURI().toURL() : new File(BUILDDIR + (fromJar ? "/jna.jar" : "/classes")).toURI().toURL(), }, new CloverLoader()); if (fromJar) { assertJarExists(); } else { assertLibraryExists(); } } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { String boot = System.getProperty("jna.boot.library.path"); if (boot != null) { System.setProperty("jna.boot.library.path", ""); } Class<?> cls = super.findClass(name); if (boot != null) { System.setProperty("jna.boot.library.path", boot); } return cls; } } protected void assertJarExists() { File jar = new File(JNAJAR); if (!jar.exists()) { throw new Error("Expected JNA jar file at " + jar + " is missing"); } } protected void assertLibraryExists() { String osPrefix = Platform.getNativeLibraryResourcePrefix(); String name = System.mapLibraryName("jnidispatch").replace(".dylib", ".jnilib"); File lib = new File(CLASSES + "/com/sun/jna/" + osPrefix + "/" + name); if (!lib.exists()) { throw new Error("Expected JNA library at " + lib + " is missing"); } } public void testAvoidJarUnpacking() throws Exception { System.setProperty("jna.nounpack", "true"); try { Class<?> cls = Class.forName("com.sun.jna.Native", true, new TestLoader(true)); fail("Class com.sun.jna.Native should not be loadable if jna.nounpack=true: " + cls.getClassLoader()); } catch(UnsatisfiedLinkError e) { } finally { System.setProperty("jna.nounpack", "false"); } } public void testAvoidResourcePathLoading() throws Exception { System.setProperty("jna.noclasspath", "true"); try { Class<?> cls = Class.forName("com.sun.jna.Native", true, new TestLoader(false)); fail("Class com.sun.jna.Native should not be loadable if jna.noclasspath=true: " + cls.getClassLoader()); } catch(UnsatisfiedLinkError e) { } finally { System.setProperty("jna.noclasspath", "false"); } } public void testLoadAndUnloadFromJar() throws Exception { ClassLoader loader = new TestLoader(true); Class<?> cls = Class.forName("com.sun.jna.Native", true, loader); assertEquals("Wrong class loader", loader, cls.getClassLoader()); assertTrue("System property jna.loaded not set", Boolean.getBoolean("jna.loaded")); Field field = cls.getDeclaredField("jnidispatchPath"); field.setAccessible(true); String path = (String)field.get(null); assertNotNull("Native library path unavailable", path); assertTrue("Native library not unpacked from jar: " + path, path.startsWith(System.getProperty("java.io.tmpdir"))); Reference<Class<?>> ref = new WeakReference<Class<?>>(cls); Reference<ClassLoader> clref = new WeakReference<ClassLoader>(loader); loader = null; cls = null; field = null; System.gc(); for (int i=0;i < GC_WAITS && (ref.get() != null || clref.get() != null);i++) { Thread.sleep(GC_WAIT_INTERVAL); System.gc(); } assertNull("Class not GC'd: " + ref.get(), ref.get()); assertNull("ClassLoader not GC'd: " + clref.get(), clref.get()); // Check for temporary file deletion File f = new File(path); for (int i=0;i < GC_WAITS && (f.exists() || Boolean.getBoolean("jna.loaded"));i++) { Thread.sleep(GC_WAIT_INTERVAL); System.gc(); } if (f.exists()) { assertTrue("Temporary jnidispatch not marked for later deletion: " + f, new File(f.getAbsolutePath()+".x").exists()); } assertFalse("System property jna.loaded not cleared", Boolean.getBoolean("jna.loaded")); // Should be able to load again without complaints about library // already loaded in another class loader try { loader = new TestLoader(true); cls = Class.forName("com.sun.jna.Native", true, loader); } catch(Throwable t) { fail("Couldn't load class again after discarding first load: " + t.getMessage()); } finally { loader = null; cls = null; System.gc(); } } // GC Fails under OpenJDK(linux/ppc) public void testLoadAndUnloadFromResourcePath() throws Exception { ClassLoader loader = new TestLoader(false); Class<?> cls = Class.forName("com.sun.jna.Native", true, loader); assertEquals("Wrong class loader", loader, cls.getClassLoader()); assertTrue("System property jna.loaded not set", Boolean.getBoolean("jna.loaded")); Field field = cls.getDeclaredField("jnidispatchPath"); field.setAccessible(true); String path = (String)field.get(null); assertNotNull("Native library not found", path); Reference<Class<?>> ref = new WeakReference<Class<?>>(cls); Reference<ClassLoader> clref = new WeakReference<ClassLoader>(loader); loader = null; cls = null; field = null; System.gc(); for (int i=0;i < GC_WAITS && (ref.get() != null || clref.get() != null || Boolean.getBoolean("jna.loaded"));i++) { Thread.sleep(GC_WAIT_INTERVAL); System.gc(); } assertNull("Class not GC'd: " + ref.get(), ref.get()); assertNull("ClassLoader not GC'd: " + clref.get(), clref.get()); assertFalse("System property jna.loaded not cleared", Boolean.getBoolean("jna.loaded")); Throwable throwable = null; // NOTE: IBM J9 needs some extra time to unload the native library, // so try a few times before failing for (int i=0;i < GC_WAITS;i++) { System.gc(); Thread.sleep(GC_WAIT_INTERVAL); try { loader = new TestLoader(false); cls = Class.forName("com.sun.jna.Native", true, loader); break; } catch(Throwable t) { loader = null; throwable = t; } } try { if (loader == null) { fail("Native library not unloaded: " + throwable.getMessage()); } } finally { loader = null; cls = null; System.gc(); } } public void testLoadFromUnicodePath() throws Exception { if (Platform.isWindows()) { String vendor = System.getProperty("java.vendor"); if (vendor != null) { vendor = vendor.toLowerCase(); if (vendor.contains("oracle") || vendor.contains("sun")) { System.out.println("Skip " + getName() + " - Fails on Sun JVM windows (32 and 64-bit) (JVM bug), Works with IBM J9 (jdk6) windows"); return; } } } final String UNICODE = getName() + "-\u0444\u043b\u0441\u0432\u0443"; File tmpdir = new File(System.getProperty("java.io.tmpdir")); File unicodeDir = new File(tmpdir, UNICODE); unicodeDir.mkdirs(); Properties props = (Properties)System.getProperties().clone(); try { System.setProperty("jnidispatch.preserve", "true"); System.setProperty("jna.tmpdir", unicodeDir.getAbsolutePath()); ClassLoader loader = new TestLoader(true); Class<?> cls = Class.forName("com.sun.jna.Native", true, loader); assertEquals("Wrong class loader", loader, cls.getClassLoader()); assertTrue("System property jna.loaded not set", Boolean.getBoolean("jna.loaded")); String path = System.getProperty("jnidispatch.path"); if (path != null) { File lib = new File(path); lib.deleteOnExit(); } } catch(UnsatisfiedLinkError e) { throw new Error("JVM error: System.load() failed to load JNA native library from " + System.getProperty("jnidispatch.path") + "): " + e); } finally { System.setProperties(props); } } public static void main(String[] args) { junit.textui.TestRunner.run(JNALoadTest.class); } }