2008-09-17 11 views
20

He pasado demasiado tiempo tratando de resolver esto. Esto debería ser lo más simple y todos los que distribuyen aplicaciones Java en frascos deben lidiar con él.¿Cómo se crea un MANIFEST.MF que está disponible cuando se está probando y ejecutándose desde un contenedor en producción?

Solo quiero saber la forma correcta de agregar versiones a mi aplicación Java para que pueda acceder a la información de la versión cuando estoy probando, p. Ej. depuración en Eclipse y ejecutándose desde un contenedor.

Esto es lo que tengo en mi 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> 

Esto crea /META-INF/MANIFEST.MF y puedo leer los valores de depuración cuando estoy en Eclipse así:

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

}

Pero, cuando se crea el archivo jAR, se carga el manifiesto de otro frasco (presumiblemente el primer frasco cargado por la aplicación - en mi caso, activation.jar).

Además, el siguiente código tampoco funciona, aunque todos los valores adecuados se encuentran en el archivo de manifiesto.

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

¿Alguna idea?

Respuesta

0

Simplemente no use el manifiesto. Crear un archivo foo.properties.original, con un contenido como version = @ VERSION @

Y en Ther misma tarea que está Jaring que puede hacer una copia de Copu foo.properties.original y luego

0

También usualmente usaré un archivo de versión. Crearé un archivo por contenedor ya que cada contenedor puede tener su propia versión.

1

Puede acceder al archivo de manifiesto (o cualquier otro) dentro de un contenedor si usa el mismo cargador de clases que utilizó para cargar las clases.

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

Si usted es el uso de múltiples subprocesos lo siguiente:

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

Ésta es también una técnica realmente útil para incluir un archivo de configuración por defecto dentro de la jarra.

2

que desea utilizar esto:

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

Usted puede analizar la URL de averiguar qué frasco de manifiesto si desde y después lee el URL a través de getInputStream() para analizar el manifiesto.

1

Esto es lo que he encontrado que funciona:

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

} 

Aquí está la construcción coincidente.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

En una aplicación real que utiliza las bibliotecas de terceros, esto es muy probable que vuelva el mal manifiesto. –

+0

El nombre de clase debe comenzar en mayúscula. Debería ser PackageVersion en lugar de packageVersion. – Carlos

1

ClassLoader.getResource(String) cargará la primera manifiesta que encuentra en la ruta de clase, que puede ser el manifiesto de algún otro archivo JAR. Por lo tanto, puede enumerate all the manifests para encontrar el que desee o utilizar algún otro mecanismo, como un archivo de propiedades con un nombre único.

+0

Tiene razón en que getResource() a menudo encuentra el manifiesto incorrecto; Estoy experimentando eso; Intenté su enlace a ClassLoader.getResources() para enumerar todos los manifiestos, pero getResources ("/ META-INF/MANIFEST.MF") no devuelve nada, y getResources ("") devuelve más que nada pero menos que útil. ¡Sospecho que estoy usando el cargador de clases incorrecto, pero luego "enumerar todos los manifiestos" se transfiere para "enumerar todos los ClassLoaders"! – metamatt

1

He encontrado que el comentario de McDowell es cierto: qué archivo MANIFEST.MF se recupera depende de la ruta de clase y puede que no sea el que se busca. Yo uso este

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

que he adaptado de link text

+0

Esto casi funciona para mí, aunque ahora está roto el enlace al lugar de donde lo obtuviste. Digo "casi" porque Class.getPackage() devuelve un nombre separado por puntos (org.foo.bar) y Class.getSimpleName() devuelve un nombre separado por barras (org/foo/bar). Debido a esto, me gusta la respuesta de gibbss que evita tener que analizar la url de clase. – metamatt

10

Usted puede obtener el manifiesto para una clase arbitraria en un frasco arbitraria sin necesidad de analizar la url de clase (que podría ser frágil). Simplemente ubique un recurso que sepa que está en el contenedor que desea, y luego conecte la conexión a JarURLConnection.

Si desea que el código funcione cuando la clase no está incluida en un contenedor, agregue una instancia de verificación del tipo de conexión URL devuelta. Las clases en una jerarquía de clases desempaquetadas devolverán un Sun FileURLConnection interno en lugar de JarUrlConnection. Luego puede cargar el Manifiesto utilizando uno de los métodos InputStream descritos en otras respuestas.

@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

Agradable, gracias. Intenté todo aquí, y de las (actualmente 3) respuestas que reconocen que Class.getResource() a menudo encuentra el manifiesto incorrecto (en el contenedor incorrecto) y ofrecen una solución para encontrar el manifiesto correcto, este me gusta más. – metamatt

+0

Si quieres algo específico usa 'atts.getValue (" Attribute-Name ")' o 'atts.get (new Attributes.Name (" Attribute-Name "))', código no genérico FTW! – TWiStErRob

Cuestiones relacionadas