Empaquetamiento de aplicaciones de escritorio (standalone) con Maven
Introducción
Actualmente he tenido la necesidad de empaquetar una aplicación de escritorio construida con Maven y me he dado cuenta de que ninguna de las alternativas de empaquetamiento que este me ofrece era la que se ajustaba a mis necesidades.
Más concretamente, lo que no se ajustaba a mis necesidades es el modo en que Maven coloca las dependencias de tu proyecto a la hora de crear el paquete a distribuir.
Maven, mete todos los .class
de las dependencias de tu proyecto dentro del propio Jar de tu aplicación. (assembly jar-with-dependencies
).
Que queréis que os diga, a mi eso no me gusta. Prefiero tener mi jar (con el método main) por un lado con el MANIFEST modificado de tal forma que haga referencia los jars que necesita para ejecutarse.
Más concretamente, me gusta tener un directorio dist
en donde dentro está mi jar (con el método main) y las dependencias en un directorio dist/lib
.
Pues bien, en este ejemplo vamos a ver como conseguir ese método, siendo fácilmente adaptable a otro tipo de necesidades que tengáis.
Ejemplo, Aplicación básica de escritorio con dependencias:
A continuación vamos a ver como conseguir lo anteriormente expuesto, para ello nuestra aplicación de escritorio será muy básica y tendrá dos dependencias.
Eso si, el esfuerzo para una aplicación con miles de clases y decenas dependencias sería practicamente igual.. quizás sería necesario algún pequeño reajuste, pero vamos poco más.
El código fuente de este tutorial puede ser descargado desde aquí (proyecto Eclipse con Maven 2).
Entorno
El siguiente ejemplo está construido en el siguiente entorno:
- HP Pavilion.
- Windows Vista Home Premium.
- Eclipse Ganymede.
- Java 6.
- Maven 2.
- Plugin Maven 4QE para Eclipse.
Mi aplicación de escritorio con su bonito método main:
Está autocomentada, pero bueno, resalto que usa las librerías commons-lang y commons-io de Apache.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package com.autentia.tutoriales.maven.standalone; import java.io.File; import org.apache.commons.lang.WordUtils; import org.apache.commons.io.FileUtils; public class MavenStandaloneExample { /** * Dado un archivo de texto imprime su contenido capitalizado, es decir, la primera letra de * cada palabra en mayúsculas. * @param args Un sólo parámetro con la ruta completa al archivo de texto a capitalizar. */ public static void main(String[] args) { try { File file = new File(args[0]); String fileContent = FileUtils.readFileToString(file); System.out.println(WordUtils.capitalizeFully(fileContent)); } catch (java.lang.ArrayIndexOutOfBoundsException ex){ System.out.println("Debe especificar el nombre de un archivo"); } catch (java.io.IOException ex){ System.out.println("Se ha producido un error al tratar el archivo especificado."); System.out.println("Verique que exista y que tenga permisos de lectura."); } } } |
Archivo de configuración de Maven 2: pom.xml
:
A continuación exponemos el archivo de configuración de Maven, se presupone que el lector ya tiene nociones de Maven.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.autentia.tutoriales</groupId> <artifactId>maven_standalone_app_example</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>maven_standalone_app_example</name> <url>http://www.adictosaltrabajo.com</url> <build> <!-- El jar principal de la aplicación se llamará así --> <finalName>mavenStandaloneExample</finalName> <plugins> <!-- Sintáxis Java 5 --> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- Creamos el JAR de nuestra aplicación modificando el archivo MANIFEST Para más información: http://maven.apache.org/plugins/maven-jar-plugin/ --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <!-- El punto de entrada a mi aplicación de escritorio --> <mainClass>com.autentia.tutoriales.maven.standalone.MavenStandaloneExample</mainClass> <!-- Le digo que me añada al MANIFEST la propiedad ClassPath con todos los jars que necesito --> <addClasspath>true</addClasspath> <!-- A cada uno de los jars le pongo el prefijo lib, pues los guardaré a ese directorio --> <classpathPrefix>lib</classpathPrefix> </manifest> <!-- Le digo que me añada estas propiedades al MANIFEST (Puedes añadir las que desees) pongo estas a modo de ejemplo --> <manifestEntries> <Autor>Carlos García Pérez</Autor> <Empresa>Autentia Real Business Solutions</Empresa> <url>http://www.autentia.com</url> </manifestEntries> </archive> </configuration> </plugin> <!-- Empaquetado de nuestra aplicación y sus dependencias Para más información: http://maven.apache.org/plugins/maven-assembly-plugin/ --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptors> <descriptor>maven_assembly_conf.xml</descriptor> </descriptors> </configuration> <!-- El assembly se creará en la fase de package, de esta forma nos evitaremos hacer mvn assembly:assembly, bastará con hacer mvn package --> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.3</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>1.4</version> </dependency> </dependencies> </project> |
Archivo de configuración del assembly (maven_assembly_conf.xml)
:
El archivo está autocomentado.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<?xml version="1.0" encoding="UTF-8"?> <assembly> <!-- Yo le doy este identificador para mis aplicaciones --> <id>standaloneapp-dist-format</id> <!-- http://maven.apache.org/plugins/maven-assembly-plugin/assembly.html --> <formats> <!-- La salida será un directorio --> <format>dir</format> </formats> <!-- Generará la salida a la carpeta dist al mismo nivel del pom.xml.. manias, me gusta así --> <baseDirectory>../../dist</baseDirectory> <fileSets> <!-- Copiamos todos los jar del directorio "target" (donde compila maven) al directorio de salida "baseDirectory" es decir, sólo copiará el jar propio de la aplicación --> <fileSet> <directory>target</directory> <outputDirectory>.</outputDirectory> <includes> <include>*.jar</include> </includes> </fileSet> </fileSets> <dependencySets> <dependencySet> <!-- Las dependencias irán al directorio lib --> <outputDirectory>lib</outputDirectory> <!-- Que genere las dependencias como JAR y no como .class en sus subdirectorios --> <unpack>false</unpack> <!-- Que sólo genere las dependencias runtime --> <scope>runtime</scope> <!-- El JAR propio de la aplicación no lo incluimos en el directorio lib --> <excludes> <exclude>${groupId}:${artifactId}</exclude> </excludes> </dependencySet> </dependencySets> </assembly> |
El archivo /META-INF/MANIFEST
generado:
Si abrimos el JAR veremos que nos ha generado el siguiente archivo MANIFEST:
1 2 3 4 5 6 7 8 9 10 |
Manifest-Version: 1.0 Archiver-Version: Plexus Archiver Created-By: Apache Maven Built-By: cgarcia Build-Jdk: 1.6.0_11 Main-Class: com.autentia.tutoriales.maven.standalone.MavenStandaloneExample Autor: Carlos García Pérez Empresa: Autentia Real Business Solutions url: http://www.autentia.com Class-Path: lib/commons-lang-2.3.jar lib/commons-io-1.4.jar |
Invocando la aplicación:
Y para terminar, vamos a ver como ejecutariamos la aplicación anterior:
1 |
java -jar mavenStandaloneExample.jar <Ruta completa a un archivo de texto> |
Referencias
- http://maven.apache.org/plugins/maven-jar-plugin/
- http://maven.apache.org/plugins/maven-assembly-plugin/assembly.html
Conclusiones
Si os gusta mi forma de organizar el proyecto teneis un esqueleto fácilmente adaptable a vuestras necesidades.
En Autentia llevamos años trabajando con Maven para gestionar nuestros proyectos, si algún día necesitais ayuda o formación al respecto espero nos tengáis en cuenta.
Un saludo, espero que os haya parecido útil este tutorial.
Carlos García Pérez. Creador de MobileTest, un complemento educativo para los profesores y sus alumnos.
cgpcosmad@gmail.com