The library in question is Tokyo Cabinet.
有问题的图书馆是东京内阁。
I want is to have the native library, JNI library, and all Java API classes in one JAR file to avoid redistribution headaches.
我想在一个JAR文件中包含本机库,JNI库和所有Java API类,以避免重新分发问题。
There seems to be an attempt at this at GitHub, but
似乎在GitHub尝试了这个,但是
- It does not include the actual native library, only JNI library.
- 它不包括实际的本机库,只包括JNI库。
- It seems to be specific to Leiningen's native dependencies plugin (it won't work as a redistributable).
- 它似乎特定于Leiningen的本机依赖插件(它不能用作可再发行的插件)。
The question is, can I bundle everything in one JAR and redistribute it? If yes, how?
问题是,我可以将所有内容捆绑在一个JAR中并重新分发吗?如果有,怎么样?
P.S.: Yes, I realize it may have portability implications.
P.S。:是的,我意识到它可能具有可移植性的含义。
5 个解决方案
#1
31
https://www.adamheinrich.com/blog/2012/12/how-to-load-native-jni-library-from-jar/
https://www.adamheinrich.com/blog/2012/12/how-to-load-native-jni-library-from-jar/
is great article, which solves my issue ..
是一篇伟大的文章,它解决了我的问题..
In my case I've got the following code for initialize the library:
在我的情况下,我有以下代码来初始化库:
static {
try {
System.loadLibrary("crypt"); // used for tests. This library in classpath only
} catch (UnsatisfiedLinkError e) {
try {
NativeUtils.loadLibraryFromJar("/natives/crypt.dll"); // during runtime. .DLL within .JAR
} catch (IOException e1) {
throw new RuntimeException(e1);
}
}
}
#2
16
Take a look at One-JAR. It will wrap your application up in a single jar file with a specialised class loader which handles "jars within jars" among other things.
看看One-JAR。它将你的应用程序包装在一个jar文件中,该文件包含一个专门的类加载器,它可以处理“jars in jars”等。
It handles native (JNI) libraries by unpacking them to a temporary working folder as required.
它通过根据需要将它们解压缩到临时工作文件夹来处理本机(JNI)库。
(Disclaimer: I've never used One-JAR, haven't needed to as yet, just had it bookmarked for a rainy day.)
(免责声明:我从来没有使用过One-JAR,还没有使用过,只是为了下雨天给它添加了书签。)
#3
5
JarClassLoader is a class loader to load classes, native libraries and resources from a single monster JAR and from JARs inside the monster JAR.
JarClassLoader是一个类加载器,用于从单个怪物JAR和怪物JAR内的JAR加载类,本机库和资源。
#4
1
You will probably have to unjar the native library to the local file system. As far as I know the bit of code that does the native loading looks at the file system.
您可能必须将本机库解开到本地文件系统。据我所知,本机加载的代码位于文件系统。
This code should help get you started (I haven't looked at it in a while, and it is for a different purpose but should do the trick, and I am pretty busy at the moment, but if you have questions just leave a comment and I'll answer as soon as I can).
这段代码应该可以帮助你入门(我暂时没有看过它,这是为了一个不同的目的,但应该做到这一点,我现在很忙,但如果你有问题只需发表评论我会尽快回答。)
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
public class FileUtils
{
public static String getFileName(final Class<?> owner,
final String name)
throws URISyntaxException,
ZipException,
IOException
{
String fileName;
final URI uri;
try
{
final String external;
final String decoded;
final int pos;
uri = getResourceAsURI(owner.getPackage().getName().replaceAll("\\.", "/") + "/" + name, owner);
external = uri.toURL().toExternalForm();
decoded = external; // URLDecoder.decode(external, "UTF-8");
pos = decoded.indexOf(":/");
fileName = decoded.substring(pos + 1);
}
catch(final FileNotFoundException ex)
{
fileName = null;
}
if(fileName == null || !(new File(fileName).exists()))
{
fileName = getFileNameX(owner, name);
}
return (fileName);
}
private static String getFileNameX(final Class<?> clazz, final String name)
throws UnsupportedEncodingException
{
final URL url;
final String fileName;
url = clazz.getResource(name);
if(url == null)
{
fileName = name;
}
else
{
final String decoded;
final int pos;
decoded = URLDecoder.decode(url.toExternalForm(), "UTF-8");
pos = decoded.indexOf(":/");
fileName = decoded.substring(pos + 1);
}
return (fileName);
}
private static URI getResourceAsURI(final String resourceName,
final Class<?> clazz)
throws URISyntaxException,
ZipException,
IOException
{
final URI uri;
final URI resourceURI;
uri = getJarURI(clazz);
resourceURI = getFile(uri, resourceName);
return (resourceURI);
}
private static URI getJarURI(final Class<?> clazz)
throws URISyntaxException
{
final ProtectionDomain domain;
final CodeSource source;
final URL url;
final URI uri;
domain = clazz.getProtectionDomain();
source = domain.getCodeSource();
url = source.getLocation();
uri = url.toURI();
return (uri);
}
private static URI getFile(final URI where,
final String fileName)
throws ZipException,
IOException
{
final File location;
final URI fileURI;
location = new File(where);
// not in a JAR, just return the path on disk
if(location.isDirectory())
{
fileURI = URI.create(where.toString() + fileName);
}
else
{
final ZipFile zipFile;
zipFile = new ZipFile(location);
try
{
fileURI = extract(zipFile, fileName);
}
finally
{
zipFile.close();
}
}
return (fileURI);
}
private static URI extract(final ZipFile zipFile,
final String fileName)
throws IOException
{
final File tempFile;
final ZipEntry entry;
final InputStream zipStream;
OutputStream fileStream;
tempFile = File.createTempFile(fileName.replace("/", ""), Long.toString(System.currentTimeMillis()));
tempFile.deleteOnExit();
entry = zipFile.getEntry(fileName);
if(entry == null)
{
throw new FileNotFoundException("cannot find file: " + fileName + " in archive: " + zipFile.getName());
}
zipStream = zipFile.getInputStream(entry);
fileStream = null;
try
{
final byte[] buf;
int i;
fileStream = new FileOutputStream(tempFile);
buf = new byte[1024];
i = 0;
while((i = zipStream.read(buf)) != -1)
{
fileStream.write(buf, 0, i);
}
}
finally
{
close(zipStream);
close(fileStream);
}
return (tempFile.toURI());
}
private static void close(final Closeable stream)
{
if(stream != null)
{
try
{
stream.close();
}
catch(final IOException ex)
{
ex.printStackTrace();
}
}
}
}
#5
0
1) Include the native library into your JAR as a Resource. E. g. with Maven or Gradle, and the standard project layout, put the native library into main/resources
directory.
1)将本机库作为资源包含在JAR中。 E. g。使用Maven或Gradle以及标准项目布局,将本机库放入main / resources目录。
2) Somewhere in static initializers of Java classes, related to this library, put the code like the following:
2)在与这个库相关的Java类的静态初始化器中的某处,放置如下代码:
String libName = "myNativeLib.so"; // The name of the file in resources/ dir
URL url = MyClass.class.getResource("/" + libName);
File tmpDir = Files.createTempDirectory("my-native-lib").toFile();
tmpDir.deleteOnExit();
File nativeLibTmpFile = new File(tmpDir, libName);
nativeLibTmpFile.deleteOnExit();
try (InputStream in = url.openStream()) {
Files.copy(in, nativeLibTmpFile.toPath());
}
System.load(nativeLibTmpFile.getAbsolutePath());
#1
31
https://www.adamheinrich.com/blog/2012/12/how-to-load-native-jni-library-from-jar/
https://www.adamheinrich.com/blog/2012/12/how-to-load-native-jni-library-from-jar/
is great article, which solves my issue ..
是一篇伟大的文章,它解决了我的问题..
In my case I've got the following code for initialize the library:
在我的情况下,我有以下代码来初始化库:
static {
try {
System.loadLibrary("crypt"); // used for tests. This library in classpath only
} catch (UnsatisfiedLinkError e) {
try {
NativeUtils.loadLibraryFromJar("/natives/crypt.dll"); // during runtime. .DLL within .JAR
} catch (IOException e1) {
throw new RuntimeException(e1);
}
}
}
#2
16
Take a look at One-JAR. It will wrap your application up in a single jar file with a specialised class loader which handles "jars within jars" among other things.
看看One-JAR。它将你的应用程序包装在一个jar文件中,该文件包含一个专门的类加载器,它可以处理“jars in jars”等。
It handles native (JNI) libraries by unpacking them to a temporary working folder as required.
它通过根据需要将它们解压缩到临时工作文件夹来处理本机(JNI)库。
(Disclaimer: I've never used One-JAR, haven't needed to as yet, just had it bookmarked for a rainy day.)
(免责声明:我从来没有使用过One-JAR,还没有使用过,只是为了下雨天给它添加了书签。)
#3
5
JarClassLoader is a class loader to load classes, native libraries and resources from a single monster JAR and from JARs inside the monster JAR.
JarClassLoader是一个类加载器,用于从单个怪物JAR和怪物JAR内的JAR加载类,本机库和资源。
#4
1
You will probably have to unjar the native library to the local file system. As far as I know the bit of code that does the native loading looks at the file system.
您可能必须将本机库解开到本地文件系统。据我所知,本机加载的代码位于文件系统。
This code should help get you started (I haven't looked at it in a while, and it is for a different purpose but should do the trick, and I am pretty busy at the moment, but if you have questions just leave a comment and I'll answer as soon as I can).
这段代码应该可以帮助你入门(我暂时没有看过它,这是为了一个不同的目的,但应该做到这一点,我现在很忙,但如果你有问题只需发表评论我会尽快回答。)
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
public class FileUtils
{
public static String getFileName(final Class<?> owner,
final String name)
throws URISyntaxException,
ZipException,
IOException
{
String fileName;
final URI uri;
try
{
final String external;
final String decoded;
final int pos;
uri = getResourceAsURI(owner.getPackage().getName().replaceAll("\\.", "/") + "/" + name, owner);
external = uri.toURL().toExternalForm();
decoded = external; // URLDecoder.decode(external, "UTF-8");
pos = decoded.indexOf(":/");
fileName = decoded.substring(pos + 1);
}
catch(final FileNotFoundException ex)
{
fileName = null;
}
if(fileName == null || !(new File(fileName).exists()))
{
fileName = getFileNameX(owner, name);
}
return (fileName);
}
private static String getFileNameX(final Class<?> clazz, final String name)
throws UnsupportedEncodingException
{
final URL url;
final String fileName;
url = clazz.getResource(name);
if(url == null)
{
fileName = name;
}
else
{
final String decoded;
final int pos;
decoded = URLDecoder.decode(url.toExternalForm(), "UTF-8");
pos = decoded.indexOf(":/");
fileName = decoded.substring(pos + 1);
}
return (fileName);
}
private static URI getResourceAsURI(final String resourceName,
final Class<?> clazz)
throws URISyntaxException,
ZipException,
IOException
{
final URI uri;
final URI resourceURI;
uri = getJarURI(clazz);
resourceURI = getFile(uri, resourceName);
return (resourceURI);
}
private static URI getJarURI(final Class<?> clazz)
throws URISyntaxException
{
final ProtectionDomain domain;
final CodeSource source;
final URL url;
final URI uri;
domain = clazz.getProtectionDomain();
source = domain.getCodeSource();
url = source.getLocation();
uri = url.toURI();
return (uri);
}
private static URI getFile(final URI where,
final String fileName)
throws ZipException,
IOException
{
final File location;
final URI fileURI;
location = new File(where);
// not in a JAR, just return the path on disk
if(location.isDirectory())
{
fileURI = URI.create(where.toString() + fileName);
}
else
{
final ZipFile zipFile;
zipFile = new ZipFile(location);
try
{
fileURI = extract(zipFile, fileName);
}
finally
{
zipFile.close();
}
}
return (fileURI);
}
private static URI extract(final ZipFile zipFile,
final String fileName)
throws IOException
{
final File tempFile;
final ZipEntry entry;
final InputStream zipStream;
OutputStream fileStream;
tempFile = File.createTempFile(fileName.replace("/", ""), Long.toString(System.currentTimeMillis()));
tempFile.deleteOnExit();
entry = zipFile.getEntry(fileName);
if(entry == null)
{
throw new FileNotFoundException("cannot find file: " + fileName + " in archive: " + zipFile.getName());
}
zipStream = zipFile.getInputStream(entry);
fileStream = null;
try
{
final byte[] buf;
int i;
fileStream = new FileOutputStream(tempFile);
buf = new byte[1024];
i = 0;
while((i = zipStream.read(buf)) != -1)
{
fileStream.write(buf, 0, i);
}
}
finally
{
close(zipStream);
close(fileStream);
}
return (tempFile.toURI());
}
private static void close(final Closeable stream)
{
if(stream != null)
{
try
{
stream.close();
}
catch(final IOException ex)
{
ex.printStackTrace();
}
}
}
}
#5
0
1) Include the native library into your JAR as a Resource. E. g. with Maven or Gradle, and the standard project layout, put the native library into main/resources
directory.
1)将本机库作为资源包含在JAR中。 E. g。使用Maven或Gradle以及标准项目布局,将本机库放入main / resources目录。
2) Somewhere in static initializers of Java classes, related to this library, put the code like the following:
2)在与这个库相关的Java类的静态初始化器中的某处,放置如下代码:
String libName = "myNativeLib.so"; // The name of the file in resources/ dir
URL url = MyClass.class.getResource("/" + libName);
File tmpDir = Files.createTempDirectory("my-native-lib").toFile();
tmpDir.deleteOnExit();
File nativeLibTmpFile = new File(tmpDir, libName);
nativeLibTmpFile.deleteOnExit();
try (InputStream in = url.openStream()) {
Files.copy(in, nativeLibTmpFile.toPath());
}
System.load(nativeLibTmpFile.getAbsolutePath());