- Bundle native library in jar and extract at runtime.

- Don't reinitialise terminal in TerminalAccess.getTerminal() if already initialised.
This commit is contained in:
Adam Murdoch
2012-08-10 08:50:40 +10:00
parent 3c9720f9ab
commit 8621ef80e9
6 changed files with 189 additions and 125 deletions

View File

@@ -64,6 +64,16 @@ task nativeHeaders {
} }
} }
task nativeJar(type: Jar) {
from compileMain
archiveName = 'native-platform-jni.jar'
}
startScripts.classpath += nativeJar.outputs.files
applicationDistribution.from(nativeJar) {
into 'lib'
}
compileMain.dependsOn nativeHeaders compileMain.dependsOn nativeHeaders
test.dependsOn compileMain test.dependsOn compileMain

View File

@@ -67,11 +67,6 @@ Java_net_rubygrapefruit_platform_internal_jni_PosixTerminalFunctions_isatty(JNIE
switch (output) { switch (output) {
case 0: case 0:
case 1: case 1:
printf("=> checking file descriptor %d\n", output+1);
printf("=> isatty(): %d\n", isatty(output+1));
result = fstat(output+1, &fileInfo);
printf("=> fstat(): %d\n", result);
printf("=> mode: %o\n", fileInfo.st_mode);
return isatty(output+1) ? JNI_TRUE : JNI_FALSE; return isatty(output+1) ? JNI_TRUE : JNI_FALSE;
default: default:
return JNI_FALSE; return JNI_FALSE;

View File

@@ -3,8 +3,8 @@ package net.rubygrapefruit.platform;
import net.rubygrapefruit.platform.internal.*; import net.rubygrapefruit.platform.internal.*;
import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions; import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions;
import java.io.File; import java.io.*;
import java.io.IOException; import java.net.URL;
/** /**
* 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.
@@ -13,15 +13,34 @@ public class Native {
private static final Object lock = new Object(); private static final Object lock = new Object();
private static boolean loaded; private static boolean loaded;
static <T extends NativeIntegration> T get(Class<T> type) { /**
Platform platform = Platform.current(); * Initialises the native integration, if not already initialized.
*
* @param extractDir The directory to extract native resources into. May be null.
*/
static void init(File extractDir) {
synchronized (lock) { synchronized (lock) {
if (!loaded) { if (!loaded) {
Platform platform = Platform.current();
if (!platform.isSupported()) { if (!platform.isSupported()) {
throw new NativeException(String.format("The current platform is not supported.")); throw new NativeException(String.format("The current platform is not supported."));
} }
try { try {
File libFile = new File("build/binaries/" + platform.getLibraryName()); File libFile;
URL resource = Native.class.getClassLoader().getResource(platform.getLibraryName());
if (resource != null) {
File libDir = extractDir;
if (libDir == null) {
libDir = File.createTempFile("native-platform", "dir");
libDir.delete();
libDir.mkdirs();
}
libFile = new File(libDir, platform.getLibraryName());
libFile.deleteOnExit();
copy(resource, libFile);
} else {
libFile = new File("build/binaries/" + platform.getLibraryName());
}
System.load(libFile.getCanonicalPath()); System.load(libFile.getCanonicalPath());
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -35,6 +54,12 @@ public class Native {
loaded = true; loaded = true;
} }
} }
}
static <T extends NativeIntegration> T get(Class<T> type) {
init(null);
Platform platform = Platform.current();
if (platform.isPosix()) { if (platform.isPosix()) {
if (type.equals(PosixFile.class)) { if (type.equals(PosixFile.class)) {
return type.cast(new DefaultPosixFile()); return type.cast(new DefaultPosixFile());
@@ -56,4 +81,29 @@ public class Native {
throw new UnsupportedOperationException(String.format("Cannot load unsupported native integration %s.", throw new UnsupportedOperationException(String.format("Cannot load unsupported native integration %s.",
type.getName())); type.getName()));
} }
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);
}
}
} }

View File

@@ -1,6 +1,10 @@
package net.rubygrapefruit.platform; package net.rubygrapefruit.platform;
public class NativeException extends RuntimeException { public class NativeException extends RuntimeException {
public NativeException(String message, Throwable throwable) {
super(message, throwable);
}
public NativeException(String message) { public NativeException(String message) {
super(message); super(message);
} }

View File

@@ -6,6 +6,7 @@ import net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions;
public class TerminfoTerminalAccess implements TerminalAccess { public class TerminfoTerminalAccess implements TerminalAccess {
private static Output currentlyOpen; private static Output currentlyOpen;
private static TerminfoTerminal current;
@Override @Override
public boolean isTerminal(Output output) { public boolean isTerminal(Output output) {
@@ -14,14 +15,15 @@ public class TerminfoTerminalAccess implements TerminalAccess {
@Override @Override
public Terminal getTerminal(Output output) { public Terminal getTerminal(Output output) {
if (currentlyOpen != null) { if (currentlyOpen != null && currentlyOpen != output) {
throw new UnsupportedOperationException("Currently only one output can be used as a terminal."); throw new UnsupportedOperationException("Currently only one output can be used as a terminal.");
} }
if (current == null) {
TerminfoTerminal terminal = new TerminfoTerminal(output); current = new TerminfoTerminal(output);
terminal.init(); current.init();
}
currentlyOpen = output; currentlyOpen = output;
return terminal; return current;
} }
} }

View File

@@ -7,6 +7,7 @@ import net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions;
public class WindowsTerminalAccess implements TerminalAccess { public class WindowsTerminalAccess implements TerminalAccess {
private static Output currentlyOpen; private static Output currentlyOpen;
private static WindowsTerminal current;
@Override @Override
public boolean isTerminal(Output output) { public boolean isTerminal(Output output) {
@@ -21,14 +22,16 @@ public class WindowsTerminalAccess implements TerminalAccess {
@Override @Override
public Terminal getTerminal(Output output) { public Terminal getTerminal(Output output) {
if (currentlyOpen != null) { if (currentlyOpen !=null && currentlyOpen != output) {
throw new UnsupportedOperationException("Currently only one output can be used as a terminal."); throw new UnsupportedOperationException("Currently only one output can be used as a terminal.");
} }
WindowsTerminal terminal = new WindowsTerminal(output); if (current == null) {
terminal.init(); current = new WindowsTerminal(output);
current.init();
}
currentlyOpen = output; currentlyOpen = output;
return terminal; return current;
} }
} }