diff --git a/src/main/java/net/rubygrapefruit/platform/Native.java b/src/main/java/net/rubygrapefruit/platform/Native.java index e9d9011..c49d6ae 100755 --- a/src/main/java/net/rubygrapefruit/platform/Native.java +++ b/src/main/java/net/rubygrapefruit/platform/Native.java @@ -1,96 +1,96 @@ -/* - * 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 net.rubygrapefruit.platform; - -import net.rubygrapefruit.platform.internal.NativeLibraryLoader; -import net.rubygrapefruit.platform.internal.NativeLibraryLocator; -import net.rubygrapefruit.platform.internal.Platform; -import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -/** - * Provides access to the native integrations. Use {@link #get(Class)} to load a particular integration. - */ -@ThreadSafe -public class Native { - private static NativeLibraryLoader loader; - private static final Map, Object> integrations = new HashMap, Object>(); - - private Native() { - } - - /** - * Initialises the native integration, if not already initialized. - * - * @param extractDir The directory to extract native resources into. May be null, in which case a default is - * selected. - * - * @throws NativeIntegrationUnavailableException When native integration is not available on the current machine. - * @throws NativeException On failure to load the native integration. - */ - @ThreadSafe - static public void init(File extractDir) throws NativeIntegrationUnavailableException, NativeException { - synchronized (Native.class) { - if (loader == null) { - Platform platform = Platform.current(); - try { - loader = new NativeLibraryLoader(new NativeLibraryLocator(extractDir)); - loader.load(platform.getLibraryName()); - int nativeVersion = NativeLibraryFunctions.getVersion(); - if (nativeVersion != NativeLibraryFunctions.VERSION) { - throw new NativeException(String.format("Unexpected native library version loaded. Expected %s, was %s.", NativeLibraryFunctions.VERSION, nativeVersion)); - } - } catch (NativeException e) { - throw e; - } catch (Throwable t) { - throw new NativeException("Failed to initialise native integration.", t); - } - } - } - } - - /** - * Locates a native integration of the given type. - * - * @return The native integration. Never returns null. - * @throws NativeIntegrationUnavailableException When the given native integration is not available on the current - * machine. - * @throws NativeException On failure to load the native integration. - */ - @ThreadSafe - public static T get(Class type) - throws NativeIntegrationUnavailableException, NativeException { - init(null); - synchronized (Native.class) { - Object instance = integrations.get(type); - if (instance == null) { - try { - instance = Platform.current().get(type, loader); - } catch (NativeException e) { - throw e; - } catch (Throwable t) { - throw new NativeException(String.format("Failed to load native integration %s.", type.getSimpleName()), t); - } - integrations.put(type, instance); - } - return type.cast(instance); - } - } -} +/* + * 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 net.rubygrapefruit.platform; + +import net.rubygrapefruit.platform.internal.NativeLibraryLoader; +import net.rubygrapefruit.platform.internal.NativeLibraryLocator; +import net.rubygrapefruit.platform.internal.Platform; +import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +/** + * Provides access to the native integrations. Use {@link #get(Class)} to load a particular integration. + */ +@ThreadSafe +public class Native { + private static NativeLibraryLoader loader; + private static final Map, Object> integrations = new HashMap, Object>(); + + private Native() { + } + + /** + * Initialises the native integration, if not already initialized. + * + * @param extractDir The directory to extract native resources into. May be null, in which case a default is + * selected. + * + * @throws NativeIntegrationUnavailableException When native integration is not available on the current machine. + * @throws NativeException On failure to load the native integration. + */ + @ThreadSafe + static public void init(File extractDir) throws NativeIntegrationUnavailableException, NativeException { + synchronized (Native.class) { + if (loader == null) { + Platform platform = Platform.current(); + try { + loader = new NativeLibraryLoader(platform, new NativeLibraryLocator(extractDir)); + loader.load(platform.getLibraryName()); + int nativeVersion = NativeLibraryFunctions.getVersion(); + if (nativeVersion != NativeLibraryFunctions.VERSION) { + throw new NativeException(String.format("Unexpected native library version loaded. Expected %s, was %s.", NativeLibraryFunctions.VERSION, nativeVersion)); + } + } catch (NativeException e) { + throw e; + } catch (Throwable t) { + throw new NativeException("Failed to initialise native integration.", t); + } + } + } + } + + /** + * Locates a native integration of the given type. + * + * @return The native integration. Never returns null. + * @throws NativeIntegrationUnavailableException When the given native integration is not available on the current + * machine. + * @throws NativeException On failure to load the native integration. + */ + @ThreadSafe + public static T get(Class type) + throws NativeIntegrationUnavailableException, NativeException { + init(null); + synchronized (Native.class) { + Object instance = integrations.get(type); + if (instance == null) { + try { + instance = Platform.current().get(type, loader); + } catch (NativeException e) { + throw e; + } catch (Throwable t) { + throw new NativeException(String.format("Failed to load native integration %s.", type.getSimpleName()), t); + } + integrations.put(type, instance); + } + return type.cast(instance); + } + } +} diff --git a/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLoader.java b/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLoader.java old mode 100644 new mode 100755 index ae9e4bb..e1ac576 --- a/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLoader.java +++ b/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLoader.java @@ -1,52 +1,53 @@ -/* - * 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 net.rubygrapefruit.platform.internal; - -import net.rubygrapefruit.platform.NativeException; -import net.rubygrapefruit.platform.NativeIntegrationUnavailableException; - -import java.io.File; -import java.util.HashSet; -import java.util.Set; - -public class NativeLibraryLoader { - private final Set loaded = new HashSet(); - private final NativeLibraryLocator locator; - - public NativeLibraryLoader(NativeLibraryLocator locator) { - this.locator = locator; - } - - public void load(String name) { - if (loaded.contains(name)) { - return; - } - try { - File libFile = locator.find(name); - if (libFile == null) { - throw new NativeIntegrationUnavailableException(String.format( - "Native library is not available for this operating system and architecture.")); - } - System.load(libFile.getCanonicalPath()); - } catch (NativeException e) { - throw e; - } catch (Throwable t) { - throw new NativeException(String.format("Failed to load native library '%s'.", name), t); - } - loaded.add(name); - } -} +/* + * 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 net.rubygrapefruit.platform.internal; + +import net.rubygrapefruit.platform.NativeException; +import net.rubygrapefruit.platform.NativeIntegrationUnavailableException; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +public class NativeLibraryLoader { + private final Set loaded = new HashSet(); + private final Platform platform; + private final NativeLibraryLocator nativeLibraryLocator; + + public NativeLibraryLoader(Platform platform, NativeLibraryLocator nativeLibraryLocator) { + this.platform = platform; + this.nativeLibraryLocator = nativeLibraryLocator; + } + + public void load(String libraryFileName) { + if (loaded.contains(libraryFileName)) { + return; + } + try { + File libFile = nativeLibraryLocator.find(libraryFileName); + if (libFile == null) { + throw new NativeIntegrationUnavailableException(String.format("Native library is not available for %s.", platform)); + } + System.load(libFile.getCanonicalPath()); + } catch (NativeException e) { + throw e; + } catch (Throwable t) { + throw new NativeException(String.format("Failed to load native library '%s' for %s.", libraryFileName, platform), t); + } + loaded.add(libraryFileName); + } +} diff --git a/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLocator.java b/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLocator.java old mode 100644 new mode 100755 index d022e11..585fee1 --- a/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLocator.java +++ b/src/main/java/net/rubygrapefruit/platform/internal/NativeLibraryLocator.java @@ -1,106 +1,106 @@ -/* - * 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 net.rubygrapefruit.platform.internal; - -import net.rubygrapefruit.platform.NativeException; -import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions; - -import java.io.*; -import java.net.URL; -import java.nio.channels.FileLock; - -public class NativeLibraryLocator { - private final File extractDir; - - public NativeLibraryLocator(File extractDir) { - this.extractDir = extractDir; - } - - public File find(String libraryName) throws IOException { - if (extractDir != null) { - File libFile = new File(extractDir, String.format("%s/%s", NativeLibraryFunctions.VERSION, libraryName)); - File lockFile = new File(libFile.getParentFile(), libFile.getName() + ".lock"); - lockFile.getParentFile().mkdirs(); - lockFile.createNewFile(); - RandomAccessFile lockFileAccess = new RandomAccessFile(lockFile, "rw"); - try { - // Take exclusive lock on lock file - FileLock lock = lockFileAccess.getChannel().lock(); - if (lockFile.length() > 0 && lockFileAccess.readBoolean()) { - // Library has been extracted - return libFile; - } - URL resource = getClass().getClassLoader().getResource(libraryName); - 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(libraryName); - if (resource != null) { - File libFile; - File libDir = File.createTempFile("native-platform", "dir"); - libDir.delete(); - libDir.mkdirs(); - libFile = new File(libDir, libraryName); - libFile.deleteOnExit(); - copy(resource, libFile); - return libFile; - } - } - - File libFile = new File("build/binaries/" + libraryName); - 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); - } - } -} +/* + * 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 net.rubygrapefruit.platform.internal; + +import net.rubygrapefruit.platform.NativeException; +import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions; + +import java.io.*; +import java.net.URL; +import java.nio.channels.FileLock; + +public class NativeLibraryLocator { + private final File extractDir; + + public NativeLibraryLocator(File extractDir) { + this.extractDir = extractDir; + } + + public File find(String libraryFileName) throws IOException { + if (extractDir != null) { + File libFile = new File(extractDir, String.format("%s/%s", NativeLibraryFunctions.VERSION, libraryFileName)); + File lockFile = new File(libFile.getParentFile(), libFile.getName() + ".lock"); + lockFile.getParentFile().mkdirs(); + lockFile.createNewFile(); + RandomAccessFile lockFileAccess = new RandomAccessFile(lockFile, "rw"); + try { + // Take exclusive lock on lock file + FileLock lock = lockFileAccess.getChannel().lock(); + if (lockFile.length() > 0 && lockFileAccess.readBoolean()) { + // Library has been extracted + return libFile; + } + URL resource = getClass().getClassLoader().getResource(libraryFileName); + 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(libraryFileName); + if (resource != null) { + File libFile; + File libDir = File.createTempFile("native-platform", "dir"); + libDir.delete(); + libDir.mkdirs(); + libFile = new File(libDir, libraryFileName); + libFile.deleteOnExit(); + copy(resource, libFile); + return libFile; + } + } + + File libFile = new File("build/binaries/" + libraryFileName); + 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); + } + } +} diff --git a/src/main/java/net/rubygrapefruit/platform/internal/Platform.java b/src/main/java/net/rubygrapefruit/platform/internal/Platform.java index 6e5556a..bf1463f 100755 --- a/src/main/java/net/rubygrapefruit/platform/internal/Platform.java +++ b/src/main/java/net/rubygrapefruit/platform/internal/Platform.java @@ -67,13 +67,11 @@ public abstract class Platform { } public T get(Class type, NativeLibraryLoader nativeLibraryLoader) { - throw new NativeIntegrationUnavailableException(String.format("Native integration %s is not supported for the current operating system (%s)", - type.getSimpleName(), toString())); + throw new NativeIntegrationUnavailableException(String.format("Native integration %s is not supported for %s.", type.getSimpleName(), toString())); } public String getLibraryName() { - throw new NativeIntegrationUnavailableException(String.format( - "Native integration is not available for the current operating system (%s)", toString())); + throw new NativeIntegrationUnavailableException(String.format("Native integration is not available for %s.", toString())); } private static String getOperatingSystem() {