viernes, 11 de noviembre de 2016

Java Maven: Hello World

No solo de .Net vive el blog, así que tal como estamos haciendo tutoriales básicos sobre .Net Core y Asp.Net Core, vamos a hacer pequeños post introductorios al mundo Java.
Al igual que hacemos con los de .Net evitaremos en la medida de lo posible usar IDEs y utilizar editores de texto para comprender mejor lo que sucede entre bambalinas.

Así que empezaremos con lo básico, el típico hola mundo de Java con Maven. Para los desarrolladores .net que no sepan que es Maven decirles que es una herramienta que sirve para compilar y empaquetar (una especie de MsBuild) unido a un gestor de paquetes (como NuGet) y que es extensible mediante plugins, creación de distintos tipos de proyectos mediante arquetipos, etc. Lo mejor es echarle un vistazo al sitio del proyecto y ver sus características.

Maven se basa en una serie de convenciones (que se pueden cambiar mediante configuración).
La primera convención de Maven es la estructura de directorios del proyecto: El código fuente del proyecto se encuentra en src/main/java/ Así que comenzamos el proyecto creando directorios: md src\main\java\hello.

Lo siguiente que vamos a hacer es crear un fichero POM, que es el fichero de configuración de Maven. En este fichero se configuran las dependencias, los empaquetados, plugins, etc. Un fichero POM sencillo sería así:

<?xml version="1.0" encoding="UTF-8"?>
<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> <!-- Versión del POM-->
    <groupId>hello</groupId><!--Grupo al que pertenece el proyeto, como el namespace del proyecto-->
    <artifactId>helloWorld</artifactId><!--Nombre que va a recibir el empaquetado del proyecto-->
    <packaging>jar</packaging><!--Tipo de empaquetado, en este casjo JAR-->
    <version>0.1.0</version><!-- Versión del proyecto-->
</project>

Ahora vamos al código, entramos en el directorio hello y creamos un fichero con el típico hola mundo de java:

package hello;

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

Para compilar, solo tenemos que escribir mvn compile (nos olvidamos del javac) compila el proyecto. Maven deja la salida de la compilación en el directorio target\classes. Así que para ejecutar entramos en ese directorio y escribimos java hello.HelloWorld
Para crear un jar solo tenemos que ejecutar desde el directorio raíz del proyecto mvn package y tendremos en el directorio target el fichero helloWorld-0.1.0.jar (aircraftId-version)
Si intentamos ejecutar el jar nos dará un error:

C:\Desarrollo\HolaMundo>java -jar target/helloWorld-0.1.0.jar
no hay ningún atributo de manifiesto principal en target/helloWorld-0.1.0.jar

Para poder generar un jar ejecutable con Maven podemos usar el plugin Maven Shade. Esto se hace añadiendo el plugin en el POM

<build>
 <plugins>
  <plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-shade-plugin</artifactId>
   <version>2.1</version>
   <executions>
    <execution>
     <phase>package</phase>
     <goals>
      <goal>shade</goal>
     </goals>
     <configuration>
      <transformers>
       <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
        <mainClass>hello.HelloWorld</mainClass>
       </transformer>
      </transformers>
     </configuration>
    </execution>
   </executions>
  </plugin>
 </plugins>
</build>

En el plugin debemos indicar la clase que contiene el método public void main. Si ahora repetimos el empaquetado y la ejecución veremos que ya funciona.
Pero veamos como se añade una referencia, por ejemplo noda joda time (en java SE 8 ya no se recomienda usar joda, pero es un ejemplo de uso de una dependencia)

<dependencies>
 <dependency>
  <groupId>joda-time</groupId>
  <artifactId>joda-time</artifactId>
  <version>2.9.6</version>
 </dependency>
</dependencies>

Y ya con esto podemos utilizar la librería en nuestro código, con lo que nuestra clase HelloWorld quedaría así:

package hello;

import org.joda.time.LocalTime;

public class HelloWorld {
    public static void main (String [] args){
        LocalTime now = new LocalTime();
        System.out.println("Now the local time is " + now);
        System.out.println("Hello World!!");
    }
}

Con esto finalizamos el hola mundo de un proyecto java con Maven, donde hemos compilado, añadido dependencias, añadido un plugin de Maven y empaquetado el código en un jar ejecutable. El código fuente lo tenéis disponible en GitHub.
No se si el acercamiento de este tema ha sido condicionado por mi experiencia con .net. Se agradecen comentarios al respecto.

Actualización: 19/11/2016

Pedro me comenta para hacer un Jar ejecutable se suele utilizar el assembly-plugin. Elimino el plugin anterior y añado este. Y lo primero que descubro al revisar la documentación en maven es que no es necesario que ponga el groupId del plugin (ni en este caso, ni en el anterior) ya que por defecto los plugins pertenecen al grupo org.apache.maven.plugins. La sección de plugisn del POM queda ahora, tal que así:

        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <mainClass>hello.HelloWorld</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <!-- this is used for inheritance merges -->
                        <phase>package</phase>
                        <!-- bind to the packaging phase -->
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>

La configuración de este plugin es diferente al anterior, seguimos teniendo que indicar la clase que tiene el public static void main (línea 11), pero además hay que indicar un Assemblie Descriptor que es lo que utiliza este plugin para crear los paquetes que se indican dentro del nodo de configuración.
Afortunadamente el plugin cuenta con algunos predefinidos, en este caso es lo que vamos a usar (jar-with-dependencies).
Ahora, al ejecutar mvn package tendremos dos jars de salida: helloWorld-0.1.0.jar y helloWorld-0.1.0-jar-with-dependencies. El ejecutable es el segundo.
Y como comentábamos anteriormente el código está disponible en GitHub.

7 comentarios:

  1. Excelente articulo , me ayudo bastante. Buen trabajo

    ResponderEliminar
  2. Muchas gracias, funciona correctamente.

    ResponderEliminar
  3. Respuestas
    1. Me alegra mucho haber ayudado, siento que el blog lo tengamos un poco abandonado, espero sacar tiempo para mantenerlo más actualizado

      Eliminar
  4. en 2021 si tenes este problema

    C:\Desarrollo\HolaMundo>java -jar target/helloWorld-0.1.0.jar
    no hay ningún atributo de manifiesto principal en target/helloWorld-0.1.0.jar

    Lo solucionas facilmente usando esta confguracion de plugin

    org.springframework.boot
    spring-boot-maven-plugin

    true

    ResponderEliminar
  5. "
    org.springframework.boot
    spring-boot-maven-plugin

    true

    "

    ResponderEliminar