forked from scream3r/java-simple-serial-connector
-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for a static native library search path (#92)
- Add optional jssc.boot.library.path for native libs, helps with sandboxed environments - Add unit tests for jssc.boot.library.path - Bump maven-surefire-plugin to 3.0.0-M4
- Loading branch information
Showing
6 changed files
with
160 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/** | ||
* License: https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
package jssc; | ||
|
||
import org.scijava.nativelib.DefaultJniExtractor; | ||
import org.scijava.nativelib.NativeLibraryUtil; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
|
||
/** | ||
* @author A. Tres Finocchiaro | ||
* | ||
* Stub <code>DefaultJniExtractor</code> class to allow native-lib-loader to conditionally | ||
* use a statically defined native search path <code>bootPath</code> when provided. | ||
*/ | ||
public class DefaultJniExtractorStub extends DefaultJniExtractor { | ||
private File bootPath; | ||
|
||
/** | ||
* Default constructor | ||
*/ | ||
public DefaultJniExtractorStub(Class libraryJarClass) throws IOException { | ||
super(libraryJarClass); | ||
} | ||
|
||
/** | ||
* Force native-lib-loader to first look in the location defined as <code>bootPath</code> | ||
* prior to extracting a native library, useful for sandboxed environments. | ||
* <code> | ||
* NativeLoader.setJniExtractor(new DefaultJniExtractorStub(null, "/opt/nativelibs"))); | ||
* NativeLoader.loadLibrary("mylibrary"); | ||
* </code> | ||
*/ | ||
public DefaultJniExtractorStub(Class libraryJarClass, String bootPath) throws IOException { | ||
this(libraryJarClass); | ||
|
||
if(bootPath != null) { | ||
File bootTest = new File(bootPath); | ||
if(bootTest.exists()) { | ||
// assume a static, existing directory will contain the native libs | ||
this.bootPath = bootTest; | ||
} else { | ||
System.err.println("WARNING " + DefaultJniExtractorStub.class.getCanonicalName() + ": Boot path " + bootPath + " not found, falling back to default extraction behavior."); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* If a <code>bootPath</code> was provided to the constructor and exists, | ||
* calculate the <code>File</code> path without any extraction logic. | ||
* | ||
* If a <code>bootPath</code> was NOT provided or does NOT exist, fallback on | ||
* the default extraction behavior. | ||
*/ | ||
@Override | ||
public File extractJni(String libPath, String libName) throws IOException { | ||
// Lie and pretend it's already extracted at the bootPath location | ||
if(bootPath != null) { | ||
return new File(bootPath, NativeLibraryUtil.getPlatformLibraryName(libName)); | ||
} | ||
// Fallback on default behavior | ||
return super.extractJni(libPath, libName); | ||
} | ||
|
||
@Override | ||
public void extractRegistered() throws IOException { | ||
if(bootPath != null) { | ||
return; // no-op | ||
} | ||
super.extractRegistered(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
src/test/java/jssc/bootpath/ManualBootLibraryPathFailedTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package jssc.bootpath; | ||
|
||
import jssc.SerialNativeInterface; | ||
import org.junit.Test; | ||
|
||
import static org.junit.Assert.assertTrue; | ||
import static org.junit.Assert.fail; | ||
|
||
/** | ||
* Tests if a valid <code>jssc.boot.library.path</code> which does NOT contain a native library | ||
* will predictably fail. This test can be run regardless of whether or not a native binary was | ||
* created during the build process. | ||
* | ||
* TODO: This MUST be in its own class to run in a separate JVM (https://stackoverflow.com/questions/68657855) | ||
* - JUnit does NOT currently offer JVM unloading between methods. | ||
* - maven-surefire-plugin DOES offer JVM unloading between classes using <code>reuseForks=false</code> | ||
* - Unloading is needed due to NativeLoader.loadLibrary(...) calls System.loadLibrary(...) which is static | ||
*/ | ||
public class ManualBootLibraryPathFailedTest { | ||
@Test | ||
public void testBootPathOverride() { | ||
String nativeLibDir = "/"; // This should be valid on all platforms | ||
System.setProperty("jssc.boot.library.path", nativeLibDir); | ||
try { | ||
SerialNativeInterface.getNativeLibraryVersion(); | ||
fail("Library loading should fail if path provided exists but does not contain a native library"); | ||
} catch (UnsatisfiedLinkError ignore) { | ||
assertTrue("Library loading failed as expected with an invalid jssc.boot.library.path", true); | ||
} | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
src/test/java/jssc/bootpath/ManualBootLibraryPathTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package jssc.bootpath; | ||
|
||
import jssc.SerialNativeInterface; | ||
import org.junit.Test; | ||
import org.scijava.nativelib.NativeLibraryUtil; | ||
|
||
import static org.hamcrest.CoreMatchers.*; | ||
import static org.junit.Assert.assertThat; | ||
import static org.junit.Assert.fail; | ||
|
||
/** | ||
* Tests if a valid <code>jssc.boot.library.path</code> which DOES contain a native library | ||
* will predictably pass. This test can ONLY be run regardless if a native binary was created | ||
* during the build process. See also <code>maven.exclude.tests</code>. | ||
* | ||
* TODO: This MUST be in its own class to run in a separate JVM (https://stackoverflow.com/questions/68657855) | ||
* - JUnit does NOT currently offer JVM unloading between methods. | ||
* - maven-surefire-plugin DOES offer JVM unloading between classes using <code>reuseForks=false</code> | ||
* - Unloading is needed due to NativeLoader.loadLibrary(...) calls System.loadLibrary(...) which is static | ||
*/ | ||
public class ManualBootLibraryPathTest { | ||
@Test | ||
public void testBootPathOverride() { | ||
String nativeLibDir = NativeLibraryUtil.getPlatformLibraryPath(System.getProperty("user.dir") + "/target/cmake/natives/"); | ||
System.setProperty("jssc.boot.library.path", nativeLibDir); | ||
try { | ||
final String nativeLibraryVersion = SerialNativeInterface.getNativeLibraryVersion(); | ||
assertThat(nativeLibraryVersion, is(not(nullValue()))); | ||
assertThat(nativeLibraryVersion, is(not(""))); | ||
} catch (UnsatisfiedLinkError linkError) { | ||
linkError.printStackTrace(); | ||
fail("Should be able to call method!"); | ||
} | ||
} | ||
} |