2008-09-17 12 views
20

나는 이것을 알아 내려고 너무 많은 시간을 보냈습니다. 이것은 가장 단순한 것이어야하며 Java 응용 프로그램을 jar 파일로 배포하는 모든 사용자는이를 처리해야합니다.프로덕션 환경에서 jar를 테스트하고 실행할 때 사용할 수있는 MANIFEST.MF를 어떻게 만듭니 까?

테스트 할 때 버전 정보에 액세스 할 수 있도록 Java 응용 프로그램에 버전을 추가하는 적절한 방법을 알고 싶습니다. Eclipse에서 디버깅 은 병에서 실행됩니다. 이 /META-INF/MANIFEST.MF를 생성

<target name="jar" depends = "compile"> 
    <property name="version.num" value="1.0.0"/> 
    <buildnumber file="build.num"/> 
    <tstamp> 
     <format property="TODAY" pattern="yyyy-MM-dd HH:mm:ss" /> 
    </tstamp> 

    <manifest file="${build}/META-INF/MANIFEST.MF"> 
     <attribute name="Built-By" value="${user.name}" /> 
     <attribute name="Built-Date" value="${TODAY}" />     
     <attribute name="Implementation-Title" value="MyApp" /> 
     <attribute name="Implementation-Vendor" value="MyCompany" />     
     <attribute name="Implementation-Version" value="${version.num}-b${build.number}"/>        
    </manifest> 

    <jar destfile="${build}/myapp.jar" basedir="${build}" excludes="*.jar" />     
</target> 

와 내가 thusly 히 이클립스에서 디버깅있어 값을 읽을 수 있습니다 :

public MyClass() 
{ 
    try 
    {       
     InputStream stream = getClass().getResourceAsStream("/META-INF/MANIFEST.MF"); 
     Manifest manifest = new Manifest(stream);    

     Attributes attributes = manifest.getMainAttributes(); 

     String implementationTitle = attributes.getValue("Implementation-Title"); 
     String implementationVersion = attributes.getValue("Implementation-Version"); 
     String builtDate = attributes.getValue("Built-Date"); 
     String builtBy = attributes.getValue("Built-By"); 
    } 
    catch (IOException e) 
    {    
     logger.error("Couldn't read manifest."); 
    }   
을 여기

내가 내 build.xml 파일에있는 무엇 내가 jar 파일을 만들 때

}

그러나, 그것은 다른 항아리의 매니페스트로드 (응용 프로그램에 의해로드 아마도 첫 번째 항아리 - 내 경우, activation.jar을에).

또한 적절한 값이 모두 매니페스트 파일에 있지만 다음 코드는 작동하지 않습니다.

Package thisPackage = getClass().getPackage(); 
    String implementationVersion = thisPackage.getImplementationVersion(); 

아이디어가 있으십니까?

답변

0

매니페스트를 사용하지 마십시오. 콘텐츠와 함께, 다음과 같은 당신이 foo.properties.original copu하는 사본을 할 수있는 JARING된다 = @ VERSION

@ 아플리케 같은 작업에 버전 및

0
을 foo.properties.original 파일을 만듭니다

또한 보통 버전 파일을 사용합니다. 각 jar는 자체 버전을 가질 수 있기 때문에 jar 당 하나의 파일을 생성 할 것입니다.

1

클래스를로드하는 데 사용 된 클래스 로더와 동일한 클래스 로더를 사용하는 경우 Jar 파일의 매니페스트 (또는 다른 파일)에 액세스 할 수 있습니다.

this.getClass().getClassLoader().getResourceAsStream(...) ; 

다음과 같은 다중 스레드를 사용하는 경우 :

Thread.currentThread().getContextClassLoader().getResourceAsStream(...) ; 

이것은 또한 단지 내에서 기본 구성 파일을 포함하기위한 정말 유용한 기술이다.

2

는이를 사용하려면 :

Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF"); 

당신은 매니페스트를 구문 분석 할 수는 getInputStream()를 통해 URL을 읽은 항아리에서 경우 다음 매니페스트 파악하기 위해 URL을 구문 분석 할 수 있습니다.

1

는 여기가 작동하는 것으로 나타났습니다 내용은 다음과 같습니다

packageVersion.java :

package com.company.division.project.packageversion; 

import java.io.IOException; 
import java.io.InputStream; 
import java.util.jar.Attributes; 
import java.util.jar.Manifest; 

public class packageVersion 
{ 
    void printVersion() 
    { 
     try 
     {   
      InputStream stream = getClass().getResourceAsStream("/META-INF/MANIFEST.MF"); 

      if (stream == null) 
      { 
       System.out.println("Couldn't find manifest."); 
       System.exit(0); 
      } 

      Manifest manifest = new Manifest(stream); 

      Attributes attributes = manifest.getMainAttributes(); 

      String impTitle = attributes.getValue("Implementation-Title"); 
      String impVersion = attributes.getValue("Implementation-Version"); 
      String impBuildDate = attributes.getValue("Built-Date"); 
      String impBuiltBy = attributes.getValue("Built-By"); 

      if (impTitle != null) 
      { 
       System.out.println("Implementation-Title: " + impTitle); 
      }    
      if (impVersion != null) 
      { 
       System.out.println("Implementation-Version: " + impVersion); 
      } 
      if (impBuildDate != null) 
      { 
       System.out.println("Built-Date: " + impBuildDate); 
      } 
      if (impBuiltBy != null) 
      { 
       System.out.println("Built-By: " + impBuiltBy); 
      } 

      System.exit(0); 
     } 
     catch (IOException e) 
     {    
      System.out.println("Couldn't read manifest."); 
     }   
    } 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) 
    { 
     packageVersion version = new packageVersion(); 
     version.printVersion();   
    } 

} 

여기 일치하는 빌드입니다.XML :

<project name="packageVersion" default="run" basedir="."> 

    <property name="src" location="src"/> 
    <property name="build" location="bin"/> 
    <property name="dist" location="dist"/> 

    <target name="init"> 
     <tstamp> 
      <format property="TIMESTAMP" pattern="yyyy-MM-dd HH:mm:ss" /> 
     </tstamp> 
     <mkdir dir="${build}"/> 
     <mkdir dir="${build}/META-INF"/> 
    </target> 

    <target name="compile" depends="init"> 
     <javac debug="on" srcdir="${src}" destdir="${build}"/> 
    </target> 

    <target name="dist" depends = "compile">   
     <mkdir dir="${dist}"/>  
     <property name="version.num" value="1.0.0"/> 
     <buildnumber file="build.num"/> 
     <manifest file="${build}/META-INF/MANIFEST.MF"> 
      <attribute name="Built-By" value="${user.name}" /> 
      <attribute name="Built-Date" value="${TIMESTAMP}" />         
      <attribute name="Implementation-Vendor" value="Company" /> 
      <attribute name="Implementation-Title" value="PackageVersion" /> 
      <attribute name="Implementation-Version" value="${version.num} (b${build.number})"/> 
      <section name="com/company/division/project/packageversion"> 
       <attribute name="Sealed" value="false"/> 
      </section>   
     </manifest>  
     <jar destfile="${dist}/packageversion-${version.num}.jar" basedir="${build}" manifest="${build}/META-INF/MANIFEST.MF"/>     
    </target> 

    <target name="clean"> 
     <delete dir="${build}"/> 
     <delete dir="${dist}"/> 
    </target> 

    <target name="run" depends="dist">  
     <java classname="com.company.division.project.packageversion.packageVersion"> 
      <arg value="-h"/> 
      <classpath> 
       <pathelement location="${dist}/packageversion-${version.num}.jar"/> 
       <pathelement path="${java.class.path}"/> 
      </classpath> 
     </java> 
    </target> 

</project> 
+0

를 제 3 자 라이브러리를 사용하여 실제 응용 프로그램에서, 이것은 잘못된를 반환 상당히 가능성이 명백한. –

+0

클래스 이름은 대문자로 시작해야합니다. packageVersion 대신 PackageVersion이어야합니다. – Carlos

1

ClassLoader.getResource(String)는 다른 JAR 파일의 매니페스트 될 수있는 클래스 경로에 찾은 첫 번째 매니페스트를로드합니다. 따라서 enumerate all the manifests을 사용하여 원하는 것을 찾거나 고유 한 이름을 가진 등록 정보 파일과 같은 다른 메커니즘을 사용할 수 있습니다.

+0

getResource()는 종종 잘못된 목록을 찾습니다. 나는 그것을 경험하고있다. 모든 매니페스트를 열거하기 위해 ClassLoader.getResources()에 대한 링크를 시도했지만 getResources ("/ META-INF/MANIFEST.MF")는 아무 것도 반환하지 않으며 getResources ("")는 유용하지 않은 것 이상을 반환합니다. 내가 잘못된 클래스 로더를 사용하고 있다고 의심하지만 "모든 클래스 목록을 열거하십시오"라는 "모든 클래스 목록을 열거하십시오"! – metamatt

1

나는 McDowell의 코멘트를 발견했다. MANIFEST.MF 파일은 클래스 패스에 따라 달라지며 원하는 것이 아닐 수도있다. 나는 link text

+0

이것은 거의 나를 위해 작동하지만, 어디서 왔는지에 대한 링크는 이제 깨졌습니다. Class.getPackage()가 점으로 구분 된 이름 (org.foo.bar)을 반환하고 Class.getSimpleName()이 슬래시로 구분 된 이름 (org/foo/bar)을 반환하기 때문에 "거의"라고 말합니다. 이 때문에, 나는 클래스 URL을 구문 분석하는 것을 피하는 gibbss의 대답을 좋아한다. – metamatt

10

에서 적응하는이

String cp = PCAS.class.getResource(PCAS.class.getSimpleName() + ".class").toString(); 
cp = cp.substring(0, cp.indexOf(PCAS.class.getPackage().getName())) 
       + "META-INF/MANIFEST.MF"; 
Manifest mf = new Manifest((new URL(cp)).openStream()); 

당신은 클래스 URL을 (취성이 될 수있는) 구문 분석하지 않고 임의의 항아리에 임의의 클래스에 대한 매니페스트를 얻을 수 있습니다 사용합니다. 원하는 항아리에있는 것으로 알고있는 리소스를 찾은 다음 JarURLConnection에 연결을 캐스트하십시오.

클래스가 jar에 번들로 제공되지 않을 때 코드가 작동하게하려면 반환 된 URL 연결 유형에 instanceof check를 추가하십시오. 압축을 푼 클래스 계층의 클래스는 JarUrlConnection 대신 내부 Sun FileURLConnection을 반환합니다. 그런 다음 다른 답변에 설명 된 InputStream 메소드 중 하나를 사용하여 Manifest를로드 할 수 있습니다.

@Test 
public void testManifest() throws IOException { 
    URL res = org.junit.Assert.class.getResource(org.junit.Assert.class.getSimpleName() + ".class"); 
    JarURLConnection conn = (JarURLConnection) res.openConnection(); 
    Manifest mf = conn.getManifest(); 
    Attributes atts = mf.getMainAttributes(); 
    for (Object v : atts.values()) { 
     System.out.println(v); 
    } 
} 
+0

니스, 고마워. 나는 여기에 모든 것을 시도했고, Class.getResource()가 (잘못된 항아리에) 종종 잘못된 명단을 발견하고 올바른 명단을 찾기위한 해결책을 제공한다는 것을 인정하는 (현재 3 가지) 대답 중 하나를 시도해 보았습니다. – metamatt

+0

당신이 뭔가'atts.getValue ("Attribute-Name")'또는'atts.get (새로운 Attributes.Name ("Attribute-Name"))'을 사용하고자한다면, 비 제네릭 코드 FTW! – TWiStErRob