問題のライブラリは Tokyo Cabinet です。
再配布の問題を回避するために、ネイティブライブラリ、JNIライブラリ、およびすべてのJava APIクラスを1つのJARファイルに含める必要があります。
GitHubでのこの試み があるようですが、
問題は、すべてを1つのJARにバンドルして再配布できますか?はいの場合、どのように?
追伸:はい、移植性に影響する可能性があることを認識しています。
1つ以上のプラットフォームのネイティブJNIライブラリを含むすべての依存関係を持つ単一のJARファイルを作成することができます。基本的なメカニズムは、Java.library.pathシステムプロパティを検索する一般的なSystem.loadLibrary(String)の代わりに、System.load(File)を使用してライブラリをロードすることです。ユーザーがシステムにJNIライブラリをインストールする必要がないため、この方法ではインストールがはるかに簡単になりますが、プラットフォームの特定のライブラリが単一のJARファイルに含まれないため、すべてのプラットフォームがサポートされない場合があります。
プロセスは次のとおりです。
Jzmqに対してこれを行う機能を追加しました。Java ZeroMQ(恥知らずのプラグ)のバインディング。コードは here にあります。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);
}
}
}
One-JAR を見てください。特に、「jar内のjar」を処理する特別なクラスローダーを使用して、アプリケーションを単一のjarファイルにラップします。
It ネイティブ(JNI)ライブラリを処理する 必要に応じて一時作業フォルダーに展開します。
(免責事項:One-JARは使用したことがないので、まだ必要ありません。雨の日にブックマークしておくだけです。)
JarClassLoader は、単一のモンスターJARおよびモンスターJAR内のJARからクラス、ネイティブライブラリ、およびリソースをロードするクラスローダーです。
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());
おそらく、ネイティブライブラリをローカルファイルシステムに解凍する必要があります。私の知る限り、ネイティブロードを行うコードの一部はファイルシステムを調べます。
このコードは、あなたが始めるのに役立つはずです(しばらく見ていないし、別の目的のためですが、トリックを行う必要があり、現時点ではかなり忙しいですが、質問がある場合はコメントを残してくださいできるだけ早く回答します)。
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();
}
}
}
}