diff --git a/src/main/java/com/eclipsesource/v8/LibraryLoader.java b/src/main/java/com/eclipsesource/v8/LibraryLoader.java index 4fbf7405c..37d12df0e 100644 --- a/src/main/java/com/eclipsesource/v8/LibraryLoader.java +++ b/src/main/java/com/eclipsesource/v8/LibraryLoader.java @@ -7,7 +7,6 @@ * * Contributors: * EclipseSource - initial API and implementation - * Wolfgang Steiner - code separation PlatformDetector/LibraryLoader ******************************************************************************/ package com.eclipsesource.v8; @@ -28,86 +27,53 @@ class LibraryLoader { SEPARATOR = System.getProperty("file.separator"); //$NON-NLS-1$ } - /** - * Returns the base-name for the native J2V8 library file. - * @param withLinuxVendor include/exclude the {vendor} part from the returned filename - *

NOTE: Vendors are only included for linux systems

- * @return The filename string has the following structure: - *
{arch}-[vendor]-{operating_system}
- */ - public static String computeLibraryShortName(boolean withLinuxVendor) { - String prefix = "j2v8"; - String vendor = withLinuxVendor && PlatformDetector.OS.isLinux() ? PlatformDetector.Vendor.getName() : null; - String os = PlatformDetector.OS.getName(); - String arch = PlatformDetector.Arch.getName(); - - final String separator = "-"; - - return - prefix + - (vendor != null ? separator + vendor : "") + - separator + os + - separator + arch; + private static String computeLibraryShortName() { + String base = "j2v8"; + String osSuffix = getOS(); + String archSuffix = getArchSuffix(); + return base + "_" + osSuffix + "_" + archSuffix; } - public static String computeLibraryFullName(boolean withLinuxVendor) { - return "lib" + computeLibraryShortName(withLinuxVendor) + "." + PlatformDetector.OS.getLibFileExtension(); + private static String computeLibraryFullName() { + return "lib" + computeLibraryShortName() + "." + getOSFileExtension(); } - static boolean tryLoad(boolean withLinuxVendor, StringBuffer message) { - String libShortName = computeLibraryShortName(withLinuxVendor); - String libFullName = computeLibraryFullName(withLinuxVendor); - String ideLocation = System.getProperty("user.dir") + SEPARATOR + "jni" + SEPARATOR + libFullName; + static void loadLibrary(final String tempDirectory) { + if ( isAndroid() ) { + System.loadLibrary("j2v8"); + return; + } + StringBuffer message = new StringBuffer(); + String libShortName = computeLibraryShortName(); + String libFullName = computeLibraryFullName(); + String ideLocation = System.getProperty("user.dir") + SEPARATOR + "jni" + SEPARATOR + computeLibraryFullName(); + + String path = null; /* Try loading library from java library path */ if (load(libFullName, message)) { - return true; + return; } if (load(libShortName, message)) { - return true; + return; } /* Try loading library from the IDE location */ if (new File(ideLocation).exists()) { if (load(ideLocation, message)) { - return true; + return; } } - return false; - } - - static void loadLibrary(final String tempDirectory) { - if (PlatformDetector.OS.isAndroid()) { - System.loadLibrary("j2v8"); - return; - } - - StringBuffer message = new StringBuffer(); - - // try loading a vendor-specific library first - if (tryLoad(true, message)) - return; - - // if there is no vendor-specific library, just try to load the default OS library - if (tryLoad(false, message)) - return; - - String path = null; - if (tempDirectory != null) { path = tempDirectory; } else { path = System.getProperty("java.io.tmpdir"); //$NON-NLS-1$ } - // try extracting a vendor-specific library first - if (extract(path, true, message)) - return; - - // if there is no vendor-specific library, just try to extract the default OS library - if (extract(path, false, message)) + if (extract(path + SEPARATOR + libFullName, libFullName, message)) { return; + } /* Failed to find the library */ throw new UnsatisfiedLinkError("Could not load J2V8 library. Reasons: " + message.toString()); //$NON-NLS-1$ @@ -132,11 +98,6 @@ static boolean load(final String libName, final StringBuffer message) { return false; } - static boolean extract(String libPath, boolean withLinuxVendor, StringBuffer message) { - String libFullName = computeLibraryFullName(withLinuxVendor); - return extract(libPath + SEPARATOR + libFullName, libFullName, message); - } - static boolean extract(final String fileName, final String mappedName, final StringBuffer message) { FileOutputStream os = null; InputStream is = null; @@ -183,7 +144,7 @@ static boolean extract(final String fileName, final String mappedName, final Str } static void chmod(final String permision, final String path) { - if (PlatformDetector.OS.isWindows()) { + if (isWindows()) { return; } try { @@ -191,4 +152,69 @@ static void chmod(final String permision, final String path) { } catch (Throwable e) { } } + + static String getOsName() { + return System.getProperty("os.name") + System.getProperty("java.specification.vendor"); + } + + static boolean isWindows() { + return getOsName().startsWith("Windows"); + } + + static boolean isMac() { + return getOsName().startsWith("Mac"); + } + + static boolean isLinux() { + return getOsName().startsWith("Linux"); + } + + static boolean isNativeClient() { + return getOsName().startsWith("nacl"); + } + + static boolean isAndroid() { + return getOsName().contains("Android"); + } + + static String getArchSuffix() { + String arch = System.getProperty("os.arch"); + if (arch.equals("i686")) { + return "x86"; + } else if (arch.equals("amd64")) { + return "x86_64"; + } else if (arch.equals("nacl")) { + return "armv7l"; + } else if (arch.equals("aarch64")) { + return "armv7l"; + } + return arch; + } + + static String getOSFileExtension() { + if (isWindows()) { + return "dll"; + } else if (isMac()) { + return "dylib"; + } else if (isLinux()) { + return "so"; + } else if (isNativeClient()) { + return "so"; + } + throw new UnsatisfiedLinkError("Unsupported platform: " + getOsName()); + } + + static String getOS() { + if (isWindows()) { + return "win32"; + } else if (isMac()) { + return "macosx"; + } else if (isLinux() && !isAndroid()) { + return "linux"; + } else if (isAndroid()) { + return "android"; + } + throw new UnsatisfiedLinkError("Unsupported platform: " + getOsName()); + } + } diff --git a/src/main/java/com/eclipsesource/v8/V8.java b/src/main/java/com/eclipsesource/v8/V8.java index fea065bb1..c0881e1a3 100644 --- a/src/main/java/com/eclipsesource/v8/V8.java +++ b/src/main/java/com/eclipsesource/v8/V8.java @@ -249,16 +249,12 @@ private void notifyReferenceDisposed(final V8Value object) { private static void checkNativeLibraryLoaded() { if (!nativeLibraryLoaded) { - String vendorName = LibraryLoader.computeLibraryShortName(true); - String baseName = LibraryLoader.computeLibraryShortName(false); - String message = "J2V8 native library not loaded (" + baseName + "/" + vendorName + ")"; - if (nativeLoadError != null) { - throw new IllegalStateException(message, nativeLoadError); + throw new IllegalStateException("J2V8 native library not loaded", nativeLoadError); } else if (nativeLoadException != null) { - throw new IllegalStateException(message, nativeLoadException); + throw new IllegalStateException("J2V8 native library not loaded", nativeLoadException); } else { - throw new IllegalStateException(message); + throw new IllegalStateException("J2V8 native library not loaded"); } } } @@ -1567,19 +1563,6 @@ protected void releaseMethodDescriptor(final long v8RuntimePtr, final long metho private native static boolean _isRunning(final long v8RuntimePtr); - private native static boolean _isNodeCompatible(); - - public static boolean isNodeCompatible() { - if (!nativeLibraryLoaded) { - synchronized (lock) { - if (!nativeLibraryLoaded) { - load(null); - } - } - } - return _isNodeCompatible(); - } - void addObjRef(final V8Value reference) { objectReferences++; if (!referenceHandlers.isEmpty()) { diff --git a/src/test/java/com/eclipsesource/v8/LibraryLoaderTest.java b/src/test/java/com/eclipsesource/v8/LibraryLoaderTest.java index 5ae5aa801..6b6daed06 100644 --- a/src/test/java/com/eclipsesource/v8/LibraryLoaderTest.java +++ b/src/test/java/com/eclipsesource/v8/LibraryLoaderTest.java @@ -7,21 +7,10 @@ * * Contributors: * EclipseSource - initial API and implementation - * Wolfgang Steiner - code separation PlatformDetector/LibraryLoader ******************************************************************************/ package com.eclipsesource.v8; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.File; -import java.io.PrintWriter; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -import java.util.HashMap; import org.junit.After; import org.junit.Before; @@ -33,136 +22,104 @@ public class LibraryLoaderTest { private String vendor; private String arch; - private Field releaseFilesField; - private String[] releaseFiles; - - static void makeFinalStaticAccessible(Field field) { - field.setAccessible(true); - - try { - // on certain JVMs this is not present and will throw the exceptions below (e.g. the Android Dalvik VM) - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - } - catch (NoSuchFieldException e) {} - catch (IllegalAccessException e) {} - } - @Before - public void setup() throws Exception { + public void setup() { osName = System.getProperty("os.name"); vendor = System.getProperty("java.specification.vendor"); arch = System.getProperty("os.arch"); - - Class vendorClass = PlatformDetector.Vendor.class; - releaseFilesField = vendorClass.getDeclaredField("LINUX_OS_RELEASE_FILES"); - makeFinalStaticAccessible(releaseFilesField); - - releaseFiles = (String[])releaseFilesField.get(null); } @After - public void tearDown() throws Exception { + public void tearDown() { System.setProperty("os.name", osName); System.setProperty("java.specification.vendor", vendor); System.setProperty("os.arch", arch); - - releaseFilesField.set(null, releaseFiles); } @Test - public void testAndroidLibNameStructure() throws Exception { - System.setProperty("os.name", "Android"); - System.setProperty("java.specification.vendor", "..."); - System.setProperty("os.arch", "x64"); + public void testGetOSMac() { + System.setProperty("os.name", "Mac OS X"); - performTests(Platform.ANDROID, null, ".so"); + assertEquals("macosx", LibraryLoader.getOS()); + } - System.setProperty("os.name", "..."); - System.setProperty("java.specification.vendor", "Android"); - System.setProperty("os.arch", "x64"); + @Test + public void testGetOSLinux() { + System.setProperty("os.name", "Linux"); - performTests(Platform.ANDROID, null, ".so"); + assertEquals("linux", LibraryLoader.getOS()); } @Test - public void testLinuxLibNameStructure() throws Exception { + public void testGetOSWindows() { + System.setProperty("os.name", "Windows"); - // skip this test on android - if (PlatformDetector.OS.isAndroid()) - return; + assertEquals("win32", LibraryLoader.getOS()); + } + @Test + public void testGetOSAndroid() { System.setProperty("os.name", "Linux"); - System.setProperty("java.specification.vendor", "OSS"); - System.setProperty("os.arch", "x64"); + System.setProperty("java.specification.vendor", "The Android Project"); + + assertEquals("android", LibraryLoader.getOS()); + } - final String os_release_test_path = "./test-mockup-os-release"; - final String test_vendor = "linux_vendor"; + @Test + public void testGetOSFileExtensionNativeClient() { + System.setProperty("os.name", "naclthe android project"); + System.setProperty("java.specification.vendor", "The Android Project"); - // mock /etc/os-release file - releaseFilesField.set(null, new String[] { os_release_test_path }); + assertEquals("so", LibraryLoader.getOSFileExtension()); + } - PrintWriter out = new PrintWriter(os_release_test_path); - out.println( - "NAME=The-Linux-Vendor\n" + - "VERSION=\"towel_42\"\n" + - "ID=" + test_vendor + "\n" + - "VERSION_ID=42\n" - ); - out.close(); + @Test + public void testGetArchxNaCl() { + System.setProperty("os.arch", "nacl"); - performTests(Platform.LINUX, test_vendor, ".so"); + assertEquals("armv7l", LibraryLoader.getArchSuffix()); + } + + @Test + public void testGetArchaarch64() { + System.setProperty("os.arch", "aarch64"); + + assertEquals("armv7l", LibraryLoader.getArchSuffix()); } @Test - public void testMacOSXLibNameStructure() throws Exception { - System.setProperty("os.name", "MacOSX"); - System.setProperty("java.specification.vendor", "Apple"); - System.setProperty("os.arch", "x64"); + public void testGetArchx86() { + System.setProperty("os.arch", "x86"); - performTests(Platform.MACOSX, null, ".dylib"); + assertEquals("x86", LibraryLoader.getArchSuffix()); } @Test - public void testWindowsLibNameStructure() throws Exception { - System.setProperty("os.name", "Windows"); - System.setProperty("java.specification.vendor", "Microsoft"); - System.setProperty("os.arch", "x64"); + public void testGetArchx86_64() { + System.setProperty("os.arch", "x86_64"); + + assertEquals("x86_64", LibraryLoader.getArchSuffix()); + } - performTests(Platform.WINDOWS, null, ".dll"); + @Test + public void testGetArchx64FromAmd64() { + System.setProperty("os.arch", "amd64"); + + assertEquals("x86_64", LibraryLoader.getArchSuffix()); } - private void performTests(String expectedOsName, String expectedVendor, String expectedLibExtension) { - // API calls - String libName = LibraryLoader.computeLibraryShortName(true); - String[] parts = libName.split("-"); - - // test assertions - int i = 0; - int expectedParts = expectedVendor != null ? 4 : 3; - assertEquals(expectedParts, parts.length); - assertEquals("j2v8", parts[i++]); - if (expectedVendor != null) - assertEquals(expectedVendor, parts[i++]); - assertEquals(expectedOsName, parts[i++]); - assertEquals("x86_64", parts[i++]); - - // API calls - libName = LibraryLoader.computeLibraryShortName(false); - parts = libName.split("-"); - - // test assertions - assertEquals(3, parts.length); - assertEquals("j2v8", parts[0]); - assertEquals(expectedOsName, parts[1]); - assertEquals("x86_64", parts[2]); - - // API calls - libName = LibraryLoader.computeLibraryFullName(false); - - // test assertions - assertTrue(libName.startsWith("libj2v8")); - assertTrue(libName.endsWith(expectedLibExtension)); + @Test + public void testGetArcharmv7l() { + System.setProperty("os.arch", "armv7l"); + + assertEquals("armv7l", LibraryLoader.getArchSuffix()); } + + @Test + public void test686isX86() { + System.setProperty("os.arch", "i686"); + + assertEquals("x86", LibraryLoader.getArchSuffix()); + } + } diff --git a/src/test/java/com/eclipsesource/v8/NodeJSTest.java b/src/test/java/com/eclipsesource/v8/NodeJSTest.java index a6ccef1e1..3d2aa95c7 100644 --- a/src/test/java/com/eclipsesource/v8/NodeJSTest.java +++ b/src/test/java/com/eclipsesource/v8/NodeJSTest.java @@ -13,7 +13,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeFalse; import java.io.File; import java.io.IOException; @@ -21,7 +20,6 @@ import org.junit.After; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; public class NodeJSTest { @@ -30,35 +28,21 @@ public class NodeJSTest { @Before public void setup() { - if (skipTest()) - return; - nodeJS = NodeJS.createNodeJS(); } @After public void tearDown() { - if (skipTest()) - return; - nodeJS.release(); } - private static boolean skipTest() { - return !V8.isNodeCompatible(); - } - - private final static String skipMessage = "Skipped test (Node.js features not included in native library)"; - @Test public void testCreateNodeJS() { - assumeFalse(skipMessage, skipTest()); // conditional skip assertNotNull(nodeJS); } @Test public void testSingleThreadAccess_Require() throws InterruptedException { - assumeFalse(skipMessage, skipTest()); // conditional skip final boolean[] result = new boolean[] { false }; Thread t = new Thread(new Runnable() { @Override @@ -80,7 +64,6 @@ public void run() { @Test public void testGetVersion() { - assumeFalse(skipMessage, skipTest()); // conditional skip String result = nodeJS.getNodeVersion(); assertEquals("7.4.0", result); @@ -88,7 +71,6 @@ public void testGetVersion() { @Test public void testSingleThreadAccess_HandleMessage() throws InterruptedException { - assumeFalse(skipMessage, skipTest()); // conditional skip final boolean[] result = new boolean[] { false }; Thread t = new Thread(new Runnable() { @Override @@ -108,7 +90,6 @@ public void run() { @Test public void testSingleThreadAccess_IsRunning() throws InterruptedException { - assumeFalse(skipMessage, skipTest()); // conditional skip final boolean[] result = new boolean[] { false }; Thread t = new Thread(new Runnable() { @Override @@ -128,7 +109,6 @@ public void run() { @Test public void testExecuteNodeScript_Startup() throws IOException { - assumeFalse(skipMessage, skipTest()); // conditional skip nodeJS.release(); File testScript = createTemporaryScriptFile("global.passed = true;", "testScript"); @@ -141,7 +121,6 @@ public void testExecuteNodeScript_Startup() throws IOException { @Test public void testExecNodeScript() throws IOException { - assumeFalse(skipMessage, skipTest()); // conditional skip nodeJS.release(); File testScript = createTemporaryScriptFile("global.passed = true;", "testScript"); @@ -155,7 +134,6 @@ public void testExecNodeScript() throws IOException { @Test public void testExecuteNodeScript_viaRequire() throws IOException { - assumeFalse(skipMessage, skipTest()); // conditional skip nodeJS.release(); File testScript = createTemporaryScriptFile("global.passed = true;", "testScript"); @@ -169,7 +147,6 @@ public void testExecuteNodeScript_viaRequire() throws IOException { @Test public void testExports() throws IOException { - assumeFalse(skipMessage, skipTest()); // conditional skip nodeJS.release(); File testScript = createTemporaryScriptFile("exports.foo=7", "testScript"); diff --git a/src/test/java/com/eclipsesource/v8/V8RuntimeNotLoadedTest.java b/src/test/java/com/eclipsesource/v8/V8RuntimeNotLoadedTest.java index 0dbbb8bff..cd40f5098 100644 --- a/src/test/java/com/eclipsesource/v8/V8RuntimeNotLoadedTest.java +++ b/src/test/java/com/eclipsesource/v8/V8RuntimeNotLoadedTest.java @@ -11,8 +11,6 @@ package com.eclipsesource.v8; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertEquals; -import static org.junit.Assume.assumeFalse; import java.lang.reflect.Field; import java.net.URLClassLoader; @@ -34,16 +32,6 @@ public class V8RuntimeNotLoadedTest { private static final String JAVA_LIBRARY_PATH = "java.library.path"; private String existingLibraryPath; - /** - * NOTE: we need to skip these tests, because on Android the SystemClassLoader - * can not be cast to an URLClassLoader and some other issues (see TestClassLoader below) - */ - private static boolean skipTest() { - return PlatformDetector.OS.isAndroid(); - } - - private final static String skipMessage = "Skipped test (not implemented for Android)"; - @Before public void before() throws Exception { existingLibraryPath = System.getProperty(JAVA_LIBRARY_PATH); @@ -57,34 +45,21 @@ public void after() throws Exception { @Test public void testJ2V8NotEnabled() { - assumeFalse(skipMessage, skipTest()); // conditional skip - assertFalse(V8.isLoaded()); } - @Test(expected = UnsatisfiedLinkError.class) + @Test(expected = IllegalStateException.class) public void testJ2V8CannotCreateRuntime() { - assumeFalse(skipMessage, skipTest()); // conditional skip - String oldValue = System.getProperty("os.arch"); System.setProperty("os.arch", "unknown"); try { V8.createV8Runtime(); - } - catch (UnsatisfiedLinkError ex) { - assertEquals("Unsupported arch: unknown", ex.getMessage()); - throw ex; - } - finally { + } finally { System.setProperty("os.arch", oldValue); } } private static void setLibraryPath(final String path) throws Exception { - // we need to skip here too, because "sys_paths" also does not exist on Android - if (skipTest()) - return; - System.setProperty(JAVA_LIBRARY_PATH, path); // set sys_paths to null so that java.library.path will be reevalueted next time it is needed @@ -101,9 +76,6 @@ public SeparateClassloaderTestRunner(final Class clazz) throws Initialization private static Class getFromTestClassloader(final Class clazz) throws InitializationError { try { - if (skipTest()) - return clazz; - ClassLoader testClassLoader = new TestClassLoader(); return Class.forName(clazz.getName(), true, testClassLoader); } catch (ClassNotFoundException e) { @@ -113,7 +85,6 @@ private static Class getFromTestClassloader(final Class clazz) throws Init public static class TestClassLoader extends URLClassLoader { public TestClassLoader() { - // TODO: this crashes on Android (see: https://stackoverflow.com/q/31920245) super(((URLClassLoader) getSystemClassLoader()).getURLs()); }