কীভাবে কোনও জেআরের অভ্যন্তরে নেটিভ লাইব্রেরি এবং একটি জেএনআই লাইব্রেরি বান্ডিল করবেন?


104

প্রশ্নযুক্ত গ্রন্থাগারটি টোকিও মন্ত্রিসভা

আমি চাই পুনরায় বিতরণ মাথাব্যথা এড়াতে একটি জেআর ফাইলটিতে নেটিভ লাইব্রেরি, জেএনআই গ্রন্থাগার এবং সমস্ত জাভা এপিআই ক্লাস থাকা উচিত।

গিটহাব এ চেষ্টা করার চেষ্টা আছে বলে মনে হয় , কিন্তু

  1. এটিতে আসল নেটিভ লাইব্রেরি অন্তর্ভুক্ত নয়, কেবল জেএনআই লাইব্রেরি।
  2. এটি লেনিনজেনের নেটিভ নির্ভরশীলতা প্লাগইনের সাথে সুনির্দিষ্ট বলে মনে হচ্ছে (এটি পুনরায় বিতরণযোগ্য হিসাবে কাজ করবে না)।

প্রশ্নটি হ'ল, আমি কি একটি জেআর-তে সমস্ত কিছু বান্ডিল করে এটিকে পুনরায় বিতরণ করতে পারি? যদি হ্যাঁ, কিভাবে?

পিএস: হ্যাঁ, আমি বুঝতে পারি এটির বহনযোগ্যতা থাকতে পারে।

উত্তর:


54

এক বা একাধিক প্ল্যাটফর্মের জন্য নেটিভ জেএনআই লাইব্রেরি সহ সমস্ত নির্ভরতা সহ একটি একক জেআর ফাইল তৈরি করা সম্ভব। মৌলিক প্রক্রিয়াটি হ'ল java.library.path সিস্টেমের সম্পত্তি অনুসন্ধান করে এমন সাধারণ সিস্টেম.লোডলাইবারি (স্ট্রিং) এর পরিবর্তে লাইব্রেরিটি লোড করার জন্য System.load (ফাইল) ব্যবহার করা। এই পদ্ধতিটি ইনস্টলেশনটিকে আরও সহজ করে তোলে কারণ ব্যবহারকারীকে তার সিস্টেমে জেএনআই লাইব্রেরি ইনস্টল করতে হবে না, ব্যয় করে, তবে যে প্ল্যাটফর্মের জন্য নির্দিষ্ট লাইব্রেরি একক জেআর ফাইলের মধ্যে অন্তর্ভুক্ত নাও হতে পারে সেজন্য সমস্ত প্ল্যাটফর্ম সমর্থন করা যায় না might ।

প্রক্রিয়াটি নিম্নরূপ:

  • প্ল্যাটফর্মের নির্দিষ্ট জায়গায় জার ফাইলটিতে নেটিভ জেএনআই লাইব্রেরিগুলি অন্তর্ভুক্ত করুন, উদাহরণস্বরূপ NATIVE / $ {os.arch} / $ {os.name} /libname.lib এ
  • মূল শ্রেণীর স্ট্যাটিক ইনিশিয়ালিয়ায়ারে কোড তৈরি করুন
    • বর্তমান os.arch এবং os.name গণনা করুন
    • ক্লাস.জেট রিসোর্স (স্ট্রিং) ব্যবহার করে পূর্বনির্ধারিত স্থানে জার ফাইলটিতে পাঠাগারটি সন্ধান করুন
    • যদি এটি বিদ্যমান থাকে তবে এটি একটি অস্থায়ী ফাইলে এক্সট্রাক্ট করুন এবং এটি System.load (ফাইল) দিয়ে লোড করুন।

আমি জেজেএমকিউয়ের জন্য এটি করার জন্য কার্যকারিতা যুক্ত করেছি, জিরোএমকিউয়ের জাভা বাইন্ডিংস (নির্লজ্জ প্লাগ)। কোডটি এখানে পাওয়া যাবে । জেজেএমকিউ কোড একটি হাইব্রিড সমাধান ব্যবহার করে যাতে এম্বেড গ্রন্থাগারটি লোড করা না গেলে, কোডটি java.library.path বরাবর জেএনআই লাইব্রেরির সন্ধানে ফিরে যাবে।


4
আমি এটা ভালোবাসি! এটি সংহতকরণের জন্য প্রচুর ঝামেলা বাঁচায় এবং যদি এটি ব্যর্থ হয় তবে আপনি সর্বদা সিস্টেম.লোডলাইবারি () দিয়ে "পুরানো" পথে ফিরে যেতে পারেন। আমি মনে করি আমি এটি ব্যবহার শুরু করব। ধন্যবাদ! :)
ম্যাথিউউ

4
আমার লাইব্রেরি dll এর অন্য একটি dll এর উপর নির্ভরতা থাকলে আমি কী করব? আমি সর্বদা UnsatisfiedLinkErrorপ্রাক্তন ডিএল বোঝা হিসাবে প্রচ্ছন্নভাবে পরবর্তীটি লোড করতে চাইব, তবে এটি জারে লুকিয়ে থাকা হিসাবে এটি খুঁজে পাচ্ছি না। প্রথমটি পরে dll লোড করাও কোনও কাজে দেয় না।
ফেব

অন্যান্য নির্ভরশীলদের DLLঅবশ্যই আগে থেকেই জানা থাকতে হবে এবং তাদের অবস্থানগুলি PATHপরিবেশের পরিবর্তনশীলতে যুক্ত করা উচিত ।
ইগেনফিল্ড

দুর্দান্ত কাজ! যদি এটির জুড়ে আসার কারও কাছে এটি পরিষ্কার না থাকে তবে আপনার প্রকল্পে এম্বেডড লাইব্রারিটুলস ক্লাসটি ফেলে দিন এবং সেই অনুযায়ী এটি পরিবর্তন করুন।
জন লা মারার

41

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);
        }
    }
}

26
NativeUtils.loadLibraryFromJar ("/ নেটিভস /" + সিস্টেম.ম্যাপলিবারিয়াননেম ("ক্রিপ্ট")) ব্যবহার করুন; আরও ভাল হতে পারে
ব্ল্যাকজোকার

4
হাই যখন আমি নেটিভইটিলস ক্লাসটি ব্যবহার করার চেষ্টা করছি এবং .so ফাইলটি libs / armeabi / libmyname.so এর ভিতরে স্থাপন করার চেষ্টা করছি তখন আমি java.lang.ExceptionInInitializerError এর মত ব্যতিক্রম পাচ্ছি: দ্বারা java.io.FileNotFoundException: ফাইল / মূল / লিবিলোলোজনি.সো জেআর-এর ভিতরে খুঁজে পাওয়া যায় নি। আমি কেন ব্যতিক্রম পাচ্ছি তা আমাকে জানান। আপনাকে ধন্যবাদ
গণেশ

16

কটাক্ষপাত এক-JAR- র । এটি আপনার অ্যাপ্লিকেশনটিকে একক জার ফাইলে একটি বিশেষ শ্রেণীর লোডার দিয়ে গুটিয়ে রাখবে যা অন্যান্য জিনিসের মধ্যে "জারের মধ্যে জারগুলি" পরিচালনা করে।

এটি স্থানীয় (জেএনআই) লাইব্রেরিগুলি প্রয়োজনীয় হিসাবে অস্থায়ী ওয়ার্কিং ফোল্ডারে আনপ্যাক করে পরিচালনা করে।

(অস্বীকৃতি: আমি কখনই ওয়ান-জেআর ব্যবহার করি নি, এখনও কোনও বৃষ্টির দিনটি বুকমার্ক করে রাখার দরকার নেই) have


আমি একটি জাভা প্রোগ্রামার নই, কোন ধারণা যদি আমি আছে অ্যাপ্লিকেশনটিতে পুরো মোড়ানো? এটি কীভাবে বিদ্যমান ক্লাসলোডারের সাথে একত্রে কাজ করবে? স্পষ্ট করতে, আমি Clojure থেকে এটি ব্যবহার করতে যাচ্ছি এবং একটি অ্যাপ্লিকেশন না করে একটি JAR একটি লাইব্রেরি হিসাবে লোড করতে সক্ষম হতে চাই।
অ্যালেক্স বি

আহ্, এটি সম্ভবত তার চেয়ে উপযুক্ত হবে না। অ্যাপ্লিকেশনটির লাইব্রেরি পথে রাখার নির্দেশাবলী সহ আপনি জার ফাইলের বাইরে আপনার স্থানীয় গ্রন্থাগার (গুলি) বিতরণ করতে আটকে যাবেন বলে মনে করুন।
ইভান

9

1) একটি উত্স হিসাবে আপনার জেআর মধ্যে নেটিভ গ্রন্থাগার অন্তর্ভুক্ত। E. g। মাভেন বা গ্রেডল এবং স্ট্যান্ডার্ড প্রজেক্ট লেআউট সহ নেটিভ লাইব্রেরিকে main/resourcesডিরেক্টরিতে রেখে দিন।

২) কোথাও এই লাইব্রেরি সম্পর্কিত জাভা ক্লাসের স্ট্যাটিক ইনিশিয়ালাইজারগুলিতে নীচের মত কোডটি রাখুন:

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());

আপনি ওয়াইল্ডফ্লাইয়ের মতো কোনও অ্যাপ্লিকেশন সার্ভারে কিছু স্থাপন করতে চান এমন ক্ষেত্রেও এই সমাধানটি কাজ করে এবং সর্বোত্তম অংশটি হ'ল এটি অ্যাপ্লিকেশনটি সঠিকভাবে আনলোড করতে সক্ষম হয়!
হ্যাশ

'নেটিভ লাইব্রেরি ইতিমধ্যে' বোঝা ব্যতিক্রম এড়ানোর জন্য এটি সেরা সমাধান।
ক্রিতিকা ভিট্টল

5

জারক্লাসলোডার একক দানব জেআর এবং দানব জেআর এর অভ্যন্তরে জেআর থেকে ক্লাস, নেটিভ গ্রন্থাগার এবং সংস্থানগুলি লোড করার জন্য একটি শ্রেণি লোডার load


1

আপনাকে সম্ভবত স্থানীয় ফাইল সিস্টেমে নেটিভ লাইব্রেরিটি আনজার করতে হবে। যতদূর আমি জানি কোডের বিটটি যা দেশীয় লোডিং ফাইল সিস্টেমকে দেখে looks

এই কোডটি আপনাকে শুরু করতে সহায়তা করবে (আমি কিছুক্ষণের মধ্যে এটির দিকে নজর দিইনি, এবং এটি অন্যরকম উদ্দেশ্যে রয়েছে তবে কৌশলটি করা উচিত এবং আমি এই মুহুর্তে বেশ ব্যস্ত, তবে আপনার যদি প্রশ্ন থাকে তবে একটি মন্তব্য করুন এবং আমি যত তাড়াতাড়ি সম্ভব উত্তর দেব)।

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();
            }
        }
    }
}

4
আপনার যদি কিছু নির্দিষ্ট সংস্থান অপসারণের দরকার হয় তবে আমি github.com/zeroturnaround/zt-zip প্রকল্পটি একবার দেখার পরামর্শ দিই
নিম প্রিক্স

zt-zip দেখতে একটি শালীন এপিআইয়ের মতো লাগে।
তোফুবিয়ার

0

কোটলিনের জন্য সমাধান:

  • build.gradle.dsl: কোটলিন রানটাইম কপি করুন

    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 পদ্ধতি: নিখরচায় পাথ ব্যবহার করে এটি লোড করার জন্য অস্থায়ী ফাইলটিতে লাইব্রেরিটি অনুলিপি করুন

     with(createTempFile()) {
         deleteOnExit()
         val bytes = My::class.java.getResource("librust_kotlin.dylib")
             .readBytes()
    
         outputStream().write(bytes)
         System.load(path)
     }
    
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.