Nuevo proyecto personal: elitedangerous-api :)

Retomo el blog después de prácticamente un año sin postear, para contar brévemente un pequeño proyecto personal que he empezado estas últimas semanas.

El proyecto lo podéis encontrar en mi github, como siempre.

La idea principal del proyecto es crear una aplicación Spring Boot con la que poder jugar con los datos que provee la web eddb.io. Esta web es una especie de base de datos con información actualizada casi en tiempo real sobre los sistemas estelares, estaciones, facciones, etc del videojuego Elite: Dangerous.

Esa web, eddb.io, proporciona ya varias funcionalidades de búsqueda, pero yo quería ir un poco más allá y crear unas búsquedas algo más complejas, y es lo que estoy intentando hacer en mi proyecto personal.

Entrando un poco más en detalle respecto a la arquitectura, librerías, etc, os comento aquí en forma de puntos:

  • Es un proyecto Spring Boot, con Maven y Java, usando la última versión de Spring Boot que no está aun en versión final, la 2.0.0. La iré actualizando según actualicen.
  • Es un proyecto en Java 9. Esto ahora mismo me provoca algún que otro problemilla con alguna librería que me gustaría usar y no puedo por que todavía no lo soportan.
  • Por ahora, la idea es descargar los jsons principales de eddb.io nada más arrancar, y dejarlos en memoria para consultar.
  • Para acceder, en principio estoy haciendo un api REST.

Si tenéis alguna sugerencia o duda sobre el proyecto, son bienvenidas. Quizá en próximos posts explique alguna de las funcionalidades / cambios / etc del proyecto.

Spring Boot series: autoconfiguración con jOOQ

Hace ya como un año publiqué un post en el que explicaba como configurar un proyecto con jOOQ, que es una librería de acceso a base de datos. Vuelvo con ello, esta vez con Spring Boot y su sencillez 🙂

Concepto:
Con la nueva versión de Spring Boot, la 1.3, aparte de cosas como las developer tools, se han añadido nuevas ‘autoconfiguraciones’, entre ellas, para jOOQ. En este post veremos como configurar fácilmente jOOQ en un proyecto Spring Boot. ¡Al lío!

Entorno usado:
Java JDK 1.8
Maven 3.3.9
Git 2.6.3
IDE Intellij 15.0.2 Ultimate version

Pasos:

1. Crearemos un nuevo proyecto Maven básico con Spring Boot igual que siempre. Nuestro pom inicial quedarí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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edwise.springbootseries.jooq</groupId>
    <artifactId>springboot-series-jooq</artifactId>
    <version>0.1.0</version>
    <packaging>jar</packaging>

    <name>springboot-series-jooq</name>
    <description>Test project for Spring Boot new featture: jooq autoconfig</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.3.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <testSource>${java.version}</testSource>
                    <testTarget>${java.version}</testTarget>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Para este caso no he añadido el starter web, simplemente el de logging para tener logback y slf4j. Así como el starter de tests, y assertj.
Añadimos también nuestra clase main para Spring Boot.

2. Añadimos ahora a nuestro pom la dependencia del nuevo ‘starter’ para jooq. Añadimos también la de H2 para usarlo como base de datos:

...
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jooq</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
...

Y con esto ya casi está. Spring Boot configurará jOOQ con valores por defecto y tendremos disponible un bean de la clase DSLContext.
Solo nos falta el código generado por jOOQ para las tablas de nuestra BD. Vamos a generarlos.

3. Para hacer más sencilla la ejecución de nuestra aplicación, primero vamos a hacer unos scripts sql para la creación y borrado de nuestra base de datos, y los ejecutaremos con el plugin ‘sql-maven-plugin‘ de maven.

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>sql-maven-plugin</artifactId>
                <version>1.5</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>execute</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>com.h2database</groupId>
                        <artifactId>h2</artifactId>
                        <version>${h2.version}</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <driver>org.h2.Driver</driver>
                    <url>jdbc:h2:${basedir}/target/springbootseriesjooqDB</url>
                    <srcFiles>
                        <srcFile>${basedir}/src/main/resources/delete.sql</srcFile>
                        <srcFile>${basedir}/src/main/resources/schema.sql</srcFile>
                        <srcFile>${basedir}/src/main/resources/data.sql</srcFile>
                    </srcFiles>
                </configuration>
            </plugin>

Podéis consultar los tres ficheros .sql aquí. No tienen mucho misterio.

4. Ahora añadimos a nuestro pom.xml el plugin de jOOQ para la generación de su código:

            <plugin>
                <groupId>org.jooq</groupId>
                <artifactId>jooq-codegen-maven</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>com.h2database</groupId>
                        <artifactId>h2</artifactId>
                        <version>${h2.version}</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <jdbc>
                        <driver>org.h2.Driver</driver>
                        <url>jdbc:h2:${basedir}/target/springbootseriesjooqDB</url>
                    </jdbc>
                    <generator>
                        <name>org.jooq.util.DefaultGenerator</name>
                        <database>
                            <name>org.jooq.util.h2.H2Database</name>
                            <includes>.*</includes>
                            <excludes/>
                            <inputSchema>PUBLIC</inputSchema>
                        </database>
                        <target>
                            <packageName>sample.jooq.domain</packageName>
                            <directory>${basedir}/generated-jooq-code/main/java</directory>
                        </target>
                    </generator>
                </configuration>
            </plugin>

El plugin es similar a como lo hicimos en el post anterior de jOOQ:
– Asignamos la ejecución del plugin al ‘goal’ generate de maven.
– Añadimos la dependencia de H2.
– En la configuración le pasamos la url de base de datos h2 (línea 21), la misma que en el sql-maven-plugin, y en la información para la generación de código, la ruta donde generarlo (línea 33).

Con ejecutar ahora un ‘mvn package’ tendremos generado el código de jOOQ.

5. Vamos ahora a probarlo. Para ello creamos un DAO simplón, anotado como @Repository, tal que así:

package com.edwise.springbootseries.jooq.dao;

// imports...

@Repository
public class BooksDao {
    private static final Logger LOG = LoggerFactory.getLogger(BooksDao.class);

    private final DSLContext create;

    @Autowired
    public BooksDao(DSLContext dslContext) {
        this.create = dslContext;
    }

    public Result<Record> getAllBookCharacters() {
        Result<Record> result = create
                .select()
                .from(Tables.BOOK_CHARACTER)
                .fetch();

        LOG.info("Resultado query getAllBookCharacters: \n{}", result);
        return result;
    }

    public Result<Record> getAllBookCharactersOrderByName() {
        Result<Record> result = create
                .select()
                .from(Tables.BOOK_CHARACTER)
                .orderBy(Tables.BOOK_CHARACTER.NAME)
                .fetch();

        LOG.info("Resultado query getAllBookCharactersOrderByName: \n{}", result);
        return result;
    }

    public Optional<Record> getBookCharacterById(int id) {
        Optional<Record> result = create
                .select()
                .from(Tables.BOOK_CHARACTER)
                .where(Tables.BOOK_CHARACTER.ID.equal(id))
                .orderBy(Tables.BOOK_CHARACTER.NAME)
                .fetchOptional();

        LOG.info("Resultado query getBookCharacterById: \n{}",s result);
        return result;
    }
}

Lo que hacemos es nada más que injectar el bean DSLContext (por contructor). Implementamos también varios métodos para recuperar distinta información de nuestra base de datos, usando la sintaxis de jOOQ.
No necesitamos abrir la conexión ni instanciar nada, como hacíamos en el post de jOOQ. Todo nos lo gestiona Spring Boot.

6. Para automatizar la prueba, he implementado un test de integración (carga todo el contexto de Spring) para probar nuestro DAO y ver que jOOQ funciona:

package com.edwise.springbootseries.jooq.dao;

// imports...

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class BooksDaoIT {
    private static final Logger LOG = LoggerFactory.getLogger(BooksDaoIT.class);

    private static final int EXISTING_CHAR_ID = 4;
    private static final int NOT_EXISTING_CHAR_ID = 13;

    @Autowired
    private BooksDao booksDao;

    @Test
    public void getAllBookCharactersShouldReturnAllTheChars() {
        Result<Record> allBookCharacters = booksDao.getAllBookCharacters();

        allBookCharacters.stream()
                .map(record -> record.getValue(Tables.BOOK_CHARACTER.NAME))
                .forEach(name -> LOG.info("Char: {}", name));

        assertThat(allBookCharacters)
                .extracting(record -> record.getValue(Tables.BOOK_CHARACTER.NAME))
                .contains("Samwise", "Gandalf", "Frodo", "Saruman", "Aragorn");
    }

    @Test
    public void getAllBookCharactersOrderByNameShouldReturnCharsSorted() {
        Result<Record> allBookCharactersByName =
                booksDao.getAllBookCharactersOrderByName();

        assertThat(allBookCharactersByName)
                .extracting(record -> record.getValue(Tables.BOOK_CHARACTER.NAME))
                .isSorted();
    }

    @Test
    public void getBookByIdThatExistsShouldReturnValidRecord() {
        Optional<Record> bookCharacter =
                booksDao.getBookCharacterById(EXISTING_CHAR_ID);

        assertThat(
                bookCharacter
                        .orElseThrow(() -> new AssertionError("Not existing character"))
                        .getValue(Tables.BOOK_CHARACTER.NAME))
                .isEqualTo("Samwise");
    }

    @Test
    public void getBookByIdThatNotExistsShouldReturnEmptyOptionalRecord() {
        Optional<Record> bookCharacter =
                booksDao.getBookCharacterById(NOT_EXISTING_CHAR_ID);

        assertThat(bookCharacter.isPresent()).isFalse();
    }
}

Como vemos, Spring Boot facilita mucho la integración con jOOQ, gestionandolo, como siempre, con autoconfiguraciones por defecto. Si por lo que sea necesitamos configurar jOOQ con algún parametro o configuración especial, podemos hacerlo con propiedades del tipo ‘spring.jooq.propiedad’ en el appication.properties, así como definir nuestro propio @Bean de la clase de configuración de jOOQ (org.jooq.Configuration).

Como siempre, tenéis el código disponible en mi github, proyecto springboot-series-jooq.

Spring Boot series: developer tools

Hoy vuelvo con un pequeño post sobre Spring Boot. Hace poco salió la versión 1.3, con varias novedades, entre las que destacan las «developer tools». Vamos a ver un pequeño ejemplo para ver como funciona esta nueva funcionalidad.

Concepto

En la última versión de Spring Boot que ha liberado la gente de Spring hay varias novedades, pero la que más llama la atención son las developer tools, unas librerías para hacer más cómodo el desarrollo. Principalmente lo que nos ofrece es que, mientras estemos desarrollando en nuestro IDE, cualquier cambio que hagamos en el código se ‘auto-despliegue’ en la aplicación, para evitarnos el tener que estar parando y arrancando cada vez que hagamos un cambio.

El truco por lo visto es que Spring Boot, aunque realmente hace un ‘restart’, lo hace muy rápido gracias a que tiene dos class loaders, uno con los jars de nuestro proyecto, y otro con nuestras clases. Cuando se realiza ese ‘restart’ solo recarga el de nuestras clases, lo cual, aunque sea un reinicio completo, es MUY rápido.

Y lo mejor es que funciona como casi todo en Spring Boot: no necesita ningún tipo de configuración ni nada, con añadir la dependencia necesaria a nuestro maven (o gradle), es suficiente.

Voy a implementar un pequeño ejemplo para probarlo, tanto con Intellij como con STS, con maven.

Entorno usado:
Java JDK 1.8
Maven 3.2.5
Git 2.6.3
IDEs Intellij 15.0.1 Ultimate version / STS 3.7.2

Pasos
1. Creamos un proyecto Spring Boot básico, con el starter web nada más, aparte de las dev tools. Nuestro pom.xml quedarí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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edwise.pocs.springbootdevtoolstest</groupId>
    <artifactId>springboot-devtools-test</artifactId>
    <version>0.1</version>
    <packaging>jar</packaging>

    <name>springboot-devtools-test</name>
    <description>Test project for Spring Boot new featture: devtools</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

No tiene nada nuevo de cualquier ejemplo básico con Spring Boot, aparte de añadir la dependencia ‘spring-boot-devtools‘.

2. Y nuestra clase base de Spring Boot, como siempre:

package com.edwise.pocs.springbootdevtoolstest;

// imports...

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3. Arrancamos en nuestro Intellij, ya sea con una ‘Run Configuration’ de Spring Boot si tenemos la versión Ultimate, o directamente ejecutando el comando maven ‘spring-boot:run’ desde la tool window de maven.

4. Ahora que tenemos arrancado el servidor, vamos a hacer un cambio: añadimos un controller sencillito, un hello world:

package com.edwise.pocs.springbootdevtoolstest.controllers;

// imports...

@RestController
@RequestMapping("/helloworld")
public class HelloWorldController {

    @RequestMapping(method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
    public String getHelloWorld() {
        return "Hello World!";
    }
}

5. Y ahora pulsamos el botón de «Make» en Intellij (CTRL + F9). Esto automáticamente recargará todo el contexto de nuestro proyecto. Si accedemos ahora a ‘http://localhost:8080/helloworld‘, nos responderá correctamente. Todo esto sin reiniciar ni parar y arrancar el servidor. Y sin hacer ningún tipo de configuración especial en nuestro IDE ni nada. Y es un redespliegue muy rápido, a mi me tarda menos de 2 segundos 🙂

Nota sobre STS: si hacemos la misma prueba con STS, es algo más sencillo. En ese caso, lo único que necesitamos para «lanzar» la recarga de nuestro proyecto es guardar el archivo, dado que en eclipse está activado por defecto el «Build automatically». Esto hace que en Intellij sea algo menos «automático», pero es debido a la manera de trabajar de este último, que es algo distinta en cuanto a los guardados de los ficheros.

He subido a mi github (proyecto springboot-devtools-test) el ejemplo, con algún controller más, para que lo probéis directamente en vuestros IDEs.

Spring Boot series: cómo cambiar el banner de arranque

Hoy vuelvo con la serie sobre Spring Boot con un sencillo post, en este caso para explicar una pequeña característica que podemos configurar en nuestro proyecto Spring Boot, el banner que se muestra al arrancar.

Concepto:
Si habéis probado Spring Boot, habréis visto que al arrancar, en la consola, sale algo como esto:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.5.RELEASE)

...

Bueno, pues este banner que se nos muestra al arrancar, al igual que otras características de Spring Boot, se puede configurar y cambiar muy fácilmente. Vamos a ver un ejemplo de como poner lo que nosotros queramos o incluso desactivarlo.

Entorno usado:
Java JDK 1.8
Maven 3.2.5
Git 1.9.5
IDE Intellij 14.1.4 Ultimate version

Pasos:

1. Creamos un proyecto Spring Boot básico, con maven. En el pom añadimos las dependencias y plugin básicos de Spring Boot, y creamos nuestra clase básica con el método main para arrancar Spring Boot. Para esto podéis seguir los pasos de mi post de PoC con Spring Boot o usar la web de start.spring.io que os genera todo fácilmente.

2. Si probamos a arrancar nuestra aplicación (‘mvn spring-boot:run’), veremos el banner por defecto de Spring que comentaba antes.

3. Vamos a cambiarlo. Es tan sencillo como añadir un nuevo fichero a nuestra carpeta ‘resources’, llamado ‘banner.txt‘. El texto que pongamos en este txt es el que saldrá como banner. En este txt podemos hacer referencia a algunas variables como la versión de Spring Boot (más info aquí). Por ejemplo, podríamos tener un banner así:

  __   __ _   __  ____  _  _  ____  ____    ____   __   _  _       __   __ _   __  ____  _  _  ____  ____    ____  _  _   ___
 / _\ (  ( \ /  \(_  _)/ )( \(  __)(  _ \  (    \ / _\ ( \/ )_    / _\ (  ( \ /  \(_  _)/ )( \(  __)(  _ \  (  _ \/ )( \ / __)
/    \/    /(  O ) )(  ) __ ( ) _)  )   /   ) D (/    \ )  /( )  /    \/    /(  O ) )(  ) __ ( ) _)  )   /   ) _ () \/ (( (_ \
\_/\_/\_)__) \__/ (__) \_)(_/(____)(__\_)  (____/\_/\_/(__/ (/   \_/\_/\_)__) \__/ (__) \_)(_/(____)(__\_)  (____/\____/ \___/

-> Spring Boot version: ${spring-boot.formatted-version} <-

@edwise

Si queréis ponerle letras en plan ‘ascii’, hay webs como esta: http://patorjk.com/software/taag/ 😉

4. Si volvemos a arrancar nuestra aplicación, veremos nuestro nuevo banner:

  __   __ _   __  ____  _  _  ____  ____    ____   __   _  _       __   __ _   __  ____  _  _  ____  ____    ____  _  _   ___
 / _\ (  ( \ /  \(_  _)/ )( \(  __)(  _ \  (    \ / _\ ( \/ )_    / _\ (  ( \ /  \(_  _)/ )( \(  __)(  _ \  (  _ \/ )( \ / __)
/    \/    /(  O ) )(  ) __ ( ) _)  )   /   ) D (/    \ )  /( )  /    \/    /(  O ) )(  ) __ ( ) _)  )   /   ) _ () \/ (( (_ \
\_/\_/\_)__) \__/ (__) \_)(_/(____)(__\_)  (____/\_/\_/(__/ (/   \_/\_/\_)__) \__/ (__) \_)(_/(____)(__\_)  (____/\____/ \___/

-> Spring Boot version:  (v1.2.5.RELEASE) <-

@edwise

...

5. ¿Y si no queremos banner? Aparte de dejar el fichero banner.txt vacio, también podemos hacerlo programaticamente con la clase SpringApplication, en nuestra clase ‘main’:

package com.edwise.springbootseries.banner;

// imports...

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.setShowBanner(false);
        app.run(args);
    }
}

Con llamar al método ‘setShowBanner‘ con un ‘false’, desactivaríamos el banner.

Y hasta aquí este mini-post 🙂 Si queréis podéis descargaros el proyecto completo de mi github, proyecto springbootseries-banner.

Spring Boot series: cómo configurar tus tests de integración

Vuelvo con Spring Boot, esta vez con un pequeño post en el que voy a explicar como configurar fácilmente los tests de integración con él. Y así empiezo a tocar por fin un poco el testing, que ya me apetecía escribir un post sobre ello 🙂

Concepto:
Mientras que los tests unitarios son tests en los que probamos cada unidad / clase / pequeña funcionalidad por separado, en los tests de integración se prueba todo (o casi todo) el sistema (o grupo de componentes) interactuando entre ellos.

También, a diferencia de los tests unitarios, que son muy sencillos de configurar (normalmente no necesitan configuración), los tests de integración suelen ser algo liosos y complejos de configurar. Con Spring, y en concreto con Spring Boot, veremos que es bastante sencillo.

Por otro lado, los tests de integración no están pensados para ejecutarse a menudo (a diferencia de los unitarios), y lo normal es ejecutarlo solo en algunos casos en nuestra maquina local y sobre todo en nuestro sistema de integración continua. Por eso lo normal es tener estos tests en otra carpeta distinta a la típica de los tests unitarios y además tener configurada la ejecución de esos tests en otro ‘goal’ (si usamos maven).
Esto último lo dejaré para explicarlo en otro post. En este me voy a centrar sólo en la parte de configuración de Spring. En este post la clase de test de integración la voy a crear en el mismo directorio ‘test’ de siempre.

El proyecto sobre el que voy a mostrar como configurar los tests de integración es un sencillo servicio REST sobre un recurso ‘Info’. Está implementado con las típicas capas Service y Repository (esta última mockeada).
También dejo para otro post el realizar unos tests de integración completos de un servicio REST.

Entorno usado:
Java JDK 1.8
Maven 3.2.1
Git 1.9.5
IDE Intellij 14.0.3 Ultimate version

Pasos:

1. Creamos el proyecto base sobre el que implementaremos nuestros tests:

  • Creamos un proyecto maven en Intellij, como siempre: File -> New Project, elegimos Maven, checkeamos «Create from archetype» y seleccionamos el quickstart básico o el mio si lo tenéis instalado.
  • Añadimos en nuestro pom.xml las dependencias básicas de Spring Boot: el parent, el starter de web, y el plugin para maven (más info en el post sobre como montar un PoC de Spring Boot, pasos 5 y 6.
  • Creamos como siempre nuestra clase base para Spring Boot, Application.java.
  • Implementamos nuestro REST. Con las siguientes clases: la entity Info, interfaz InfoRepository, implementación ‘mock’ de ese ‘Repository’, interfaz InfoService, e implementación de ese ‘Service’.
  • Por último, implementamos un pequeño controller para terminar nuestro REST. Es bastante básico, solo he implementado los métodos GET, POST y DELETE:
    package com.edwise.springbootseries.integrationtests.controller;
    
    // imports
    
    @RestController
    @RequestMapping("/api/info/")
    public class InfoController {
    
        @Autowired
        private InfoService infoService;
    
        @ResponseStatus(HttpStatus.OK)
        @RequestMapping(method = RequestMethod.GET, value = "{id}",
                produces = MediaType.APPLICATION_JSON_VALUE)
        public Info getInfo(@PathVariable Long id) {
            return infoService.findOne(id);
        }
    
        @RequestMapping(method = RequestMethod.POST,
                produces = MediaType.APPLICATION_JSON_VALUE)
        public ResponseEntity createInfo(@RequestBody Info info) {
            Info infoCreated = infoService.save(info);
            return new ResponseEntity<>(infoCreated, HttpStatus.CREATED);
        }
    
        @ResponseStatus(HttpStatus.NO_CONTENT)
        @RequestMapping(method = RequestMethod.DELETE, value = "{id}")
        public void deleteInfo(@PathVariable Long id) {
            infoService.delete(id);
        }
    }
    

    Como puede verse, usa el ‘InfoService’ definido anteriormente.

2. Vamos ya al lío con nuestro test. Primero, añadimos las dependencias necesarias para testing: el ‘starter’ de tests para spring boot (spring-boot-starter-test). Entre otras librerías, este starter nos incluye junit, mockito, hamcrest, spring-test… Nos quedaría un pom.xml tal que así:

...
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.1.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.5.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
...

Como veis, también he añadido también la dependencia de la librería de jackson para parsear la nueva Java date API (jsr310), jackson-datatype-jsr310. La necesitamos ya que nuestro entity tiene un campo fecha de ese tipo. (Revisad mi post anterior sobre Spring Boot y jackson para más información. Importante: para que el formato del campo fecha se parsee correctamente es necesario añadir una propiedad al application.properties, tanto en el directorio ‘main’ como en el ‘test’).

3. Crearemos una clase con sufijo ‘IT’ (es lo que se suele poner a los tests de integración para diferenciarlos de los unitarios) en el subdirectorio ‘test’ y le pondremos varias anotaciones Spring:

package com.edwise.springbootseries.integrationtests;

import org.junit.runner.RunWith;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest({"server.port=0"})
public class InfoControllerIT {
    // tests here
}

Con estas tres anotaciones tendremos un test de integración completamente funcional:
@RunWith(SpringJUnit4ClassRunner.class): runner básico de Spring que necesitamos para cualquier test en el que necesitemos un contexto de Spring.
@SpringApplicationConfiguration: anotación alternativa a la clásica @ContextConfiguration, pero para Spring Boot. Con ella le pasamos información de como configurar el contexto. Lo normal es ponerle la clase básica de Spring Boot (nuestra Application.java). También podríamos añadirle otras clases de configuración especificas para los tests.
@WebIntegrationTest: con esta, le decimos que necesitamos probar una ‘web application’ (nos permitirá crear un mockMvc, entre otras cosas), y además nos levanta un servidor para test completo. Le podemos pasar el puerto en el que arrancar, si le ponemos 0, como es nuestro caso, será aleatorio. Está anotación es similar a poner estas dos: @WebAppConfiguration y @IntegrationTest

4. Para probar una aplicación web o, en este caso, un servicio REST, necesitamos poder lanzar peticiones http. Para simular eso, en Spring, lo normal es usar la clásica clase MockMvc. Spring Boot además provee una nueva clase para testing: TestRestTemplate. Y hay otras librerías, como REST-Assured En este caso, crearemos el test con la MockMvc, en próximos posts entraré más en detalle con el resto.
Primero es necesario construir un MockMvc, para ello añadiremos lo siguiente a nuestra ‘InfoControllerIT’:

...
    @Autowired
    protected WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders
                .webAppContextSetup(this.webApplicationContext)
                .build();
    }
...

Creamos en el método @Before el mockMvc con el builder que nos provee Spring. Es necesario pasarle como parámetro el objeto con la información de contexto, WebApplicationContext, el cual lo podemos obtener gracias a la anotación @WebIntegrationTest (o @WebAppConfiguration).

5. Con eso ya tendríamos configurado completamente nuestro test de integración. Añadimos ahora 3 tests básicos, para cada uno de los métodos del servicio REST (GET, POST y DELETE):

...

    @Test
    public void getInfo_ShouldReturnCorrectInfo() throws Exception {
        String jsonExpected = "{\"id\":1234,\"info\":\"Info 1234\",\"creationDateTime\":\"2001-12-12T13:40:30\"}";

        mockMvc.perform(get("/api/info/{id}", INFO_ID_1234))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(content().string(jsonExpected));
    }

    @Test
    public void postInfo_ShouldReturnCreatedStatusAndCorrectInfo() throws Exception {
        String jsonExpected = "{\"id\":1234,\"info\":\"Info 1234 New\",\"creationDateTime\":\"2015-10-25T19:13:21\"}";

        mockMvc.perform(post("/api/info/")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"info\":\"Info 1234 New\",\"creationDateTime\":\"2015-10-25T19:13:21\"}"))
                .andExpect(status().isCreated())
                .andExpect(content().string(jsonExpected));
        ;
    }

    @Test
    public void deleteInfo_ShouldReturnNoContentStatus() throws Exception {
        mockMvc.perform(delete("/api/info/{id}", INFO_ID_1234))
                .andExpect(status().isNoContent());
    }
...

En próximos posts entraré más en detalle, pero el código es bastante autoexplicativo: como se puede ver, en cada test, llamamos al método ‘perform’ de nuestro mockMvc, pasándole si es get, post…, y sobre ese mismo objeto, hacemos varios ‘asserts’ (andExpected es el método concreto) sobre el status devuelvo por la respuesta, contenido, etc.

Nuestra clase de test quedaría finalmente así.

Así de fácil. Si queréis jugar con el código, podéis bajaroslo como siempre de mi github, proyecto springbootseries-integrationtests.

En próximos posts explicaré como separar los tests de integración de los tests unitarios, con maven, e implementaré algún ejemplo más en detalle de como probar un servicio REST a fondo.

Spring Boot series: autoconfiguración de Jackson

Para comenzar este año 2015, vuelvo con los minipost sobre Spring Boot. Esta vez explicaré, con un ejemplo, como configurar el parseo de objetos a json y viceversa con Jackson, muy fácilmente en Spring Boot, sobre todo desde la versión 1.2 de este.

Concepto:
Jackson es una librería Java muy potente para el procesamiento / parseo de objetos a json y viceversa. Ofrece muchas opciones y posibilidades, anotaciones propias, etc. Suele ser casi la librería estándar para esto en los proyectos en los que he trabajado.

En un típico proyecto Spring, como los que he explicado en otros posts, como el de implementar un servicio REST con Spring, suele venir por defecto Jackson.
También en el post de implementar un servicio REST con JAX-RS usé Jackson para lo propio.

Para el caso de Spring, suele ser suficiente con cargar en el contexto un Bean de Jackson (suele ser un ObjectMapper), configurado tal cual lo necesitemos, y ya automáticamente nuestros REST usarán ese «mapper».

Para este ejemplo voy a explicar como configurar Jackson en Spring Boot 1.2 (es más facil aún en esta nueva versión), para el uso de fechas con la librería Joda-time, así como también para fechas con la nueva API de Java 8 (la famosa especificación JSR-310).

Entorno usado:
Java JDK 1.8
Maven 3.2.1
Git 1.9.5
IDE Intellij 14.0.2 Ultimate version
Lombok

Pasos:

Vamos a implementar una pequeña aplicación Spring Boot, con un entity Info y un controller que será un mini servicio REST: vamos a implementar solo el GET de un elemento y el POST, sin servicio ni repositorios. Esto no lo haríamos así si quisiéramos tener una implementación por capas correcta, claro :P. Pero para nuestro ejemplo, es suficiente.
Esta entity Info tendrá un atributo de tipo fecha y hora (LocalDateTime de joda-time primero, y LocalDateTime de la API Java 8 Date después). Y para simplificar el código, usaremos Lombok
El proyecto completo os lo podéis descargar como siempre de mi github, proyecto springboot-series-jackson. Está montado con la librería joda-time, pero también está comentado para usar la librería JSR-310.

¡Al lio!

Con Joda-time

1. Añadimos a nuestro pom.xml de maven lo necesario para montar una aplicación Spring Boot básica:

    ...
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.0.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        ...
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>               
    ...

Lo de siempre: la dependencia «padre» del proyecto Spring Boot, la dependencia para el starter web y el plugin para maven (para poder arrancarlo). Nada nuevo.

2. Añadimos la dependencia de la librería joda, así como de la sublibrería jackson para joda:

    ...
    <dependencies>
        ...
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-joda</artifactId>
            <version>2.4.4</version>
        </dependency>
        ...
    </dependencies>
    ...

La librería core de jackson ya nos la incluye Spring, solo necesitamos añadirle la libreria «third party» para el tipo joda-time.
En nuestro pom.xml añadimos también lombok, junit y alguna otra librería. El pom.xml completo lo tenéis aquí.

3. Creamos nuestra clase Application para el arranque de Spring Boot:

package com.edwise.springbootseries.jackson;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

La anotación @SpringBootApplication es nueva en la versión 1.2. Simplemente es un ‘atajo’ para las tres anotaciones @ComponentScan, @EnableAutoConfiguration y @Configuration. Nada más.

4. Creamos nuestra clase entity, Info:

package com.edwise.springbootseries.jackson.entity;

import lombok.Data;
import lombok.experimental.Accessors;
import org.joda.time.LocalDateTime;

@Data
@Accessors(chain = true)
public class Info {

    private long id;
    private String info;
    private LocalDateTime creationDateTime;
}

Consta solo de tres atributos, un long, un String y un LocalDateTime. Usamos lombok para que genere el código típico (getters, setter, toString…).

5. Creamos nuestro RestController, solo con un GET y un POST, mockeados (ni servicio, ni repositorio, etc):

package com.edwise.springbootseries.jackson.controller;

import com.edwise.springbootseries.jackson.entity.Info;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.joda.time.LocalDateTime;

@RestController
@RequestMapping("/api/info/")
public class InfoController {

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(method = RequestMethod.GET, value = "{id}",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Info getInfo(@PathVariable long id) {
        return new Info()
                .setId(id)
                .setInfo("Info 1234")
                .setCreationDateTime(new LocalDateTime(2001, 12, 12, 13, 40, 30));
    }

    @RequestMapping(method = RequestMethod.POST,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity createInfo(@RequestBody Info info) {
        return new ResponseEntity<>(info.setId(1234), HttpStatus.CREATED);
    }
}

Nada nuevo tampoco: un método para obtener con GET un Info, por id, y un método para crear un Info completo, por POST.

6. Si probamos a arrancar nuestra aplicación Spring Boot, y accedemos (mediante navegador o cliente REST mejor) a http://localhost:8080/api/info/1, nos devolverá algo como esto:

{
  "id": 1,
  "info": "Info 1234",
  "creationDateTime": [
    2001,
    12,
    12,
    13,
    40,
    30,
    0
  ]
}

Si, un json. Spring Boot nos carga automáticamente en el contexto un bean de jackson para realizar ese parseo. Además, con la nueva versión 1.2, solo por tener en el proyecto la librería jackson de joda (jackson-datatype-joda), también nos carga el JodaModule que usábamos en los posts de montar un servicio REST el solito. No tenemos que hacer nada más.

7. El formato en el que se muestra el atributo fecha por defecto es una especie de array con los valores. Si queremos que sea algo como esto: «2001-12-12T13:40:30.000», en principio tendríamos que añadir al mapper la configuración siguiente, como ya hicimos en alguno de los posts anteriores:

   ...
   objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
   ...

No es necesario hacerlo así. Esto también ha sido simplificado en Spring Boot, y lo único que tenemos que hacer es añadir, en un archivo .properties (o .yml) la configuración que queramos. En este caso, crearíamos un application.properties bajo una carpeta ‘resources’:

spring.jackson.serialization.write_dates_as_timestamps=false

Si probamos a arrancar la aplicación, y accedemos otra vez http://localhost:8080/api/info/1, ahora nos devolverá algo así:

{
  "id": 1,
  "info": "Info 1234",
  "creationDateTime": "2001-12-12T13:40:30.000"
}

Resumiendo: hemos configurado Jackson, con joda-time, y con un formato concreto de fecha, simplemente añadiendo las librerías al proyecto y añadiendo una propiedad a un fichero properties. El resto, se ha encargado Spring Boot 🙂

Con la API Date Java 8 (JSR-310)

Los pasos son casí los mismos que en el anterior, solo voy a detallar los que cambian:

2. Añadimos al pom la dependencia de la librería jackson la nueva api Date en Java 8:

  ...
    <dependencies>
        ...
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.4.4</version>
        </dependency>
        ...
    </dependencies>
  ...

4. Creamos nuestra entity Info, pero ahora con el LocalDateTime de Java 8… no cambia nada más que el import:

...
import java.time.LocalDateTime;
...

5. Creamos nuestro RestController: solo cambia el import del LocalDateTime, y la forma en que lo creamos en el setter:

...
import java.time.LocalDateTime;

@RestController
@RequestMapping("/api/info/")
public class InfoController {

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(method = RequestMethod.GET, value = "{id}",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Info getInfo(@PathVariable long id) {
        return new Info()
                .setId(id)
                .setInfo("Info 1234")
                .setCreationDateTime(LocalDateTime.of(2001, 12, 12, 13, 40, 30));
    }

    ...
}

¡Y listo! el resto de pasos son exactamente iguales: añadiríamos la misma propiedad a nuestro application.properties, y ya tendríamos Jackson funcionando en nuestra aplicación Spring Boot, esta vez usando la nueva API Date de Java 8. Y, de la misma manera, lo único que hemos necesitado para configurarla es añadir la librería jackson de la JSR-310 al proyecto, añadir esa propiedad a nuestro properties.

Como comente antes, el proyecto completo está en mi github, proyecto springboot-series-jackson 🙂

Spring Boot series: actuator, monitoriza tu aplicación

Retomando mi serie de miniposts sobre Spring Boot, hoy voy a explicar una funcionalidad muy interesante que podemos añadir fácilmente a cualquier aplicación Spring Boot, es Spring Boot Actuator, con el que podremos monitorizar e interactuar con nuestra aplicación.

Concepto:
Actuator es un subproyecto de Spring Boot que añade una serie de «endpoints» para monitorizar, auditar e incluso gestionar tu aplicación fácilmente. Como todo en Spring Boot, tiene una configuración por defecto y apenas tenemos que hacer nada para tenerlo funcionando desde el primer día (aparte de añadirlo en las librerías del proyecto, con maven o lo que corresponda).
En este post explicaré como añadir actuator a un proyecto Spring Boot, y comentaré algunos de sus endpoints y otras posibles personalizaciones. Como proyecto base he usado el que ya creé en el post sobre Spring Boot, lo podéis descargar de mi github, aquí.

Entorno usado:
Java JDK 1.8
Maven 3.2.1
Git 1.9.4
IDE Intellij 14 Ultimate version

Pasos:

1. Lo primero, editaremos nuestro pom.xml de maven, para añadir en nuestras dependencias el paquete «starter» de actuator:

...
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
...

2. Y… ya está. Con esto, Spring Boot nos autoconfigura todos los endpoints de actuator con los parámetros por defecto. Para probarlo, arrancamos con ‘mvn spring-boot:run’ o desde la tool window de maven en Intellij, y accedemos, por ejemplo, a http://localhost:8080/health/ desde un navegador o cliente Rest. Nos devolverá un json como este:

    {
       "status": "UP"
    }

Por defecto tenemos varios endpoints disponibles para ver todo tipo de información, siempre devuelta en formato json:

– health: información del estado de la aplicación (muy básico, como vemos en el ejemplo).
– env: información de variables de entorno y propiedades.
– beans: beans registrados en el contexto de Spring.
– trace: trazas de los últimos accesos.

Para más información sobre los endpoints, podéis consultar la documentación oficial de Spring Boot.
En los siguientes pasos vamos a personalizar y cambiar alguna configuración de estos endpoints.

3. Cambiar la url base de todos los endpoints: Probablemente nos gustaría tener todos estos endpoints bajo una url común en plan http://localhost:8080/adminpath/el-endpoint-que-sea. Es muy fácil, simplemente creamos un fichero de propiedades bajo la carpeta ‘src/main/resources/’ (si no lo tenemos ya), y añadimos la siguiente propiedad:

management.contextPath=/actuator-admin

Si arrancamos ahora, y accedemos, por ejemplo, a http://localhost:8080/actuator-admin/env veremos que responde. Todos nuestros endpoints cuelgan ahora de /actuator-admin.
Se pueden modificar de esta manera también el puerto (management.port) y otros parámetros.

4. Endpoint ‘info’: este endpoint por defecto no devuelve nada. Está pensado para devolver información general de nuestra aplicación, como versión, nombre… lo que nosotros queramos. Podemos personalizarlo muy fácilmente, también mediante propiedades. Vamos a añadir las siguientes propiedades a nuestro application.properties:

...
info.application.name=Spring Boot Series: actuator example app
info.application.description=Example Spring Boot app with actuator, for  my blog
info.application.version=0.1.1

Arrancamos la aplicación, y accedemos a http://localhost:8080/actuator-admin/info. Nos devolverá un json tal que así:

    {
       "application":
       {
           "version": "0.1.1",
           "description": "Example Spring Boot app with actuator, for my blog",
           "name": "Spring Boot Series: actuator example app"
       }
    }

 

5. También podemos personalizar el endpoing ‘health’. Como veis, simplemente devuelve un ‘OK’, haciendo ciertas comprobaciones que tiene por defecto. Quizás nos interese comprobar cosas concretas a nuestra aplicación, como espacio libre en disco, testear si responde una base de datos… Para hacer esto, simplemente registramos un bean que herede de HealthIndicator. Por ejemplo:

package com.edwise.springbootseries.actuator.endpoints;

// imports...

@Component
public class BetterHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        FileManager.Status diskStatus = FileManager.checkStatus();

        Health health = null;
        if (diskStatus.equals(FileManager.Status.OK)) {
            health = Health.up().build();
        } else {
            health = Health.down().withDetail("diskStatus", diskStatus).build();
        }

        return health;
    }
}

En el código, lo que hacemos es llamar a un ‘checker’ de un manager ficticio, y según lo que nos devuelva, devolvemos que nuestra aplicación está ‘UP’, o que está ‘DOWN’, añadiendo el estado devuelto por el manager.
Ahora, el endpoint nos podría devolver algo como esto: (con un 503, además, no un 200).

    {
       "status": "DOWN",
       "diskStatus": "LOW"
    }

6. Por último, también podemos crear nuestro propio endpoint y añadirlo a actuator. Para ello registramos un bean que herede de AbstractEndpoint. Por ejemplo:

package com.edwise.springbootseries.actuator.endpoints;

// imports...

@Component
public class BugsEndpoint extends AbstractEndpoint<List<Bug>> {

    @Autowired
    BugsService bugsService;

    public BugsEndpoint() {
        super("bugs");
    }


    @Override
    public List<Bug> invoke() {
        return bugsService.getAllBugs();
    }
}

Heredamos de AbstractEndpoint, poniéndole como parámetro genérico lo que vaya a devolver el endpoint. implementamos el método ‘invoke‘, que es el que lo devolverá. Y en el constructor, llamando al constructor de AbstractEndpoint, le pasamos como parámetro la url que queremos para nuestro endpoint. Para el ejemplo, sería http://localhost:8080/actuator-admin/bugs

Y hasta aquí esta pequeña revisión de Spring Boot actuator. Os recomiendo que reviséis la documentación oficial, para más opciones y posibilidades.

Si queréis bajaros el proyecto de prueba completo, lo podéis bajar directamente de mi github, como siempre 🙂

Spring Boot series: generar war para Tomcat

Comienzo hoy una serie de miniposts sobre Spring Boot, en los que trataré funcionalidades, consejos y demás sobre este proyecto de los chicos de Spring que me gusta tanto.
Y, para empezar con esta serie, empezaré con una funcionalidad sencilla de Spring Boot: generar un war que funcione perfectamente en cualquier Tomcat u otro contenedor web similar. ¡Al lio!

Concepto:
Como ya comenté en mi primer post sobre Spring Boot, con Spring Boot, por defecto, lo que hacemos realmente es generar un jar, ejecutable, con el que podemos arrancar nuestra aplicación fácilmente (o directamente con el plugin de maven spring-boot-maven-plugin, que viene a hacer lo mismo).
Pero… ¿Y si queremos crear un war para llevárnoslo a nuestro Tomcat de toda la vida? Para ello, tendremos que añadir un par de cosas a nuestro proyecto Spring Boot.
Como proyecto base he usado el que ya creé en el post sobre Spring Boot, lo podéis descargar de github, aquí.

Entorno usado:
Java JDK 1.8
Maven 3.2.1
Git 1.9.4
IDE Intellij 13.1.5 Ultimate version
Apache Tomcat 8.0.14 (64bits)

Pasos:

1. Lo primero, editaremos nuestro pom.xml de maven, para poner como tipo de «empaquetado», war (por defecto está en jar).

    ...
    <packaging>war</packaging>
    ...

2. Modificaremos nuestra clase Application, desde donde se arranca toda la «magia» de Spring Boot, y haremos que herede de SpringBootServletInitializer. Sobreescribiremos el método ‘configure‘ de esa clase, quedando nuestra Application.java tal que así:

package com.edwise.springbootseries.war;

// imports...

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
}

Esta clase SpringBootServletInitializer es cargada al inicio, y da información al contenedor sobre nuestra aplicación (viene a ser una especie de web.xml). Con Spring Boot solo es necesario usarla si queremos precisamente lo que buscamos en este post: generar un war para desplegarlo en un contenedor aparte.

No es necesario que sea nuestra clase «base» de Spring Boot (Application.java), la que herede de está clase. Podríamos crear una clase nueva solo para heredar de SpringBootServletInitializer, si preferimos tenerlo organizado de otra manera. Lo que si es necesario, por supuesto, es que el parámetro que pasemos, dentro del método ‘configure’, al método ‘application.sources’ siga siendo Application.class.

3. Ejecutamos el comando maven ‘mvn clean package’ desde una consola o desde la tool window de maven en intellij, lo que nos generará nuestro war.

4. Si no tenemos tomcat instalado, lo descargamos directamente de su web. Descomprimimos el zip descargado donde queramos, y, para arrancar, ejecutamos el script ‘bin\startup.bat‘. Copiamos nuestro war generado (estará directamente en la carpeta ‘target‘ de nuestro proyecto) en la subcarpeta ‘webapps’ de tomcat. Se desplegará automáticamente.

5. Accediendo directamente (con el navegador o un cliente REST) a la url http://localhost:8080/springbootseries-war-1.0/ , veremos que nuestro controller responde correctamente.

De esta manera tendríamos ya un proyecto Spring Boot que fácilmente podemos generar como war y llevarnos a nuestro contenedor web de turno. Podéis bajaros el proyecto completo en mi github.

Cualquier duda o sugerencia, espero vuestros comentarios 🙂