diff --git a/CHANGES.md b/CHANGES.md index 10394ad1a7..6016125704 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ Bug Fixes * [#863](https://github.com/java-native-access/jna/pull/863): Fix ARM softfloat/hardfloat detection by modifying armSoftFloat condition in ELFAnalyser. Before this fix a softfloat binary could be misdetected as hardfloat. - [@kunkun26](https://github.com/kunkun26). * [#867](https://github.com/java-native-access/jna/issues/867): Fix memory leak in `COMLateBindingObject#getStringProperty` - [@matthiasblaesing](https://github.com/matthiasblaesing). * [#871](https://github.com/java-native-access/jna/issues/871): Fix mapping of libc function `gethostname`, `sethostname`, `getdomainname` and `setdomainname` and bind `com.sun.jna.platform.win32.Winsock2.gethostname(byte[], int)` - [@matthiasblaesing](https://github.com/matthiasblaesing). +* [#876](https://github.com/java-native-access/jna/pull/876): Restore java 6 compatibility - [@matthiasblaesing](https://github.com/matthiasblaesing). Breaking Changes ---------------- diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java index a405890d88..46407578ab 100755 --- a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java @@ -544,7 +544,7 @@ public static Account[] getCurrentUserGroups() { if (err == null) { err = e; } else { - err.addSuppressed(e); + err.addSuppressedReflected(e); } } } @@ -2443,7 +2443,7 @@ public static boolean accessCheck(File file, AccessCheckPermission permissionToC if (err == null) { err = e; } else { - err.addSuppressed(e); + err.addSuppressedReflected(e); } } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Crypt32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/Crypt32Util.java index ebf20a4c28..0c73a0d05b 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Crypt32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Crypt32Util.java @@ -150,7 +150,7 @@ public static byte[] cryptUnprotectData(byte[] data, byte[] entropy, int flags, if (err == null) { err = e; } else { - err.addSuppressed(e); + err.addSuppressedReflected(e); } } } @@ -162,7 +162,7 @@ public static byte[] cryptUnprotectData(byte[] data, byte[] entropy, int flags, if (err == null) { err = e; } else { - err.addSuppressed(e); + err.addSuppressedReflected(e); } } } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/GDI32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/GDI32Util.java index 3a807626ed..5bf755217f 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/GDI32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/GDI32Util.java @@ -153,7 +153,7 @@ public static BufferedImage getScreenshot(HWND target) { if (result == null || WinGDI.HGDI_ERROR.equals(result)) { Win32Exception ex = new Win32Exception(Native.getLastError()); if (we != null) { - ex.addSuppressed(we); + ex.addSuppressedReflected(we); } we = ex; } @@ -163,7 +163,7 @@ public static BufferedImage getScreenshot(HWND target) { if (!GDI32.INSTANCE.DeleteObject(hBitmap)) { Win32Exception ex = new Win32Exception(Native.getLastError()); if (we != null) { - ex.addSuppressed(we); + ex.addSuppressedReflected(we); } we = ex; } @@ -174,7 +174,7 @@ public static BufferedImage getScreenshot(HWND target) { if (!GDI32.INSTANCE.DeleteDC(hdcTargetMem)) { Win32Exception ex = new Win32Exception(Native.getLastError()); if (we != null) { - ex.addSuppressed(we); + ex.addSuppressedReflected(we); } we = ex; } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java index 9a7892b884..a06fcb1d8b 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java @@ -110,7 +110,7 @@ public static void closeHandleRefs(HANDLEByReference... refs) { if (err == null) { err = e; } else { - err.addSuppressed(e); + err.addSuppressedReflected(e); } } } @@ -148,7 +148,7 @@ public static void closeHandles(HANDLE... handles) { if (err == null) { err = e; } else { - err.addSuppressed(e); + err.addSuppressedReflected(e); } } } @@ -341,7 +341,7 @@ public static int getFileType(String fileName) throws FileNotFoundException { if (err == null) { err = e; } else { - err.addSuppressed(e); + err.addSuppressedReflected(e); } } @@ -905,7 +905,7 @@ public static byte[] getResource(String path, String type, String name) { if (!Kernel32.INSTANCE.FreeLibrary(target)) { Win32Exception we = new Win32Exception(Kernel32.INSTANCE.GetLastError()); if (err != null) { - we.addSuppressed(err); + we.addSuppressedReflected(err); } throw we; } @@ -1013,7 +1013,7 @@ public boolean invoke(HMODULE module, Pointer type, Pointer name, Pointer lParam if (!Kernel32.INSTANCE.FreeLibrary(target)) { Win32Exception we = new Win32Exception(Kernel32.INSTANCE.GetLastError()); if (err != null) { - we.addSuppressed(err); + we.addSuppressedReflected(err); } throw we; } @@ -1075,7 +1075,7 @@ public static List getModules(int processID) { if (we == null) { we = e; } else { - we.addSuppressed(e); + we.addSuppressedReflected(e); } } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Win32Exception.java b/contrib/platform/src/com/sun/jna/platform/win32/Win32Exception.java index 934ab59ff0..dfc6f5d99f 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Win32Exception.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Win32Exception.java @@ -25,6 +25,10 @@ import com.sun.jna.LastErrorException; import com.sun.jna.platform.win32.WinNT.HRESULT; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Win32 exception. @@ -69,4 +73,31 @@ protected Win32Exception(int code, HRESULT hr, String msg) { super(code, msg); _hr = hr; } + + private static Method addSuppressedMethod = null; + static { + try { + addSuppressedMethod = Throwable.class.getMethod("addSuppressed", Throwable.class); + } catch (NoSuchMethodException ex) { + // This is the case for JDK < 7 + } catch (SecurityException ex) { + Logger.getLogger(Win32Exception.class.getName()).log(Level.SEVERE, "Failed to initialize 'addSuppressed' method", ex); + } + } + + void addSuppressedReflected(Throwable exception) { + if(addSuppressedMethod == null) { + // Make this a NOOP on an unsupported JDK + return; + } + try { + addSuppressedMethod.invoke(this, exception); + } catch (IllegalAccessException ex) { + throw new RuntimeException("Failed to call addSuppressedMethod", ex); + } catch (IllegalArgumentException ex) { + throw new RuntimeException("Failed to call addSuppressedMethod", ex); + } catch (InvocationTargetException ex) { + throw new RuntimeException("Failed to call addSuppressedMethod", ex); + } + } } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WininetUtil.java b/contrib/platform/src/com/sun/jna/platform/win32/WininetUtil.java index 4c64fa8e58..7da5969d10 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/WininetUtil.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/WininetUtil.java @@ -123,7 +123,7 @@ public static Map getCache() { if (!Wininet.INSTANCE.FindCloseUrlCache(cacheHandle)) { if (we != null) { Win32Exception e = new Win32Exception(Native.getLastError()); - e.addSuppressed(we); + e.addSuppressedReflected(we); we = e; } } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WinspoolUtil.java b/contrib/platform/src/com/sun/jna/platform/win32/WinspoolUtil.java index ac6e5ce811..e5a0bdc7f1 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/WinspoolUtil.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/WinspoolUtil.java @@ -118,7 +118,7 @@ public static PRINTER_INFO_2 getPrinterInfo2(String printerName) { if (!Winspool.INSTANCE.ClosePrinter(pHandle.getValue())) { Win32Exception ex = new Win32Exception(Kernel32.INSTANCE.GetLastError()); if (we != null) { - ex.addSuppressed(we); + ex.addSuppressedReflected(we); } } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java index 7870c9e263..6c35edcb53 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java @@ -1552,7 +1552,7 @@ public void testModule32FirstW() { if (we == null) { we = e; } else { - we.addSuppressed(e); + we.addSuppressedReflected(e); } } @@ -1589,7 +1589,7 @@ public void testModule32NextW() { if (we == null) { we = e; } else { - we.addSuppressed(e); + we.addSuppressedReflected(e); } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java b/contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java index 635e1f4749..22e1d0cf39 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/PsapiTest.java @@ -135,7 +135,7 @@ public void testEnumProcessModules() { if (we == null) { we = e; } else { - we.addSuppressed(e); + we.addSuppressedReflected(e); } } if (we != null) { @@ -186,7 +186,7 @@ public void testGetModuleInformation() { if (we == null) { we = e; } else { - we.addSuppressed(e); + we.addSuppressedReflected(e); } } if (we != null) { @@ -218,7 +218,7 @@ public void testGetProcessImageFileName() { if (we == null) { we = e; } else { - we.addSuppressed(e); + we.addSuppressedReflected(e); } } if (we != null) { diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Win32ExceptionTest.java b/contrib/platform/test/com/sun/jna/platform/win32/Win32ExceptionTest.java index 863f803dae..d703b18372 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Win32ExceptionTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Win32ExceptionTest.java @@ -15,6 +15,7 @@ import com.sun.jna.LastErrorException; import junit.framework.TestCase; +import org.junit.Assume; /** * @author dblock[at]dblock[dot]org @@ -50,8 +51,27 @@ public void testFormatMessageFromHR() { } } + public void testAddSuppressed() { + Assume.assumeTrue(isMethodPresent(Win32Exception.class, "addSuppressed", Throwable.class)); + + Win32Exception demoException = new Win32Exception(WinError.E_FAIL); + demoException.addSuppressed(new RuntimeException("Demo")); + + assertEquals(1, demoException.getSuppressed().length); + assertEquals("Demo", demoException.getSuppressed()[0].getMessage()); + } + private void assertLastErrorValue(LastErrorException e, int code, String msg) { assertEquals("Mismatched error code", code, e.getErrorCode()); assertEquals("Mismatched error message", msg, e.getMessage()); } + + private boolean isMethodPresent(Class baseClass, String methodName, Class... parameters) throws SecurityException { + try { + baseClass.getMethod(methodName, parameters); + return true; + } catch (NoSuchMethodException ex) { + return false; + } + } } diff --git a/test/com/sun/jna/ELFAnalyserTest.java b/test/com/sun/jna/ELFAnalyserTest.java index b1ce5c6c00..fb65a21c92 100644 --- a/test/com/sun/jna/ELFAnalyserTest.java +++ b/test/com/sun/jna/ELFAnalyserTest.java @@ -2,9 +2,6 @@ package com.sun.jna; import java.io.*; -import java.nio.file.CopyOption; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.junit.AfterClass; @@ -15,14 +12,13 @@ public class ELFAnalyserTest { - - private static File testResources = new File("build/test-resources"); - private static File win32Lib = new File(testResources, "win32-x86-64.dll"); - private static File linuxArmelLib = new File(testResources, "linux-armel.so"); - private static File linuxArmelNoflagLib = new File(testResources, "linux-armel-noflag.so"); - private static File linuxArmhfLib = new File(testResources, "linux-armhf.so"); - private static File linuxAmd64Lib = new File(testResources, "linux-amd64.so"); + private static final File TEST_RESOURCES = new File("build/test-resources"); + private static final File WIN32_LIB = new File(TEST_RESOURCES, "win32-x86-64.dll"); + private static final File LINUX_ARMEL_LIB = new File(TEST_RESOURCES, "linux-armel.so"); + private static final File LINUX_ARMEL_NOFLAG_LIG = new File(TEST_RESOURCES, "linux-armel-noflag.so"); + private static final File LINUX_ARMHF_LIB = new File(TEST_RESOURCES, "linux-armhf.so"); + private static final File LINUX_AMD64_LIB = new File(TEST_RESOURCES, "linux-amd64.so"); @BeforeClass public static void initClass() throws IOException { @@ -31,24 +27,24 @@ public static void initClass() throws IOException { File linuxArmhfZip = new File("dist/linux-arm.jar"); File linuxAmd64Zip = new File("dist/linux-x86-64.jar"); - testResources.mkdirs(); + TEST_RESOURCES.mkdirs(); - extractFileFromZip(win32Zip, "jnidispatch.dll", win32Lib); - extractFileFromZip(linuxArmelZip, "libjnidispatch.so", linuxArmelLib); - extractFileFromZip(linuxArmhfZip, "libjnidispatch.so", linuxArmhfLib); - extractFileFromZip(linuxAmd64Zip, "libjnidispatch.so", linuxAmd64Lib); - makeLinuxArmelNoflagLib(linuxArmelLib, linuxArmelNoflagLib); + extractFileFromZip(win32Zip, "jnidispatch.dll", WIN32_LIB); + extractFileFromZip(linuxArmelZip, "libjnidispatch.so", LINUX_ARMEL_LIB); + extractFileFromZip(linuxArmhfZip, "libjnidispatch.so", LINUX_ARMHF_LIB); + extractFileFromZip(linuxAmd64Zip, "libjnidispatch.so", LINUX_AMD64_LIB); + makeLinuxArmelNoflagLib(LINUX_ARMEL_LIB, LINUX_ARMEL_NOFLAG_LIG); } @Test public void testNonELF() throws IOException { - ELFAnalyser ahfd = ELFAnalyser.analyse(win32Lib.getAbsolutePath()); + ELFAnalyser ahfd = ELFAnalyser.analyse(WIN32_LIB.getAbsolutePath()); assertFalse(ahfd.isELF()); } @Test public void testNonArm() throws IOException { - ELFAnalyser ahfd = ELFAnalyser.analyse(linuxAmd64Lib.getAbsolutePath()); + ELFAnalyser ahfd = ELFAnalyser.analyse(LINUX_AMD64_LIB.getAbsolutePath()); assertTrue(ahfd.isELF()); assertFalse(ahfd.isArm()); assertTrue(ahfd.is64Bit()); @@ -56,7 +52,7 @@ public void testNonArm() throws IOException { @Test public void testArmhf() throws IOException { - ELFAnalyser ahfd = ELFAnalyser.analyse(linuxArmhfLib.getAbsolutePath()); + ELFAnalyser ahfd = ELFAnalyser.analyse(LINUX_ARMHF_LIB.getAbsolutePath()); assertTrue(ahfd.isELF()); assertTrue(ahfd.isArm()); assertFalse(ahfd.is64Bit()); @@ -66,7 +62,7 @@ public void testArmhf() throws IOException { @Test public void testArmel() throws IOException { - ELFAnalyser ahfd = ELFAnalyser.analyse(linuxArmelLib.getAbsolutePath()); + ELFAnalyser ahfd = ELFAnalyser.analyse(LINUX_ARMEL_LIB.getAbsolutePath()); assertTrue(ahfd.isELF()); assertTrue(ahfd.isArm()); assertFalse(ahfd.is64Bit()); @@ -76,7 +72,7 @@ public void testArmel() throws IOException { @Test public void testArmelNoflag() throws IOException { - ELFAnalyser ahfd = ELFAnalyser.analyse(linuxArmelNoflagLib.getAbsolutePath()); + ELFAnalyser ahfd = ELFAnalyser.analyse(LINUX_ARMEL_NOFLAG_LIG.getAbsolutePath()); assertTrue(ahfd.isELF()); assertTrue(ahfd.isArm()); assertFalse(ahfd.is64Bit()); @@ -86,12 +82,12 @@ public void testArmelNoflag() throws IOException { @AfterClass public static void afterClass() throws IOException { - linuxAmd64Lib.delete(); - linuxArmhfLib.delete(); - linuxArmelLib.delete(); - win32Lib.delete(); - linuxArmelNoflagLib.delete(); - testResources.delete(); + LINUX_AMD64_LIB.delete(); + LINUX_ARMHF_LIB.delete(); + LINUX_ARMEL_LIB.delete(); + WIN32_LIB.delete(); + LINUX_ARMEL_NOFLAG_LIG.delete(); + TEST_RESOURCES.delete(); } private static void extractFileFromZip(File zipTarget, String zipEntryName, File outputFile) throws IOException { @@ -104,11 +100,7 @@ private static void extractFileFromZip(File zipTarget, String zipEntryName, File InputStream is = zip.getInputStream(entry); // Implicitly closed by closing ZipFile OutputStream os = new FileOutputStream(outputFile); try { - int read; - byte[] buffer = new byte[1024 * 1024]; - while((read = is.read(buffer)) > 0) { - os.write(buffer, 0, read); - } + copyStream(is, os); } finally { os.close(); } @@ -121,7 +113,8 @@ private static void extractFileFromZip(File zipTarget, String zipEntryName, File // The procedure call standard is coded on the second byte. private static void makeLinuxArmelNoflagLib(File sourceFile, File outputFile) throws IOException { final int POS_ABI_FLOAT_BIT = (byte) 0x25; - Files.copy(sourceFile.toPath(), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + copyFile(sourceFile, outputFile); + RandomAccessFile out = new RandomAccessFile(outputFile, "rw"); out.seek(POS_ABI_FLOAT_BIT); @@ -129,5 +122,35 @@ private static void makeLinuxArmelNoflagLib(File sourceFile, File outputFile) th out.close(); } + + private static void copyFile(File sourceFile, File outputFile) throws IOException { + InputStream inputStream = null; + OutputStream outputStream = null; + try { + inputStream = new FileInputStream(sourceFile); + outputStream = new FileOutputStream(outputFile); + copyStream(inputStream, outputStream); + } finally { + closeSilently(inputStream); + closeSilently(outputStream); + } + } + + private static void copyStream(InputStream is, OutputStream os) throws IOException { + int read; + byte[] buffer = new byte[1024 * 1024]; + while ((read = is.read(buffer)) > 0) { + os.write(buffer, 0, read); + } + } + + private static void closeSilently(Closeable closeable) { + if(closeable == null) { + return; + } + try { + closeable.close(); + } catch (IOException ex) {} + } }