Refactor code to allow loading of arbitrary libraries from group and name of artifact

This commit is contained in:
2014-11-09 21:28:51 +00:00
parent 6d80bdcca6
commit 0a41017b02
6 changed files with 41 additions and 116 deletions

View File

@@ -16,14 +16,11 @@
package net.rubygrapefruit.platform; package net.rubygrapefruit.platform;
import java.io.File;
import net.rubygrapefruit.platform.internal.NativeLibraryLoader; import net.rubygrapefruit.platform.internal.NativeLibraryLoader;
import net.rubygrapefruit.platform.internal.NativeLibraryLocator; import net.rubygrapefruit.platform.internal.NativeLibraryLocator;
import net.rubygrapefruit.platform.internal.Platform; 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. * Provides access to the native integrations. Use {@link #get(Class)} to load a particular integration.
@@ -31,7 +28,6 @@ import java.util.Map;
@ThreadSafe @ThreadSafe
public class Native { public class Native {
private static NativeLibraryLoader loader; private static NativeLibraryLoader loader;
private static final Map<Class<?>, Object> integrations = new HashMap<Class<?>, Object>();
private Native() { private Native() {
} }
@@ -42,21 +38,16 @@ public class Native {
* @param extractDir The directory to extract native resources into. May be null, in which case a default is * @param extractDir The directory to extract native resources into. May be null, in which case a default is
* selected. * selected.
* *
* @throws NativeIntegrationUnavailableException When native integration is not available on the current machine. * @throws NativeLibraryUnavailableException When the native library is not available on the current machine.
* @throws NativeException On failure to load the native integration. * @throws NativeException On failure to load the native library.
*/ */
@ThreadSafe @ThreadSafe
static public void init(File extractDir) throws NativeIntegrationUnavailableException, NativeException { static public void init(File extractDir) throws NativeLibraryUnavailableException, NativeException {
synchronized (Native.class) { synchronized (Native.class) {
if (loader == null) { if (loader == null) {
Platform platform = Platform.current(); Platform platform = Platform.current();
try { try {
loader = new NativeLibraryLoader(platform, new NativeLibraryLocator(extractDir)); 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) { } catch (NativeException e) {
throw e; throw e;
} catch (Throwable t) { } catch (Throwable t) {
@@ -66,31 +57,14 @@ public class Native {
} }
} }
/** public static void load(String group, String name) {
* Locates a native integration of the given type. init(null);
* try {
* @return The native integration. Never returns null. loader.load(group, Platform.current().getLibraryName(name));
* @throws NativeIntegrationUnavailableException When the given native integration is not available on the current } catch (NativeException e) {
* machine. throw e;
* @throws NativeException On failure to load the native integration. } catch (Throwable t) {
*/ throw new NativeException("Failed to load native library.");
@ThreadSafe
public static <T extends NativeIntegration> T get(Class<T> 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);
} }
} }
} }

View File

@@ -19,8 +19,8 @@ package net.rubygrapefruit.platform;
/** /**
* Thrown when a given integration is not available for the current machine. * Thrown when a given integration is not available for the current machine.
*/ */
public class NativeIntegrationUnavailableException extends NativeException { public class NativeLibraryUnavailableException extends NativeException {
public NativeIntegrationUnavailableException(String message) { public NativeLibraryUnavailableException(String message) {
super(message); super(message);
} }
} }

View File

@@ -1,10 +1,12 @@
package net.rubygrapefruit.platform.internal; package net.rubygrapefruit.platform.internal;
public class LibraryDef { public class LibraryDef {
final String group;
final String name; final String name;
final String platform; final String platform;
public LibraryDef(String name, String platform) { public LibraryDef(String group, String name, String platform) {
this.group = group;
this.name = name; this.name = name;
this.platform = platform; this.platform = platform;
} }
@@ -25,4 +27,8 @@ public class LibraryDef {
public int hashCode() { public int hashCode() {
return name.hashCode() ^ platform.hashCode(); return name.hashCode() ^ platform.hashCode();
} }
public String getGroupPath() {
return group.replace(".", "/");
}
} }

View File

@@ -17,7 +17,7 @@
package net.rubygrapefruit.platform.internal; package net.rubygrapefruit.platform.internal;
import net.rubygrapefruit.platform.NativeException; import net.rubygrapefruit.platform.NativeException;
import net.rubygrapefruit.platform.NativeIntegrationUnavailableException; import net.rubygrapefruit.platform.NativeLibraryUnavailableException;
import java.io.File; import java.io.File;
import java.util.HashSet; import java.util.HashSet;
@@ -33,14 +33,14 @@ public class NativeLibraryLoader {
this.nativeLibraryLocator = nativeLibraryLocator; this.nativeLibraryLocator = nativeLibraryLocator;
} }
public void load(String libraryFileName) { public void load(String libraryGroupName, String libraryFileName) {
if (loaded.contains(libraryFileName)) { if (loaded.contains(libraryFileName)) {
return; return;
} }
try { try {
File libFile = nativeLibraryLocator.find(new LibraryDef(libraryFileName, platform.getId())); File libFile = nativeLibraryLocator.find(new LibraryDef(libraryGroupName, libraryFileName, platform.getId()));
if (libFile == null) { if (libFile == null) {
throw new NativeIntegrationUnavailableException(String.format("Native library '%s' is not available for %s.", libraryFileName, platform)); throw new NativeLibraryUnavailableException(String.format("Native library '%s' is not available for %s.", libraryFileName, platform));
} }
System.load(libFile.getCanonicalPath()); System.load(libFile.getCanonicalPath());
} catch (NativeException e) { } catch (NativeException e) {

View File

@@ -31,7 +31,8 @@ public class NativeLibraryLocator {
} }
public File find(LibraryDef libraryDef) throws IOException { public File find(LibraryDef libraryDef) throws IOException {
String resourceName = String.format("net/rubygrapefruit/platform/%s/%s", libraryDef.platform, libraryDef.name); String resourceName = String.format("%s/%s/%s", libraryDef.getGroupPath(), libraryDef.platform, libraryDef.name);
System.out.println(resourceName);
if (extractDir != null) { if (extractDir != null) {
File libFile = new File(extractDir, String.format("%s/%s/%s", NativeLibraryFunctions.VERSION, libraryDef.platform, libraryDef.name)); File libFile = new File(extractDir, String.format("%s/%s/%s", NativeLibraryFunctions.VERSION, libraryDef.platform, libraryDef.name));
File lockFile = new File(libFile.getParentFile(), libFile.getName() + ".lock"); File lockFile = new File(libFile.getParentFile(), libFile.getName() + ".lock");
@@ -62,7 +63,7 @@ public class NativeLibraryLocator {
URL resource = getClass().getClassLoader().getResource(resourceName); URL resource = getClass().getClassLoader().getResource(resourceName);
if (resource != null) { if (resource != null) {
File libFile; File libFile;
File libDir = File.createTempFile("native-platform", "dir"); File libDir = File.createTempFile(libraryDef.name, "dir");
libDir.delete(); libDir.delete();
libDir.mkdirs(); libDir.mkdirs();
libFile = new File(libDir, libraryDef.name); libFile = new File(libDir, libraryDef.name);

View File

@@ -16,10 +16,8 @@
package net.rubygrapefruit.platform.internal; package net.rubygrapefruit.platform.internal;
import net.rubygrapefruit.platform.*; import net.rubygrapefruit.platform.NativeIntegration;
import net.rubygrapefruit.platform.Process; import net.rubygrapefruit.platform.NativeLibraryUnavailableException;
import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions;
import net.rubygrapefruit.platform.internal.jni.TerminfoFunctions;
public abstract class Platform { public abstract class Platform {
private static Platform platform; private static Platform platform;
@@ -77,11 +75,11 @@ public abstract class Platform {
} }
public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) { public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) {
throw new NativeIntegrationUnavailableException(String.format("Native integration %s is not supported for %s.", type.getSimpleName(), toString())); throw new NativeLibraryUnavailableException(String.format("Native integration %s is not supported for %s.", type.getSimpleName(), toString()));
} }
public String getLibraryName() { public String getLibraryName(String name) {
throw new NativeIntegrationUnavailableException(String.format("Native integration is not available for %s.", toString())); throw new NativeLibraryUnavailableException(String.format("Native integration is not available for %s.", toString()));
} }
public abstract String getId(); public abstract String getId();
@@ -101,30 +99,12 @@ public abstract class Platform {
} }
@Override @Override
public String getLibraryName() { public String getLibraryName(String name) {
return "native-platform.dll"; return String.format("%s.dll", name);
} }
@Override @Override
public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) { public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) {
if (type.equals(Process.class)) {
return type.cast(new WrapperProcess(new DefaultProcess(), true));
}
if (type.equals(Terminals.class)) {
return type.cast(new WindowsTerminals());
}
if (type.equals(ProcessLauncher.class)) {
return type.cast(new WrapperProcessLauncher(new WindowsProcessLauncher(new DefaultProcessLauncher())));
}
if (type.equals(SystemInfo.class)) {
return type.cast(new DefaultSystemInfo());
}
if (type.equals(FileSystems.class)) {
return type.cast(new PosixFileSystems());
}
if (type.equals(WindowsRegistry.class)) {
return type.cast(new DefaultWindowsRegistry());
}
return super.get(type, nativeLibraryLoader); return super.get(type, nativeLibraryLoader);
} }
} }
@@ -144,46 +124,16 @@ public abstract class Platform {
} }
private static abstract class Posix extends Platform { private static abstract class Posix extends Platform {
abstract String getCursesLibraryName();
@Override @Override
public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) { public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) {
if (type.equals(PosixFiles.class)) {
return type.cast(new DefaultPosixFiles());
}
if (type.equals(Process.class)) {
return type.cast(new WrapperProcess(new DefaultProcess(), false));
}
if (type.equals(ProcessLauncher.class)) {
return type.cast(new WrapperProcessLauncher(new DefaultProcessLauncher()));
}
if (type.equals(Terminals.class)) {
nativeLibraryLoader.load(getCursesLibraryName());
int nativeVersion = TerminfoFunctions.getVersion();
if (nativeVersion != NativeLibraryFunctions.VERSION) {
throw new NativeException(String.format("Unexpected native library version loaded. Expected %s, was %s.", nativeVersion, NativeLibraryFunctions.VERSION));
}
return type.cast(new TerminfoTerminals());
}
if (type.equals(SystemInfo.class)) {
return type.cast(new DefaultSystemInfo());
}
if (type.equals(FileSystems.class)) {
return type.cast(new PosixFileSystems());
}
return super.get(type, nativeLibraryLoader); return super.get(type, nativeLibraryLoader);
} }
} }
private abstract static class Unix extends Posix { private abstract static class Unix extends Posix {
@Override @Override
public String getLibraryName() { public String getLibraryName(String name) {
return "libnative-platform.so"; return String.format("lib%s.so", name);
}
@Override
String getCursesLibraryName() {
return "libnative-platform-curses.so";
} }
} }
@@ -217,13 +167,8 @@ public abstract class Platform {
private static abstract class OsX extends Posix { private static abstract class OsX extends Posix {
@Override @Override
public String getLibraryName() { public String getLibraryName(String name) {
return "libnative-platform.dylib"; return String.format("lib%s.dylib", name);
}
@Override
String getCursesLibraryName() {
return "libnative-platform-curses.dylib";
} }
} }
@@ -247,5 +192,4 @@ public abstract class Platform {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }
}
}