jump to navigation

Crear un unico JAR marzo 21, 2007

Posted by superpiwi in Eclipse, Java, Programacion.
15 comments

«Un One-jar unico, Un One-jar para encontrarlos a todos y atarlos en las tinieblas».
(Perdonar la gracia, no he podido resistirme).

One-jar es una solucion que nos permite usar un unico jar para unir todas nuestras dependencias y hacer mas facil la distribucion de nuestras aplicaciones entre maquinas. Especialmente cuando nuestra aplicacion depende de multiples ficheros .jar. La idea es usar un unico fichero .jar.

Imaginate por ejemplo un proyecto que estas desarrollando y que internamente usa
estas tres librerias: log4j.jar, dom.jar y mysql.jar
Cuando quisieras llevar ese proyecto a otra maquina tendrias que mover 4 ficheros:
el de tu proyecto, p.ej si lo compilas y empaquetas como Miapp.jar
y esas otras 3 librerias.
Ademas tendrias que configurar el CLASSPATH para apuntar a esas librerias o incluir un fichero de manifiesto en Miapp.jar que indicara la ruta de las mismas. No seria mas sencillo tener un unico JAR con el codigo de tu aplicacion y que incluyera todo lo que necesitas utilizar. De eso trata One-jar.
Basicamente, se trata de un boot-loader que cuando lo inicializas carga en memoria todas las librerias que utilizas y ya no te da el conocido error de «Class Not Found» cuando intentas ejecutar tu aplicacion.

onejar03.jpg

Para utilizar one-jar descargate el siguiente fichero de su pagina web: one-jar-boot-0.95.jar

Tienes que montar una estructura de carpetas similar a la siguiente:

Cras una carpeta Tu_Proyecto y en ella añades lo siguiente:

Las clases de one-jar (las que resultan al descomprimir one-jar-boot-0.95.jar)

una Carpeta Main con el .jar de tu aplicacion (y que incluye internamente su manifiesto)

una Carpeta Lib (Las librerias que usas)
y el manifiesto de one-jar

Es decir,
tendras una carpeta Main donde dejaras el .jar de tu proyecto. Imaginate que tu proyecto se llama Miapp.jar, pues lo añades en esa carpeta sin olvidar de incluir el Manifiesto que es un fichero de esta
forma:

Manifest-Version: 1.0
Main-Class: <tu clase Main>
Class-Path: <las librerias que usas>

Es decir, este fichero de manifiesto tiene 3 entradas, en una de ellas (Main-Class) indicas la clase principal que quieres ejecutar, y en otra (Class-Path) indicas las librerias de las que depende, y que necesita para poder ejecutarse.

En la carpeta Lib añadirias todos esos .jar adicionales que usas. y por ultimo el manifiesto de one-jar que te encuentras al descomprimir el fichero descargado.

Con todo eso crearias un nuevo jar que empaquetas con el mismo nombre p.ej MiApp.jar de manera que al ejecutarlo con:

java -jar MiApp.jar

realmente se llama a la clase proncipal de one-jar que se encarga de cargar en memoria los .jar que usas y despues llama a tu clase principal. El resultado, se ejecuta tu clase principal y no necesita buscar los .jar adicionales porque ya estan cargados.

Es un poco complicado, Veamos de manera rapida como utilizarlo.

Yo tengo este proyecto:

mi clase principal se llama: comun.util.unix.ScriptActionExecution

y depende de estas librerias:

bsh-2.0b4.jar
profiler.jar
jsch-0.1.20.jar
log4j-1.2.8.jar
jdom.jar

El fichero de manifiesto («MANIFEST.MF») que tengo para mi aplicacion es:


Manifest-Version: 1.0
Main-Class: comun.util.unix.ScriptActionExecution
Class-Path: bsh-2.0b4.jar profiler.jar jsch-0.1.20.jar log4j-1.2.8.jar jdom.jar

onejar01.jpg

El fichero build.xml de ANT para generarlo podria ser el siguiente:


<?xml version="1.0"?>
<project name="clienteSSH" default="init" basedir=".">
<property name="fuente" value="." />
<property name="doc" value="javadoc" />
<property name="paquetes" value="comun.util.unix.*" />
<!-- textos relativos al proyecto -->
<property name="proyecto" value="clienteSSH" />
<property name="copyright" value="(k) 2005" />
<property name="jarfile" value="Main.jar" />
<target name="init">
<tstamp/>
<property name="nombre" value="Definiciones para Tareas"/>
<echo message="------------------- ${nombre} ----------------"/>
<property name="debug" value="on"/>
<property name="optimize" value="off"/>
<property name="deprecation" value="off"/>
<property name="build.compiler" value="classic"/>
<property name="target.vm" value="1.3"/>
<path id="classpath">
<fileset dir="./lib">
<include name="*.jar"/>
</fileset>
</path>
</target>
<target name="compilar" depends="init">
<echo message="-------------- Compilando Clases --------------"/>
<mkdir dir="build"/>
<javac srcdir="."
destdir="./build"
debug="${debug}"
optimize="${optimize}"
deprecation="${deprecation}"
target="${target.vm}">
<classpath refid="classpath"/>
</javac>
</target>
<target name="jar" depends="compilar">
<echo message="-------------- Generando Archivo JAR --------------"/>
<jar jarfile="Main.jar" manifest="./MANIFEST.MF" basedir="./build" includes="**"/>
</target>
<target name="limpiar" depends="compilar" >
<delete file="Main.jar"/>
</target>
<target name="limpiardoc">
<!--
Eliminamos los directorios de la vez anterior.
Eliminamos los dos juntos para no favorecer que
existan versiones de docuemntacion que no se
corresponden con las clases compiladas y viceversa
-->
<delete dir="${doc}" />
</target>
<target name="documentar" depends="limpiardoc">
<!--
Creamos el directorio si no existe. Y no existira porque
lo hemos eliminado con el objetivo limpiar, del cual
depende documentar
-->
<mkdir dir="${doc}" />
<!--
Generamos la documentcion de nuestro proyecto.
Los atributos de esta clase son incontables, asi
que ahora solo mostreré unos pocos. Consultar la
documentación de Ant y de javadoc si necesitais
algo más.
-->
<javadoc packagenames="${paquetes}"
sourcepath="${fuente}"
destdir="${doc}"
author="true"
version="true"
private="true"
locale="es"
windowtitle="API de ${proyecto}"
doctitle="${proyecto}"
bottom="${copyright}"
/>
</target>
<target name="one-jar" depends="jar" description="Build one ONE-JAR">
<property name="onejardir" location="./one-jar"/>
<mkdir dir="${onejardir}/main"/>
<mkdir dir="${onejardir}/lib"/>
<copy tofile="${onejardir}/${jarfile}"
file="lib/one-jar-boot-0.95.jar"/>
<copy todir="${onejardir}/main"
file="./${jarfile}"/>
<copy todir="${onejardir}/lib" flatten="true">
<fileset dir="lib" includes="*.jar"
excludes="one-jar-boot-0.95.jar"/>
</copy>
<jar jarfile="${onejardir}/${jarfile}" update="true"
basedir="${onejardir}" excludes="${jarfile}"/>
</target>
</project>

Disculpad el fichero ANT (lo he recuperado de hace unos cuantos añitos) seguro que es mejorable, pero de todas formas es funcional.

Aqui la tarea que nos interesa es «one-jar» pues al ejecutarla desde eclipse se me genera ya el fichero unico .jar con todas las dependencias incluidas. Que hace, pues lo que os comente al principio.

Me copia la estructura de lib/one-jar-boot-0.95.jar, tambien me añade al directorio «main» el .jar de mi aplicacion original (incluyendo
su fichero de manifiesto) y por ultimo añade a la carpeta «lib» todos esos .jar de los que depende mi aplicacion.
Y me genera un .jar nuevo (y unico) con el mismo nombre que el que haya configurado en la propiedad ${jarfile} que en este caso es
«Main.jar»

y para ejecutarlo me lo llevo el .jar recien generado a cualquier maquina con el JRE correspondiente y lo invoco como java -jar Main.jar (no hay problemas ya de Class not found)

onejar02.jpg

Mas informacion