diff --git a/build.gradle b/build.gradle index 6371226..2c0b801 100755 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ task wrapper(type: Wrapper) { group = 'com.github.boukefalos' archivesBaseName = 'jlibloader' -version = '0.2' +version = '0.3' uploadArchives { repositories.mavenDeployer { diff --git a/src/main/java/com/github/boukefalos/jlibloader/Native.java b/src/main/java/com/github/boukefalos/jlibloader/Native.java index 3024162..ad237b3 100755 --- a/src/main/java/com/github/boukefalos/jlibloader/Native.java +++ b/src/main/java/com/github/boukefalos/jlibloader/Native.java @@ -1,29 +1,17 @@ -/* - * Copyright 2012 Adam Murdoch - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.github.boukefalos.jlibloader; import java.io.File; +import com.github.boukefalos.jlibloader.internal.NativeBinaryLoader; +import com.github.boukefalos.jlibloader.internal.NativeBinaryLocator; import com.github.boukefalos.jlibloader.internal.NativeLibraryLoader; import com.github.boukefalos.jlibloader.internal.NativeLibraryLocator; import com.github.boukefalos.jlibloader.internal.Platform; public class Native { - private static NativeLibraryLoader loader; + private static Platform platform; + private static NativeLibraryLoader libraryLoader; + private static NativeBinaryLoader binaryLoader; private Native() { } @@ -37,12 +25,13 @@ public class Native { * @throws NativeLibraryUnavailableException When the native library is not available on the current machine. * @throws NativeException On failure to load the native library. */ - static public void init(File extractDir) throws NativeLibraryUnavailableException, NativeException { + static public void init(File extractDir) throws NativeException { synchronized (Native.class) { - if (loader == null) { - Platform platform = Platform.current(); + if (platform == null) { + platform = Platform.current(); try { - loader = new NativeLibraryLoader(platform, new NativeLibraryLocator(extractDir)); + libraryLoader = new NativeLibraryLoader(platform, new NativeLibraryLocator(extractDir)); + binaryLoader = new NativeBinaryLoader(platform, new NativeBinaryLocator(extractDir)); } catch (NativeException e) { throw e; } catch (Throwable t) { @@ -59,12 +48,26 @@ public class Native { public static void load(String group, String name, String file) { init(null); try { - loader.load(group, name, Platform.current().getLibraryName(file)); + libraryLoader.load(group, name, Platform.current().getLibraryName(file)); } catch (NativeException e) { - throw e; + throw e; } catch (Throwable t) { - throw new NativeException("Failed to load native library."); - } - + throw new NativeException("Failed to load native library."); + } } + + public static void binary(String group, String name) { + binary(group, name, name); + } + + public static String binary(String group, String name, String file) { + init(null); + try { + return binaryLoader.load(group, name, Platform.current().getBinaryName(file)); + } catch (NativeException e) { + throw e; + } catch (Throwable t) { + throw new NativeException("Failed to load native binary."); + } + } } diff --git a/src/main/java/com/github/boukefalos/jlibloader/NativeBinaryUnavailableException.java b/src/main/java/com/github/boukefalos/jlibloader/NativeBinaryUnavailableException.java new file mode 100644 index 0000000..a264116 --- /dev/null +++ b/src/main/java/com/github/boukefalos/jlibloader/NativeBinaryUnavailableException.java @@ -0,0 +1,12 @@ +package com.github.boukefalos.jlibloader; + +/** + * Thrown when a given integration is not available for the current machine. + */ +public class NativeBinaryUnavailableException extends NativeException { + private static final long serialVersionUID = 1L; + + public NativeBinaryUnavailableException(String message) { + super(message); + } +} diff --git a/src/main/java/com/github/boukefalos/jlibloader/NativeException.java b/src/main/java/com/github/boukefalos/jlibloader/NativeException.java index bbedd62..c6c1717 100644 --- a/src/main/java/com/github/boukefalos/jlibloader/NativeException.java +++ b/src/main/java/com/github/boukefalos/jlibloader/NativeException.java @@ -1,19 +1,3 @@ -/* - * Copyright 2012 Adam Murdoch - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.github.boukefalos.jlibloader; public class NativeException extends RuntimeException { diff --git a/src/main/java/com/github/boukefalos/jlibloader/NativeLibraryUnavailableException.java b/src/main/java/com/github/boukefalos/jlibloader/NativeLibraryUnavailableException.java index b95281c..aad2622 100644 --- a/src/main/java/com/github/boukefalos/jlibloader/NativeLibraryUnavailableException.java +++ b/src/main/java/com/github/boukefalos/jlibloader/NativeLibraryUnavailableException.java @@ -1,19 +1,3 @@ -/* - * Copyright 2012 Adam Murdoch - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.github.boukefalos.jlibloader; /** diff --git a/src/main/java/com/github/boukefalos/jlibloader/internal/NativeBinaryLoader.java b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeBinaryLoader.java new file mode 100644 index 0000000..3660e83 --- /dev/null +++ b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeBinaryLoader.java @@ -0,0 +1,37 @@ +package com.github.boukefalos.jlibloader.internal; + +import java.io.File; +import java.util.HashMap; + +import com.github.boukefalos.jlibloader.NativeBinaryUnavailableException; +import com.github.boukefalos.jlibloader.NativeException; + +public class NativeBinaryLoader extends NativeLoader { + private HashMap binaryFileMap = new HashMap(); + private NativeBinaryLocator nativeBinaryLocator; + + public NativeBinaryLoader(Platform platform, NativeBinaryLocator nativeBinaryLocator) { + this.platform = platform; + this.nativeBinaryLocator = nativeBinaryLocator; + } + + public String load(String binaryGroupName, String binaryName, String binaryFileName) { + if (binaryFileMap.containsKey(binaryFileName)) { + return binaryFileMap.get(binaryFileName); + } + try { + File binFile = nativeBinaryLocator.find(new NativeDef(binaryGroupName, binaryName, binaryFileName, platform.getId())); + if (binFile == null) { + throw new NativeBinaryUnavailableException(String.format("Native binary '%s' is not available for %s.", binaryFileName, platform)); + } + String binaryPath = binFile.getAbsolutePath(); + binaryFileMap.put(binaryFileName, binaryPath); + return binaryPath; + } catch (NativeException e) { + throw e; + } catch (Throwable t) { + t.printStackTrace(); + throw new NativeException(String.format("Failed to load native library '%s' for %s.", binaryFileName, platform), t); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/boukefalos/jlibloader/internal/NativeBinaryLocator.java b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeBinaryLocator.java new file mode 100644 index 0000000..5205d0c --- /dev/null +++ b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeBinaryLocator.java @@ -0,0 +1,9 @@ +package com.github.boukefalos.jlibloader.internal; + +import java.io.File; + +public class NativeBinaryLocator extends NativeLocator { + public NativeBinaryLocator(File extractDir) { + super(extractDir); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/boukefalos/jlibloader/internal/LibraryDef.java b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeDef.java similarity index 79% rename from src/main/java/com/github/boukefalos/jlibloader/internal/LibraryDef.java rename to src/main/java/com/github/boukefalos/jlibloader/internal/NativeDef.java index 8b0db89..eb10a0d 100644 --- a/src/main/java/com/github/boukefalos/jlibloader/internal/LibraryDef.java +++ b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeDef.java @@ -1,12 +1,12 @@ package com.github.boukefalos.jlibloader.internal; -public class LibraryDef { +public class NativeDef { final String group; final String name; final String file; final String platform; - public LibraryDef(String group, String name, String file, String platform) { + public NativeDef(String group, String name, String file, String platform) { this.group = group; this.name = name; this.file = file; @@ -21,7 +21,7 @@ public class LibraryDef { if (obj == null || obj.getClass() != getClass()) { return false; } - LibraryDef other = (LibraryDef) obj; + NativeDef other = (NativeDef) obj; return name.equals(other.name) && platform.equals(other.platform); } @@ -33,4 +33,4 @@ public class LibraryDef { public String getGroupPath() { return group.replace(".", "/"); } -} +} \ No newline at end of file diff --git a/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLibraryLoader.java b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLibraryLoader.java index 2f7f6a2..9c2780a 100755 --- a/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLibraryLoader.java +++ b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLibraryLoader.java @@ -1,19 +1,3 @@ -/* - * Copyright 2012 Adam Murdoch - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.github.boukefalos.jlibloader.internal; import java.io.File; @@ -23,9 +7,8 @@ import java.util.Set; import com.github.boukefalos.jlibloader.NativeException; import com.github.boukefalos.jlibloader.NativeLibraryUnavailableException; -public class NativeLibraryLoader { - private final Set loaded = new HashSet(); - private final Platform platform; +public class NativeLibraryLoader extends NativeLoader { + private final Set loaded = new HashSet(); private final NativeLibraryLocator nativeLibraryLocator; public NativeLibraryLoader(Platform platform, NativeLibraryLocator nativeLibraryLocator) { @@ -38,7 +21,7 @@ public class NativeLibraryLoader { return; } try { - File libFile = nativeLibraryLocator.find(new LibraryDef(libraryGroupName, libraryName, libraryFileName, platform.getId())); + File libFile = nativeLibraryLocator.find(new NativeDef(libraryGroupName, libraryName, libraryFileName, platform.getId())); if (libFile == null) { throw new NativeLibraryUnavailableException(String.format("Native library '%s' is not available for %s.", libraryFileName, platform)); } @@ -51,4 +34,4 @@ public class NativeLibraryLoader { } loaded.add(libraryFileName); } -} +} \ No newline at end of file diff --git a/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLibraryLocator.java b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLibraryLocator.java index 5db6ddb..8c82a86 100755 --- a/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLibraryLocator.java +++ b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLibraryLocator.java @@ -1,118 +1,30 @@ -/* - * Copyright 2012 Adam Murdoch - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.github.boukefalos.jlibloader.internal; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.net.URL; - -import com.github.boukefalos.jlibloader.NativeException; - -public class NativeLibraryLocator { - private final File extractDir; +public class NativeLibraryLocator extends NativeLocator { public NativeLibraryLocator(File extractDir) { - this.extractDir = extractDir; - } + super(extractDir); + } - public File find(LibraryDef libraryDef) throws IOException { - String resourceName = String.format("%s/%s/%s/%s", libraryDef.getGroupPath(), libraryDef.name, libraryDef.platform, libraryDef.file); - if (extractDir != null) { - File libFile = new File(extractDir, String.format("%s/%s", libraryDef.platform, libraryDef.file)); - File lockFile = new File(libFile.getParentFile(), libFile.getName() + ".lock"); - lockFile.getParentFile().mkdirs(); - lockFile.createNewFile(); - RandomAccessFile lockFileAccess = new RandomAccessFile(lockFile, "rw"); - try { - if (lockFile.length() > 0 && lockFileAccess.readBoolean()) { - // Library has been extracted - return libFile; - } - URL resource = getClass().getClassLoader().getResource(resourceName); - if (resource != null) { - // Extract library and write marker to lock file - libFile.getParentFile().mkdirs(); - copy(resource, libFile); - lockFileAccess.seek(0); - lockFileAccess.writeBoolean(true); - return libFile; - } - } finally { - // Also releases lock - lockFileAccess.close(); - } - } else { - URL resource = getClass().getClassLoader().getResource(resourceName); - if (resource != null) { - File libFile; - File libDir = File.createTempFile(libraryDef.file, "dir"); - libDir.delete(); - libDir.mkdirs(); - libFile = new File(libDir, libraryDef.file); - libFile.deleteOnExit(); - libDir.deleteOnExit(); - copy(resource, libFile); - return libFile; - } - } - - String componentName = libraryDef.file.replaceFirst("^lib", "").replaceFirst("\\.\\w+$", ""); - int pos = componentName.indexOf("-"); - while (pos >= 0) { - componentName = componentName.substring(0, pos) + Character.toUpperCase(componentName.charAt(pos + 1)) + componentName.substring(pos + 2); - pos = componentName.indexOf("-", pos); - } - File libFile = new File(String.format("build/binaries/%sSharedLibrary/%s/%s", componentName, libraryDef.platform.replace("-", "_"), libraryDef.file)); - if (libFile.isFile()) { - return libFile; - } - libFile = new File(String.format("build/binaries/mainSharedLibrary/%s/%s", libraryDef.platform.replace("-", "_"), libraryDef.file)); - if (libFile.isFile()) { - return libFile; - } + public File find(NativeDef nativeDef) throws IOException { + if (super.find(nativeDef) == null) { + String componentName = nativeDef.file.replaceFirst("^lib", "").replaceFirst("\\.\\w+$", ""); + int pos = componentName.indexOf("-"); + while (pos >= 0) { + componentName = componentName.substring(0, pos) + Character.toUpperCase(componentName.charAt(pos + 1)) + componentName.substring(pos + 2); + pos = componentName.indexOf("-", pos); + } + File libFile = new File(String.format("build/binaries/%sSharedLibrary/%s/%s", componentName, nativeDef.platform.replace("-", "_"), nativeDef.file)); + if (libFile.isFile()) { + return libFile; + } + libFile = new File(String.format("build/binaries/mainSharedLibrary/%s/%s", nativeDef.platform.replace("-", "_"), nativeDef.file)); + if (libFile.isFile()) { + return libFile; + } + } return null; } - - private static void copy(URL source, File dest) { - try { - InputStream inputStream = source.openStream(); - try { - OutputStream outputStream = new FileOutputStream(dest); - try { - byte[] buffer = new byte[4096]; - while (true) { - int nread = inputStream.read(buffer); - if (nread < 0) { - break; - } - outputStream.write(buffer, 0, nread); - } - } finally { - outputStream.close(); - } - } finally { - inputStream.close(); - } - } catch (IOException e) { - throw new NativeException(String.format("Could not extract native JNI library."), e); - } - } } \ No newline at end of file diff --git a/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLoader.java b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLoader.java new file mode 100644 index 0000000..f562449 --- /dev/null +++ b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLoader.java @@ -0,0 +1,5 @@ +package com.github.boukefalos.jlibloader.internal; + +public abstract class NativeLoader { + protected Platform platform; +} \ No newline at end of file diff --git a/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLocator.java b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLocator.java new file mode 100644 index 0000000..5c11504 --- /dev/null +++ b/src/main/java/com/github/boukefalos/jlibloader/internal/NativeLocator.java @@ -0,0 +1,87 @@ +package com.github.boukefalos.jlibloader.internal; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.net.URL; + +import com.github.boukefalos.jlibloader.NativeException; + +public class NativeLocator { + protected final File extractDir; + + public NativeLocator(File extractDir) { + this.extractDir = extractDir; + } + + public File find(NativeDef nativeDef) throws IOException { + String resourceName = String.format("%s/%s/%s/%s", nativeDef.getGroupPath(), nativeDef.name, nativeDef.platform, nativeDef.file); + if (extractDir != null) { + File libFile = new File(extractDir, String.format("%s/%s", nativeDef.platform, nativeDef.file)); + File lockFile = new File(libFile.getParentFile(), libFile.getName() + ".lock"); + lockFile.getParentFile().mkdirs(); + lockFile.createNewFile(); + RandomAccessFile lockFileAccess = new RandomAccessFile(lockFile, "rw"); + try { + if (lockFile.length() > 0 && lockFileAccess.readBoolean()) { + // Library has been extracted + return libFile; + } + URL resource = getClass().getClassLoader().getResource(resourceName); + if (resource != null) { + // Extract library and write marker to lock file + libFile.getParentFile().mkdirs(); + copy(resource, libFile); + lockFileAccess.seek(0); + lockFileAccess.writeBoolean(true); + return libFile; + } + } finally { + // Also releases lock + lockFileAccess.close(); + } + } else { + URL resource = getClass().getClassLoader().getResource(resourceName); + if (resource != null) { + File libFile; + File libDir = File.createTempFile(nativeDef.file, "dir"); + libDir.delete(); + libDir.mkdirs(); + libFile = new File(libDir, nativeDef.file); + libFile.deleteOnExit(); + libDir.deleteOnExit(); + copy(resource, libFile); + return libFile; + } + } + return null; + } + + protected static void copy(URL source, File dest) { + try { + InputStream inputStream = source.openStream(); + try { + OutputStream outputStream = new FileOutputStream(dest); + try { + byte[] buffer = new byte[4096]; + while (true) { + int nread = inputStream.read(buffer); + if (nread < 0) { + break; + } + outputStream.write(buffer, 0, nread); + } + } finally { + outputStream.close(); + } + } finally { + inputStream.close(); + } + } catch (IOException e) { + throw new NativeException(String.format("Could not extract native file."), e); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/boukefalos/jlibloader/internal/Platform.java b/src/main/java/com/github/boukefalos/jlibloader/internal/Platform.java index aab365f..41c59bc 100644 --- a/src/main/java/com/github/boukefalos/jlibloader/internal/Platform.java +++ b/src/main/java/com/github/boukefalos/jlibloader/internal/Platform.java @@ -16,6 +16,7 @@ package com.github.boukefalos.jlibloader.internal; +import com.github.boukefalos.jlibloader.NativeBinaryUnavailableException; import com.github.boukefalos.jlibloader.NativeLibraryUnavailableException; public abstract class Platform { @@ -64,7 +65,7 @@ public abstract class Platform { } } - public boolean isWindows() { + public boolean isWindows() { return false; } @@ -77,6 +78,10 @@ public abstract class Platform { throw new NativeLibraryUnavailableException(String.format("Native library is not available for %s.", toString())); } + public String getBinaryName(String name) { + throw new NativeBinaryUnavailableException(String.format("Native binary is not available for %s.", toString())); + } + public abstract String getId(); private static String getOperatingSystem() { @@ -97,6 +102,11 @@ public abstract class Platform { public String getLibraryName(String name) { return String.format("%s.dll", name); } + + @Override + public String getBinaryName(String name) { + return String.format("%s.exe", name); + } } private static class Window32Bit extends Windows {