[java] 내 항아리의 매니페스트 읽기

Manifest클래스를 전달한 파일 을 읽어야 하지만 다음을 사용할 때 :

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

Java Runtime에 MANIFEST처음 .jar로드 된 것을 얻습니다 .
내 응용 프로그램이 애플릿 또는 웹 스타트에서 실행
되므로 내 .jar파일에 액세스 할 수 없습니다 .

실제로 Felix OSGi를 시작한 Export-package속성 을 읽고 싶습니다 .jar. 따라서 해당 패키지를 Felix에 공개 할 수 있습니다. 어떤 아이디어?



답변

다음 두 가지 중 하나를 수행 할 수 있습니다.

  1. getResources()반환 된 URL 모음을 호출 하고 반복하여 찾을 때까지 매니페스트로 읽습니다.

    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. 를 포함하여 대다수의 Sun 클래스 로더가 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
    }

답변

수업의 URL을 먼저 찾을 수 있습니다. JAR 인 경우 매니페스트를로드합니다. 예를 들어

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


답변

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


답변

이 답변이 원래 질문, 일반적으로 매니페스트에 액세스 할 수있는 질문에 답변하지 않는다는 점을 인정합니다. 그러나 실제로 필요한 것이 많은 “표준”매니페스트 속성 중 하나를 읽는 것이라면 다음 솔루션은 위에 게시 된 것보다 훨씬 간단합니다. 그래서 중재자가 허락하기를 바랍니다. 이 솔루션은 Java가 아닌 Kotlin에 있지만 Java 포트는 사소한 것으로 예상합니다. (나는 “.`package`”와 같은 Java를 모른다는 것을 인정하지만.

필자의 경우 “Implementation-Version”속성을 읽고 스트림을 얻기 위해 위에서 주어진 솔루션으로 시작한 다음 값을 얻기 위해 읽었습니다. 이 솔루션이 작동하는 동안 내 코드를 검토하는 동료가 원하는 것을 쉽게 수행 할 수있는 방법을 보여주었습니다. 이 솔루션은 Java가 아닌 Kotlin에 있습니다.

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

다시 한 번 이것은 원래 질문에 대한 답변이 아니며 특히 “내보내기 패키지”는 지원되는 속성 중 하나가 아닌 것 같습니다. 즉, 값을 반환하는 myPackage.name이 있습니다. 어쩌면 이것을 이해하는 사람은 원래 포스터가 요구하는 가치를 반환하는지 여부에 대해 언급 할 수 있습니다.


답변

특정 번들을로드 한 번들을 포함하여 모든 번들의 매니페스트를 얻는 가장 적절한 방법은 Bundle 또는 BundleContext 객체를 사용하는 것입니다.

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

Bundle 객체는 getEntry(String path)해당 번들의 전체 클래스 경로를 검색하지 않고 특정 번들에 포함 된 리소스를 조회 할 수도 있습니다 .

일반적으로 번들 특정 정보를 원하는 경우 클래스 로더에 대한 가정에 의존하지 말고 OSGi API를 직접 사용하십시오.


답변

다음 코드는 여러 유형의 아카이브 (jar, war) 및 여러 유형의 클래스 로더 (jar, url, vfs 등)에서 작동합니다.

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


답변

가장 쉬운 방법은 JarURLConnection 클래스를 사용하는 것입니다.

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:/하므로 추가로 처리해야합니다.