5 cosas que no sabía sobre...JARs

Un archivo Java es más que un simple paquete de clases

Hay muchos desarrolladores de Java™ que nunca piensan más allá de las bases de los JARs — sólo los usan para empaquetar clases antes de enviarlas a los servidores de producción. Pero un JAR es más que un simple archivo ZIP renombrado. Aprenda cómo usar los Archivos Java a su máxima capacidad, así como consejos para dependencias discordantes de Spring y archivos de configuración.

Ted Neward, Director, Neward & Associates

Ted NewardTed Neward es el director de Neward & Associates, donde hace consultoría, es mentor, enseña y hace presentaciones sobre Java, .NET, XML Services y otras plataformas. Vive en Seattle, Washington.



05-11-2012

Para la mayoría de los desarrolladores de Java, los archivos JAR y sus parientes especializados, los WARs y EARs, son simplemente el resultado de un largo proceso de Ant o Maven. Es un procedimiento estándar para copiar el JAR en el lugar correcto del servidor (o, en raras ocasiones, en la máquina del usuario) y olvidarse de él.

En realidad, los JARs pueden hacer más que almacenar código de origen, pero tiene que saber qué más es posible y cómo conseguirlo. Los consejos en esta cuota de la serie de las 5 cosas le mostrará cómo obtener el máximo de los Archivos Java (y en algunos casos también de los WARs y EARs), especialmente al momento de la implementación.

Ya que existen muchos desarrolladores de Java que utilizan Spring (y porque la infraestructura de Spring presenta algunos retos particulares para nuestro uso tradicional de JARs), muchos de los consejos atienden específicamente los JARs en aplicaciones de Spring.

Sobre esta serie

¿Así que piensa que sabe de programación en Java? La realidad es que la mayoría de los desarrolladores sólo conocen las bases de la plataforma de Java, aprendiendo sólo lo necesario para realizar el trabajo. En esta serie, Ted Neward explora las funcionalidades principales de la plataforma de Java para descubrir hechos poco conocidos que podrían ayudarle a solucionar incluso los más complicados retos de programación.

Comenzaré con un ejemplo rápido de un procedimiento estándar de Archivo Java, el cual servirá como base para los consejos siguientes.

Póngalo en un JAR

Normalmente, usted crea un archivo JAR después de que su código de origen ha sido compilado, recolectando el código Java (el cual ha sido segregado por paquete) en una sola colección mediante la utilidad de línea de comandos de jar o, más comúnmente, la tarea jar de Ant. El proceso es sencillo y no lo demostraré aquí, aunque más adelante en el artículo regresaremos al tema de cómo son creados los JARs. Por ahora, sólo necesitamos archivar Hello, una utilidad de consola autónoma que realiza la increíblemente útil tarea de imprimir un mensaje en la consola, mostrado en el Listado 1:

Listado 1. Archivando la utilidad de consola
package com.tedneward.jars;

public class Hello
{
    public static void main(String[] args)
    {
        System.out.println("Howdy!");
    }
}

La utilidad Hello no es la gran cosa, pero es una base útil para explorar archivos JAR, comenzando con la ejecución del código.


1. Los JARs son ejecutables

Desarrolle habilidades de este tema

Este contenido es parte de un knowledge path progresivo para avanzar en sus habilidades. Vea Conviértase en un desarrollador de Java

Lenguajes como .NET y C++ históricamente han tenido la ventaja de ser compatibles con el SO en el sentido de que sólo hay que referenciar su nombre en la línea de comandos (helloWorld.exe) o hacer doble clic en su icono en el shell de la GUI iniciaría la aplicación. Sin embargo, en la programación de Java una aplicación iniciadora de —java— arranca la JVM en el proceso, y tenemos que pasar un argumento de línea de comandos (com.tedneward.Hello) indicando la clase cuyo método main() queremos lanzar.

Estas etapas adicionales hacen que sea más difícil crear aplicaciones fáciles de usar en Java. El usuario final no sólo tiene que escribir todos estos elementos en la línea de comandos, lo cual muchos usuarios finales prefieren evitar, sino que existen muchas probabilidades de que cometa errores de tipografía y eso provoque que se regresen errores.

La solución es hacer "ejecutable" al archivo JAR, de forma que el iniciador de Java automáticamente sabrá qué clase lanzar al ejecutar el archivo JAR. Lo único que tenemos que hacer es introducir una entrada en el manifiesto del archivo JAR (MANIFEST.MF en el subdirectorio META-INF del JAR), de la siguiente manera:

Listado 2. ¡Muéstrame el punto de entrada!
Main-Class: com.tedneward.jars.Hello

El manifiesto es sólo un conjunto de pares de nombre-valor. Ya que el manifiesto algunas veces puede ser sensible respecto a retornos de carro y espacios en blanco, es más fácil usar Ant par generarlo cuando se crea el JAR. En el Listado 3, He usado el elemento manifest de la tarea jar de Ant para especificar el manifiesto:

Listado 3. ¡Creame el punto de entrada!
    <target name="jar" depends="build">
        <jar destfile="outapp.jar" basedir="classes">
            <manifest>
                <attribute name="Main-Class" value="com.tedneward.jars.Hello" />
            </manifest>
        </jar>
    </target>

Todo lo que un usuario tiene que hacer para ejecutar ahora el archivo JAR es especificar su nombre de archivo en la línea de comandos, mediante java -jar outapp.jar. En el caso de algunos shells de GUI, hacer doble clic en el archivo JAR también funciona.


2. Los JARs puede incluir información de dependencia

Parece que se ha hablado mucho de la utilidad Hello , y con ello ha surgido la necesidad de variar la implementación. Los contenedores de inyección de dependencia (DI) como Spring o Guice manejan muchos de los detalles para nosotros, pero aún existe un obstáculo: modificar el código para incluir el uso de un contenedor de DI puede entregar resultados como los que ve en el Listado 4:

Listado 4. ¡Hola, mundo de Spring!
package com.tedneward.jars;

import org.springframework.context.*;
import org.springframework.context.support.*;

public class Hello
{
    public static void main(String[] args)
    {
        ApplicationContext appContext =
            new FileSystemXmlApplicationContext("./app.xml");
        ISpeak speaker = (ISpeak) appContext.getBean("speaker");
        System.out.println(speaker.sayHello());
    }
}

Más sobre Spring

Este consejo asume que está familiarizado con la inyección de dependencia y la infraestructura de Spring. Consulte la sección Recursos si necesita repasar cualquier tema.

Ya que la opción -jar del iniciador sobrescribe cualquier cosa que suceda en la opción de línea de comandos -classpath , Spring necesita estar en la variable CLASSPATHy en la variable de entorno cuando ejecute este código. Afortunadamente, los JARs permiten que una declaración de otras dependencias de JAR aparezcan en el manifiesto, lo que implícitamente crea la variable CLASSPATH sin que tenga que declararla, como se muestra en el Listado 5:

Listado 5. ¡Hola, CLASSPATH de Spring!
    <target name="jar" depends="build">
        <jar destfile="outapp.jar" basedir="classes">
            <manifest>
                <attribute name="Main-Class" value="com.tedneward.jars.Hello" />
                <attribute name="Class-Path" 
                    value="./lib/org.springframework.context-3.0.1.RELEASE-A.jar 
                      ./lib/org.springframework.core-3.0.1.RELEASE-A.jar 
                      ./lib/org.springframework.asm-3.0.1.RELEASE-A.jar 
                      ./lib/org.springframework.beans-3.0.1.RELEASE-A.jar 
                      ./lib/org.springframework.expression-3.0.1.RELEASE-A.jar 
                      ./lib/commons-logging-1.0.4.jar" />
            </manifest>
        </jar>
    </target>

Note que el atributo Class-Path contiene una referencia relativa a los archivos de JAR de los que depende la aplicación. También puede escribir esto como una referencia absoluta o completamente sin un prefijo, en cuyo caso se asumiría que los archivos JAR estaban en el mismo directorio que el JAR de la aplicación.

Desafortunadamente, el atributo value para el atributo Class-Path de Ant tiene que aparecer en una línea, ya que el manifiesto de JAR no puede hacer frente a la idea de múltiples atributos Class-Path . Así, todas esas dependencias tiene que aparecer en una línea en el archivo de manifiesto. Lo sé, es horrible, ¡pero poder hacer java -jar outapp.jar vale la pena!


3. Los JARs pueden ser referenciados implícitamente

Si tiene muchas utilidades de línea de comando distintas (u otras aplicaciones) que usan la infraestructura de Spring, puede ser más fácil poner los archivos JAR de Spring en una ubicación común que todas las aplicaciones puedan referenciar. Hacer esto evita tener múltiples copias de JARs saltando en todo el sistema de archivos. La ubicación común del tiempo de ejecución de Java para los JARs, conocida como el "directorio de extensión", está ubicado de forma predeterminada en el subdirectorio lib/ext , bajo la ubicación del JRE instalado.

El JRE es una ubicación personalizable, pero es tan raro que se personalice en un entorno de Java dado que es completamente seguro asumir que lib/ext es un lugar seguro para almacenar JARs y que estarán implícitamente disponibles en la variable CLASSPATH del entorno de Java.


4. Java 6 permite comodines de la variable classpath

En un esfuerzo por evitar enormes variables de entorno de CLASSPATH (la cuales los desarrolladores de Java debieron haber dejado atrás hace años) y/o parámetros de línea de comandos -classpath , Java 6 presento la noción del comodín de la variable classpath. En lugar de tener que lanzar con cada archivo JAR explícitamente listado en un argumento, el comodín de la variable classpath le permite especificar lib/* y todos los archivos JAR listados en ese directorio (no en forma recursiva), en la variable classpath.

Desafortunadamente, el comodín de la variable classpath no se adhiere a la entrada de manifiesto del atributo Class-Path previamente discutida. Pero sí hace que sea más fácil lanzar aplicaciones Java (incluyendo servidores) para tareas de desarrollador tales como herramientas de generación de código o herramientas de análisis.


5. Los JARs contienen más que sólo código

Spring, como muchas partes del ecosistema de Java, depende de un archivo de configuración que describe cómo debe ser establecido el entorno. Como se escribió, Spring depende de un archivo app.xml que reside en el mismo directorio que el archivo JAR — pero es muy común que los desarrolladores olviden copiar el archivo de configuración junto con el archivo JAR.

Algunos archivos de configuración son editables por un administrador del sistema, pero muchos de ellos (como los correlacionamientos de Hibernación) están fuera del dominio del administrador del sistema, lo que produce errores en la implementación. Una buena solución sería empaquetar el archivo de configuración junto con el código — y esto es factible gracias a que un JAR es básicamente un ZIP disfrazado. Simplemente incluya archivos de configuración en la tarea de Ant o la línea de comandos de jar al compilar un JAR.

Los JARs también incluyen otros tipos de archivos, no sólo archivos de configuración. Por ejemplo, si mi componente SpeakEnglish quisiera acceder a un archivo de propiedades, yo podría configurar eso como se muestra en el Listado 6:

Listado 6. Responder en forma aleatoria
package com.tedneward.jars;

import java.util.*;

public class SpeakEnglish
    implements ISpeak
{
    Properties responses = new Properties();
    Random random = new Random();

    public String sayHello()
    {
        // Pick a response at random
        int which = random.nextInt(5);
        
        return responses.getProperty("response." + which);
    }
}

Poner responses.properties en el archivo JAR significa que hay un archivo menos por el cual preocuparse en el despliegue junto con el archivo JAR. Hacer eso sólo requiere incluir el archivo responses.properties durante la etapa de JAR.

Sin embargo, una vez que ha almacenado sus propiedades en un JAR tal vez se pregunte cómo recuperarlas. Si los datos deseados están co-ubicados dentro del mismo archivo JAR, como en el ejemplo anterior, entonces no se moleste en intentar descubrir la ubicación del archivo del JAR y resuélvalo con un objeto JarFile . En lugar de eso, deje que el ClassLoader de la clase la encuentre como un "recurso" dentro del archivo JAR, utilizando el método ClassLoader getResourceAsStream() mostrado en el Listado 7:

Listado 7. ClassLoader ubica un Recurso
package com.tedneward.jars;

import java.util.*;

public class SpeakEnglish
    implements ISpeak
{
    Properties responses = new Properties();
    // ...

    public SpeakEnglish()
    {
        try
        {
            ClassLoader myCL = SpeakEnglish.class.getClassLoader();
            responses.load(
                myCL.getResourceAsStream(
                    "com/tedneward/jars/responses.properties"));
        }
        catch (Exception x)
        {
            x.printStackTrace();
        }
    }
    
    // ...
}

Es posible seguir este procedimiento para cualquier tipo de recurso: archivo de configuración, archivo de audio, archivo de gráficos, lo que usted quiera. Virtualmente, cualquier tipo de archivo puede ser empaquetado en un JAR, obtenido como un InputStream (mediante el ClassLoader) y usado en cualquier forma que le plazca.


En conclusión

Este artículo ha cubierto las cinco principales cosas que la mayoría de los desarrolladores de Java no saben sobre los JARs — al menos con base en evidencia histórica y anecdótica. Tenga en cuenta que todos estos consejos relacionados con los JARs son igualmente aplicables para los WARs. Algunos consejos (los atributos Class-Path y Main-Class en particular) son menos emocionantes en el caso de los WARs, ya que el entorno de servlet recopila todo el contenido de un directorio y tiene un punto de entrada previamente definido. Aun así, si se toman en forma colectiva, estos conejos nos llevan más allá del paradigma que dice "Está bien, comencemos por copiar todo en este directorio..." Esto también hace que el despliegue de aplicaciones de Java sea mucho más fácil.

A continuación en esta serie: 5 cosas que no sabía sobre la supervisión del rendimiento para aplicaciones de Java.


Descargar

DescripciónNombretamaño
Sample code for this articlej-5things6-src.zip10KB

Recursos

Aprender

  • 5 cosas que no sabía sobre ... (Ted Neward, developerworks): Esta serie convierte la trivia de Java en oro para la programación.
  • "JAR files revealed" (Pagadala Suresh y Palaniyappan Thiagarajan, developerWorks, octubre de 2003): Presenta los dispositivos y beneficios del formato de Archivo Java, incluyendo el empaquetado, los archivos JAR ejecutables, la seguridad y el indexado.
  • The Packaging programs in JAR files (The Java Tutorials trail): The basics of JARs.
  • Spring: Información sobre esta infraestructura robusta, flexible y popular, directamente desde el origen.
  • "Dependency injection with Guice" (Nicholas Lesiecki, developerworks, diciembre de 2008): La DI mejora la capacidad de mantenimiento, de pruebas y de flexibilidad, y Guice hace que la DI sea fácil.
  • "Setting multiple jars in java classpath" (StackOverflow Q&A, última actualización en marzo de 2010): Más sobre comodines de la variable classpath.
  • "See JARs run" (Shawn Silverman, JavaWorld.com, mayo de 2002): Un tutorial más amplio sobre la creación de archivos JAR ejecutables.
  • "Advanced topics in programming languages series: JSR 277: Java Module System" (Google Tech Talks, mayo de 2007): Los JARs tienen sus desventajas — el infierno de la variable classpath, del archivo JAR y de la extensión, por nombrar algunas. Esta Google Tech Talk explica cómo JSR 277 las puede corregir.
  • La zona de tecnología de Java de developerWorks: Cientos de artículos sobre todos los aspectos de la programación en Java.

Comentar

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=tecnologia Java
ArticleID=844440
ArticleTitle=5 cosas que no sabía sobre...JARs
publish-date=11052012