2008-09-17 11 views
20

Spędziłem zbyt dużo czasu, próbując to rozgryźć. To powinno być najprostsze, a każdy, kto rozprowadza aplikacje Java w słoikach, musi sobie z tym poradzić.Jak utworzyć MANIFEST.MF, który jest dostępny podczas testowania i uruchamiania ze słoika w produkcji?

Po prostu chcę wiedzieć, w jaki sposób dodać wersję do mojej aplikacji Java, aby uzyskać dostęp do informacji o wersji podczas testowania, np. debugowanie w środowisku Eclipse i z poziomu słoika.

Oto co mam w moim build.xml:

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

Stwarza /META-INF/MANIFEST.MF i mogę odczytać wartości, kiedy jestem debugowania w Eclipse wygląda następująco:

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

}

Ale, gdy tworzę plik jar, ładuje manifest innego słoika (przypuszczalnie pierwszym słoiku załadowany przez aplikację - w moim przypadku, activation.jar).

Również poniższy kod nie działa, mimo że wszystkie właściwe wartości znajdują się w pliku manifestu.

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

Jakieś pomysły?

Odpowiedz

0

Po prostu nie używaj manifestu. Tworzenie foo.properties.original plik o zawartości takich jak wersji = @ VERSION @

A w ther samego zadania jesteś jaring można zrobić kopię Copu foo.properties.original a następnie

0

Zwykle będę również używać pliku wersji. Stworzę jeden plik na słoik, ponieważ każdy słoik może mieć własną wersję.

1

Możesz uzyskać dostęp do pliku manifestu (lub dowolnego innego) w słoiku, jeśli używasz tego samego programu ładującego klasy do tego, który został użyty do załadowania klas.

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

Jeśli jesteś wielowątkowy zastosowanie następujące:

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

Jest to także naprawdę przydatna technika tym domyślny plik konfiguracyjny w słoiku.

2

Chcesz użyć tego:

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

Można analizować URL dowiedzieć się, które jar manifestu jeśli od a następnie odczytać URL poprzez getInputStream() do analizowania manifestu.

1

Oto co znalazłem, że działa:

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

} 

Oto kompilacja dopasowanie.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

W aplikacji świata rzeczywistego, który używa żadnych zewnętrznych bibliotek, to jest niezmiernie prawdopodobne, aby powrócić źle oczywisty. –

+0

Nazwa klasy powinna zaczynać się od wielkich liter. Powinien to być PackageVersion zamiast packageVersion. – Carlos

1

ClassLoader.getResource(String) załaduje pierwszy manifest stwierdzi na ścieżce klasy, które mogą być manifest do innego pliku JAR. W ten sposób można albo wybrać jeden z nich, albo wybrać inny mechanizm, taki jak plik właściwości o unikalnej nazwie.

+0

Masz rację, że funkcja getResource() często znajduje niewłaściwy manifest; Doświadczam tego; Próbowałem twój link do ClassLoader.getResources(), aby wyliczyć wszystkie manifesty, ale getResources ("/ META-INF/MANIFEST.MF") nic nie zwraca, a getResources ("") zwraca więcej niż nic, ale mniej niż użyteczne. Podejrzewam, że używam niewłaściwego programu ładującego klasy, ale potem "wylicza wszystkie manifesty", aby "wyliczyć wszystkie klasyLoaderów"! – metamatt

1

Znalazłem komentarz przez McDowell, aby było prawdziwe - który plik MANIFEST.MF zostanie pobrany zależy od ścieżki klasy i może nie być tym, który chciał. Używam tego

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

którego adaptacja link text

+0

To prawie dla mnie działa, chociaż link do tego, skąd się wziął, jest teraz zepsuty. Mówię "prawie", ponieważ Class.getPackage() zwraca nazwę oddzieloną kropką (org.foo.bar), a Class.getSimpleName() zwraca oddzieloną ukośnikami nazwę (org/foo/bar). Z tego powodu lubię gibbss 'odpowiedź, która unika konieczności analizowania adresu URL klasy. – metamatt

10

Można uzyskać manifest dla dowolnej klasy w dowolnym słoiku bez parsowania URL klasy (który może być krucha). Po prostu zlokalizuj zasób, o którym wiesz, że znajduje się w wybranym przez ciebie słoju, a następnie odrzuć połączenie do połączenia JarURLConnection.

Jeśli chcesz, aby kod działał, gdy klasa nie jest dołączona do słoika, dodaj test instanceof na typie zwróconego połączenia URL. Klasy w rozpakowanej hierarchii klas zwrócą wewnętrzny plik Sun FileURLConnection zamiast JarUrlConnection. Następnie możesz załadować Manifest przy użyciu jednej z metod InputStream opisanych w innych odpowiedziach.

@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

Miło, dzięki. Próbowałem wszystkiego tutaj i (obecnie 3) odpowiedzi, które potwierdzają, że Class.getResource() często znajduje niewłaściwy manifest (w niewłaściwym słoiku) i oferuje rozwiązanie, aby znaleźć właściwy manifest, najbardziej mi się podoba. – metamatt

+0

Jeśli chcesz czegoś konkretnego, użyj 'atts.getValue (" Attribute-Name ")' lub 'atts.get (new Attributes.Name (" Attribute-Name "))', nietypowy kod FTW! – TWiStErRob

Powiązane problemy