Enmilocalfunciona

Thoughts, stories and ideas.

¿Por qué utilizar Arquetipos Maven?

Publicado por Jose Antonio Navarro Fuentes el

JavaMavenArquetipos

Muy a menudo me encuentro, a la hora de llevar a cabo una aplicación, con la necesidad de realizar un gran número de proyectos o módulos para construir servicios que en muchos casos comparten una serie de características en común.

En estas situaciones se suele realizar el servicio totalmente nuevo, o haciendo copy&paste de uno anterior, provocando un aumento en los recursos necesarios para llevarlo acabo y aumentando el riesgo de errores.

Para optimizar esta situación existen los arquetipos de Maven, los cuales son plantillas que se pueden utilizar para crear proyectos, módulos, etc. que se basan en parámetros establecidos en la definición del arquetipo.

¿Qué es un arquetipo?

En esencia, un arquetipo es un patrón o modelo sobre el que se pueden desarrollar todas aquellas tareas que son de un mismo tipo. Puede decirse que son plantillas, parametrizadas o configuradas para utilizar determinadas tecnologías que los desarrolladores utilizan como base para escribir y organizar el código de la aplicación.

Que todos los proyectos que involucren ciertas tecnologías partan de una base (arquetipo, plantilla o esqueleto configurado) común, tiene ventajas evidentes:

  • Homogeneidad entre distintos desarrollos que utilizan las mismas tecnologías.
  • Reutilización y construcción de unos arquetipos como suma de otros.
  • Estandarización de los proyectos dentro de una organización. La homogeneidad en la estructura del proyecto facilita las tareas de desarrollar, desplegar y mantener el proyecto generado.
  • Reducción del tiempo necesario para la construcción de los diversos servicios.

Cómo crear un arquetipo

Para crear la base de un arquetipo, disponemos del siguiente comando:
mvn archetype:generate -DgroupId=[groupID del arquetipo] -DartifactId=[artifactId del arquetipo] -DarchetypeArtifactId=maven-archetype-archetype

Para nuestro ejemplo ejecutamos el siguiente comando:
mvn archetype:generate -DgroupId=com.atSistemas -DartifactId=arquetipoAT -DarchetypeArtifactId=maven-archetype-archetype

Obteniendo la siguiente estructura:

alt tag

A partir de este arquetipo podríamos generar un proyecto Maven con el siguiente comando:
mvn archetype:generate -DarchetypeGroupId=com.atSistemas -DarchetypeArtifactId=arquetipoAT

Que en este caso lo que obtendríamos es el siguiente proyecto:

alt tag

Como se observa ha construido un proyecto con la misma estructura definida en el arquetipo. Pero esto nos sabe a poco, en la siguiente sección vamos a desarrollar paso a paso un arquetipo en el que se vea la verdadera potencia de los mismos.

Ejemplo de arquetipo

Para ver las posibilidades que ofrecen los arquetipos, vamos a crear un arquetipo que permite crear servicios CQRS con Spring Boot  que expongan un API REST. El código del ejemplo se encuentra en GitHub .

Analizando la estructura anterior generada al crear el arquetipo vemos que se compone de:

1.- Un pom.xml a nivel raíz del arquetipo, para poder construirlo.

2.- Los ficheros y carpetas que compondrán el cuerpo del arquetipo. Se sitúan dentro de la carpeta src/main/resources/archetype-resources/ y a partir de ellos se crearán las clases que componen el proyecto final.

3.- Los pom.xml del proyecto o módulos que se crearán a partir del arquetipo.

4.- El descriptor del arquetipo archetype.xml, que se sitúa en la carpeta src/main/resources/META-INF y que indica al mecanismo de generación de arquetipos todo el contenido que se va a generar.

Para comenzar a construir el arquetipo debemos realizar las siguientes tareas:

  • Definir la estructura del arquetipo.
  • Escribir el contenido de los ficheros que generarán las clases.
  • Completar el descriptor del arquetipo.

Estructura del arquetipo

Debemos de planificar la estructura del proyecto final, tanto los paquetes como las clases que lo compondrán.

Para la definición de esta estructura nos apoyaremos en la funcionalidad que proporciona el arquetipo Maven para crear nombres dinámicos tanto de ficheros como de carpetas. Para poder crear un nombre dinámico se debe poner __nombreCorto__, tanto para carpeta como nombre de fichero, este __nombreCorto__ se expandirá con el contenido definido en el descriptor del arquetipo.

Si quisieramos que en nuestro proyecto final apareciera una interfaz y una clase, con el mismo nombre del artefacto que se va a generar, se debería poner lo siguiente:

alt tag

Si queremos crear un folder a partir de un atributo llamado resourceName, deberíamos indicar:

alt tag

En nuestro caso, queremos que se cree un paquete con el nombre del servicio que se va a crear, por lo que crearemos una carpeta llamada __serviceName__, en la cual se crearán las carpetas que crearán los distintos paquetes del proyecto final.

Para nuestro ejemplo (que como he indicado es un servicio Spring Boot que ofrece un API REST para realizar CRUD sobre un dato), creamos las carpetas y clases necesarias y nuestro arquetipo pasaría a tener el siguiente aspecto:

alt tag

Contenido de los ficheros

Una vez definida la estructura, el siguiente paso consiste en dar contenido a cada uno de los ficheros, los cuales generaran las clases de nuestro proyecto final, el maven-archetype-plugin utiliza el motor de Velocity, para la generación y conversión de los ficheros que se utilizan como plantilla para la creación de las clases en el proyecto final.

Esto nos permite dentro de la plantilla poner, por ejemplo en el fichero que generará la clase controller:

package ${package}.${serviceNameFolder.replace('/','.')}.rest.controller;  

Lo cual en el momento de crear el controlador definirá el paquete correspondiente a la clase.

Si en nuestro caso damos la posibilidad de definir condicionalmente el método DELETE en la interfaz ofrecida podríamos tener dentro del fichero lo siguiente:

#if ($type == "DELETE")
    /**
     * Borra ${serviceName}.
     *
     * @param id Integer
     * @return ${serviceName}DTO
     */
    @ApiOperation(value = "Elimina un ${serviceName}", tags = { "Controlador ${serviceName}s" })
    @ApiResponses(value = { //
            @ApiResponse(code = 200, message = "${serviceName} eliminado", response = ${serviceName}DTO.class), @ApiResponse(code = 404, message = "No eliminado") })
    @RequestMapping(method = RequestMethod.DELETE, value = "{id}")
    public ResponseEntity<${serviceName}DTO> delete(@ApiParam(value = "Id del ${serviceName} que se va a crear", required = true) @PathVariable String id) {

        ${serviceName} ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Deleted = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Service.delete(new Integer(id));

        HttpHeaders headers = new HttpHeaders();

        ${serviceName}DTO output = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Deleted.to${serviceName}DTO();
        return new ResponseEntity<>(output, headers, HttpStatus.ACCEPTED);
    }
    #end

A continuación se muestran algunos ejemplos de los ficheros generados. El ejemplo completo se encuentra en GitHub.

Fichero __serviceName__CommandsController.java del arquetipo:

package ${package}.${serviceNameFolder.replace('/','.')}.rest.controller;

import io.swagger.annotations.Api;  
import io.swagger.annotations.ApiImplicitParam;  
import io.swagger.annotations.ApiImplicitParams;  
import io.swagger.annotations.ApiOperation;  
import io.swagger.annotations.ApiParam;  
import io.swagger.annotations.ApiResponse;  
import io.swagger.annotations.ApiResponses;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.http.HttpHeaders;  
import org.springframework.http.HttpStatus;  
import org.springframework.http.ResponseEntity;  
import org.springframework.web.bind.annotation.PathVariable;  
import org.springframework.web.bind.annotation.RequestBody;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RequestMethod;  
import org.springframework.web.bind.annotation.RestController;  
import org.springframework.web.util.UriComponentsBuilder;

import ${package}.${serviceNameFolder.replace('/','.')}.service.domain.${serviceName};  
import ${package}.${serviceNameFolder.replace('/','.')}.service.${serviceName}Service;  
import ${package}.${serviceNameFolder.replace('/','.')}.rest.dto.${serviceName}DTO;

@RestController
@RequestMapping(value = "/${serviceNameFolder}")
@Api(value = "${serviceName}CommandsController", produces = "application/json")
/**
 * Controlador de commands 
 *
 * @author Jose Antonio Navarro janavarro.fuentes@atsistemas.com
 */
public class  ${serviceName}CommandsController {

    @Autowired
    private ${serviceName}Service ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Service;

    /**
     * Crea ${serviceName}.
     *
     * @param ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)} ${serviceName}DTO
     * @param builder UriComponentsBuilder
     * @return ${serviceName}DTO
     */
    @ApiOperation(value = "Crea un ${serviceName}", tags = { "Controlador ${serviceName}s" })
    @ApiResponses(value = { //
            @ApiResponse(code = 200, message = "${serviceName} creado", response = ${serviceName}DTO.class), @ApiResponse(code = 404, message = "No creado") })
    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<${serviceName}DTO> create(@ApiParam(value = "${serviceName} que se va a crear", required = true) @RequestBody @Valid ${serviceName}DTO ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}, UriComponentsBuilder builder) {

        ${serviceName} ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Service.save(${serviceName}.from${serviceName}DTO(${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}));

        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(builder.path("${serviceNameFolder}/{id}")
                .buildAndExpand(String.valueOf(${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created.getId())).toUri());

        ${serviceName}DTO output = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created.to${serviceName}DTO();
        return new ResponseEntity<>(output, headers, HttpStatus.CREATED);
    }

    /**
     * Actualiza ${serviceName}.
     *
     * @param ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)} ${serviceName}DTO
     * @param builder UriComponentsBuilder
     * @return ${serviceName}DTO
     */
    @ApiOperation(value = "Actualiza un ${serviceName}", tags = { "Controlador ${serviceName}s" })
    @ApiResponses(value = { //
            @ApiResponse(code = 200, message = "${serviceName} actualiza", response = ${serviceName}DTO.class), @ApiResponse(code = 404, message = "No actualizado") })
    @RequestMapping(method = RequestMethod.PUT)
    public ResponseEntity<${serviceName}DTO> update(@ApiParam(value = "${serviceName} que se va a actualizar", required = true) @RequestBody @Valid ${serviceName}DTO ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}, UriComponentsBuilder builder) {

        ${serviceName} ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Service.save(${serviceName}.from${serviceName}DTO(${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}));

        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(builder.path("${serviceNameFolder}/{id}")
                .buildAndExpand(String.valueOf(${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created.getId())).toUri());

        ${serviceName}DTO output = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created.to${serviceName}DTO();
        return new ResponseEntity<>(output, headers, HttpStatus.ACCEPTED);
    }
    #if ($type == "DELETE")
    /**
     * Borra ${serviceName}.
     *
     * @param id Integer
     * @return ${serviceName}DTO
     */
    @ApiOperation(value = "Elimina un ${serviceName}", tags = { "Controlador ${serviceName}s" })
    @ApiResponses(value = { //
            @ApiResponse(code = 200, message = "${serviceName} eliminado", response = ${serviceName}DTO.class), @ApiResponse(code = 404, message = "No eliminado") })
    @RequestMapping(method = RequestMethod.DELETE, value = "{id}")
    public ResponseEntity<${serviceName}DTO> delete(@ApiParam(value = "Id del ${serviceName} que se va a crear", required = true) @PathVariable String id) {

        ${serviceName} ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Deleted = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Service.delete(new Integer(id));

        HttpHeaders headers = new HttpHeaders();

        ${serviceName}DTO output = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Deleted.to${serviceName}DTO();
        return new ResponseEntity<>(output, headers, HttpStatus.ACCEPTED);
    }
    #end
}

Resultado en la clase PersonaCommandsController.java del proyecto final:

package com.atSistemas.persona.gestion.rest.controller;

import io.swagger.annotations.Api;  
import io.swagger.annotations.ApiImplicitParam;  
import io.swagger.annotations.ApiImplicitParams;  
import io.swagger.annotations.ApiOperation;  
import io.swagger.annotations.ApiParam;  
import io.swagger.annotations.ApiResponse;  
import io.swagger.annotations.ApiResponses;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.http.HttpHeaders;  
import org.springframework.http.HttpStatus;  
import org.springframework.http.ResponseEntity;  
import org.springframework.web.bind.annotation.PathVariable;  
import org.springframework.web.bind.annotation.RequestBody;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RequestMethod;  
import org.springframework.web.bind.annotation.RestController;  
import org.springframework.web.util.UriComponentsBuilder;

import com.atSistemas.persona.gestion.service.domain.Persona;  
import com.atSistemas.persona.gestion.service.PersonaService;  
import com.atSistemas.persona.gestion.rest.dto.PersonaDTO;

@RestController
@RequestMapping(value = "/persona/gestion")
@Api(value = "PersonaCommandsController", produces = "application/json")
/**
 * Controlador de commands 
 *
 * @author Jose Antonio Navarro janavarro.fuentes@atsistemas.com
 */
public class  PersonaCommandsController {

    @Autowired
    private PersonaService personaService;

    /**
     * Crea Persona.
     *
     * @param persona PersonaDTO
     * @param builder UriComponentsBuilder
     * @return PersonaDTO
     */
    @ApiOperation(value = "Crea un Persona", tags = { "Controlador Personas" })
    @ApiResponses(value = { //
            @ApiResponse(code = 200, message = "Persona creado", response = PersonaDTO.class), @ApiResponse(code = 404, message = "No creado") })
    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<PersonaDTO> create(@ApiParam(value = "Persona que se va a crear", required = true) @RequestBody @Valid PersonaDTO persona, UriComponentsBuilder builder) {

        Persona personaCreated = personaService.save(Persona.fromPersonaDTO(persona));

        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(builder.path("persona/gestion/{id}")
                .buildAndExpand(String.valueOf(personaCreated.getId())).toUri());

        PersonaDTO output = personaCreated.toPersonaDTO();
        return new ResponseEntity<>(output, headers, HttpStatus.CREATED);
    }

    /**
     * Actualiza Persona.
     *
     * @param persona PersonaDTO
     * @param builder UriComponentsBuilder
     * @return PersonaDTO
     */
    @ApiOperation(value = "Actualiza un Persona", tags = { "Controlador Personas" })
    @ApiResponses(value = { //
            @ApiResponse(code = 200, message = "Persona actualiza", response = PersonaDTO.class), @ApiResponse(code = 404, message = "No actualizado") })
    @RequestMapping(method = RequestMethod.PUT)
    public ResponseEntity<PersonaDTO> update(@ApiParam(value = "Persona que se va a actualizar", required = true) @RequestBody @Valid PersonaDTO persona, UriComponentsBuilder builder) {

        Persona personaCreated = personaService.save(Persona.fromPersonaDTO(persona));

        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(builder.path("persona/gestion/{id}")
                .buildAndExpand(String.valueOf(personaCreated.getId())).toUri());

        PersonaDTO output = personaCreated.toPersonaDTO();
        return new ResponseEntity<>(output, headers, HttpStatus.ACCEPTED);
    }
        /**
     * Borra Persona.
     *
     * @param id Integer
     * @return PersonaDTO
     */
    @ApiOperation(value = "Elimina un Persona", tags = { "Controlador Personas" })
    @ApiResponses(value = { //
            @ApiResponse(code = 200, message = "Persona eliminado", response = PersonaDTO.class), @ApiResponse(code = 404, message = "No eliminado") })
    @RequestMapping(method = RequestMethod.DELETE, value = "{id}")
    public ResponseEntity<PersonaDTO> delete(@ApiParam(value = "Id del Persona que se va a crear", required = true) @PathVariable Integer id) {

        Persona personaDeleted = personaService.delete(id);

        HttpHeaders headers = new HttpHeaders();

        PersonaDTO output = personaDeleted.toPersonaDTO();
        return new ResponseEntity<>(output, headers, HttpStatus.ACCEPTED);
    }
    }

Descriptor del arquetipo

El último paso es completar el descriptor del arquetipo. Como he indicado anteriormente es el fichero archetype-metadata.xml que se encuentra en la carpeta src\main\resources\META-INF\maven del arquetipo. En el caso del arquetipo de prueba el descriptor es:

<?xml version="1.0" encoding="UTF-8"?>  
<archetype-descriptor  
    xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd"
    name="Subprocesses Maven Archetype"
    xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <requiredProperties>
        <requiredProperty key="groupId">
            <defaultValue>com.atSistemas</defaultValue>
        </requiredProperty>
        <requiredProperty key="version">
            <defaultValue>1.0.0-SNAPSHOT</defaultValue>
        </requiredProperty>
        <requiredProperty key="package">
            <defaultValue>com.atSistemas</defaultValue>
        </requiredProperty>
        <requiredProperty key="type">
            <defaultValue>NODELETE</defaultValue>
        </requiredProperty>
        <requiredProperty key="serviceName" />
        <requiredProperty key="serviceNameFolder" />
    </requiredProperties>

    <fileSets>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.java</include>
            </includes>
        </fileSet>
        <fileSet filtered="true" encoding="UTF-8">
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
            </includes>
        </fileSet>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
            <directory>src/test/java</directory>
            <includes>
                <include>**/*.java</include>
            </includes>
        </fileSet>
        <fileSet filtered="true" encoding="UTF-8">
            <directory>src/test/resources</directory>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
            </includes>
        </fileSet>
    </fileSets>
</archetype-descriptor>

Como desplegar un arquetipo

Una vez generado el arquetipo Maven, es necesario instalar la librería JAR correspondiente en el repositorio. Para ello se accede a la URL del repositorio que se este utilizando y se realizan las acciones siguientes:

Almacenar el JAR del arquetipo:

mvn install:install-file -Dfile=arquetipoAT-1.0.0-SNAPSHOT.jar -DgroupId=com.atSistemas -DartifactId=arquetipoAT -Dversion=1.0.0-SNAPSHOT -Dpackaging=jar

Instalación del catálogo:

Para habilitar la visibilidad del arquetipo desde IDE's es necesario llevar a cabo la instalación de un catálogo. Un catálogo es un archivo XML que se encuentra en la ruta del repositorio Maven. Este fichero lo tenemos que editar para añadirle:

<?xml version="1.0" encoding="UTF-8"?>  
<archetype-catalog xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0 http://maven.apache.org/xsd/archetype-catalog-1.0.0.xsd"  
    xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <archetypes>
    <archetype>
      <groupId>com.atSistemas</groupId>
      <artifactId>arquetipoAT</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <description>arquetipo AT</description>
    </archetype>
  </archetypes>
</archetype-catalog>  

Utilización del arquetipo de ejemplo

Para ejecutar el arquetipo, y siguiendo el modo estándar de utilización, debemos servirnos del plugin 'archetype' y el goal 'generate', indicando el arquetipo en concreto que queremos ejecutar. Para ello escribimos el siguiente comando:
mvn archetype:generate -DarchetypeGroupId=com.atSistemas -DarchetypeArtifactId=arquetipoAT -DarchetypeVersion=1.0.0-SNAPSHOT

En la ejecución se nos indicará que elijamos el artifactId y el serviceNameFolder. Este último sirve para crear la estructura de paquetes del proyecto. Por ejemplo, con el valor 'persona/gestion' se generará una estructura de paquetes como esta: 'com.atSistemas.persona.gestion'.

Otra forma de ejecutar el arquetipo es en modo batch de la siguiente forma:
mvn archetype:generate -B -DarchetypeGroupId=com.atSistemas -DarchetypeArtifactId=arquetipoAT -DarchetypeVersion=1.0.0-SNAPSHOT -DartifactId=personaAT -DserviceName=Persona -DserviceNameFolder=persona/gestion -Dtype=DELETE

La siguiente salida indica que se ha generado correctamente el proyecto

[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: arquetipoAT:1.0.0-SNAPSHOT
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.atSistemas
[INFO] Parameter: artifactId, Value: personaAT
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: package, Value: com.atSistemas
[INFO] Parameter: packageInPathFormat, Value: com/atSistemas
[INFO] Parameter: package, Value: com.atSistemas
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: serviceNameFolder, Value: persona/gestion
[INFO] Parameter: groupId, Value: com.atSistemas
[INFO] Parameter: serviceName, Value: Persona
[INFO] Parameter: artifactId, Value: personaAT
[INFO] Project created from Archetype in dir: personaAT
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.658 s
[INFO] Finished at: 2018-02-06T11:39:50+01:00
[INFO] Final Memory: 17M/223M
[INFO] ------------------------------------------------------------------------

La estructura del servicio generado es:

alt tag

Prueba del servicio generado

El último paso que nos queda es probar el servicio generado, para construirlo lanzamos el siguiente comando:
mvn clean install

mvn clean install  
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building personaAT 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ personaAT ---
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ personaAT ---

.........

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.954 s
[INFO] Finished at: 2018-02-06T11:40:26+01:00
[INFO] Final Memory: 39M/327M
[INFO] ------------------------------------------------------------------------

El proyecto generado para este caso ofrece la siguiente documentación:

alt tag

Y para ponerlo en funcionamiento lo hacemos con el comando:
java -jar personaAT-1.0.0-SNAPSHOT.jar

Lo que nos da un servicio en ejecución, y funcionando

2018-02-06 14:53:51.644  INFO 4164 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8066 (http)  
2018-02-06 14:53:51.651  INFO 4164 --- [           main] c.a.persona.gestion.PersonaApplication   : Started PersonaApplication in 7.491 seconds (JVM running for 8.296)  

Si accedemos al cliente swagger generado, en el puerto indicado en el fichero application.properties:

alt tag

Se puede ver el API ofrecido por el servicio y se pueden realizar acciones sobre el mismo:

Creación:
alt tag
Consulta de todos los elementos:
alt tag
Modificación:
alt tag
Consulta de un elemento:
alt tag
Borrado de un elemento:
alt tag

Conclusión

A lo largo de este artículo hemos visto la utilidad de la utilización de arquetipos Maven, y con la realización del ejemplo se ha visto que con un coste semejante a la realización de un servicio, se puede llevar a cabo la realización de un arquetipo, a partir del cual se puede proceder a la construcción de sucesivos servicios plenamente funcionales con un coste significativamente inferior.

Otras ventajas adiciones a la reducción de coste, es la homogeneización del código producido y de la documentación generada, lo cuál redunda en una reducción en los fallos y facilita el posterior mantenimiento.

Si te ha gustado el artículo, ¡síguenos en Twitter para estar al día de nuevos posts!

Autor

Jose Antonio Navarro Fuentes

Líder técnico de arquitectura de soluciones en knowmad mood. Arquitecto de soluciones GCP, Azure y AWS. Mas de 30 años aprendiendo, con la seguridad que me queda todavía un mundo por conocer.