需要翻译的内容如下:
所涉及的库是Tokyo Cabinet。
我想要将本地库、JNI库和所有Java API类放在一个JAR文件中,以避免重新分发时出现问题。
似乎GitHub上有人尝试这样做,但是:
- 它不包括实际的本地库,只有JNI库。
- 它似乎是特定于Leiningen的本地依赖项插件(它不能作为可再分发的程序使用)。
问题是,我能否将所有内容捆绑到一个JAR文件中并重新分发?如果可以,应该怎么做?
P.S.:是的,我意识到这可能会有可移植性方面的影响。
需要翻译的内容如下:
所涉及的库是Tokyo Cabinet。
我想要将本地库、JNI库和所有Java API类放在一个JAR文件中,以避免重新分发时出现问题。
似乎GitHub上有人尝试这样做,但是:
问题是,我能否将所有内容捆绑到一个JAR文件中并重新分发?如果可以,应该怎么做?
P.S.:是的,我意识到这可能会有可移植性方面的影响。
可以创建一个包含所有依赖项(包括一个或多个平台的本地JNI库)的单个JAR文件。基本机制是使用System.load(File)来加载库,而不是通常搜索java.library.path系统属性的System.loadLibrary(String)。这种方法使安装变得更简单,因为用户不必在其系统上安装JNI库,但代价是可能不支持所有平台,因为特定平台的库可能未包含在单个JAR文件中。
具体步骤如下:
我添加了一个功能来处理ZeroMQ的Java绑定(jzmq),代码可以在此处找到。jzmq代码使用混合解决方案,因此如果无法加载嵌入式库,则会回退到沿着java.library.path搜索JNI库的代码。
https://www.adamheinrich.com/blog/2012/12/how-to-load-native-jni-library-from-jar/
这是一篇很棒的文章,解决了我的问题。
在我的情况下,我有以下代码来初始化库:
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);
}
}
}
1)将本地库作为资源包含在您的JAR文件中。例如,使用Maven或Gradle和标准项目布局,将本地库放入main/resources
目录中。
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());
看看One-JAR吧。它能将您的应用程序封装在一个单独的jar文件中,其中包含一个专门的类加载器,可处理“jar中的jar”等其他事项。
它通过按需将其解压缩到临时工作文件夹来处理本地 (JNI) 库。
(免责声明:我从未使用过 One-JAR,至今还没有需要使用它,只是将其书签保存在那里以备不时之需。)
Kotlin解决方案:
build.gradle.dsl
: copy kotlin runtime (kotlin-stdlib-1.4.0.jar) and native library (librust_kotlin.dylib) to JAR
tasks.withType<Jar> {
manifest {
attributes("Main-Class" to "MainKt")
}
val libs = setOf("kotlin-stdlib-1.4.0.jar")
from(configurations.runtimeClasspath.get()
.filter { it.name in libs }
.map { zipTree(it) })
from("librust_kotlin.dylib")
}
main
method: copy library to a temporary file to load it using absolute path
with(createTempFile()) {
deleteOnExit()
val bytes = My::class.java.getResource("librust_kotlin.dylib")
.readBytes()
outputStream().write(bytes)
System.load(path)
}
你可能需要将本地库解压到本地文件系统中。据我所知,执行本地加载的代码段会查看文件系统。
这段代码应该能帮助你入门(我已经有一段时间没有看过它了,而且它是为不同的目的编写的,但应该能够完成任务,而且我现在很忙,但如果你有问题,请留言,我会尽快回答)。
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();
}
}
}
}
DLL
必须事先知道,并且它们的位置应该添加到PATH
环境变量中。 - daparic