Java 8 tips: collectingAndThen en Streams

Como apasionado de java que soy, la última versión me ha encantado por todos los cambios y mejoras que incluye. Por ello, comienzo hoy una nueva serie de posts relacionados con Java 8,  serán pequeños posts con curiosidades que me hayan llamado la atención o ‘tips’ que me parezcan especialmente útiles de la actual versión de la JDK.

Empiezo este, con un método interesante de la clase Collectors, usada al «terminar» un stream, para agrupar los elementos.

Concepto

Los Streams en java constan principalmente de 2 tipos de métodos u operaciones que pueden realizarse sobre ellos:

  • intermediate operations
  • terminal operations

Las ‘terminal’ son operaciones que pueden verse como operaciones para obtener un resultado final del stream (ya sea para obtener una lista, un sumatorio, etc).

Una de las más comunes es ‘collect‘:

   List<String> characters = Stream.of("Geralt", "Triss", "Yennefer", "Cirilla")
                                        .collect(Collectors.toList());

Lo que hace es agrupar los datos del stream y devolverlos en una Collection. En este caso, en un List.

Collectors.collectingAndThen

Imaginemos ahora que en el ejemplo anterior queremos que el list que nos devuelva el stream sea una lista inmutable. La idea es hacer esto:

        List<String> characters = Stream.of("Geralt", "Triss", "Yennefer", "Cirilla")
                                        .collect(Collectors.toList());
        List<String> charactersImmutable = Collections.unmodifiableList(characters);

Con Java 8 podemos hacerlo de una manera más elegante, usando el método ‘collectingAndThen‘ de Collectors:

    List<String> charactersImmutable =
                Stream.of("Geralt", "Triss", "Yennefer", "Cirilla")
                      .collect(Collectors.collectingAndThen(Collectors.toList(),
                                                            c -> Collections.unmodifiableList(c)));

Como vemos, ‘collectingAndThen’ nos proporciona la posibilidad de realizar una tarea ‘extra’ después de realizar el ‘collect’ en si mismo. En este caso, una vez obtenido el List, creamos con él una lista inmutable, que es lo que al final devolverá todo el procesamiento del stream.

Y por supuesto, podemos hacerlo más limpio aún, haciendo que sea una ‘method reference’:

    List<String> charactersImmutable =
                Stream.of("Geralt", "Triss", "Yennefer", "Cirilla")
                      .collect(Collectors.collectingAndThen(Collectors.toList(),
                                                            Collections::unmodifiableList));

El método ‘unmodifiableList‘ de Collections puede usarse perfectamente como ‘method reference’, quedando más limpio el código.

Gracias al método ‘collectingAndThen’ de Collectors podemos realizar alguna acción sobre la Collection (List, Map, etc) que queramos sacar del stream, antes de devolverla, quedando el código mucho más elegante 🙂

Espero que os sea útil. Tenéis el código de este post en mi github, proyecto java8-collectingandthen-example.

Futuros en Java, Parte 4: CompletableFuture, uso avanzado

Este último post de la serie de Futuros en Java es la continuación del anterior donde lo dejamos, viendo las funcionalidades de la clase CompletableFuture. Si no lo has leído ya, mejor empieza por ahí 🙂

Esta serie consta de las siguientes partes:
Parte 1: Introducción
Parte 2: interfaz Future
Parte 3: CompletableFuture, introducción
Parte 4: CompletableFuture, uso avanzado

Continúo con el uso de los CompletableFuture, a un nivel más avanzado:

Listeners / Callbacks
Como hemos visto, sobre esos futuros creados en los ejemplos anteriores podemos ahora ejecutar el método ‘get‘ de siempre para obtener el valor. Pero eso es justo lo que queríamos evitar. Para eso tenemos los callbacks y listeners, que serán ejecutados en cuanto el futuro se complete.
Además, la mayoría de estos métodos para crear callbacks devuelven a su vez un CompletableFuture también. Muy útil para encadenar varios futuros, como veremos después.
Todas tienen sus tres versiones, como ya expliqué en el anterior post.

whenComplete: este ya lo hemos visto en el anterior post, añade un callback para ejecutarlo cuando el futuro se complete. La lambda tiene 2 parámetros, uno es el posible resultado, y el otro es la excepción, si la hubiera habido. Veremos el tratamiento de excepciones más adelante.

thenApply: para transformar futuros. La idea es pasarle una función lambda que transforme el resultado del primero. Es similar al ‘map‘ de Scala.

    CompletableFuture<String> futureAsync = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando supplyAsync for thenApply...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado supplyAsync for thenApply!");
        return "Terminado";
    }, executor);

    CompletableFuture<String> futureApply = futureAsync.thenApplyAsync(s -> {
        LOGGER.info("Comenzando applyAsync...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado applyAsync!");
        return s.toUpperCase();
    }, executor);

    futureApply.whenCompleteAsync((s, e) -> LOGGER.info("Resultado applyAsync: {}", s),
        executor);

thenAccept y thenRun: muy similares al whenComplete, ejecutaran el lambda una vez se complete el futuro. El primero recibe un resultado, y el segundo no. Son equivalentes al supplyAsync y runAsync respectivamente.

    // thenAccept
    CompletableFuture<String> futureAsync = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando supplyAsync for thenAccept...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado supplyAsync for thenAccept!");
        return "Terminado";
    }, executor);

    futureAsync.thenAcceptAsync(s -> {
        LOGGER.info("Comenzando thenAccept...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado thenAccept!");
        LOGGER.info("Resultado: {}", s);
    }, executor);

    // thenRun
    CompletableFuture<Void> futureRun = CompletableFuture.runAsync(() -> {
        LOGGER.info("Comenzando runAsync for thenRun...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado runAsync for thenRun!");
    }, executor);

    futureRun.thenRunAsync(() -> {
        LOGGER.info("Comenzando thenRun...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado thenRun!");
    }, executor);

Excepciones
Tenemos varias maneras de gestionar las excepciones de futuros con la clase CompletableFuture, usando estos métodos:

exceptionally: registra un callback para gestionar la excepción. Recibe una lambda que solo tiene de parámetro la excepción, debe retornar un valor del mismo tipo que el futuro en el que se originó la excepción.

    CompletableFuture<String> futureAsync = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando supplyAsync with exception...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado supplyAsync with exception!");
        throw new RuntimeException("Error en el futuro");
    }, executor);

    CompletableFuture<String> futureEx = futureAsync.exceptionally(e -> {
        LOGGER.error("Resultado con excepción!!", e);
        return "StringPorDefecto";
    });

    futureEx.whenCompleteAsync((s, e) -> LOGGER.info("Resultado futureEx: {}", s),
            executor);

handle: registra un callback para gestionar el resultado o excepción. Recibe una lambda que tiene dos parámetros, el resultado y la excepción. Si la excepción no es nula, es que ha habido una excepción. También deber retornar un valor del tipo del futuro que lanzo la excepción.

    CompletableFuture<String> futureAsync = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando supplyAsync with exception...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado supplyAsync with exception!");
        throw new RuntimeException("Error en el futuro");
    }, executor);

    CompletableFuture<String> handledFuture = futureAsync.handleAsync((s, e) -> {
        if (e != null) {
            LOGGER.error("Resultado con excepción!!", e);
            return "StringPorDefecto";
        } else {
            LOGGER.info("Resultado: {}", s);
            return s;
        }
    }, executor);

    handledFuture.whenCompleteAsync((s, e) -> LOGGER.info("Resultado handle: {}", s),
            executor);

whenComplete: con este método que ya hemos explicado podemos hacer algo parecido al ‘handle’, dado que la lambda que registra tiene también los dos parámetros.

    CompletableFuture<String> futureAsync = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando supplyAsync with exception...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado supplyAsync with exception!");
        throw new RuntimeException("Error en el futuro");
    }, executor);

    futureAsync.whenCompleteAsync((s, e) -> {
        if (e != null) {
            LOGGER.error("Resultado con excepción!!", e);
        } else {
            LOGGER.info("Resultado applyAsync: {}", s);
        }
    }, executor);

Combinar futuros
En casi todos los ejemplos anteriores prácticamente solo hemos usado callbacks sobre un mismo futuro. Pero, como hemos visto en algún ejemplo (el ‘thenApply’), podemos encadenar futuros y combinarlos. En esta funcionalidad es donde se ve el verdadero potencial del desarrollo usando futuros.

Tenemos los siguientes métodos:

thenCompose: Muy similar a ‘thenApply’, pero este es equivalente al ‘flatMap’ de Scala. Lo que hace es una cadena de futuro también. Por ejemplo, aquí llamamos a ‘thenCompose’ con una lambda que a su vez es otro futuro:

    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando supplyAsync for thenCompose...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado supplyAsync for thenCompose!");
        return "Terminado";
    }, executor);

    CompletableFuture<String> fCompose =
            future1.thenComposeAsync(s -> CompletableFuture.supplyAsync(() -> {
                        LOGGER.info("Comenzando thenCompose...");
                        Sleep.sleepSeconds(2);
                        LOGGER.info("Terminado thenCompose!");
                        return s.concat(" + Terminado other");
                    }, executor),
                    executor);

    fCompose.whenCompleteAsync((s, e) -> LOGGER.info("Resultado thenCompose: {}", s),
            executor);

Parece algo retorcido, pero puede tener su lógica 😉

thenCombine: En este caso, en lugar de una cadena de futuros, espera a que terminen dos futuros, para luego hacer algo. En este caso la lambda tendrá dos parámetros, que son el resultado de cada uno de los dos futuros:

        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            LOGGER.info("Comenzando future1 for thenCombine...");
            Sleep.sleepSeconds(2);
            LOGGER.info("Terminado future1 for thenCombine!");
            return "Terminado";
        }, executor);

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            LOGGER.info("Comenzando future2 for thenCombine...");
            Sleep.sleepSeconds(1);
            LOGGER.info("Terminado future2 for thenCombine!");
            return "Terminado other";
        }, executor);

        CompletableFuture<String> fCombine =
                future1.thenCombineAsync(future2, (s1, s2) -> {
                    LOGGER.info("En el thenCombine, recibidos results: {}, {}", s1, s2);
                    return s1 + s2;
                }, executor);

        fCombine.whenCompleteAsync((s, e) -> LOGGER.info("Resultado thenCombine: {}", s),
                executor);

thenAcceptBoth y runAfterBoth: Muy similares al ‘thenCombine’, excepto que no generan un nuevo futuro, simplemente ejecutan la lambda cuando los dos futuros terminen. Es como un ‘whenComplete‘ pero esperando dos futuros:

    // thenAcceptBoth
    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future1 for thenAcceptBoth...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado future1 for thenAcceptBoth!");
        return "Terminado";
    }, executor);

    CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future2 for thenAcceptBoth...");
        Sleep.sleepSeconds(1);
        LOGGER.info("Terminado future2 for thenAcceptBoth!");
        return "Terminado other";
    }, executor);

    future1.thenAcceptBothAsync(future2, (s1, s2) ->
                    LOGGER.info("En el thenAcceptBoth, recibidos results: {}, {}", s1, s2)
            , executor);

    // runAfterBoth
    CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
        LOGGER.info("Comenzando future1 for runAfterBoth...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado future1 for runAfterBoth!");
    }, executor);

    CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
        LOGGER.info("Comenzando future2 for runAfterBoth...");
        Sleep.sleepSeconds(1);
        LOGGER.info("Terminado future2 for runAfterBoth!");
    }, executor);

    future1.runAfterBothAsync(future2, () -> LOGGER.info("En el runAfterBoth, futuros terminados.")
            , executor);

acceptEither y runAfterEither: En algunos casos en que tengamos dos futuros nos interesará hacer algo cuando uno de los dos termine, el primero que lo haga. Para eso están estos dos métodos:

    // acceptEither
    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future1 for acceptEither...");
        Sleep.sleepSeconds(3);
        LOGGER.info("Terminado future1 for acceptEither!");
        return "Segundo";
    }, executor);

    CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future2 for acceptEither...");
        Sleep.sleepSeconds(1);
        LOGGER.info("Terminado future2 for acceptEither!");
        return "Primero";
    }, executor);

    future1.acceptEitherAsync(future2, (s) ->
                    LOGGER.info("En el acceptEither, recibido el primer resultado: {}", s)
            , executor);

    // runAfterEither
    CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
        LOGGER.info("Comenzando future1 for runAfterEither...");
        Sleep.sleepSeconds(3);
        LOGGER.info("Terminado future1 for runAfterEither!");
    }, executor);

    CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
        LOGGER.info("Comenzando future2 for runAfterEither...");
        Sleep.sleepSeconds(1);
        LOGGER.info("Terminado future2 for runAfterEither!");
    }, executor);

    future1.runAfterEitherAsync(future2, () -> LOGGER.info("En el runAfterEither, primero terminado.")
            , executor);

applyToEither: muy similar a ‘acceptEither’, pero este devuelve a su vez un futuro. Es como el ‘thenApply’ pero sobre el futuro que termine antes:

    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future1 for applyToEither...");
        Sleep.sleepSeconds(3);
        LOGGER.info("Terminado future1 for applyToEither!");
        return "Segundo";
    }, executor);

    CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future2 for applyToEither...");
        Sleep.sleepSeconds(1);
        LOGGER.info("Terminado future2 for applyToEither!");
        return "Primero";
    }, executor);

    CompletableFuture<String> applyToEitherFuture = future1.applyToEitherAsync(future2, s -> {
        LOGGER.info("Comenzando applyToEither...");
        Sleep.sleepSeconds(1);
        LOGGER.info("Terminado applyToEither!");
        return s.toUpperCase();
    }, executor);

    applyToEitherFuture.whenCompleteAsync((s, e) -> LOGGER.info("Resultado applyToEither: {}", s),
            executor);

allOf y anyOf: Hasta ahora parece que solo podíamos combinar dos futuros. Con estos dos métodos podemos hacer un ‘thenAcceptBoth’ o ‘acceptEither’ sobre un número ilimitado de futuros:

    // allOf
    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future1 for allOf...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado future1 for allOf!");
        return "Terminado future1";
    }, executor);

    CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future2 for allOf...");
        Sleep.sleepSeconds(1);
        LOGGER.info("Terminado future2 for allOf!");
        return "Terminado future2";
    }, executor);

    CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future3 for allOf...");
        Sleep.sleepSeconds(3);
        LOGGER.info("Terminado future3 for allOf!");
        return "Terminado future3";
    }, executor);

    CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2, future3);

    all.whenCompleteAsync((s, e) -> LOGGER.info("Resultado all: {}", s), executor);

    // anyOf
    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future1 for allOf...");
        Sleep.sleepSeconds(2);
        LOGGER.info("Terminado future1 for allOf!");
        return "Terminado future1";
    }, executor);

    CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future2 for allOf...");
        Sleep.sleepSeconds(1);
        LOGGER.info("Terminado future2 for allOf!");
        return "Terminado future2";
    }, executor);

    CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
        LOGGER.info("Comenzando future3 for allOf...");
        Sleep.sleepSeconds(3);
        LOGGER.info("Terminado future3 for allOf!");
        return "Terminado future3";
    }, executor);

    CompletableFuture<Object> all = CompletableFuture.anyOf(future1, future2, future3);

    all.whenCompleteAsync((s, e) -> LOGGER.info("Resultado any: {}", s), executor);

La única pega es que el tipo que devuelven es Void y Object respectivamente.
Lógicamente, si esperas a que terminen todos los futuros (allOf), y devuelven resultado, no quieres un resultado, querrás todos. Quizá deberían haber hecho que devolviera un List en lugar de Void.

Hasta aquí este pequeño tutorial de cómo usar los futuros en Java con la clase CompletableFuture. Espero que os sea útil. Tenéis todos los ejemplos y alguno más en mi github, proyecto completablefuture-example.

Futuros en Java, Parte 3: CompletableFuture, introducción

En este tercer post sobre Futuros en Java veremos la nueva clase CompletableFuture, añadida en Java 8, y las funcionalidades que nos ofrece, comparado con la simple Future.
La clase CompletableFuture tiene mucha funcionalidad, por lo que he dividido en dos posts su explicación. Este primer post estará enfocado a hacer una introducción, y en el próximo post (y último de esta serie) veremos un uso más avanzado.

Esta serie consta de las siguientes partes:
Parte 1: Introducción
Parte 2: interfaz Future
Parte 3: CompletableFuture, introducción
Parte 4: CompletableFuture, uso avanzado

CompletableFuture: futuros «de verdad», gracias a Java 8
Con Java 8, entre otras muchas cosas, como los streams, lambdas, etc, se añadió la clase CompletableFuture (entre otras), con la que podemos en Java, por fin, tener unos futuros potentes, prácticamente al nivel de los de Scala. Es parecida funcionalmente a la clase ListenableFuture de la librería Guava.
Esta nueva clase implementa la interfaz Future que ya vimos, pero aporta muchísima más funcionalidad. Implementa también la nueva interfaz CompletionStage que es la que tiene todos los nuevos métodos.

Vamos a ver qué podemos hacer con esta clase. Tiene muchas posibilidades. Vamos a verlas directamente con ejemplos sencillos ¡Al lio!

Creación
Veamos tres ejemplos:

CompletableFuture<String> future = CompletableFuture.completedFuture("Prueba");
...
CompletableFuture<String> future = new CompletableFuture<>();
// other stuff
future.complete("Completado!");
...
CompletableFuture<Void> futureAsync = CompletableFuture.runAsync(() -> {
    // Some stuff...
});

Con la nueva clase CompletableFuture podemos crear el futuro ya directamente «completo», con el valor, con el método estático ‘completedFuture‘.
También podemos crearla directamente, con un new. Más adelante podemos simplemente «completar» el futuro, con el método ‘complete‘, como vemos en el segundo ejemplo.
Y, como es lógico, podemos crear un futuro que ejecute un pequeño proceso, como vemos en el tercer ejemplo, pasándole directamente una función lambda. En el usamos el método ‘runAsync‘, aunque tenemos más opciones (las veremos más adelante).

Variedad de métodos
Es importante explicar, para no liarse al ver el API de la clase CompletableFuture, que nos proporciona muchos métodos, casi todos ellos en tres «versiones». Por ejemplo, para el método ‘thenAccept‘ tenemos:

CompletableFuture<Void> thenAccept(Consumer<? super T> action);

CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action);

CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor);

– La versión sin «Async» ejecutará la función lambda en el mismo thread que el que la llama.
– La versión con «Async» ejecutará la ejecutara en un thread nuevo, usando para ello el Executor por defecto.
– La versión con «Async» y con parámetro ExecutorService, la ejecutará en un thread nuevo usando el Executor pasado como parámetro.

Lo normal si buscamos un buen sistema ‘reactivo’ sería usar siempre la versión con «Async», pero eso ya depende de cada situación. Por ejemplo, cuando encadenamos operaciones sobre un futuro, las siguientes puede ser lógico no usar las «Async» para que se sigan ejecutando en el miso thread que la anterior operación.

Métodos run y supply (y runAsync y supplyAsync)
Hemos visto que en la creación de CompletableFutures, la manera más común será pasándole una función lambda. Para ello tendremos dos métodos, ‘runAsync‘ y supplyAync‘ (y su correspondiente versión con parámetro ExecutorService).

CompletableFuture<Void> futureRunAsync = CompletableFuture.runAsync(() -> {
    LOGGER.info("Comenzando runAsync...");
    Sleep.sleepSeconds(3);
    LOGGER.info("Terminado runAsync!");
}, executor);

CompletableFuture<String> futureSupplyAsync = CompletableFuture.supplyAsync(() -> {
    LOGGER.info("Comenzando supplyAsync...");
    Sleep.sleepSeconds(3);
    LOGGER.info("Terminado supplyAsync!");
    return "Terminado";
}, executor);

Básicamente, estos métodos son equivalentes a lo que hacíamos en el post anterior para crear un Future, llamando al método ‘submit‘ del ExecutorService, pasándole nuestra lambda a ejecutar.

¿Cómo obtenemos el resultado?
Para obtener el resultado tenemos siempre la posibilidad de «bloquearnos» en el futuro, llamando al método ‘get‘ (CompletableFuture implementa también la interfaz Future).

Por ejemplo, para obtener el resultado del anterior futuro creado con ‘supplyAsync’:

LOGGER.info("Resultado bloqueando supplyAsync: " + futureSupplyAsync.get());

También nos ofrece un nuevo método ‘getNow‘ el cual lo que hace es, si el futuro se ha completado, devolver el resultado, y si no, devolver un parámetro que le pasamos a ese método.

Pero ninguna de esas opciones es lo que queremos. Ahora vamos a obtener el resultado sin bloquearnos, añadiendo algo así como un listener o callback a nuestro futuro.

CompletableFuture<String> futureSupplyAsync = CompletableFuture.supplyAsync(() -> {
    LOGGER.info("Comenzando supplyAsync...");
    Sleep.sleepSeconds(3);
    LOGGER.info("Terminado supplyAsync!");
    return "Terminado";
}, executor);

futureAsync.whenCompleteAsync((s, e) -> LOGGER.info("Resultado supplyAsync: " + s),
        executor);
LOGGER.info("Terminado main thread");

La llamada al método ‘whenCompleteAsync‘ realmente no se bloquea en el futuro. Lo que hace es «registrar» en el futuro que cuando se complete, ejecute esa función lambda.
El resultado por consola de este último ejemplo sería así:

19:07:34.030 [pool-1-thread-1] INFO CompletableFutureTest - Comenzando supplyAsync...
19:07:34.030 [main] INFO CompletableFutureTest - Terminado main thread
19:07:37.034 [pool-1-thread-1] INFO CompletableFutureTest - Terminado supplyAsync!
19:07:37.035 [pool-1-thread-2] INFO CompletableFutureTest - Resultado supplyAsync: Terminado

Como vemos, el último log «Terminado main thread» se muestra rapidamente, antes que el log que mostramos en la lambda del ‘whenCompleteAsync’.

Y hasta aquí por hoy. En el próximo post veremos a fondo el uso de callbacks, como este último, así como otras funcionalidades para combinar futuros.
Todo el código de los ejemplos está en mi github, proyecto completablefuture-example.

Futuros en Java, Parte 2: Interfaz Future

Continuo con la serie de post sobre Futuros en Java que comencé con el anterior post. En este hablaré sobre la interfaz Future añadida en java 5.

Esta serie constará inicialmente de las siguientes partes:
Parte 1: Introducción
Parte 2: interfaz Future
Parte 3: CompletableFuture, introducción
Parte 4: CompletableFuture, uso avanzado

Interfaz Future: uso y ejemplos

Para poder evitar los bloqueos de los que hablé en el anterior post, nuestra primera herramienta en Java es la interfaz Future. Esta fue añadida hace tiempo ya, con Java 1.5, y nos provee lo básico para el trabajo con futuros. Como veremos, demasiado básico… Vamos con algunos ejemplos para demostrar su funcionamiento.

Creación:
No podemos crear directamente un futuro. Es una interfaz, y nos lo devolverá, por ejemplo, un método.

        Future<String> future = someBigProcess.processVeryLong("test");

Tiene un parámetro genérico que será el tipo del objeto que esperamos obtener cuando el futuro se complete.

Para crearlo, no hay una manera directa. Por ejemplo, la más sencilla sería esta:

    private final ExecutorService executor = Executors.newFixedThreadPool(5);

    public Future<String> processVeryLong(String param1) throws InterruptedException {
        return executor.submit(() -> {
            TimeUnit.SECONDS.sleep(5);
            LOG.info("Terminando processVeryLong...");
            return param1.concat(" result");
        });
    }

Usamos la interfaz ExecutorService para «lanzar» un Callable (con un lambda de Java 8). Para eso necesitamos haber definido el Executor, que es una clase que se encarga de gestionar pool de threads. Su método ‘submit’ ejecuta en otro thread el Callable o Runnable recibido, y devuelve un Future, que, cuando se complete la ejecución, contendrá el resultado.

Uso y bloqueos:
El uso principal de la interfaz Future es su método ‘get’:

        String result = future.get();

Este método es el que obtiene el valor real del futuro. Si el futuro todavía no se ha completado, al llamar a este método nos quedaremos bloqueados hasta que se complete.
Ojo, si al completarse el futuro se genera una excepción, la llamada a este método es la que lanzará esa excepción («envuelta» en una ExecutionException).
También existe una versión del mismo método que recibe un timeout, para no quedarnos bloqueados eternamente:

        String result = future.get(5, TimeUnit.SECONDS);

Aparte tiene algunos métodos más:
cancel(boolean mayInterruptIfRunning) -> Cancela la ejecución del futuro.
isCancelled() -> Comprueba si ha sido cancelado.
isDone() -> Comprueba si el futuro se ha completado.

Pero no nos ofrece ninguna posibilidad más. Si usamos la interfaz Future, en algún momento, si o si, tendremos que bloquear nuestro thread para obtener el resultado.

La interfaz Future de java se queda muy corta para siquiera empezar a montar un sistema «reactivo». Nos ofrece una manera sencilla de ejecutar métodos de manera asíncrona, pero nada más.

En el próximo post de la serie veremos como la clase CompletableFuture, añadida en Java 8, nos ayuda a resolver esto.

Tenéis en mi github (proyecto future-example) un ejemplo completo con la interfaz Future, con un test sencillo con el que probarlo, tanto para un caso normal, como para un caso que lanza una excepción.

Futuros en Java, Parte 1: Introducción

Comienzo hoy una seríe de post sobre los futuros en Java y sus posibilidades. En este primer post me centraré en explicar el concepto y los problemas de las aplicaciones «clásicas».

Esta serie constará inicialmente de las siguientes partes:
Parte 1: Introducción
Parte 2: interfaz Future
Parte 3: CompletableFuture, introducción
Parte 4: CompletableFuture, uso avanzado

Threads, bloqueos y demás

El funcionamiento de una clásica aplicación web montada con Java, con Spring por ejemplo, se basa principalmente en aceptar peticiones http, usando para cada una de estas peticiones (o request) un thread propio. De tal manera que cuando a nuestra aplicación acceden varios usuarios concurrentemente, tendremos un thread para cada uno de esos usuarios.

Si nuestra aplicación no es muy eficiente, e incluso tiene cuellos de botella grandes, por ejemplo en el acceso a base de datos, en peticiones a otras apis, etc, cada una de esos threads estará «activo» mucho tiempo… Lo que implicará que si tenemos una aplicación con un gran volumen de usuarios, en un momento dado puede reventar el servidor por el número de threads.

La única manera inicial para resolver esto es a base de fuerza bruta: añadiendo más memoria a la maquina, para que aguante más threads… lo cual no es práctico, al final volverá a pasar. Un sistema así no escala. Lo ideal es que los threads que usan las requests se liberen lo antes posible (para poder servir otras requests esos mismos, en lugar de crecer el número de threads hasta el infinito…).

Todo esto ocurre principalmente por que nuestros threads están mucho tiempo activos, y la gran mayoria de ese tiempo están bloqueados. En esos accesos a base de datos, otros recursos web, apis externas, etc es donde se va el mayor tiempo con diferencia. Y, como digo, el thread se queda ahi bloqueado. No se libera, aunque realmente no está haciendo nada, pero esta ocupado, esperando…

Manifiesto reactivo, asincronía y futuros

Para solucionar este problema surge el Manifiesto Reactivo. Viene a ser unas buenas prácticas y una manera concreta de desarrollo de aplicaciones en las que, entre otras cosas, debemos evitar esos bloqueos de threads a toda costa, para desarrollar aplicaciones que escalen correctamente.

¿Y cómo evitamos esos bloqueos? Con los futuros. El concepto de Futuro es el de un objeto que, en algún momento, contendrá el resultado de un método. Cuando llamemos a ese método, devolverá inmediatamente el resultado (un objeto Futuro), y se irá ejecutando de manera asincrona, mientras nuestro método «llamante» continua. En algún momento, el método llamado terminará, y dejara el resultado en el futuro.

Un pequeño ejemplo:

        // LLamada al método, que será asincrona
        Future<String> resultFuture = dao.methodAccessingDB();

        // Hacemos otras cosas

        // Más cosas

        // Obtenemos el resultado del futuro
        String result = resultFuture.get();

Aún así, en algún momento habrá que bloquear al método llamado para obtener ese resultado (como vemos en la linea 9 del ejemplo anterior). Para evitar esto tenemos varías «estrategias» a la hora de programar, que veremos más adelante.

En los proximos posts de esta seríe me centraré en explicar ejemplos con las clases que Java nos provee para los futuros: Future y CompletableFuture (esta última añadido en Java 8).

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.

Los números de 2015 en mi blog

WordPress, como cada fin de año, nos prepara unas estadísticas anuales de nuestros blogs, os comparto aquí las de mi blog 🙂
Estoy bastante contento con los posts que he escrito este año, en total 17, no está mal. El año que viene más, y mejor.

Feliz año! 😀

Aquí hay un extracto:

Un tren subterráneo de la ciudad de Nueva York transporta 1.200 personas. Este blog fue visto alrededor de 5.900 veces en 2015. Si fuera un tren de NY, le tomaría cerca de 5 viajes transportar tantas personas.

Haz click para ver el reporte completo.

Documentar un servicio REST, con Swagger 2 (SpringFox)

Hace bastante tiempo publiqué un post en el que explicaba como documentar un servicio REST montado con Spring, con Swagger. Desde entonces, la librería usada para Spring ha cambiado bastante (incluso de nombre y de dueños…). En este post veremos como hacerlo con la nueva librería, SpringFox.

Concepto

Desde hace tiempo la antigua librería ‘swagger-springmvc‘, que servia para integrar swagger fácilmente en un proyecto Spring, ha pasado a llamarse SpringFox, y ha cambiado un poco. En sus últimas versiones soporta Swagger 2.0. Ya hablé un poco sobre Swagger en el post que comentaba antes, pero tenéis más info sobre swagger y sobre SpringFox en estos links:
http://swagger.io/
https://github.com/swagger-api
http://springfox.github.io/springfox/
https://github.com/springfox/springfox

En este post voy a documentar un servicio REST Spring con Swagger 2.0, usando SpringFox. Para ello usare como base un proyecto SpringBoot con un servicio REST ‘Infos’ que ya use en otros posts anteriores:
https://anotherdayanotherbug.wordpress.com/2015/03/16/tests-de-integracion-para-un-servicio-rest-con-spring/
https://anotherdayanotherbug.wordpress.com/2015/05/25/tests-de-integracion-usando-rest-assured/

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

Pasos

1. Creamos nuestro proyecto SpringBoot como siempre , con la versión 1.3. Lo principal que necesitamos es el pom.xml de Maven, la clase ‘main’ de SpringBoot … y listo.

2. Implementamos nuestro sencillo servicio REST. Un controller, que usa un Service, él cual usa un Repository (mockeado este último). Nuestra entity y el controller son estos:

public class Info {

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

    // getters and setters
}

@RestController
@RequestMapping("/api/infos")
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;
    }
}

3. Vamos a empezar a integrarle Swagger 2 con SpringFox. Primero añadimos las librerías necesarias a nuestro pom.xml:

...
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.3.0</version>
        </dependency>
...

La principal que necesitamos es ‘springfox-swagger2‘.
La de ui es más bien un ‘webjar’ con la ui de swagger de siempre. Lo bueno es que esta todo en el jar, y no tenemos que bajarnos toda la ui en una subcarpeta del proyecto, como hice en el post anterior.

4. Creamos una clase de configuración de Spring, con la anotación @EnableSwagger2 y con el bean básico que necesita Swagger 2 (Docket):

package com.edwise.pocs.swagger2.config;

// imports...

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket newsApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("api-infos")
                .apiInfo(apiInfo())
                .directModelSubstitute(LocalDateTime.class, Date.class)
                .select()
                .paths(regex("/api.*"))
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Infos REST api")
                .description("PoC of a REST api, Infos")
                .termsOfServiceUrl("http://en.wikipedia.org/wiki/Terms_of_service")
                .contact("edwise.null@gmail.com")
                .license("Apache License Version 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
                .version("2.0")
                .build();
    }
}

Esto es similar a la anterior configuración con la librería swagger-springmvc, pero aquí en lugar de un SwaggerSpringMvcPlugin, tenemos un bean llamado Docket, pero es similar. Tiene alguna mejora interesante. Por ejemplo, aquí estamos diciéndole que, a la hora de mostrar los tipos de dato LocalDateTime en la documentación de la API salgan como String (si no, te sale todos los objetos que contiene LocalDateTime, es un guarreo…)

5. Ya solo falta documentar nuestro api REST. Para eso, documentaremos primero la entity ‘Info’ así:

package com.edwise.pocs.swagger2.entity;

// imports...

@ApiModel(value = "Info entity", description = "Complete data of a entity Info")
public class Info {

    @ApiModelProperty(value = "The id of the info", required = false)
    private Long id;

    @ApiModelProperty(value = "The text of the info", required = true)
    private String infoText;

    @ApiModelProperty(value = "The date of the info", required = true)
    private LocalDateTime creationDateTime;

    // getters and settters
}

Apenas hay cambios respecto a la versión anterior. Usamos las anotaciones @ApiModel y @ApiModelProperty para documentar nuestro pojo.

6. Y ahora documentamos nuestro controller:

package com.edwise.pocs.swagger2.controller;

// imports...

@RestController
@RequestMapping("/api/infos")
@Api(value = "infos", description = "Infos API", produces = "application/json")
public class InfoController {

    @Autowired
    private InfoService infoService;

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "Get Infos", notes = "Returns all infos")
    @ApiResponses({
            @ApiResponse(code = 200, message = "Exits one info at least")
    })
    public List<Info> getAllInfos() {
        return infoService.findAll();
    }

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(method = RequestMethod.GET, value = "{id}",
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "Get one Info", notes = "Returns one info")
    @ApiResponses({
            @ApiResponse(code = 200, message = "Exists this info")
    })
    public Info getInfo(@ApiParam(defaultValue = "1", value = "The id of the info to return")
                        @PathVariable Long id) {
        return infoService.findOne(id);
    }

    @RequestMapping(method = RequestMethod.POST,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "Create Info", notes = "Create a info")
    @ApiResponses({
            @ApiResponse(code = 201, message = "Successful create of a info")
    })
    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}")
    @ApiOperation(value = "Update Info", notes = "Update a info")
    @ApiResponses({
            @ApiResponse(code = 204, message = "Successful update of a info")
    })
    public void updateInfo(@ApiParam(defaultValue = "1", value = "The id of the info to update")
                           @PathVariable Long id,
                           @RequestBody Info info) {
        infoService.update(info.setId(id));
    }

    @ResponseStatus(HttpStatus.NO_CONTENT)
    @RequestMapping(method = RequestMethod.DELETE, value = "{id}")
    @ApiOperation(value = "Delete Info", notes = "Delete a info")
    @ApiResponses({
            @ApiResponse(code = 204, message = "Successful delete of a info")
    })
    public void deleteInfo(@ApiParam(defaultValue = "1", value = "The id of the info to delete")
                           @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;
    }
}

Tampoco cambia nada, seguimos usando las mismas anotaciones para los controlers: @Api, @ApiOperation, @ApiResponses, @ApiParam
En el anterior post sobre Swagger las explico un poco por encima, aunque creo que viendo el código es bastante sencillo entender como funcionan.

7. Arrancamos nuestro proyecto SpringBoot (mvn spring-boot:run). Si accedemos a http://localhost:8080/swagger-ui.html podemos ver la interfaz de Swagger y juguetear con nuestro API.
ScreenSwagger2

Si queremos acceder directamente a la info (en formato json) de Swagger, accedemos a http://localhost:8080/v2/api-docs?group=api-infos. El formato ha cambiado bastante respecto a la versión anterior, echadle un vistazo.

El código de este post está en mi github, proyecto springfox-swagger2-example.