আমার নিজের জারের প্রকাশ্য পঠন Read


134

আমার Manifestফাইলটি পড়তে হবে যা আমার ক্লাস সরবরাহ করেছে, তবে আমি যখন ব্যবহার করি:

getClass().getClassLoader().getResources(...)

আমি MANIFESTপ্রথমটি .jarজাভা রানটাইমে লোড করেছি।
আমার অ্যাপ্লিকেশনটি কোনও অ্যাপলেট বা ওয়েবস্টার্ট থেকে চলবে,
তাই আমার নিজের .jarফাইলটিতে অ্যাক্সেস থাকবে না বলে আমার ধারণা।

আমি প্রকৃতপক্ষে সেই Export-packageবৈশিষ্ট্যটি পড়তে চাই .jarযা ফেলিক্স ওএসজি শুরু করেছিল, তাই আমি সেই প্যাকেজগুলি ফেলিক্সে প্রকাশ করতে পারি। কোন ধারনা?


3
আমি মনে করি নীচে ফ্রেমওয়ার্ক ইউটেল.জেটবান্ডেল () উত্তরটি সেরা। এটি আপনি যা চেয়েছিলেন (ম্যানিফেস্টটি পড়ুন) এর চেয়ে আপনি আসলে কী করতে চান (বান্ডেলের রফতানি পান) এর উত্তর দেয় ।
ক্রিস ডোলান

উত্তর:


117

আপনি দুটি কাজের একটি করতে পারেন:

  1. getResources()ইউআরএলগুলির প্রত্যাশিত সংগ্রহের মাধ্যমে কল করুন এবং পুনরাবৃত্তি করুন , যতক্ষণ না আপনি নিজের খুঁজে পান ততক্ষণ এগুলিকে প্রকাশ করে:

    Enumeration<URL> resources = getClass().getClassLoader()
      .getResources("META-INF/MANIFEST.MF");
    while (resources.hasMoreElements()) {
        try {
          Manifest manifest = new Manifest(resources.nextElement().openStream());
          // check that this is your manifest and do what you need or get the next one
          ...
        } catch (IOException E) {
          // handle
        }
    }
  2. getClass().getClassLoader()উদাহরণস্বরূপ কিনা তা পরীক্ষা করে দেখতে পারেন java.net.URLClassLoader। সান শ্রেণিবদ্ধের সংখ্যাগরিষ্ঠতা সহ AppletClassLoader। এরপরে আপনি এটিকে কাস্ট করতে পারেন এবং কল করতে পারেন findResource()যা জানা গেছে - অ্যাপলেটগুলির জন্য, কমপক্ষে - প্রয়োজনীয় ম্যানিফেস্টটি সরাসরি ফেরত দিতে:

    URLClassLoader cl = (URLClassLoader) getClass().getClassLoader();
    try {
      URL url = cl.findResource("META-INF/MANIFEST.MF");
      Manifest manifest = new Manifest(url.openStream());
      // do stuff with it
      ...
    } catch (IOException E) {
      // handle
    }

5
পারফেক্ট! আমি কখনও জানতাম না যে আপনি একই নামে সংস্থান দিয়ে পুনরুক্তি করতে পারেন।
হাটম্যান

আপনি কীভাবে জানবেন যে ক্লাসলোডার কেবল একটি একক .jar ফাইল সম্পর্কে সচেতন? (অনেক ক্ষেত্রে সত্যই আমি অনুমান করি) আমি বরং বরং প্রশ্নযুক্ত শ্রেণীর সাথে সরাসরি যুক্ত কিছু ব্যবহার করব।
জেসন এস

7
এটি একটি এর ভাল অভ্যাস করতে পৃথক পরিবর্তে একটি উত্তর 2 সংশোধন করা হয়েছে সহ, প্রত্যেকের জন্য উত্তর। পৃথক উত্তরগুলি স্বাধীনভাবে ভোট দেওয়া যায়।
আলবা মেন্ডেজ

কেবল একটি নোট: আমার অনুরূপ কিছু প্রয়োজন ছিল তবে আমি জেবস-এর একটি যুদ্ধের মধ্যে রয়েছি, সুতরাং দ্বিতীয় পদ্ধতির আমার পক্ষে কাজ হয়নি। আমি স্ট্যাকওভারফ্লো
গ্রেগোর

1
প্রথম বিকল্পটি আমার পক্ষে কার্যকর হয়নি। আমি আমার 62 নির্ভরতা বয়াম এর টেপা পেয়েছিলাম, কিন্তু না এক যেখানে বর্তমান বর্গ সংজ্ঞায়িত করা হয় ...
Jolta

120

আপনি প্রথমে আপনার ক্লাসের জন্য URL খুঁজে পাবেন। যদি এটি একটি জার হয়, তবে আপনি সেখান থেকে ম্যানিফেস্টটি লোড করুন। উদাহরণ স্বরূপ,

Class clazz = MyClass.class;
String className = clazz.getSimpleName() + ".class";
String classPath = clazz.getResource(className).toString();
if (!classPath.startsWith("jar")) {
  // Class not from JAR
  return;
}
String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + 
    "/META-INF/MANIFEST.MF";
Manifest manifest = new Manifest(new URL(manifestPath).openStream());
Attributes attr = manifest.getMainAttributes();
String value = attr.getValue("Manifest-Version");

আমি এই সমাধানটি পছন্দ করি কারণ এটি অনুসন্ধান না করে সরাসরি আপনার নিজস্ব প্রকাশ পায়।
জে

1
শর্ত চেক অপসারণ করে কিছুটা উন্নতি করা যায়classPath.replace("org/example/MyClass.class", "META-INF/MANIFEST.MF"
জয়

2
স্রোত কে বন্ধ করে দেয়?
14

1
এটি অভ্যন্তরীণ ক্লাসগুলিতে কাজ করে না, কারণ getSimpleNameবাইরের শ্রেণীর নাম সরিয়ে দেয়। এই ভেতরের শ্রেণীর জন্য কাজ করবে: clazz.getName().replace (".", "/") + ".class"
সিলিং

3
আপনার স্ট্রিমটি বন্ধ করতে হবে, ম্যানিফেস্ট কনস্ট্রাক্টর তা করেন না।
ব্রায়ানটি

21

আপনি jcabi-manifestsManifests থেকে ব্যবহার করতে পারেন এবং কেবলমাত্র একটি লাইনের সাহায্যে উপলব্ধ যে কোনও MANIFEST.MF ফাইল থেকে যে কোনও বৈশিষ্ট্য পড়তে পারেন:

String value = Manifests.read("My-Attribute");

আপনার কেবলমাত্র নির্ভরতা হ'ল:

<dependency>
  <groupId>com.jcabi</groupId>
  <artifactId>jcabi-manifests</artifactId>
  <version>0.7.5</version>
</dependency>

এছাড়াও, আরও বিশদের জন্য এই ব্লগ পোস্টটি দেখুন: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html


খুব সুন্দর লাইব্রেরি। লগ স্তর নিয়ন্ত্রণ করার কোন উপায় আছে?
assylias

1
সমস্ত jcabi libs SLF4J মাধ্যমে লগ। আপনি যে কোনও সুবিধা ব্যবহার করে লগ বার্তা প্রেরণ করতে পারেন, উদাহরণস্বরূপ লগ 4 জ বা লগব্যাক
ইয়েগার 256

যদি logback.xml ব্যবহার লাইন আপনি যোগ করতে প্রয়োজন মত হল<logger name="com.jcabi.manifests" level="OFF"/>
driftcatcher

1
একই ক্লাসলোডার ওভারল্যাপ থেকে একাধিক প্রকাশ এবং একে অপরকে ওভাররাইট করে
গুয়াই

13

আমি সামনে স্বীকার করব যে এই উত্তরটি মূল প্রশ্নের উত্তর দেয় না, সাধারণভাবে ম্যানিফেস্টে অ্যাক্সেস করতে সক্ষম হওয়ায় of তবে যদি সত্যিই প্রয়োজনীয় হয় তবে বেশ কয়েকটি "স্ট্যান্ডার্ড" ম্যানিফেস্ট বৈশিষ্ট্যগুলির মধ্যে একটি পড়তে হলে উপরের পোস্টগুলির তুলনায় নিম্নলিখিত সমাধানটি অনেক সহজ। সুতরাং আমি আশা করি যে মডারেটর এটি অনুমতি দেবে। নোট করুন যে এই সমাধানটি জাভা নয়, কোটলিনে রয়েছে, তবে আমি আশা করব যে জাভাতে কোনও বন্দর ক্ষুদ্র হবে। (যদিও আমি স্বীকার করি আমি ".`package`" এর জাভা সমতুল্য জানি না।

আমার ক্ষেত্রে আমি "ইমপ্লিমেন্টেশন-ভার্সন" বৈশিষ্ট্যটি পড়তে চেয়েছিলাম তাই আমি স্ট্রিমটি পাওয়ার জন্য উপরে বর্ণিত সমাধানগুলি দিয়ে শুরু করেছিলাম এবং তারপরে মানটি পেতে এটি পড়ি। এই সমাধানটি কাজ করার সময়, একজন সহকর্মী আমার কোডটি পর্যালোচনা করেছেন আমাকে যা করতে চান তা করার একটি সহজ উপায় আমাকে দেখায়। নোট করুন যে এই সমাধানটি জাভা নয়, কোটলিনে।

val myPackage = MyApplication::class.java.`package`
val implementationVersion = myPackage.implementationVersion

আবার নোট করুন যে এটি আসল প্রশ্নের উত্তর দেয় না, বিশেষত "রফতানি-প্যাকেজ" সমর্থিত বৈশিষ্ট্যগুলির মধ্যে একটি বলে মনে হয় না। বলেছিল, এখানে একটি মাইপ্যাকেজ.নাম রয়েছে যা একটি মান দেয়। সম্ভবত যে কেউ আমার চেয়ে বেশি বোঝে এটির মূল পোস্টারটি যে অনুরোধ করছে তার মানটি ফিরিয়ে দেয় কিনা সে সম্পর্কে মন্তব্য করতে পারেন।


4
প্রকৃতপক্ষে, জাভা বন্দরটি সোজা:String implementationVersion = MyApplication.class.getPackage().getImplementationVersion();
আয়ান রবার্টসন

এটি সত্য যা আমি খুঁজছিলাম। আমিও খুশি যে জাভা এরও সমতুল্য।
আলেকসান্দার স্টেলম্যাকজোনেক

12

আমি বিশ্বাস করি যে কোনও বান্ডিলের জন্য ম্যানিফেস্টটি পাওয়ার সবচেয়ে উপযুক্ত উপায় (প্রদত্ত বর্গটি যে বান্ডিলটি সরবরাহ করেছে) বান্ডেল বা বান্ডেল কনটেক্সট অবজেক্টটি ব্যবহার করা।

// If you have a BundleContext
Dictionary headers = bundleContext.getBundle().getHeaders();

// If you don't have a context, and are running in 4.2
Bundle bundle = FrameworkUtil.getBundle(this.getClass());
bundle.getHeaders();

নোট করুন যে বান্ডিল অবজেক্টটি getEntry(String path)সেই বান্ডেলের পুরো ক্লাসপথটি অনুসন্ধান করার পরিবর্তে একটি নির্দিষ্ট বান্ডিলের মধ্যে থাকা সংস্থানগুলি অনুসন্ধান করতে সরবরাহ করে।

সাধারণভাবে, আপনি যদি বান্ডিল-নির্দিষ্ট তথ্য চান তবে শ্রেণিবদ্ধদের সম্পর্কে অনুমানের উপর নির্ভর করবেন না, কেবল ওএসজিআই এপিআই সরাসরি ব্যবহার করুন।


9

নিম্নলিখিত কোডটি একাধিক ধরণের সংরক্ষণাগার (জার, যুদ্ধ) এবং একাধিক ধরণের শ্রেণীবদ্ধার (জার, ইউআরএল, ভিএফএস, ...) এর সাথে কাজ করে works

  public static Manifest getManifest(Class<?> clz) {
    String resource = "/" + clz.getName().replace(".", "/") + ".class";
    String fullPath = clz.getResource(resource).toString();
    String archivePath = fullPath.substring(0, fullPath.length() - resource.length());
    if (archivePath.endsWith("\\WEB-INF\\classes") || archivePath.endsWith("/WEB-INF/classes")) {
      archivePath = archivePath.substring(0, archivePath.length() - "/WEB-INF/classes".length()); // Required for wars
    }

    try (InputStream input = new URL(archivePath + "/META-INF/MANIFEST.MF").openStream()) {
      return new Manifest(input);
    } catch (Exception e) {
      throw new RuntimeException("Loading MANIFEST for class " + clz + " failed!", e);
    }
  }

clz.getResource(resource).toString()ব্যাকস্ল্যাশস ফলাফল হতে পারে?
বেসিন

9

সবচেয়ে সহজ উপায় হ'ল জারআরএল সংযোগ শ্রেণি ব্যবহার করা:

String className = getClass().getSimpleName() + ".class";
String classPath = getClass().getResource(className).toString();
if (!classPath.startsWith("jar")) {
    return DEFAULT_PROPERTY_VALUE;
}

URL url = new URL(classPath);
JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
Manifest manifest = jarConnection.getManifest();
Attributes attributes = manifest.getMainAttributes();
return attributes.getValue(PROPERTY_NAME);

কারণ কিছু ক্ষেত্রে ...class.getProtectionDomain().getCodeSource().getLocation();পথ দেয় vfs:/, সুতরাং এটি অতিরিক্তভাবে পরিচালনা করা উচিত।


এটি এখন পর্যন্ত এটি করার সবচেয়ে সহজ এবং পরিষ্কার উপায়।
ওয়ালেন

6

আপনি getProtectionDomain () ব্যবহার করতে পারেন। GetCodeSource () এর মতো:

URL url = Menu.class.getProtectionDomain().getCodeSource().getLocation();
File file = DataUtilities.urlToFile(url);
JarFile jar = null;
try {
    jar = new JarFile(file);
    Manifest manifest = jar.getManifest();
    Attributes attributes = manifest.getMainAttributes();
    return attributes.getValue("Built-By");
} finally {
    jar.close();
}

1
getCodeSourceফিরে আসতে পারে null। মানদণ্ডগুলি কী কী, এটি কাজ করবে? ডকুমেন্টেশন এই ব্যাখ্যা নেই।
সিলিং

4
কোথা DataUtilitiesথেকে আমদানি করা হয়? এটি জেডিকে রয়েছে বলে মনে হয় না।
Jolta

2

আপনি কেন getClassLoader পদক্ষেপটি অন্তর্ভুক্ত করছেন? যদি আপনি "this.getClass ()। GetResource ()" বলে থাকেন তবে কলিং ক্লাসের তুলনায় আপনার রিসোর্স পাওয়া উচিত। আমি কখনও ClassLoader.getResource () ব্যবহার করি নি, যদিও জাভা ডক্সের এক তাত্ক্ষণিক দৃষ্টিভঙ্গি থেকে মনে হয় যে এটি আপনাকে বর্তমানের কোনও ক্লাসপথের মধ্যে পাওয়া নামেরটির প্রথম উত্স পেয়েছে।


যদি আপনার ক্লাসটির নাম দেওয়া হয়েছে "com.mypackage.MyClass", কলিং সেই উত্সটি class.getResource("myresource.txt")লোড করার চেষ্টা করবে com/mypackage/myresource.txt। প্রকাশটি পেতে আপনি কীভাবে এই পদ্ধতির ব্যবহার করতে চলেছেন?
ChssPly76

1
ঠিক আছে, আমাকে ব্যাকট্র্যাক করতে হবে। এটিই টেস্টিং না আসে। আমি ভাবছিলাম যে আপনি এটি বলতে পারেন। ডিরেক্টরি শ্রেণীর ফাইলগুলি ডিরেক্টরি ডিরেক্টরিতে কাজ করার জন্য এটি কাজ করে, এটি দৃশ্যত JARs এর জন্য কাজ করে না। আমি কেন দেখছি না, তবে এটি এমনই। না এটি.বিটক্লাস ()। গেট রিসোর্স ("/ মেটা-আইএনএফ / ম্যানিফেসেস্ট। এমএফ") কাজ করে - যা আমাকে আরটি.জারের জন্য প্রকাশিত করে। (চালিয়ে যেতে ...)
জয়

আপনি যা করতে পারেন তা হল নিজের শ্রেণীর ফাইলের পথ সন্ধানের জন্য getResource ব্যবহার করা, তারপরে "!" এর পরে সমস্ত কিছু সরিয়ে ফেলুন! জারে পাথ পেতে, তারপরে "/META-INF/MANIFEST.MF" যুক্ত করুন। ঝিহং যেমন পরামর্শ দিয়েছে, তাই আমি তার ভোট দিয়েছি।
জে

1
  public static Manifest getManifest( Class<?> cl ) {
    InputStream inputStream = null;
    try {
      URLClassLoader classLoader = (URLClassLoader)cl.getClassLoader();
      String classFilePath = cl.getName().replace('.','/')+".class";
      URL classUrl = classLoader.getResource(classFilePath);
      if ( classUrl==null ) return null;
      String classUri = classUrl.toString();
      if ( !classUri.startsWith("jar:") ) return null;
      int separatorIndex = classUri.lastIndexOf('!');
      if ( separatorIndex<=0 ) return null;
      String manifestUri = classUri.substring(0,separatorIndex+2)+"META-INF/MANIFEST.MF";
      URL url = new URL(manifestUri);
      inputStream = url.openStream();
      return new Manifest( inputStream );
    } catch ( Throwable e ) {
      // handle errors
      ...
      return null;
    } finally {
      if ( inputStream!=null ) {
        try {
          inputStream.close();
        } catch ( Throwable e ) {
          // ignore
        }
      }
    }
  }

এই উত্তরটি ম্যানিফেস্টটি লোড করার একটি খুব জটিল এবং ত্রুটিযুক্ত প্রবণতা ব্যবহার করে। আরও সহজ সমাধান ব্যবহার করা হয় cl.getResourceAsStream("META-INF/MANIFEST.MF")
রবার্ট

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

কোনও নির্দিষ্ট সংস্থান লোড করার জন্য আপনি কীভাবে আমাদের এবং ক্লাসলোডারটি নিয়ে আমি সমালোচনা করি নি। আমি উল্লেখ করছিলাম যে এর মধ্যে সমস্ত কোড classLoader.getResource(..)এবং url.openStream()সম্পূর্ণ অপ্রাসঙ্গিক এবং ত্রুটি প্রবণ হিসাবে এটি একইভাবে করার চেষ্টা classLoader.getResourceAsStream(..)করে।
রবার্ট

নাঃ। এটা ভিন্ন. আমার কোডটি নির্দিষ্ট জার থেকে উদ্ভূত হয় যেখানে ক্লাসপথের প্রথম জারটির চেয়ে ক্লাসটি অবস্থিত।
অ্যালেক্স কনশিন

আপনার "জার নির্দিষ্ট লোডিং কোড" নিম্নলিখিত দুটি লাইনের সমতুল্য:ClassLoader classLoader = cl.getClassLoader(); return new Manifest(classLoader.getResourceAsStream("/META-INF/MANIFEST.MF"));
রবার্ট

0

আমি অ্যান্টনি জুকেলের কাছ থেকে সমাধানটি ব্যবহার করেছি তবে MANIFEST.MF- এ চাবিটি বড় হাতের সাহায্যে শুরু করতে হবে।

সুতরাং আমার MANIFEST.MF ফাইলের মধ্যে একটি কী রয়েছে:

মাইকি: মান

তারপরে অ্যাক্টিভেটর বা অন্য কোনও ক্লাসে আপনি অ্যান্টনি থেকে কোডটি MANIFEST.MF ফাইল এবং আপনার প্রয়োজনীয় মানটি পড়তে পারেন।

// If you have a BundleContext 
Dictionary headers = bundleContext.getBundle().getHeaders();

// If you don't have a context, and are running in 4.2 
Bundle bundle = `FrameworkUtil.getBundle(this.getClass()); 
bundle.getHeaders();

0

আমার কাছে এই অদ্ভুত সমাধান রয়েছে যা এম্বেড জেটি সার্ভারে যুদ্ধের অ্যাপ্লিকেশনগুলি চালিত করে তবে এই অ্যাপ্লিকেশনগুলির স্ট্যান্ডার্ড টোম্যাট সার্ভারে চালানো দরকার এবং ম্যানফেস্টে আমাদের কিছু বিশেষ বৈশিষ্ট্য রয়েছে।

সমস্যাটি হ'ল টমকেটে যখন ম্যানিফেস্টটি পড়তে পারে তবে জেটিতে যখন একটি এলোমেলো ম্যানিফেস্টটি বেছে নেওয়া হয়েছিল (যা বিশেষ বৈশিষ্ট্যগুলি মিস করে)

অ্যালেক্স কনশিনের উত্তরের ভিত্তিতে আমি নিম্নলিখিত সমাধানটি নিয়ে এসেছি (ইনপুটস্ট্রিমটি তখন ম্যানিফেস্ট ক্লাসে ব্যবহৃত হয়):

private static InputStream getWarManifestInputStreamFromClassJar(Class<?> cl ) {
    InputStream inputStream = null;
    try {
        URLClassLoader classLoader = (URLClassLoader)cl.getClassLoader();
        String classFilePath = cl.getName().replace('.','/')+".class";
        URL classUrl = classLoader.getResource(classFilePath);
        if ( classUrl==null ) return null;
        String classUri = classUrl.toString();
        if ( !classUri.startsWith("jar:") ) return null;
        int separatorIndex = classUri.lastIndexOf('!');
        if ( separatorIndex<=0 ) return null;
        String jarManifestUri = classUri.substring(0,separatorIndex+2);
        String containingWarManifestUri = jarManifestUri.substring(0,jarManifestUri.indexOf("WEB-INF")).replace("jar:file:/","file:///") + MANIFEST_FILE_PATH;
        URL url = new URL(containingWarManifestUri);
        inputStream = url.openStream();
        return inputStream;
    } catch ( Throwable e ) {
        // handle errors
        LOGGER.warn("No manifest file found in war file",e);
        return null;
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.