itsource

내 항아리의 명세서 읽기

mycopycode 2022. 9. 17. 10:00
반응형

내 항아리의 명세서 읽기

제가 읽어야 할 것 같아Manifest이 파일은 내 수업을 전달했지만, 내가 사용할 때는:

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

이해하다MANIFEST처음부터.jarJava Runtime에 로드됩니다.
내 앱은 애플릿이나 웹스타트에서 실행됩니다.
그래서 나는 내 자신의 것에 접근할 수 없을 것이다..jar파일인 것 같아요.

사실 제가 읽고 싶은 건Export-package로부터의 어트리뷰트.jarFelix OSGi가 시작됐기 때문에 그 패키지를 Felix에게 노출시킬 수 있습니다.좋은 생각 있어요?

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

  1. 불러getResources()반환된 URL 컬렉션을 반복하여 자신의 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.URLClassLoaderSun 클래스 로더의 대부분은 다음과 같습니다.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-manifests에서 를 사용하여 사용 가능한 모든 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에 대한 포트는 간단할 것으로 예상됩니다.(자바에서 "arbit package"에 해당하는 것은 모릅니다만).

제 경우 "Implementation-Version" 속성을 읽고 싶었기 때문에 스트림을 얻기 위해 위에 제시된 솔루션부터 시작하여 그 값을 얻기 위해 읽었습니다.이 솔루션이 효과가 있는 동안 코드를 검토하는 동료가 원하는 작업을 보다 쉽게 수행할 수 있는 방법을 알려주었습니다.이 솔루션은 Java가 아닌 Kotlin에 있습니다.

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

다시 한 번 말씀드리지만, 이는 원래 질문에 대한 답변이 아닙니다.특히 "Export-package"는 지원되는 속성 중 하나가 아닌 것 같습니다.즉, 값을 반환하는 myPackage.name이 있습니다.아마 제가 말할 수 있는 것보다 더 잘 알고 있는 누군가가 그것이 원래의 포스터가 요구하는 가치를 반환하는지 여부에 대해 언급할 수 있을 것입니다.

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

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(); gives with gives 、 gives 、 gives 、 gives 。vfs:/추가 처리가 필요합니다.

다음 코드는 여러 유형의 아카이브(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);
    }
  }

모든 번들(특정 클래스를 로드한 번들 포함)의 매니페스트를 얻는 가장 적절한 방법은 Bundle 또는 Bundle Context 개체를 사용하는 것이라고 생각합니다.

// 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)특정 번들의 전체 클래스 경로를 검색하는 대신 해당 번들에 포함된 리소스를 검색합니다.

일반적으로 번들 고유의 정보를 필요로 하는 경우 클래스로더에 관한 전제조건에 의존하지 말고 OSGi API를 직접 사용합니다.

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

보다 간단한 방법은 get Package()를 사용하는 것입니다.예를 들어 Implementation-Version을 취득하려면 다음 절차를 수행합니다.

Application.class.getPackage().getImplementationVersion()

getClassLoader 스텝이 포함된 이유는 무엇입니까?"this.getClass().getResource()"라고 하면 호출 클래스에 관련된 리소스를 얻을 수 있습니다.ClassLoader.getResource()는 사용한 적이 없습니다만, Java Docs를 간단하게 보면 현재 클래스 패스에서 발견된 해당 이름의 첫 번째 리소스를 얻을 수 있을 것 같습니다.

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

나는 Anthony Juckel의 용액을 사용했지만, MANIFIST에서 사용하였습니다.MF 키는 대문자로 시작해야 합니다.

그래서 나의 매니페스트.MF 파일에는 다음과 같은 키가 포함되어 있습니다.

마이키:

그런 다음 액티베이터 또는 다른 클래스에서 Anthony의 코드를 사용하여 매니페스트를 읽을 수 있습니다.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();

임베디드 Jetty 서버에서 전쟁 애플리케이션을 실행하는 이상한 솔루션이 있지만 이 앱들은 표준 Tomcat 서버에서도 실행해야 합니다.맨페스트에는 특별한 속성이 몇 가지 있습니다.

문제는 Tomcat에서는 매니페스트를 읽을 수 있었지만, jetty에서는 랜덤 매니페스트를 픽업(특수 속성을 놓쳤다)하는 것이었습니다.

Alex Konshin의 답변을 바탕으로 다음과 같은 해결책을 생각해냈습니다(입력 스트림은 Manifest 클래스에서 사용됩니다).

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

언급URL : https://stackoverflow.com/questions/1272648/reading-my-own-jars-manifest

반응형