Entender el Manifiesto

Los ficheros JAR pueden soportan un amplio rango de funcionalidades, incluyendo la firma electrónica, el control de versiones, el sellado de paquetes, las extensiones, etc. ¿Qué le da a los ficheros JAR la habilidad para ser tan versátiles? La respuesta se encuentra dentro del manifiesto de los ficheros JAR.

El manifiesto es un fichero especial que puede contener información sobre los otros ficheros empaquetados en un fichero JAR. Personalizar la información "meta" del manifiesto, te permite utilizar los ficheros JAR para una gran variedad de propósitos.

Antes de ver algunas de las formas en que puede ser modificado el manifiesto para permitir funcionalidades especiales a los ficheros JAR, echemos un vitazo a la línea base del manifiesto por defecto.

El Manifiesto por defecto

Siempre que creas un fichero JAR, automáticamente recibe un fichero de manifiesto por defecto. Sólo puede haber un fichero de manifiesto en un fichero JAR, y siempre debe tener el path:
META-INF/MANIFEST.MF

Cuando un fichero JAR es creado con la versión 1.2 del JDK, el manifiesto por defecto es muy sencillo. Aquí tienes todo su contenido:

Manifest-Version: 1.0
Created-By: Manifest JDK1.2

Como puedes ver en este ejemplo, las entradas de un fichero de manifiesto tienen la forma de parejas: "cabecera:valor". El nombre de una cabecera está separado de su valor por dos puntos.

El manifiesto mostrado arriba es conforme a la versión 1.0 de la especificación de manifiesto y ha sido creado con la versión 1.2 del JDK, Estas son propiedades del propio manifiesto, pero también puede contener información sobre otros ficheros empaquetados en el archivo.

La información exacta grabada en el fichero de manifiesto depende del uso previsto del fichero JAR. El fichero de manifiesto por defecto no asume nada sobre la información que debería almacenar sobre otros ficheros, por eso sólo contiene datos sobre él mismo.

El formato del fichero de manifiesto por defecto cambia de la versión 1.1 a la versión 1.2 del JDK. Si creas un fichero JAR para el paquete, java.math, por ejemplo, el fichero de manifiesto por defecto del JDK 1.1 se parecería a esto:

Manifest-Version: 1.0

Name: java/math/BigDecimal.class
Digest-Algorithms: SHA MD5
SHA-Digest: TD1GZt8G11dXY2p4olSZPc5Rj64=
MD5-Digest: z6z8xPj2AW/Q9AkRSPF0cg==

Name: java/math/BigInteger.class
Digest-Algorithms: SHA MD5
SHA-Digest: oBmrvIkBnSxdNZzPh5iLyF0S+bE=
MD5-Digest: wFymhDKjNreNZ4AzDWWg1Q==

Al contrario que el manifiesto del JDK 1.2 el del JDK 1.1 tiene entradas para cada uno de los ficheros contenidos en el archivo, incluyendo los paths de los ficheros y valores digest. Estos últimos valores son solo importantes con respecto a la firma de ficheros JAR. De echo, el por qué la información digest no está en el fichero de manifiesto del JDK 1.2 - es porque nunca la necesita. Para aprender más sobre la firma, puedes ver la lección Firmar y Autentificar ficheros JAR.

Cabeceras de Manifiesto para Propósitos Especiales

Dependiendo del papel que quieres que juegue tu fichero JAR, podrías modificar el manifiesto por defecto. Sí sólo estas interesado en las caracteristicas "ZIP" del fichero JAR como la compresión o el archivado, no tendrás que preocuparte del fichero de manifiesto. Este fichero no juega ningún papel en estas situaciones.

La mayoría de los usos de los ficheros JAR que van más allá del simple archivado y comprensión necesitan que alguna información especial sea almacenada en el fichero de manifiesto. Abajo tienes una breve descripción de las cabeceras requeridas para algunas funciones de propósito especial de los ficheros JAR:

Aplicaciones empaquetadas en ficheros JAR - sólo versión 1.2
Si tienes una aplicación en un fichero JAR, necesitas indicar de alguna forma que clase es el punto de entrada de las que se incluyen en el fichero JAR. (Recuerda que el punto de entrada es una clase que tenga un método con la firma: public static void main(String[] args).)

Esta información se proporciona con la cabecera Main-Class, que tiene esta forma general:

Main-Class: classname

donde classname es el nombre de la clase que es el punto de entrada de la aplicación.

Descarga de Extensiones - sólo versión 1.2
La descarga de extensiones son ficheros JAR que son referenciados por el fichero de manifiesto de otros ficheros JAR. En una situación típica, un applet estaría empaquetado en un fichero JAR cuyo manifiesto referenciara a un fichero JAR (o a varios ficheros JAR) que servirán como una extensión para los propósitos del applet. Las extensiones pueden referenciarse unas a otras de la misma forma.

La descarga de extensiones se especifica en el campo de cabecera Class-Path en el fichero de manifiesto del applet, aplicación, u otra extensión. Una cabecera Class-Path se podría parecer a esto, por ejemplo:

Class-Path: servlet.jar infobus.jar acme/beans.jar

Con esta cabcera, las clases de los ficheros servlet.jar, infobus.jar, y acme/beans.jar servirán como extensiones para los propósitos del applet o aplicación. Las URLs en la cabecera Class-Path son relativas a la URL del fichero JAR del applet o de la aplicación.

Sellado de Paquetes - sólo versión 1.2
Los paquetes almacenados en ficheros JAR pueden ser sellados opcionalmente para que el paquete pueda reforzar su consistencia. El sellado de un paquete dentro de un fichero JAR significa que todas las clases definidas en ese paquete deben encontrarse dentro del mismo fichero JAR.

Un paquete puede sellarse añadiendo la cabecera Sealed:

Name: myCompany/myPackage/
Sealed: true

Versionado de Paquetes - sólo versión 1.2
La pagina Especificación de Versionado de Paquetes define varias cabeceras de manifiesto para contener información del versionado. Un conjunto de dichas cabeceras puede ser asignado a cada paquete. Las cabeceras de versionado deberían aparecer directamente debajo de la cabecera name del paquete. Este ejemplo muestra las cabeceras de versionado:

Name: java/util/
Specification-Title: "Java Utility Classes" 
Specification-Version: "1.2"
Specification-Vendor: "Sun Microsystems Inc.".
Implementation-Title: "java.util" 
Implementation-Version: "build57"
Implementation-Vendor: "Sun Microsystems. Inc."

Información Adicional

La Especificación del formato de manifiesto es forma parte de la documentación on-line del JDK.

Ozito