Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extended search #35

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,61 +11,71 @@

import java.io.*;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Random;
import java.util.Set;
import java.util.regex.Pattern;

/**
* Used to optionally extract and load a JNI library.
* Used to find and load a JNI library, eventually after having extracted it.
*
* It will search for the library in order at the following locations:
* <ol>
* <li> in the custom library path: If the "library.${name}.path" System property is set to a directory
* <li> in the custom library path: If the "<code>library.${name}.path</code>" System property is set to a directory,
* subdirectories are searched:
* <ol>
* <li> "${name}-${version}" if the version can be determined.
* <li> "${name}"
* <li> "<code>${platform}/${arch}</code>"
* <li> "<code>${platform}</code>"
* <li> "<code>${os}</code>"
* <li> "<code></code>"
* </ol>
* for 2 namings of the library:
* <ol>
* <li> as "<code>${name}-${version}</code>" library name if the version can be determined.
* <li> as "<code>${name}</code>" library name
* </ol>
* <li> system library path: This is where the JVM looks for JNI libraries by default.
* <ol>
* <li> "${name}-${version}" if the version can be determined.
* <li> "${name}"
* <li> as "<code>${name}${bit-model}-${version}</code>" library name if the version can be determined.
* <li> as "<code>${name}-${version}</code>" library name if the version can be determined.
* <li> as "<code>${name}</code>" library name
* </ol>
* <li> classpath path: If the JNI library can be found on the classpath, it will get extracted
* and and then loaded. This way you can embed your JNI libraries into your packaged JAR files.
* and then loaded. This way you can embed your JNI libraries into your packaged JAR files.
* They are looked up as resources in this order:
* <ol>
* <li> "META-INF/native/${platform}/${arch}/${library}" : Store your library here if you want to embed
* <li> "<code>META-INF/native/${platform}/${arch}/${library[-version]}</code>": Store your library here if you want to embed
* more than one platform JNI library on different processor archs in the jar.
* <li> "META-INF/native/${platform}/${library}" : Store your library here if you want to embed more
* <li> "<code>META-INF/native/${platform}/${library[-version]}</code>": Store your library here if you want to embed more
* than one platform JNI library in the jar.
* <li> "META-INF/native/${library}": Store your library here if your JAR is only going to embedding one
* <li> "<code>META-INF/native/${os}/${library[-version]}</code>": Store your library here if you want to embed more
* than one platform JNI library in the jar but don't want to take bit model into account.
* <li> "<code>META-INF/native/${library[-version]}</code>": Store your library here if your JAR is only going to embedding one
* platform library.
* </ol>
* The file extraction is attempted until it succeeds in the following directories.
* <ol>
* <li> The directory pointed to by the "library.${name}.path" System property (if set)
* <li> a temporary directory (uses the "java.io.tmpdir" System property)
* <li> The directory pointed to by the "<code>library.${name}.path</code>" System property (if set)
* <li> a temporary directory (uses the "<code>java.io.tmpdir</code>" System property)
* </ol>
* </ol>
*
* where:
* <ul>
* <li>"${name}" is the name of library
* <li>"${version}" is the value of "library.${name}.version" System property if set.
* <li>"<code>${name}</code>" is the name of library
* <li>"<code>${version}</code>" is the value of "<code>library.${name}.version</code>" System property if set.
* Otherwise it is set to the ImplementationVersion property of the JAR's Manifest</li>
* <li>"${os}" is your operating system, for example "osx", "linux", or "windows"</li>
* <li>"${bit-model}" is "64" if the JVM process is a 64 bit process, otherwise it's "32" if the
* <li>"<code>${os}</code>" is your operating system, for example "<code>osx</code>", "<code>linux</code>", or "<code>windows</code>"</li>
* <li>"<code>${bit-model}</code>" is "<code>64</code>" if the JVM process is a 64 bit process, otherwise it's "<code>32</code>" if the
* JVM is a 32 bit process</li>
* <li>"${arch}" is the architecture for the processor, for example "amd64" or "sparcv9"</li>
* <li>"${platform}" is "${os}${bit-model}", for example "linux32" or "osx64" </li>
* <li>"${library}": is the normal jni library name for the platform. For example "${name}.dll" on
* windows, "lib${name}.jnilib" on OS X, and "lib${name}.so" on linux</li>
* <li>"<code>${arch}</code>" is the architecture for the processor, for example "<code>amd64</code>" or "<code>sparcv9</code>"</li>
* <li>"<code>${platform}</code>" is "<code>${os}${bit-model}</code>", for example "<code>linux32</code>" or "<code>osx64</code>" </li>
* <li>"<code>${library[-version]}</code>": is the normal jni library name for the platform (eventually with <code>-${version}</code>) suffix.
* For example "<code>${name}.dll</code>" on
* windows, "<code>lib${name}.jnilib</code>" on OS X, and "<code>lib${name}.so</code>" on linux</li>
* </ul>
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
* @see System#mapLibraryName(String)
*/
public class Library {

Expand Down Expand Up @@ -154,13 +164,19 @@ private void doLoad() {
}
ArrayList<String> errors = new ArrayList<String>();

String[] specificDirs = getSpecificSearchDirs();
String libFilename = map(name);
String versionlibFilename = (version == null) ? null : map(name + "-" + version);

/* Try loading library from a custom library path */
String customPath = System.getProperty("library."+name+".path");
if (customPath != null) {
if( version!=null && load(errors, file(customPath, map(name + "-" + version))) )
return;
if( load(errors, file(customPath, map(name))) )
return;
for ( String dir: specificDirs ) {
if( version!=null && load(errors, file(customPath, dir, versionlibFilename)) )
return;
if( load(errors, file(customPath, dir, libFilename)) )
return;
}
}

/* Try loading library from java library path */
Expand All @@ -174,59 +190,91 @@ private void doLoad() {

/* Try extracting the library from the jar */
if( classLoader!=null ) {
if( exractAndLoad(errors, version, customPath, getArchSpecifcResourcePath()) )
return;
if( exractAndLoad(errors, version, customPath, getPlatformSpecifcResourcePath()) )
return;
if( exractAndLoad(errors, version, customPath, getOperatingSystemSpecifcResourcePath()) )
return;
// For the simpler case where only 1 platform lib is getting packed into the jar
if( exractAndLoad(errors, version, customPath, getResorucePath()) )
return;
for ( String dir: specificDirs ) {
if( version!=null && extractAndLoad(errors, customPath, dir, versionlibFilename) )
return;
if( extractAndLoad(errors, customPath, dir, libFilename) )
return;
}
}

/* Failed to find the library */
throw new UnsatisfiedLinkError("Could not load library. Reasons: " + errors.toString());
}

final public String getArchSpecifcResourcePath() {
return "META-INF/native/"+ getPlatform() + "/" + System.getProperty("os.arch") + "/" +map(name);
/**
* Search directories for library:<ul>
* <li><code>${platform}/${arch}</code> to enable platform JNI library for different processor archs</li>
* <li><code>${platform}</code> to enable platform JNI library</li>
* <li><code>${os}</code> to enable OS JNI library</li>
* <li>no directory</li>
* </ul>
* @return the list
*/
final public String[] getSpecificSearchDirs() {
return new String[] {
getPlatform() + "/" + System.getProperty("os.arch"),
getPlatform(),
getOperatingSystem(),
null
};
}

final public String getOperatingSystemSpecifcResourcePath() {
return getPlatformSpecifcResourcePath(getOperatingSystem());
@Deprecated
final public String getArchSpecifcResourcePath() {
return getPlatformSpecifcResourcePath(getPlatform() + "/" + System.getProperty("os.arch"));
}
@Deprecated
final public String getPlatformSpecifcResourcePath() {
return getPlatformSpecifcResourcePath(getPlatform());
}
final public String getPlatformSpecifcResourcePath(String platform) {
return "META-INF/native/"+platform+"/"+map(name);
@Deprecated
final public String getOperatingSystemSpecifcResourcePath() {
return getPlatformSpecifcResourcePath(getOperatingSystem());
}

final public String getResorucePath() {
@Deprecated
final public String getResourcePath() {
return "META-INF/native/"+map(name);
}

@Deprecated
final public String getLibraryFileName() {
return map(name);
}


private boolean exractAndLoad(ArrayList<String> errors, String version, String customPath, String resourcePath) {
@Deprecated
final public String getPlatformSpecifcResourcePath(String platform) {
return "META-INF/native/"+platform+"/"+map(name);
}

@Deprecated
final public String getResorucePath() {
return getResourcePath();
}

/**
* Extract <code>META-INF/native/${dir}/${filename}</code> resource to a temporary file
* in customPath (if defined) or java.io.tmpdir
* @param errors list of error messages gathered during the whole loading mechanism
* @param customPath custom path to store the temporary file (can be null)
* @param dir the directory
* @param filename the file name
* @return a boolean telling if the library was found and loaded
*/
private boolean extractAndLoad(ArrayList<String> errors, String customPath, String dir, String filename) {
String resourcePath = "META-INF/native/" + ( dir == null ? "" : (dir + '/')) + filename;
URL resource = classLoader.getResource(resourcePath);
if( resource !=null ) {

String libName = name + "-" + getBitModel();
if( version !=null) {
libName += "-" + version;
}
String []libNameParts = map(libName).split("\\.");
String prefix = libNameParts[0]+"-";
String suffix = "."+libNameParts[1];
if( resource ==null ) {
errors.add( "resource " + resourcePath + " not found" );
} else {
String []libNameParts = resourcePath.substring(16).replace('/','_').split("\\.");
String tmpFilePrefix = libNameParts[0]+"-";
String tmpFileSuffix = "."+libNameParts[1];

if( customPath!=null ) {
// Try to extract it to the custom path...
File target = extract(errors, resource, prefix, suffix, file(customPath));
File target = extracttoTmpFile(errors, resource, tmpFilePrefix, tmpFileSuffix, file(customPath));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extractToTmpFile?

if( target!=null ) {
if( load(errors, target) ) {
return true;
Expand All @@ -236,7 +284,7 @@ private boolean exractAndLoad(ArrayList<String> errors, String version, String c

// Fall back to extracting to the tmp dir
customPath = System.getProperty("java.io.tmpdir");
File target = extract(errors, resource, prefix, suffix, file(customPath));
File target = extracttoTmpFile(errors, resource, tmpFilePrefix, tmpFileSuffix, file(customPath));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

if( target!=null ) {
if( load(errors, target) ) {
return true;
Expand All @@ -251,7 +299,7 @@ private File file(String ...paths) {
for (String path : paths) {
if( rc == null ) {
rc = new File(path);
} else {
} else if( path != null ) {
rc = new File(rc, path);
}
}
Expand All @@ -271,7 +319,7 @@ private String map(String libName) {
return libName;
}

private File extract(ArrayList<String> errors, URL source, String prefix, String suffix, File directory) {
private File extracttoTmpFile(ArrayList<String> errors, URL source, String tmpFilePrefix, String tpmFileSuffix, File directory) {
File target = null;
if (directory != null) {
directory = directory.getAbsoluteFile();
Expand All @@ -280,7 +328,7 @@ private File extract(ArrayList<String> errors, URL source, String prefix, String
FileOutputStream os = null;
InputStream is = null;
try {
target = File.createTempFile(prefix, suffix, directory);
target = File.createTempFile(tmpFilePrefix, tpmFileSuffix, directory);
is = source.openStream();
if (is != null) {
byte[] buffer = new byte[4096];
Expand Down