2009-03-05 13 views
45

Tengo una aplicación web que contiene un archivo de manifiesto, en el que escribo la versión actual de mi aplicación durante una tarea de compilación de ant. El archivo de manifiesto se crea correctamente, pero cuando trato de leerlo durante el tiempo de ejecución, obtengo algunos efectos secundarios extraños. Mi código para leer en el manifiesto es algo como esto:¿Cómo leo el archivo de manifiesto para una aplicación web que se ejecuta en apache tomcat?

InputStream manifestStream = Thread.currentThread() 
           .getContextClassLoader() 
           .getResourceAsStream("META-INFFFF/MANIFEST.MF"); 
    try { 
     Manifest manifest = new Manifest(manifestStream); 
     Attributes attributes = manifest.getMainAttributes(); 
     String impVersion = attributes.getValue("Implementation-Version"); 
     mVersionString = impVersion; 
    } 
    catch(IOException ex) { 
     logger.warn("Error while reading version: " + ex.getMessage()); 
    } 

Cuando ato Eclipse para Tomcat, veo que el código anterior funciona, pero parece que se pondrá un archivo de manifiesto diferente a la que yo esperaba, que Puedo decirlo porque la versión ant y la marca de tiempo de compilación son ambas diferentes. Luego, puse "META-INFFFF" allí, ¡y el código anterior aún funciona! Esto significa que estoy leyendo algún otro manifiesto, no el mío. También probé

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

Pero el resultado fue el mismo. ¿Cuál es la forma correcta de leer el archivo de manifiesto desde dentro de una aplicación web que se ejecuta en tomcat?

Editar: Gracias por las sugerencias hasta ahora. Además, debería tener en cuenta que I am ejecutar tomcat independiente; Lo lanzo desde la línea de comando y luego lo conecto a la instancia en ejecución en el depurador de Eclipse. Eso no debería hacer la diferencia, ¿o sí?

+0

@ La respuesta de PascalThivent es la manera correcta de hacerlo. Agregue un control de excepciones en caso de que su MANIFIESTO no exista (es decir ,:IDE) y debería ser A1. – jmelanson

+0

Este método universal me ayudó: [http://stackoverflow.com/a/29103019/3158918] – user3158918

Respuesta

1

No se conoce una forma "oficial" de leerlo, pero si el MANIFEST.MF no se puede cargar correctamente como un recurso, ¿qué hay de intentar derivar su ruta de un "ServletContext.getRealPath()" en alguna ruta web definida en tu aplicación?

Escribir la versión de la aplicación también en otro lugar (un archivo de propiedad en WEB-INF/classes) por hormiga durante la compilación es otra solución que me viene a la mente.

86

Quizás sus efectos secundarios provengan del hecho de que casi todos los frascos incluyen un MANIFEST.MF y no está obteniendo el correcto. Para leer el MANIFIEST.MF de la aplicación web, diría:

ServletContext application = getServletConfig().getServletContext(); 
InputStream inputStream = application.getResourceAsStream("/META-INF/MANIFEST.MF"); 
Manifest manifest = new Manifest(inputStream); 

Tenga en cuenta que la ejecución de Tomcat desde Eclipse no es lo mismo que ejecutar Tomcat solo como Eclipse juega con el cargador de clases.

+1

De hecho, el manifiesto de guerra no estará en el classpath. –

+2

+1 por dar el código real que realmente funciona. –

+4

Esta es claramente la respuesta correcta +1 –

2

La manera predeterminada en que los cargadores de clase funcionan es diferir al padre antes de intentar buscar sus propios recursos. Entonces, si un cargador de clases padre tiene algún manifiesto disponible, eso es lo que obtendrás. De hecho, los servidores de aplicaciones no necesariamente hacen esto para permitir que las aplicaciones anulen las versiones de las bibliotecas. Además, los cargadores de clase pueden tener múltiples jarras y, por lo tanto, manifiestos múltiples.

Puede obtener un URL de recurso de uno de sus recursos con nombre único. Abra una conexión. Transmitir a JarURLConnection. Obtenga el JarFile. Cargue el manifiesto de eso. Eso puede no funcionar, particularmente si Tomcat explota la guerra.

[Actualizar] Por supuesto, el archivo war en sí no está en el classpath. El classpath tendrá algo como WEB-INF/lib/(.jar | .zip) y WEB-INF/classes /. Obtener un recurso del ServletContext debería funcionar.

La mejor solución: hacer algo diferente.:)

10

un poco tarde, pero esto funciona para mí (Appl web en Glassfish)

Properties prop = new Properties(); 
prop.load(getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF")); 
System.out.println("All attributes:" + prop.stringPropertyNames()); 
System.out.println(prop.getProperty("{whatever attribute you want}")); 
+9

preste atención: siguiendo [RFC-822] (http://www.ietf.org/rfc/rfc0822.txt) encontrará problemas al usar 'Propiedades' ya que los valores largos están rotos en más líneas. El usuario 'Manifest' clase en su lugar. –

3

intenta utilizar jcabi-manifests, que hace todo este trabajo de carga para usted. Por ejemplo:

String version = Manifests.read("My-Version"); 

cargas My-Version atributo de uno de MANIFEST.MF archivos disponibles.

Es importante mencionar que (más detalles son here) en la mayoría de los contenedores web el cargador de clases de hilo actual no es lo mismo que el cargador de clases de contexto de servlet. Es por eso que usted debe anexar su contexto servlet para el registro en tiempo de ejecución (more info):

Manifests.append(servletContext); 

Además, mira esto: existe http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html

2

El manifiesto justo en raíz de la aplicación en el servidor. Descubre la raíz appication, por ejemplo mediante la búsqueda de ruta de clase de la clase:

String rootPath = getClass().getProtectionDomain().getCodeSource().getLocation().getPath() 

luego vuelva a colocar la ruta anterior con la ruta fundada: Glassfish ejemplo:

/applications/<webProject>/META-INF/MANIFEST.MF 

que funcione para mí.

0

Esto es lo que hago para imprimir varias versiones en un archivo de registro. He codificado una ruta expandida, pero las aplicaciones pueden usar servletContext.getRealPath("/") para leer una ruta completa a la carpeta de aplicaciones web. Puede imprimir solo bibliotecas determinadas o todo desde la carpeta lib.

// print library versions (jersey-common.jar, jackson-core-2.6.1.jar) 
try { 
    List<String> jars = Arrays.asList("jersey-common", "jackson-core", "openjpa", "mylib"); 
    StringBuilder verbuf = new StringBuilder(); 
    for(File file : new File("/opt/tomcat/webapps/myapp/WEB-INF/lib/").listFiles()) { 
     String name = file.getName(); 
     if (file.isDirectory() || !file.isFile() || !name.endsWith(".jar")) continue; 
     name = name.substring(0, name.length()-4); 
     boolean found = jars.contains(name); 
     if (!found) { 
      int idx = name.lastIndexOf('-'); 
      if (idx>0) 
       found = jars.contains(name.substring(0, idx)); 
     } 
     if (!found) continue; 

     JarFile jarFile = new JarFile(file, false); 
     try { 
      String ver; 
      Manifest mf = jarFile.getManifest(); 
      if (mf!=null) { 
       ver = mf.getMainAttributes().getValue("Bundle-Version"); 
       if (ver==null || ver.isEmpty()) 
        ver = mf.getMainAttributes().getValue("Implementation-Version"); 
      } else ver=null; 
      if (verbuf.length()>0) verbuf.append(", "); 
      verbuf.append(name + "=" + (ver!=null?ver:""));       
     } finally { 
      jarFile.close(); 
     } 
    } 
    System.out.println(verbuf.toString()); 
} catch(Exception ex) { 
    ex.printStackTrace(); 
} 
Cuestiones relacionadas