Dropwizard series: clase Configuration

Comienzo otra serie de post, esta vez sobre Dropwizard, para el que ya hice un poc en un post previo. En este caso explicaré como cargar fácilmente un archivo yaml como configuración en Dropwizard, directamente en su clase Configuration, y como usarlo.

Concepto

Como vimos en el anterior post sobre Dropwizard, montar un proyecto con él consta de unos pocos pasos, entre ellos tener una clase Application, que es la clase principal que arranca nuestro servidor. Esta clase debe estar parametrizada con la clase de configuración Configuration. En el ejemplo anterior la dejamos vacía, pero en este caso vamos a ver como añadir fácilmente parámetros de configuración a nuestra aplicación.

Vamos a añadirle un par de parámetros de configuración a nuestro poc, los cuales usaremos desde nuestro resource hello-world.

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

Pasos:

1. Usaremos como base el mismo proyecto del anterior post de dropwizard, podéis descargarlo de aquí.

2. Añadimos en nuestra clase de configuración dos atributos nuevos:

package com.edwise.pocs.dropwizardseries.configurationclass.config;

// imports...

public class DropwizardConfig extends Configuration {

  @NotEmpty
  private String host;

  private String defaultName;

  public String getHost() {
  return host;
  }

  public void setHost(String host) {
  this.host = host;
  }

  public String getDefaultName() {
  return defaultName;
  }

  public void setDefaultName(String defaultName) {
  this.defaultName = defaultName;
  }
}

Tendremos 2 parámetros, uno con el host y otro con un nombre por defecto para el mensaje de «hello world».
Como vemos. para añadir parámetros a nuestra configuración basta con añadir atributos en nuestra clase Configuration, y crearle sus getters y setters. Jackson se encarga del parseo, de manera trasparente.
En el caso del host, por ejemplo, hemos puesto que sea un parámetro obligatorio (con el @NotEmpty de hibernate validations). Si nuestra configuración no tiene valor para ese parámetro, dará error el arranque.

3. Añadimos nuestro yaml con los dos parámetros de configuración, bajo el directorio ‘src/main/resources’ (puede ser en cualquier otro directorio, no es obligatorio):

host: localhost
defaultName: Nobody

Con esto ya automáticamente dropwizard cargará esos datos en nuestra clase Configuration.

4. Ahora vamos a usar los parámetros desde la aplicación: en nuestro MessageResource le añadimos un constructor que reciba esos dos datos, y cambiamos el formato del mensaje que devolvemos:

package com.edwise.pocs.dropwizardseries.configurationclass.resource;

import com.edwise.pocs.dropwizardseries.configurationclass.model.Message;

// imports...

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

  private final String host;
  private final String defaultName;

  public MessageResource(String host, String defaultName) {
  this.host = host;
  this.defaultName = defaultName;
  }

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

En el mensaje mostraremos tanto el nombre recibido (o el nombre por defecto si no) como el host.

5. En nuestra clase Application de dropwizard, modificamos el punto donde registramos el resource, pasándole los datos desde la clase Configuration:

package com.edwise.pocs.dropwizardseries.configurationclass;

// 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(dropwizardConfig.getHost(),
  dropwizardConfig.getDefaultName()));
  }
}

El objeto ‘dropwizardConfig’ recibido como parámetro en el método ‘run’ contiene ya los datos recogidos del yaml. Se los pasamos como parámetro al resource, y listo.

6. Solo falta arrancar y probar. Pero para que dropwizard procese el yaml, tenemos que pasárselo como parámetro al arrancar, de la siguiente manera:

java -jar target/dropwizard-series-configuration-class-1.0.0-SNAPSHOT.jar server src/main/resources/config.yaml

Si tenemos configurado un ‘Run configuration’ en Intellij para arrancarlo, también debemos añadir la ruta del yaml como segundo parámetro, de la misma manera.

7. Si ahora lanzamos una petición GET http://localhost:8080/hello-world contra nuestro servidor nos devolverá algo como esto:

{
  "message": "Hello world Nobody, from localhost!"
}

Todo el código de este post lo tenéis en mi github, proyecto dropwizard-series-configuration-class.

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: 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.

Implementar un cliente REST con JAX-RS (Jersey)

Hace tiempo escribí un post explicando como implementar un cliente REST con el cliente de Spring (RestTemplate), así que ahora haremos lo mismo pero con un cliente JAX-RS (Jersey).

Concepto:

La implementación Jersey de JAX-RS nos provee también de un cliente REST para poder acceder a servicios REST facilmente, de una manera muy similar a la del RestTemplate de Spring.

El cliente de Jersey es también configurable, y le podemos «registrar» módulos, por ejemplo para logging, para parsear los jsons (jackson), etc.

En el ejemplo, al igual que en el post de RestTemplate, veremos como llamar al servicio con los métodos básicos (GET, POST, PUT, DELETE), directamente desde una aplicación java desde su método ‘main’. Lo haremos, tanto atacando contra un servicio definido en apiary, como contra un servicio arrancado en local. Vamos a ello.

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

Ejemplo con apiary:

1. Usaré otra vez el servicio definido en mi cuenta de apiary, con un recurso ‘book’.
La url del servicio: http://docs.booksapi.apiary.io/

2. Creamos un proyecto maven básico, y añadimos las siguientes dependencias:

    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-client</artifactId>
            <version>2.21</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.jaxrs</groupId>
            <artifactId>jackson-jaxrs-json-provider</artifactId>
            <version>2.6.1</version>
        </dependency>
    </dependencies>

La librería jersey-client es la única que necesitamos de Jersey. También necesitamos un ‘provider’ de Jackson, para el parseo de los jsons. Lo veremos al instanciar el cliente.

3. Creamos una típica clase con método main, con las rutas a nuestro servicio:

package com.edwise.pocs.jerseyrestclient;

public class AppBookRestApiary {

    private static final String URL_BASE_API =
            "http://private-114e-booksapi.apiary-mock.com/";
    private static final String BOOKS_RESOURCE = "books/";

    public static void main(String[] args) {
      
    }
}

4. Creamos una entity, exactamente igual que la usada en el post con RestTemplate:

package com.edwise.pocs.jerseyrestclient.model;

public class Book {
    private Long id;
    private String title;

    // getter and setters...

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}

Entity muy simple, que cumple la ‘interfaz’ del servicio.

5. Instanciamos nuestro cliente con Jersey:

    public static void main(String[] args) {
        Client client = ClientBuilder.newClient(
                new ClientConfig().register(JacksonJsonProvider.class));
        WebTarget webTarget = client.target(URL_BASE_API).path(BOOKS_RESOURCE);
      
    }

Para instanciar un cliente, usamos un método estático de la clase ClientBuilder. Como parámetro hay que pasarle un ClientConfig, que en este caso le registramos el provider de Jackson, para el parseo de los jsons.
Por último, le pasamos la url base de nuestro servicio, así como el path concreto. Esto nos devuelve un objeto WebTarget que será el que usaremos para ‘hablar’ con el servicio.

6. Implementamos ahora la llamada a cada uno de los métodos que acepta el servicio:

– GET all: para obtener todos los ‘books’:

        Response response = webTarget
                .request(MediaType.APPLICATION_JSON_TYPE)
                .get();

        System.out.println();
        System.out.println("GET All StatusCode = " + response.getStatus());
        System.out.println("GET All Headers = " + response.getHeaders());
        System.out.println("GET Body (object list): ");
        Arrays.asList(response.readEntity(Book[].class))
                .forEach(book -> System.out.println("--> " + book));

Con el método request de la interfaz WebTarget haremos todas las peticiones. Le pasamos como parámetro el tipo a recibir, y llamamos al metodo get.
Esto nos devuelve un objeto Response en el que podemos acceder como siempre al código html devuelto, los ‘headers’, etc.
Para el parseo del json a objeto, usamos el método readEntity, pasandole el tipo como parámetro (un array en este caso).

– GET: para obtener un ‘book’ en concreto, por su id:

        Response response = webTarget.path("12")
                .request(MediaType.APPLICATION_JSON_TYPE)
                .get();

        System.out.println();
        System.out.println("GET StatusCode = " + response.getStatus());
        System.out.println("GET Headers = " + response.getHeaders());
        System.out.println("GET Body (object): "
                + response.readEntity(Book.class));

Es similar al anterior, pero le añadimos al path el id (quedaría como /books/12).
Esta vez en el readEntity el tipo a recibir es un único objeto, no un array.

– POST: para insertar un ‘book’ nuevo:

        Book bookToInsert = createBook(null, "New book title");
        Response response = webTarget
                .request()
                .post(Entity.entity(bookToInsert, MediaType.APPLICATION_JSON_TYPE));

        System.out.println();
        System.out.println("POST executed");
        System.out.println("POST StatusCode = " + response.getStatus());
        System.out.println("POST Header location = "
                + response.getHeaders().get("location"));

En este caso el método request no necesita tipo a recibir, dado que no devuelve ‘body’ el servicio para el POST. Con el método post le pasamos el objeto a insertar, gracias a la clase Entity y su método estático entity en el que además le pasamos el MediaType al que parsear el objeto.
Para obtener un header en concreto, usamos su método get, en este caso obtenemos el ‘location’.

– PUT: para actualizar un ‘book’ en concreto, por su id:

        Book bookToUpdate = createBook(123L, "Book title updated");
        Response response = webTarget.path("123")
                .request()
                .put(Entity.entity(bookToUpdate, MediaType.APPLICATION_JSON_TYPE));

        System.out.println();
        System.out.println("PUT StatusCode = " + response.getStatus());
        System.out.println("PUT Headers = " + response.getHeaders());

Muy similar al POST, excepto que le añadimos al ‘path’ el id del recurso a actualizar. Usamos el método put.

– DELETE: para borrar un ‘book’ en concreto, por su id:

        Response response = webTarget.path("12")
                .request()
                .delete();

        System.out.println();
        System.out.println("DELETE StatusCode = " + response.getStatus());
        System.out.println("DELETE Headers = " + response.getHeaders());

No necesita mucha explicación: añadimos al path el id del recurso a borrar, y llamamos al método delete.

Ejemplo contra un servicio en local:

1. Si no lo tenemos todavía, descargamos mi proyecto integrationtests-rest-example de mi github, y lo arrancamos con ‘mvn spring-boot:run’.

2. Creamos una clase para la entity que nos devuelve el servicio:

package com.edwise.pocs.jerseyrestclient.model;

import java.time.LocalDateTime;

public class Info {

    private Long id;
    private String infoText;
    private LocalDateTime creationDateTime;

    // getters and setters...

    @Override
    public String toString() {
        return "Info{" +
                "id=" + id +
                ", infoText='" + infoText + '\'' +
                ", creationDateTime=" + creationDateTime +
                '}';
    }
}

3. Como en la entity tenemos un campo de tipo LocalDateTime (de la nueva API Date de Java 8, especificación JSR310), necesitamos una de los submódulos de jackson para parseo. Añadimos la siguiente dependencia en nuestro pom.xml:

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

4. Creamos otra clase con método main en la que implementaremos las llamadas a los métodos del servicio (igual que antes). En el método, instanciamos el cliente REST de la siguiente manera:

package com.edwise.pocs.jerseyrestclient;

// imports...

public class AppInfoRestLocal {

    private static final String URL_BASE_API = "http://localhost:8080/api/";
    private static final String INFO_RESOURCE = "info/";

    public static void main(String[] args) {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        Client client = ClientBuilder.newClient(
                new ClientConfig().register(new JacksonJsonProvider(mapper)));
        WebTarget webTarget = client.target(URL_BASE_API).path(INFO_RESOURCE);

    }

Para crear el cliente en este caso, necesitamos ‘personalizar’ el provider de Jackson para que parsee correctamente los campos de tipo ‘Java 8 API Date’. Para ello instanciamos un mapper de Jackson, le registramos el módulo de para la jsr310 (JavaTimeModule ahora, JSR310Module ha sido deprecado hace poco), y usamos ese mapper para crear nuestro provider de Jackson para jsons.

5. El resto es casi identico al caso anterior, podéis ver el código en mi github: AppInfoRestLocal

Todo el código de los dos ejemplos está subido en mi github, proyecto jersey-rest-client-example. Se puede ejecutar cada ejemplo ejecutando su clase ‘main’, la cual ejecuta todos los métodos.

Como mapear beans con Orika

En varios proyectos en los que he trabajado últimamente hemos necesitado de alguna librería para simplificar y automatizar el mapeo de beans. En concreto he usado Dozer varias veces, y últimamente Orika, que es del que voy a implementar y explicar un pequeño ejemplo básico.

Concepto:
Durante el desarrollo de cualquier proyecto con cierto tamaño, nos encontramos con la necesidad de hacer algún tipo de ‘transformación’ o ‘mapping’ de unos beans a otros, que realmente son, en cuanto a datos, lo mismo. Por ejemplo, entities que para enviarlas a un servicio queremos pasarselas con un DTO o similar.

En estos casos, es necesario convertir continuamente esas entities en DTOs. Para hacer esto tendríamos que hacer código muy simple, pero muy repetitivo, seteando valores del uno en el otro. Esto, a la larga, a parte de ser tedioso, llena nuestro código de lineas ‘boilerplate’, que no aportan nada a cualquiera que quiera leerlo.
Para evitar esto, tenemos librerías para el mapeo de beans, como son Dozer u Orika. En el caso del primero, la configuración puede hacerse toda por xml, o por anotaciones en los beans. Por su parte, Orika, te provee de una manera de configurar el mapeo de beans mediante código, aparte de, según parece, tener un rendimiento mucho mejor que Dozer.

En el siguiente ejemplo voy a implementar un mapeo de beans utilizando Orika y explicando como configurar el mapeo, todo por código, sin usar ningún xml ni anotación. ¡Al lio!

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

Pasos:

1. Creamos un proyecto maven básico, usando el ‘quickstart’ de maven, o con el maven-archetype-edwise-quickstart. En el pom, añadimos la dependencia para la librería de Orika:

        <dependency>
            <groupId>ma.glasnost.orika</groupId>
            <artifactId>orika-core</artifactId>
            <version>1.4.6</version>
        </dependency>

2. Tendremos dos beans, que son los que usaremos en el ejemplo, mapeando los datos de uno a otro:

public class SourceEntity {
    private Long id;
    private String name;
    private String userSurname;
    private String[] options;
    private EntityType entityType;
    private LocalDateTime creationDateTime;
    private Integer[] nums;

    // getters and setters...

    // toString method implementation...

    public class EntityType {

        private int id;
        private String type;

        // getters and setters...
    }
}

public class DestinationDTO {
    private Long id;
    private String name;
    private String surname;
    private int type;
    private List<String> options;
    private LocalDate creationDate;
    private Integer firstNum;

    // getters and setters...

    // toString method implementation...
}

Como vemos, los beans tienes datos de varios tipos, y entre si no son exactamente iguales, ni en estructura, ni en tipo de datos… ahora veremos como podemos hacer el mapeo.

3. En Orika, para configurar el mapeo, usaremos un ‘builder’, ClassMapBuilder. Vamos a empezar con un mapeo básico, para explicarlo:

    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

    mapperFactory.classMap(SourceEntity.class, DestinationDTO.class)
            .field("userSurname", "surname")
            .byDefault()
            .register();

Lo primero es crear un MapperFactory, que será nuestra «configuración» de Orika.
Con el método ‘classMap‘ (al que le pasamos la clase origen y la clase destino), usaremos ese ClassMapBuilder para informar a Orika como debe mapear nuestros beans. En este caso le decimos solo que mapee el campo ‘userSurname’ en el campo destino ‘surname’.
Con el método ‘byDefault‘ le decimos que si hay otros campos que tengan nombres iguales, que los mapee directamente (esto afectaría, por ejemplo, al campo ‘name’).
Con ‘register‘ el mapeo queda registrado en nuestra aplicación.

4. Eso sería la parte fácil. Vamos a extenderlo un poco más, mapeando el resto de datos, usando varias opciones distintas de Orika:

    mapperFactory.classMap(SourceEntity.class, DestinationDTO.class)
            .exclude("id")
            .field("userSurname", "surname")
            .field("entityType.id", "type")
            .field("creationDateTime", "creationDate")
            .field("nums[0]", "firstNum")
            .byDefault()
            .register();

Aquí he añadido varios mapeos más:
Con el método ‘exclude‘ le decimos que no mapee el campo id.
En la linea 4, le decimos como mapear el ‘type’, cogiéndolo del ‘id’ del objeto EntityType.
En la linea 6, le decimos que obtenga el primer elemento del array ‘nums’ para mapearlo en ‘firstNum’.

5. Aún nos faltaría algo más, lo anterior daría un error. Tenemos un campo ‘creationDateTime’ que hay que mapear a ‘creationDate’, los cuales son respectivamente de tipos LocalDateTime y LocalDate. No son los mismos tipos, así que aquí necesitaremos implementar un converter de Orika:

package com.edwise.pocs.orikapoc.config;

// imports...

public class LocalDateTimeToLocalDateConverter
        extends CustomConverter<LocalDateTime, LocalDate> {
    public LocalDate convert(LocalDateTime source, 
                             Type<? extends LocalDate> destinationType) {
        return source.toLocalDate();
    }
}

El código es muy sencillo. Simplemente tenemos que heredar de CustomConverter, «tipandolo» con nuestras clases origen y destino, e implementando el método ‘convert‘, que devolverá el dato destino.

Y luego registramos el converter. Nuestra configuración completa de Orika quedaría entonces así:

    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

    ConverterFactory converterFactory = mapperFactory.getConverterFactory();
    converterFactory.registerConverter(new LocalDateTimeToLocalDateConverter());

    mapperFactory.classMap(SourceEntity.class, DestinationDTO.class)
            .exclude("id")
            .field("userSurname", "surname")
            .field("entityType.id", "type")
            .field("creationDateTime", "creationDate")
            .field("nums[0]", "firstNum")
            .byDefault()
            .register();

En este caso se registra globalmente. También podríamos registrarlo solo para ese campo de ese objeto en concreto.

6. Por último, vamos a realizar el mapeo: obtendremos un DestinationDTO a partir de un SourceEntity:

    MapperFacade mapper = mapperFactory.getMapperFacade();

    SourceEntity sourceEntity = new SourceEntity();
    sourceEntity
            .setId(12345L)
            .setName("entityName")
            .setUserSurname("surnameEntity")
            .setEntityType(new EntityType().setId(9999).setType("type9"))
            .setOptions(new String[]{"option1", "option2", "option3"})
            .setCreationDateTime(LocalDateTime.of(2015, 6, 16, 9, 10, 30))
            .setNums(new Integer[]{14, 25, 67});

    DestinationDTO destDTO = mapper.map(sourceEntity, DestinationDTO.class);

    System.out.println("Source: " + sourceEntity);
    System.out.println("Destination: " + destDTO);

Creamos un objeto SourceEntity, con todos sus campos. Y obteniendo un MapperFacadede Orika, llamamos al método ‘map‘, pasandole este objeto y la clase a la que queremos mapearlo.
Si ejecutamos el código, veremos que se ha mapeado correctamente tal como nosotros queríamos.
Hay mapeos que Orika hace directamente, aúnque los tipos sean distintos: por ejemplo, en el SourcEntity tenemos un array de String que Orika nos mapea directamente a un List de String en el DestinationDTO (por tener el mismo nombre no necesitamos ponerlo en la configuración)

En este ejemplo hemos hecho mapeo con varios tipos de datos y opciones, pero hay algunas más. Os recomiendo leer la guía de Orika en su página web, que está muy bien explicado y organizado.

Como siempre, podéis descargaros el ejemplo de mi github, proyecto orika-example. En el tenéis implementados unos tests (usando Junit y Hamcrest) para validar cada una de los mapeos, aparte del ejemplo completo en un método ‘main’.

Tests de integración usando REST-assured

Vamos a volver un poco más con el testing 🙂 En mi último post sobre este tema, tests de integración contra un servicio REST, con Spring, expliqué como implementar esos tests de integración con las herramientas que nos provee Spring (MockMvc principalmente). Como ya comenté en alguno de mis posts de testing, hay otras librerías para realizar este tipo de test, como es REST-Assured. Y es la que vamos a usar en este para implementar unos tests completos contra un servicio REST, de manera muy sencilla y legible.

Concepto:
En varios posts anteriores expliqué el concepto de tests de integración y configuramos tanto maven como Spring para poder implementarlos, y en otro implementé los tests usando MockMvc de Spring y la librería jsonpath.
En este caso, voy a implementar unos tests de integración contra un servicio REST (el mismo usado en los últimos posts, un REST para un recurso ‘Info’), pero esta vez lo haré usando la librería REST-Assured, en lugar del clásico MockMvc. Está librería nos ofrece una manera muy limpia y eficiente de realizar este tipo de tests, en «formato» BDD: Given – When – Then
Como base usaré el mismo proyecto que ya monté en esos posts, con toda la configuración de maven y Spring para tener tests de integración.

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

Pasos:

1. Recordamos como es el servicio REST a testear, este es el controller:

package com.edwise.pocs.itrestassured.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<>(createHeadersWithLocation(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);
    }

    private HttpHeaders createHeadersWithLocation(Info info) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setLocation(
                ServletUriComponentsBuilder
                        .fromCurrentRequest()
                        .path("/{id}")
                        .buildAndExpand(info.getId())
                        .toUri());
        return httpHeaders;
    }
}

Es el mismo controller del post anterior de testing, excepto que he implementado el método del post como debe ser.

2. Añadimos la dependencia de la librería REST-assured en nuestro pom.xml:

        <dependency>
            <groupId>com.jayway.restassured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>2.4.1</version>
            <scope>test</scope>
        </dependency>

Con ‘scope’ test, por supuesto, solo lo necesitamos en nuestros tests.

3. Tendremos la misma clase de tests de integración que en el post anterior:

package com.edwise.pocs.itrestassured.controller;

// imports...

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

Con la misma configuración de Spring, ya explicada en su momento.

4. Primero necesitamos configurar un pequeño detalle en REST-assured: el puerto al que atacar. En nuestro test de integración, con la anotación ‘@WebIntegrationTest({«server.port=0»})’ lo que le decimos a Spring, entre otras cosas, es que arranque un servidor con todo el contexto necesario, en el puerto que le decimos ahí. Si el puerto es 0, usará un puerto aleatorio… así que haremos lo siguiente para configurarlo en REST-assured:

package com.edwise.pocs.itrestassured.controller;

// imports...

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

    @Value("${local.server.port}")
    private int serverPort;

    @Before
    public void setUp() {
        RestAssured.port = serverPort;
    }
   
}

Con la anotación de Spring @Value obtenemos el puerto sobre el que se ha arrancado, y en el método @Before de Junit se lo seteamos a Rest-Assured.

5. Implementamos nuestro primer test con REST-assured, el método GET /api/info/{id}, que obtiene un objeto Info por su id. Lo implementaremos así:

    @Test
    public void getInfo_InfoFound_ShouldReturnCorrectInfo() {
        given()
            .accept(ContentType.JSON)
            .pathParam("id", INFO_ID_1234)
        .when()
            .get("/api/info/{id}")
        .then()
            .statusCode(HttpStatus.SC_OK)
            .body(INFO_TEXT_FIELD, equalTo(INFO_TEXT_1234))
            .body(CREATION_DATE_TIME_FIELD, notNullValue());
    }

Todo el test es una linea de código (aunque lo hemos separado por partes, para ser más legible). En la parte de ‘given‘ prepararíamos ciertas cosas del test, en la de ‘when‘ realizamos el test, y en ‘then‘ hacemos las comprobaciones o ‘assertions’. El método ‘given’ lo importamos estaticamente.
En este caso, marcamos como que esperamos un json, seteamos en el parámetro id de la url un id, y con eso hacemos la petición GET. Después comprobamos el código http, y con el método ‘body‘ podemos hacer comprobaciones sobre los campos del json devuelto, en este caso con matchers de hamcrest (equalTo y notNullValue).

6. Implementamos ahora de la misma manera el GET /api/info/ que nos devolverá todos:

    @Test
    public void getAll_InfosFound_ShouldReturnFoundInfos() {
        given()
            .accept(ContentType.JSON)
        .when()
            .get("/api/info/")
        .then()
            .statusCode(HttpStatus.SC_OK)
            .body(INFO_TEXT_FIELD,
                    hasItems(INFO_TEXT_1234, INFO_TEXT_4567, INFO_TEXT_7892))
            .body(CREATION_DATE_TIME_FIELD, everyItem(notNullValue()));

    }

Muy similar al anterior, aquí en las comprobaciones con el método ‘body’ usamos otros matchers de hamcrest, en este caso que afectan a listas (hasItems y everyItems). Comprobando siempre cómodamente sobre un campo del json.

7. Continuamos con los POST y PUT, respectivamente:

    @Test
    public void postInfo_InfoCorrect_ShouldReturnCreatedStatusAndNoBody() {
        given()
            .body(createMockInfo(INFO_TEXT_1234_NEW, CREATION_DATE_TIME_NEW))
            .contentType(ContentType.JSON)
        .when()
            .post("/api/info/")
        .then()
            .statusCode(HttpStatus.SC_CREATED)
            .body(isEmptyString())
            .header(HEADER_LOCATION, containsString("/api/info/" + INFO_ID_1234));
    }

    @Test
    public void putInfo_InfoCorrect_ShouldReturnNoContentStatus() {
        given()
            .body(createMockInfo(INFO_TEXT_1234_UPDATED, CREATION_DATE_TIME_UPDATED))
            .contentType(ContentType.JSON)
            .pathParam("id", INFO_ID_1234)
        .when()
            .put("/api/info/{id}")
        .then()
            .statusCode(HttpStatus.SC_NO_CONTENT);
    }

    private Info createMockInfo(String text, LocalDateTime creationDateTime) {
        return new Info()
                .setInfoText(text)
                .setCreationDateTime(creationDateTime);
    }

En los dos casos, en el ‘given’, con ‘body’ lo que hacemos es pasarle el objeto a enviar. Podriamos pasarselo en formato json en un String, pero no es necesario, él se encarga de hacer el parseo. En el caso del POST, en las comprobaciones, revisamos que el ‘location’ de la respuesta corresponda con la url del nuevo recurso creado.

8. Y por último, el DELETE:

    @Test
    public void deleteInfo_ShouldReturnNoContentStatus() {
        given()
            .pathParam("id", INFO_ID_1234)
        .when()
            .delete("/api/info/{id}")
        .then()
            .statusCode(HttpStatus.SC_NO_CONTENT);
    }

Nada nuevo en este caso.

Con esto habriamos terminado nuestro test de integración. La clase completa la podeís ver aquí.
Como vemos, nos quedan unos tests de integración muy limpios, y siguiendo la «nomenclatura» típica de Given-When-Then, y en plan ‘fluent interface’. A mi personalmente me gustan bastante los tests usando MockMvc, pero hay que reconocer que REST-assured facilita mucho tanto la implementación como la legibilidad de nuestros tests.

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

 

Implementar un cliente REST con Spring: RestTemplate

He hablado ya mucho de servicios REST y de como montarlos pero, y ¿si queremos acceder a un servicio REST desde nuestra aplicación? Hay varias maneras, desde hacerlo «a pelo» creando una conexión http, o usando clientes Rest. Para este post vamos a ver en concreto el cliente de Spring, RestTemplate.

Concepto:
Spring nos provee de un cliente Rest, RestTemplate, muy sencillo de usar. El se encarga de realizar la conexión http, lo único que hace falta es pasarle la url del servicio contra el que conectar. El mismo RestTemplate gestiona sus propios ‘messageConverters’, con los que parsear los datos enviados y recibidos de/a json, por ejemplo. Solo es necesario añadir en nuestro proyecto la dependencia necesaria (Jackson, en nuestro caso).

En el ejemplo veremos como llamar al servicio con los métodos básicos (GET, POST, PUT, DELETE). Para hacerlo más sencillo, lo haremos directamente desde un programa java desde su método ‘main’. Además, lo haremos atacando contra un servicio definido en apiary. También realizaremos otro ejemplo contra un servicio arrancado en local, en concreto el proyecto integrationtests-rest-example de mi github.

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

Ejemplo con apiary:

1. Voy a usar como servicio uno definido en mi cuenta de apiary, con un recurso ‘book’ Para el que no conozca apiary, es una web que nos ofrece el poder definir ‘mocks’ de servicios REST, contra los que poder atacar. Es muy útil a la hora de desarrollar.
En concreto vamos a atacar contra este servicio: http://docs.booksapi.apiary.io/

2. Nos creamos un proyecto maven básico (por ejemplo, con el archetype ‘maven-archetype-quickstart’), y añadimos las siguientes dependencias:

   
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.5.3</version>
        </dependency>
        ...
    </dependencies>

La dependencia de ‘spring-web’ es precisamente para poder tener acceso a la clase RestTemplate. La ‘jackson-databind’ es necesaria para el parseo de datos a json.

3. Creamos una clase con método main, en la que implementaremos los ejemplos:

package com.edwise.pocs.springrestclient;

public class AppBookRestApiary {

    private static final String URL_API_BOOKS =
            "http://private-114e-booksapi.apiary-mock.com/books/";

    public static void main(String[] args) {

    }

}

Tendremos la url a nuestro servicio (http://private-114e-booksapi.apiary-mock.com/books/) directamente en una constante.

4. Creamos una clase para la entity que nos devuelve el servicio:

package com.edwise.pocs.springrestclient.model;

public class Book {
    private Long id;
    private String title;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}

Necesitamos este bean o entity para recuperar los datos, etc. Es tan sencillo como revisar el servicio, ver sus campos e implementar una clase java con esos campos. Jackson se encargará del parseo a/desde json.

5. Creamos nuestro objeto RestTemplate, con el que accederemos al servicio. Al crearse, por defecto ya tiene los converters necesarios para el parseo a/desde json, al tener como dependencia la librería Jackson.

    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();

    }

Es asi de simple. Tiene también disponible algún otro constructor, con parámetros, como los converters, etc. Pero con el constructor sin parámetros nos sobra.

6. Ahora probaremos cada uno de los métodos que acepta el servicio:

– GET all: para obtener todos los ‘books’:

    ResponseEntity<Book[]> response =
                restTemplate.getForEntity(URL_API_BOOKS, Book[].class);

    System.out.println();
    System.out.println("GET All StatusCode = " + response.getStatusCode());
    System.out.println("GET All Headers = " + response.getHeaders());
    System.out.println("GET Body (object list): ");
    Arrays.asList(response.getBody())
                .forEach(book -> System.out.println("--> " + book));

Tanto para get como para post, tenemos disponible dos tipos de métodos: getForObject/postForObject y getForEntity/postForEntity. Los segundos te devuelven una respuesta completa (es el que uso en el ejemplo), en la que poder obtener tanto headers, como código http devuelto, etc. Los ‘xxxForObject’ devuelven directamente el objeto (el body de la respuesta).
En este caso, le tenemos que pasar la url y el tipo esperado para el body (array de clase ‘Book’ en nuestro caso). Mostramos luego el código http, los headers y, en bucle, todos los ‘books’ devueltos. El método ‘getBody’ nos devolverá un ‘Book[]’, al haberlo parametrizado así en la llamada al get.

– GET: para obtener un ‘book’ en concreto, por su id:

    ResponseEntity<Book> response =
                restTemplate.getForEntity(URL_API_BOOKS + "{id}", Book.class, 12L);

    System.out.println();
    System.out.println("GET StatusCode = " + response.getStatusCode());
    System.out.println("GET Headers = " + response.getHeaders());
    System.out.println("GET Body (object): " + response.getBody());

Similar al caso anterior. En este caso, le pasamos Book.class como tipo esperado.
Por otro lado, el tercer parámetro serían las ‘pathvariables’ del endpoint. En este caso el id.

– POST: para insertar un ‘book’ nuevo:

    Book bookToInsert = createBook(null, "New book title");
    ResponseEntity<Book> response =
                restTemplate.postForEntity(URL_API_BOOKS, bookToInsert, Book.class);

    System.out.println();
    System.out.println("POST executed");
    System.out.println("POST StatusCode = " + response.getStatusCode());
    System.out.println("POST Header location = " + response.getHeaders().getLocation());

Creamos un nuevo ‘Book’, sin id, y lo enviamos por post, con un ‘postForEntity’. En el segundo parámetro le pasamos lo que seria el ‘body’ de la request (nuestro objeto) y en el tercer parámetro el tipo.
En este caso no mostramos el body de la respuesta, dado que lo único que nos devuelve es el ‘location’ (más info en mi anterior post)

– PUT: para actualizar un ‘book’ en concreto, por su id:

    Book bookToUpdate = createBook(123L, "Book title updated");
    restTemplate.put(URL_API_BOOKS + "{id}", bookToUpdate, 123L);

    System.out.println();
    System.out.println("PUT executed");

Aquí cambia un poco la cosa. Excepto para get y post, para el resto de métodos http, RestTemplate nos ofrece unos métodos más simples, demasiado en mi opinión. Como vemos en este caso, el método ‘put’ no devuelve nada. Si necesitáis poder comprobar el código http, los headers o algún otro dato de la respuesta, echadle un vistazo al método ‘exchange’. Es un método con el que podemos realizar cualquier tipo de llamada (GET, POST, etc).

– DELETE: para borrar un ‘book’ en concreto, por su id:

    restTemplate.delete(URL_API_BOOKS + "{id}", 12L);

    System.out.println();
    System.out.println("DELETE executed");

Igual que el caso de put 😦

Ejemplo contra un servicio en local:

1. Si no lo tenemos ya, nos descargamos mi proyecto integrationtests-rest-example de mi github, y lo arrancamos con ‘mvn spring-boot:run’.

2. Como antes, creamos una clase con método main, en la que implementaremos los ejemplos (igual que antes) y también creamos una clase para la entity que nos devuelve el servicio:

package com.edwise.pocs.springrestclient.model;

import java.time.LocalDateTime;

public class Info {

    private Long id;
    private String infoText;
    private LocalDateTime creationDateTime;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getInfoText() {
        return infoText;
    }

    public void setInfoText(String infoText) {
        this.infoText = infoText;
    }

    public LocalDateTime getCreationDateTime() {
        return creationDateTime;
    }

    public void setCreationDateTime(LocalDateTime creationDateTime) {
        this.creationDateTime = creationDateTime;
    }

    @Override
    public String toString() {
        return "Info{" +
                "id=" + id +
                ", infoText='" + infoText + '\'' +
                ", creationDateTime=" + creationDateTime +
                '}';
    }
}

En este caso usamos la nueva API Date de Java 8. Para que Jackson la parsee correctamente, tenemos que añadir una dependencia más:

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

4. El resto es prácticamente igual al caso anterior, no voy a poner el código, pero está disponible en mi github: AppInfoRestLocal

Tenéis todo el código de los dos ejemplos preparado para probarlo en mi github, proyecto spring-rest-client-example. Se puede ejecutar cada ejemplo ejecutando su clase ‘main’, la cual ejecuta todos los casos.

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’ 🙂