POC con Dropwizard

Desde que publiqué mi primer post en el blog, POC con Spring Boot, tenía pensado escribir uno similar con Dropwizard, y ahora que además lo estoy usando en mi actual proyecto, es el mejor momento para verlo 🙂

Concepto

Dropwizard es una especie de framework o librería para poder desarrollar rápidamente una aplicación web, muy al estilo de lo que nos aporta Spring Boot:
– Servidor «embebido», los famosos fat jars.
– Conjunto de librerías ya probadas y perfectamente compatibles entre sí.
– Simplicidad en la configuración.
Para mi la principal diferencia con Spring Boot, es que Dropwizard no usa ninguna librería de Spring. Su «stack» de tecnologias / librerías principales es la siguiente:
Maven
Jersey (implementación JAX-RS)
Jetty como servidor embebido.
Jackson, para todo el procesamiento y parseo de jsons.
Metrics, librería muy sencilla de usar para métricas, healtchecks, etc.
– Otras librerías secundarias: Guava, Logback, Hibernate Validator…

Vamos a ver un ejemplo de como montar desde cero un proyecto Dropwizard, con un endpoint «/helloworld».

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

Pasos:

1. Crearemos un proyecto maven con el típico quick-start de maven (ver los 4 primeros pasos del post sobre Spring Boot). Yo lo he hecho directamente con mi archetype de maven.

2. Añadimos la dependencia principal de Dropwizard a nuestro pom:

    <dependency>
        <groupId>io.dropwizard</groupId>
        <artifactId>dropwizard-core</artifactId>
        <version>0.9.2</version>
    </dependency>

Con esa dependencia es suficiente. A su vez tiene todas las dependencias necesarias así como las librerías que comenté antes.

3. Para nuestro ejemplo tendremos el siguiente bean:

package com.edwise.pocs.dropwizard.model;

public class Message {

    private final String message;

    public Message(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

4. Que será lo que devolveremos en el único endpoint de nuestro Resource, que será así:

package com.edwise.pocs.dropwizard.resource;

// imports...

@Path("/hello-world")
@Produces(MediaType.APPLICATION_JSON)
public class MessageResource {
    private static final String HELLO_WORLD_SENTENCE = "Hello world %s!";
    private static final String DEFAULT_NAME = "Nobody";

    @GET
    public Message getHelloWorldMessage(@QueryParam("name") String name) {
        return new Message(
                String.format(HELLO_WORLD_SENTENCE, name != null ? name : DEFAULT_NAME)
        );
    }
}

Con las anotaciones básicas de un servicio REST JAX-RS. Lo único que hacemos es devolver un Message, con un mensaje construido a partir del nombre recibido como parámetro.
Con @Produces(MediaType.APPLICATION_JSON) el endpoint del resource devolverá un json. Jackson se encargará de manera trasparente de parsear la clase a json.

5. Creamos una clase de configuración. Dropwizard necesita que creemos una clase extendiendo de Configuration:

package com.edwise.pocs.dropwizard.config;

import io.dropwizard.Configuration;

public class DropwizardConfig extends Configuration {
}

En nuestro caso no añadiremos nada a la clase, dado que el ejemplo quiero que sea lo más simple posible, pero lo normal es añadir aquí cualquier parámetro de configuración necesario para el proyecto. En un próximo post seguramente lo veamos.

6. Y creamos nuestra clase ‘main’. Es muy similar a Spring Boot:

package com.edwise.pocs.dropwizard;

// imports...

public class DropwizardApplication extends Application<DropwizardConfig> {

    public static void main(String[] args) throws Exception {
        new DropwizardApplication().run(args);
    }

    @Override
    public void run(DropwizardConfig dropwizardConfig, Environment environment) {
        environment.jersey().register(new MessageResource());
    }
}

Tenemos que extender la clase Application y parametrizandola con nuestra clase de configuración.
Además, es necesario implementar el método ‘run’, y en este caso lo único que hacemos en él es registrar en el entorno nuestro resource como componente Jersey. Como vemos, si necesitáramos usar algún parámetro de configuración al crear nuestros componentes aquí, podríamos hacerlo ya que el método ‘run’ recibe la clase de configuración como parámetro.

7. Ya solo nos falta añadir dos plugins a nuestro pom.xml de maven, son los siguientes:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.4.3</version>
        <configuration>
            <createDependencyReducedPom>true</createDependencyReducedPom>
            <filters>
                <filter>
                    <artifact>*:*</artifact>
                    <excludes>
                        <exclude>META-INF/*.SF</exclude>
                        <exclude>META-INF/*.DSA</exclude>
                        <exclude>META-INF/*.RSA</exclude>
                    </excludes>
                </filter>
            </filters>
        </configuration>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
                <configuration>
                    <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <mainClass>com.edwise.pocs.dropwizard.DropwizardApplication</mainClass>
                        </transformer>
                    </transformers>
                </configuration>
            </execution>
        </executions>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.0.0</version>
        <configuration>
            <archive>
                <manifest>
                    <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                </manifest>
            </archive>
        </configuration>
    </plugin>

El maven-shade-plugin se encarga de que el jar generado sea un «fat jar», o jar con todas las librerías necesarias para ejecutarse solo. En el plugin le indicamos nuestra clase ‘main’.
El maven-jar-plugin se suele usar para la generación del nombre del jar, según versiones y demás.

8. Vamos a arrancar ya nuestro proyecto dropwizard. Podemos hacerlo directamente desde una consola desde la raiz del proyecto ejecutando lo siguiente:

java -jar target/dropwizard-example-1.0.0-SNAPSHOT.jar server

O en Intellij podemos crear fácilmente un «Run configuration» de tipo Application con nuestra clase DropwizardApplication como MainClass y «server» como Program Arguments:

ScreenClip

Si ahora accedemos a http://localhost:8080/hello-world?name=edwise el servicio nos devolverá algo como esto:

{"message": "Hello world edwise!"}

Y aquí terminamos nuestro pequeño POC con Dropwizard. Quizá en próximos post explique alguna otra característica sobre Dropwizard.
Tenéis como siempre el proyecto completo en mi github, con el nombre dropwizard-example.

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.

Tests de integración para un servicio REST, con Spring

En mis dos últimos post he explicado como configurar los tests de integración con Spring Boot y como configurarlos además con Maven. Teniendo ya toda esa configuración, es hora ya de implementar unos tests de integración completos, para un servicio REST.

Concepto:
Ya expliqué en los anteriores post el concepto de tests de integración: en resumen, son tests en los que probamos varios componentes de un sistema trabajando juntos (a diferencia de los unitarios, en los que solo probamos cada componente por separado).
En este ejemplo voy a realizar unos tests de integración completos de un servicio REST (el mismo usado en los últimos posts, un servicio REST para un recurso sencillo ‘Info’). Para realizar los tests correctamente usaré, por un lado el clásico MockMvc de Spring, y para probar los jsons que devuelve el rest usaré la librería jsonpath.

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

Pasos:

1. Los tests van a tratar simplemente de probar todo el servicio REST Info el cual tenemos implementado en InfoController:

package com.edwise.pocs.integrationtestsrest.controller;

// imports...

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

    @Autowired
    private InfoService infoService;

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public List<Info> getAllInfos() {
        return infoService.findAll();
    }

    @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<Info> createInfo(@RequestBody Info info) {
        Info infoCreated = infoService.save(info);
        return new ResponseEntity<>(infoCreated, HttpStatus.CREATED);
    }

    @ResponseStatus(HttpStatus.NO_CONTENT)
    @RequestMapping(method = RequestMethod.PUT, value = "{id}")
    public void updateInfo(@PathVariable Long id, @RequestBody Info info) {
        infoService.update(info.setId(id));
    }

    @ResponseStatus(HttpStatus.NO_CONTENT)
    @RequestMapping(method = RequestMethod.DELETE, value = "{id}")
    public void deleteInfo(@PathVariable Long id) {
        infoService.delete(id);
    }

}

Como se puede ver, he completado el controller de los posts anteriores con un método PUT (para actualizar) y un método GET all, para tener lo que podría ser un servicio REST completo.

2. Añadimos a las dependencias de maven la librería jsonpath, para testear fácilmente jsons:

     ...
     <dependencies>
          ...
         <dependency>
             <groupId>com.jayway.jsonpath</groupId>
             <artifactId>json-path-assert</artifactId>
             <version>1.2.0</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
     ...

2. Comenzamos nuestros tests de integración con la configuración que hicimos en los posts anteriores, tanto de maven como de Spring y Spring Boot. Para ello usaremos la clase que ya creamos, InfoControllerIT:

package com.edwise.pocs.integrationtestsrest.controller;

// imports...

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

    @Autowired
    protected WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;

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

    // TODO aquí implementaremos los tests
}

3. Creamos nuestro primer test. En el vamos a probar el método GET /api/info/{id}, es decir, el que obtiene un objeto Info por su id. Lo implementaremos así:

    @Test
    public void getInfo_InfoFound_ShouldReturnCorrectInfo() throws Exception {
        mockMvc.perform(get("/api/info/{id}", INFO_ID_1234))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("$.id", is(INFO_ID_1234.intValue())))
                .andExpect(jsonPath("$.infoText", is(INFO_TEXT_1234)))
                .andExpect(jsonPath("$.creationDateTime", is(notNullValue())));
    }

Todos nuestros tests de integración consisten en una llamada con el objeto MockMvc. En ella se ve como realizamos un get, con un parámetro de path variable (id), y sobre esa llamada hacemos varías comprobaciones (métodos ‘andExpect‘):
– Que el http status devuelto es OK (200).
– Que el contenido recibido es de tipo JSON.
– Usando la librería jsonpath comprobamos que el body de la respuesta contiene un json correcto (en este caso compruebo que el id y el texto son los correctos y que la fecha no es nula, por ejemplo).

4. Implementamos el test que pruebe el método que devuelve todos los Info’s, GET /api/info/:

    @Test
    public void getAll_InfosFound_ShouldReturnFoundInfos() throws Exception {
        mockMvc.perform(get("/api/info/"))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("$", hasSize(3)))
                .andExpect(jsonPath("$[0].id", is(INFO_ID_120.intValue())))
                .andExpect(jsonPath("$[0].infoText", is(INFO_TEXT_1234)))
                .andExpect(jsonPath("$[0].creationDateTime", is(notNullValue())))
                .andExpect(jsonPath("$[1].id", is(INFO_ID_121.intValue())))
                .andExpect(jsonPath("$[1].infoText", is(INFO_TEXT_4567)))
                .andExpect(jsonPath("$[1].creationDateTime", is(notNullValue())))
                .andExpect(jsonPath("$[2].id", is(INFO_ID_122.intValue())))
                .andExpect(jsonPath("$[2].infoText", is(INFO_TEXT_7892)))
                .andExpect(jsonPath("$[2].creationDateTime", is(notNullValue())));
    }

El tests es muy similar al anterior, lo único que en este caso nos devuelve un array de Info’s, con lo que comprobamos el tamaño del array (método hasSize), y cada uno de los Info’s por separado, con jsonpath, accediendo a cada Info como si fuera un array.

5. Seguimos con el test que pruebe el método crea un nuevo Info, POST /api/info/, y el que actualice un Info ya existente, PUT /api/info/{id}:

    @Test
    public void postInfo_InfoCorrect_ShouldReturnCreatedStatusAndCorrectInfo() throws Exception {
        mockMvc.perform(post("/api/info/")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"infoText\":\"Info 1234 New\",\"creationDateTime\":\"2013-10-11T20:10:10\"}"))
                .andExpect(status().isCreated())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("$.id", is(INFO_ID_1234.intValue())))
                .andExpect(jsonPath("$.infoText", is(INFO_TEXT_1234_NEW)))
                .andExpect(jsonPath("$.creationDateTime", is(notNullValue())));
    }

    @Test
    public void putInfo_InfoCorrect_ShouldReturnNoContentStatus() throws Exception {
        mockMvc.perform(put("/api/info/{id}", INFO_ID_1234)
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"infoText\":\"Info 1234 Updated\",\"creationDateTime\":\"2014-10-25T19:16:21\"}"))
                .andExpect(status().isNoContent());
    }

En el primero, realizamos un POST y le marcamos como ‘Content-Type’ del mismo el típo ‘application/json’. Luego le pasamos como contenido de la request el json del objeto a crear. Las comprobaciones son las de siempre, comprobando en este caso que el http status es CREATED (201).
En el segundo, realizamos un PUT, con path variable (el id de siempre) y hacemos lo mismo que en el POST, excepto que no comprobamos el json del body (no devuelve nada) y que el http status esperado es NO CONTENT (204).

6. Por último, implementamos el que pruebe el método que borre un Info, DELETE /api/info/{id}:

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

Realizamos un DELETE, con el id path variable, y solo es necesario comprobar el http status, NO CONTENT (204).

7. Y hemos terminado. Nuestra clase completa ‘InfoControllerIT’ quedaría así. Podemos ahora ejecutar los tests tanto desde Intellij como con maven (profile ‘integration-test’, con el goal ‘verify’).

8. Temas importantes a tener en cuenta:
– El servicio REST implementado es muy sencillo y tiene la capa de datos mockeada. Si tuviéramos un servicio completo, con su capa de base de datos, habría que buscar como configurar una base de datos en memoria o similar, para realizar los tests (o incluso mockear con mockito la capa de datos).
– Faltarían varios tests, principalmente tests que fallen: GET, PUT y DELETE sobre un Info que no exista, etc.
– El servicio REST, al realizar un POST, devuelve en el body de la response el json completo. Realmente, lo más correcto sería no devolver nada en el body, y devolver en el ‘location’ de los headers un link al nuevo Info creado (principio HATEOAS). Esto probablemente lo veamos en otro post más adelante.

Si queréis descargaros el proyecto completo, lo tenéis como siempre en mi github (proyecto integrationtests-rest-example).

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.

Como configurar jOOQ en un proyecto maven

En este post voy a explicar como montar un sencillo PoC con la librería jOOQ, para el acceso a base de datos usando queries SQL «implementadas» directamente con clases y métodos Java. La idea es detallar los pasos para poder usarlo, en un proyecto maven. Más adelante es probable que escriba otro post explicando como integrarlo con Spring.

Concepto:
jOOQ (Java Object Oriented Querying) es una librería Java para el acceso a base de datos cuya principal característica es que define un DSL en java, con fluent interface, para la generación de queries, tal cual las generaríamos en SQL.

Podemos verlo con un ejemplo. Esto sería una query en SQL:

SELECT BOOK_CHARACTER.NAME
FROM BOOK_CHARACTER
WHERE BOOK_CHARACTER.ID = 1

Y esto sería con jOOQ, en java:

DSL.using(connection)
    .select(Tables.BOOK_CHARACTER.NAME)
    .from(Tables.BOOK_CHARACTER)
    .where(Tables.BOOK_CHARACTER.ID.equal(1))
    .fetch();

Su principal ventaja es la seguridad que ofrece: mientras que al picarnos una query SQL y lanzarla desde Java con JDBC o similar, hasta que no la ejecutamos, no podemos estar seguro de que no hemos metido la pata en la query. Con jOOQ, cualquier error sintáctico o de tipos es «controlado» por el compilador. Es decir, si la query no es correcta… ¡no compila!

El PoC que voy a montar va a ser una aplicación maven simple en la que usaremos H2 como base de datos y usaré logback para logs. En el voy a mostrar principalmente como se configura jOOQ.

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

Pasos:

1. Crearemos un nuevo proyecto Maven igual que lo creamos en mi primer post de Spring Boot desde Intellij (pasos 1 al 4). Podemos hacerlo con el archetype maven ‘quickstart’. Yo para este PoC he usado un archetype creado por mi que es un quickstart actualizado. Os lo podeís descargar de mi github: https://github.com/edwise/maven-archetypes

2. Añadimos a nuestro pom.xml las dependencias necesarias, tanto de jOOQ como de H2, logback y slfj4 ( estos dos últimos para logs). Serían todas estas:

...
<dependencies>
    <dependency>
        <groupId>org.jooq</groupId>
        <artifactId>jooq</artifactId>
        <version>3.5.1</version>
    </dependency>
    <dependency>
        <groupId>org.jooq</groupId>
        <artifactId>jooq-meta</artifactId>
        <version>3.5.1</version>
    </dependency>
    <dependency>
        <groupId>org.jooq</groupId>
        <artifactId>jooq-codegen</artifactId>
        <version>3.5.1</version>
    </dependency>

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.182</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.10</version>
    </dependency>

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.1.2</version>
    </dependency>

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.1.2</version>
    </dependency>
    ...
</dependencies> 
...

3. Dado que vamos a usar H2 como base de datos, y en memoria, vamos a picarnos un pequeño script para recrear la base de datos cada vez que conectamos a ella. Creamos una subcarpeta ‘resources’ y en ella creamos el siguiente fichero:

create schema if not exists pocjooq;

use schema pocjooq;

create table if not exists book_character (id integer primary key, name varchar(30));

delete from book_character;
insert into book_character values (1, 'Saruman');
insert into book_character values (2, 'Gandalf');
insert into book_character values (3, 'Aragorn');
insert into book_character values (4, 'Samwise');
insert into book_character values (5, 'Frodo');

commit;

4. Ahora vamos a hacer que jOOQ autogenere sus clases. jOOQ realmente lo que hace es leer toda la estructura de nuestra base de datos y generar código y clases basado en nuestras tablas y columnas. Se puede hacer de varias maneras (más info en su documentación), pero para nuestro caso lo vamos a hacer con el plugin maven de jOOQ. Añadimos lo siguiente a nuestro pom.xml:

...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.2</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <testSource>1.8</testSource>
                <testTarget>1.8</testTarget>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.jooq</groupId>
            <artifactId>jooq-codegen-maven</artifactId>
            <version>3.5.1</version>

            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>

            <dependencies>
                <dependency>
                    <groupId>com.h2database</groupId>
                    <artifactId>h2</artifactId>
                    <version>1.4.182</version>
                </dependency>
            </dependencies>

            <configuration>
                <jdbc>
                    <driver>org.h2.Driver</driver>
                    <url>jdbc:h2:mem:pocjooq;INIT=RUNSCRIPT FROM 'src/main/resources/populate_db.sql'</url>
                    <user>sa</user>
                    <password></password>
                </jdbc>

                <generator>
                    <name>org.jooq.util.JavaGenerator</name>
                    <database>
                        <name>org.jooq.util.h2.H2Database</name>
                        <includes>.*</includes>
                        <excludes></excludes>
                        <inputSchema>POCJOOQ</inputSchema>
                    </database>
                    <target>
                        <packageName>org.jooq.util.maven.generated</packageName>
                        <directory>target/generated-sources/jooq</directory>
                    </target>
                </generator>
            </configuration>
        </plugin>
    </plugins>
</build>
...

He añadido primero el maven-compiler-plugin, necesario para poder decirle a maven que compile con la JDK 8.
El plugin de jOOQ para la generación de código, jooq-codegen-maven, consta de lo siguiente:
– El ‘goal’ en el que se generará el código (‘generate’ en este caso).
– Dependencias que necesita: en nuestro caso la dependencia de H2 para la base de datos.
– Configuración de acceso a la base de datos (url, user, pass y driver). En nuestro caso, los por defecto de H2 para una base de datos en memoria. En la url de la conexión a H2, le pasamos como script para ejecutar al arrancar, el fichero .sql creado anteriormente.
– Configuración del generador de código de jOOQ (tag ‘generator’): el generador usado (JavaGenerator), información sobre la base de datos: tipo de base de datos, si queremos excluir algo y el schema principal (‘POCJOOQ’ en nuestro caso), así como directorio destino donde se generará el código, y nombre de paquetería.

5. Ahora podemos, o bien ejecutar ese plugin (desde consola con ‘mvn jooq-codegen:generate’ o desde la tool window de maven en intellij), o bien compilar el proyecto completo (con maven también), y generará todas las clases. Podemos acceder a la ruta ‘target/generated-sources/jooq’ para ver el código generado. La que más usaremos será la clase ‘Tables.java’.

6. jOOQ necesita una conexión jdbc para funcionar. Para ello he creado una clase ‘DBConnector.java’, podéis verla directamente en github aquí. No es necesario que entremos mucho en detalle, lo importante es que realiza una conexión con la misma base de datos usada en el plugin, y nos devuelve un clásico ‘java.sql.Connection’. Los métodos para obtener la conexión y para cerrarla son los siguientes:

// Obtener una conexión a la BD
java.sql.Connection connection = DBConnector.getInstance().connection();

// Cerrar la conexión a la BD
DBConnector.getInstance().closeConnection();

7. Con esto ya podemos desarrollar directamente queries en java con jOOQ. Vamos a realizar unos ejemplos, para ello crearemos una clase de test jUnit. Empezaremos implementando los métodos @Before y @After, para obtener la conexión y para cerrarla:

package com.edwise.pocs.jooq;

// imports...

public class JOOQExampleTest {
    private final static Logger log = LoggerFactory.getLogger(JOOQExampleTest.class);

    private DSLContext jooqDSL;

    @Before
    public void setUp() {
        jooqDSL = DSL.using(DBConnector.getInstance().connection(), SQLDialect.H2);
    }

    @After
    public void tearDown() {
        DBConnector.getInstance().closeConnection();
    }
}

Aparte de obtener un logger, declaramos un atributo de tipo DSLContext y lo inicializamos en el método @Before, pasandole la conexión, y el el tipo de base de datos (H2 en este caso). Con ese objeto DSLContext podemos llamar a todos los métodos de jOOQ.
En el método after cerramos la conexión.

8. Implementamos un pequeño ejemplo de una select que nos devuelva todo el contenido de la única tabla de la base de datos, BOOK_CHARACTER:

    @Test
    public void testSelectAll() {
        Result<Record> result = jooqDSL
                .select()
                .from(Tables.BOOK_CHARACTER)
                .fetch();

        log.info("Resultado query: \n{}", result.toString());
    }

Usamos para ello el DSLContext definido, y sobre el, con fluent interface, montamos la query. Para los nombres de tablas o columnas usamos la clase Tables (si importamos estáticamente toda el contenido de esa clase sería incluso más legible el ejemplo).
El resultado nos lo devuelve en un objeto Result. Al ejecutar ese método veremos que jOOQ tiene todos sus ‘toString()’ bastante currados, el log de este método nos mostrará algo como esto:

18:34:06.953 [main] INFO  com.edwise.pocs.jooq.JOOQExampleTest - Resultado query:
+----+-------+
|  ID|NAME   |
+----+-------+
|   1|Saruman|
|   2|Gandalf|
|   3|Aragorn|
|   4|Samwise|
|   5|Frodo  |
+----+-------+

Más aún, como tenemos logback configurado con su configuración por defecto, está activado el nivel DEBUG. jOOQ muestra mucha información a nivel DEBUG. El resultado por consola completo de ejecuta el test anterior muestra, antes de ese log, logs de jOOQ en el que muestra la query resultante al «traducirla» a SQL, el tiempo que le ha llevado, el resultado obtenido, etc.

9. Como último ejemplo, vamos a implementar un test que haga uso de los Streams de Java 8. jOOQ es totalmente compatible con Java 8:

    @Test
    public void testStreamForEachJava8() {
        jooqDSL.selectFrom(Tables.BOOK_CHARACTER)
                .where(Tables.BOOK_CHARACTER.ID.in(2, 3))
                .orderBy(Tables.BOOK_CHARACTER.NAME)
                .fetch()
                .stream()
                .map(bookCharacter -> bookCharacter.getName().toUpperCase())
                .forEach(s -> log.info("Name: {}", s));
    }

En este caso obtenemos solo los registros con ids 2 y 3, los ordenamos por nombre, y mediante el uso de streams lo ponemos en mayúsculas y los logamos.
El resultado por consola es el siguiente:

18:34:23.038 [main] INFO  com.edwise.pocs.jooq.JOOQExampleTest - Name: ARAGORN
18:34:23.038 [main] INFO  com.edwise.pocs.jooq.JOOQExampleTest - Name: GANDALF

No me enrollo más, que el post era solo para configurarlo 😉 . jOOQ por supuesto proporciona métodos para inserts, updates, deletes… El manual es bastante didáctico y completo. Además, en su web, en la sección ‘Community’ tenéis muchos tutoriales, posts, etc. Echadles un vistazo.

Por último, como siempre, podéis descargar el proyecto completo en mi github, proyecto ‘jooq-example’ 🙂

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 🙂

Implementar la capa de base de datos con Spring Data

En este post voy a montar otro PoC, esta vez es el turno de Spring Data, que junto con Spring Boot y sus autoconfiguraciones nos va a simplificar mucho el código necesario para el acceso a base de datos. El ejemplo lo haremos sobre un ejemplo de servicio REST sobre una aplicación Spring Boot que ya hice en un post anterior.

Concepto:
Spring Data es otro de los proyectos de Spring, cuyo objetivo es simplificar el desarrollo de la capa de base de datos en nuestra aplicación. En concreto, Spring Data JPA realmente es una capa de abstracción más, montada sobre JPA, para evitarnos al máximo posible el código boilerplate necesario en cualquier aplicación que use la especificación JPA para el acceso a base de datos.

La herramienta principal son los repositorios (Repository). Spring nos provee de varias interfaces que simplemente debemos heredar (con una interfaz nuestra), y que contienen los métodos necesarios para el acceso a base de datos. Spring, por su parte, se encarga de implementar esa interfaz que hayamos creado.

En el ejemplo que vamos a seguir en este post, le implementaremos la capa de base de datos al servicio REST que ya implementamos en el post Implementar un servicio Rest con Spring.

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

Pasos:

1. Vamos a usar como base el proyecto de ejemplo usado en el post del servicio REST. Antes de empezar, resumo algunos cambios iniciales que le he hecho al proyecto:
– Campo fecha joda-time de User cambiado a Java 8 Date API (JSR310), así como el bean de jackson para ese parseo.
– Modificado la url del servicio a ‘/api/users’ (antes era ‘/user’)
– Modificados nombre de paquetes (estaban en plural)
– Eliminado método ‘update’ del UserService, ahora se usa ‘save’ tanto para actualizar como crear
– Nuevo método ‘existsUser’ en el UserService.
– Otras pequeñas mejoras en la implementación del controller, respecto al update y delete.

2. Como comenté arriba, usaremos como base de datos H2, una base de datos «embebida», perfecta para pocs, test de integración y demás. Para usarla, con añadir la dependencia al pom, es suficiente. La autoconfiguración de Spring Boot, hará el resto (le pasará los parámetros por defecto para el datasource).

...
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.182</version>
    </dependency>
...

Añadimos también al pom la dependencia para spring-data-jpa, en concreto el ‘starter’ de spring boot para la misma:

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

3. Anotamos nuestra entity con las anotaciones básicas para persistencia, quedaría así:

package com.edwise.pocs.springdata.entity;

// imports...

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;
    private Integer type;
    private String phone;
    private LocalDate birthDate;

    // getter and setters...

    public User copyFrom(User user) {
        if (user.name != null) {
            this.name = user.name;
        }
        if (user.type != null) {
            this.type = user.type;
        }
        if (user.phone != null) {
            this.phone = user.phone;
        }
        if (user.birthDate != null) {
            this.birthDate = user.birthDate;
        }
        return this;
    }

    // equals, hashcode and toString methods...
}

Explicación rápida de las anotaciones usadas:
Entity: marca nuestra entity como eso, una entity de base de datos, para que sea gestionada como tal.
Table: el nombre de la tabla de base de datos. Si no lo ponemos, tiene que ser igual al nombre de la clase.
Id: marcamos el atributo que será la primary key con esta anotación
GeneratedValue: Como se genera la primary key.

4. Creamos un repositorio de Spring Data. Para ello simplemente creamos una interfaz que herede de JpaRepository (como parámetros genéricos, nuestro entity y Long por el tipo de la primary key).

package com.edwise.pocs.springdata.repository;

// imports

public interface UserRepository extends JpaRepository<User, Long> {

}

El código está correcto, es una interfaz vacía. Con esto es suficiente, ya tenemos implementada nuestra capa de base de datos. Al crear esa interfaz, es Spring quien se encargará de implementarla. Simplemente tenemos que usarla, obteniéndola como cualquier bean del contexto de Spring.
JpaRepository hereda, entre otras de CrudRepository, que tiene los métodos básicos para implementar un CRUD (getAll, save, delete…).

5. Enganchamos ahora el repositorio con nuestro UserService. Nuestra implementación era antes un mock (UserServiceMock.java), lo renombramos (como UserServiceImpl) y lo modificamos para que use nuestro repositorio para las distintas operaciones, quedando:

package com.edwise.pocs.springdata.service.impl;

// imports...

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public User findById(Long id) {
        return userRepository.findOne(id);
    }

    @Override
    public List<User> findAll() {
        return userRepository.findAll();
    }

    @Override
    public User save(User user) {
        return userRepository.save(user);
    }

    @Override
    public void delete(Long id) {
        userRepository.delete(id);
    }

    @Override
    public boolean existsUser(Long id) {
        return userRepository.exists(id);
    }
}

Simplemente obtenemos con Autowired el repository, y llamamos en cada método de nuestro service al método que corresponda. Nada raro.

6. ¡Terminado! Como siempre, ejecutamos ‘mvn spring-boot:run’ en una consola o desde la tool window de maven en Intellij, para arrancar nuestra aplicación. Si accedemos con un cliente REST a http://localhost:8080/api/users responderá nuestro servicio. Como la base de datos está vacia, lo mejor es hacer algún POST primero para meter algún user, el formato del body de la petición sería algo así:

{
  "name": "Triss",
  "type": 2,
  "phone": "645338822",
  "birthDate": "1985-06-19"
}

7. Más opciones de Spring Data: lo que hemos implementado es lo básico, y lo más sencillo. Quizá, por ejemplo, necesitaríamos un método que nos devuelva los usuarios de un determinado tipo (campo ‘type’). Muy fácil también, añadiríamos el siguiente método en nuestro repository:

public interface UserRepository extends JpaRepository<User, Long> {

    public List<User> findByType(Integer type);    
}

Este método también se encargará el propio Spring de implementarlo. El truco está en que la nomenclatura del método debe ser siempre ‘findByField’ donde ‘Field’ es el nombre del atributo por el que filtramos (siguiendo camelcase), y debe tener un parámetro que sea del tipo del atributo por el que filtrar.

8. Queries más complejas: lo ideal es implementar todos nuestros métodos extra de la manera anterior, pero quizá necesitemos algo más concreto. Por ejemplo, quizá necesitemos un método que devuelva la lista de usuarios para un nombre determinado, sin tener en cuenta mayúsculas o minúsculas. Inicialmente no podemos hacer eso con la estrategia anterior, así que tendríamos que picarnos la query, por ejemplo, con una NamedQuery de JPA añadiendo la anotación correspondiente en nuestra Entity:

@Entity
@Table(name = "users")
@NamedQuery(name = "User.findByNameIgnoreCase", query = "SELECT u FROM User u WHERE LOWER(u.name) = LOWER(?1)")
public class User {
    // all code...
}

Y el siguiente método en nuestro repository:

public interface UserRepository extends JpaRepository<User, Long> {

    public List<User> findByType(Integer type);

    public List<User> findByNameIgnoreCase(String name);   
}

Para que funcione, es necesario, como se ve en el código, que el ‘name’ de la namedQuery contenga el nombre del método del Repository en el que queremos que se ejecute.

9. Similar a lo anterior, podemos usar la anotación Query de Spring Data, directamente modificando solo el repository. Por ejemplo, un método que devuelva la lista de usuarios dado el prefijo de un teléfono, sería añadir el siguiente método a nuestro UserRepository:

public interface UserRepository extends JpaRepository<User, Long> {

    public List<User> findByType(Integer type);

    public List<User> findByNameIgnoreCase(String name);

    @Query("SELECT u FROM User u WHERE u.phone LIKE CONCAT(:phonePrefix, '%')")
    public List<User> findByPhonePrefix(@Param("phonePrefix") String phonePrefix);
}

Como se puede ver, con la anotación @Param marcamos el nombre del parámetro dentro de la query.

Hay más posibilidades: Criteria Queries, QueryDSL…, pero lo vamos a dejar aquí. Si queréis descargaros el proyecto completo, está en mi github, proyecto spring-data-example. También he añadido, comentado, el código necesario en el pom y en un properties para poder hacer la prueba conectando contra un MySQL. Jugad con él 🙂

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 🙂