Hace poco empecé a jugar con Ratpack. 
Es un conjunto de bibliotecas Java para crear aplicaciones HTTP modernas.
Para ir más allá de esa simple definición, Ratpack es no bloqueante, asíncrono y basado en la red. Un poco como nuestro SDK de Java. Por tanto, es un candidato natural para un marco de aplicaciones ligero.
Una de las primeras cosas que puedes hacer cuando pruebas un nuevo framework web es crear una API básica. Y construir un CRUD simple es algo natural cuando trabajas con una base de datos de documentos como Couchbase. Así que este post es una introducción básica a Ratpack mostrando la abstracción del repositorio de entidades de Couchbase disponible con nuestro Java SDK. No me centraré aquí en la API en sí, sino más bien en el mecanismo utilizado en Ratpack y el repositorio Couchbase.
Creación de un proyecto Ratpack Java
La mayoría de los usuarios de Ratpack que conozco suelen codificar con Groovy. Yo suelo codificar en Java, así que esto es lo que voy a usar hoy. El inicio rápido Sin embargo, las guías que encontrarás para Ratpack incluyen Groovy. La plantilla lazybones es una plantilla Groovy, por ejemplo. Así que empecé generando un simple proyecto Java Gradle en mi IDE favorito. Luego abrí el archivo build.gradle y lo completé con las dependencias correctas. He añadido el archivo jcenter una dependencia a ratpack-gradle 1.3.3 y me aseguré de que estaba usando la versión io.ratpack.ratpack-java plugin. Y no olvidemos la dependencia de Couchbase.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
buildscript { repositories { jcenter() } dependencies { classpath "io.ratpack:ratpack-gradle:1.3.3" } } apply plugin: "io.ratpack.ratpack-java" apply plugin: 'eclipse' sourceCompatibility = 1.8 version = '1.0' jar { manifest { attributes 'Implementation-Title': 'Ratpack Couchbase Repository Sample', 'Implementation-Version': version } } repositories { mavenCentral() jcenter() } dependencies { compile "com.couchbase.client:java-client:2.2.5" } mainClassName = "org.couchbase.devex.Application" test { systemProperties 'property': 'value' } |
El siguiente paso es iniciar la aplicación con un simple Hola Mundo. Tengo una clase de aplicación como se puede ver en el archivo de compilación de arriba con la clase mainClassName opción. Todo lo que voy a hacer aquí para empezar es poner en marcha el servidor Ratpack y registrar la ruta URL '/hello' para devolver un mensaje de Hola Mundo.
Ratpack tiene Manipuladores objetos. Un Manejador es una función con un Contexto como parámetro. Puedes asociar un handler a una ruta. Así que aquí voy a asociar una función Handler a la ruta '/hello'. Para entender Ratpack tienes que entender los handlers ya que los usarás en todas tus aplicaciones.
El punto de entrada en una aplicación Ratpack es la clase RatpackServer. Desde esta clase se puede utilizar el método estático iniciar. Toma una función con un objeto RatpackServerSpec como parámetro. Este objeto te da una API fluida para configurar el servidor. Lo único que necesito hacer aquí es añadir mi manejador. El manipuladores toma como parámetro una función con una Cadena de Manejadores. A partir de esa cadena puedo crear mi handler:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package org.couchbase.devex; import ratpack.server.RatpackServer; public class Application { public static void main(String... args) throws Exception { RatpackServer .start(server -> server.handlers(chain -> chain.path("hello", ctx -> ctx.render("Hello World!")))); } } |
Cuando inicio la aplicación y voy a localhost:5050/hello, obtengo el mensaje hola. Pasando a la parte de Couchbase.
El modelo de entidades de Couchbase
El modelo de entidad apareció con la versión 2.2.x. Puedes declarar entidad fácilmente usando la anotación @Id de Couchbase. También necesitas asegurarte de que tu clase de entidad tiene un constructor público de arg cero y que setter y getter están disponibles para los campos que quieres en el documento JSON resultante. Para modelar un usuario con un nombre de usuario, edad, nombre y apellidos, utilizo la siguiente clase User:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
package org.couchbase.devex; import com.couchbase.client.java.repository.annotation.Field; import com.couchbase.client.java.repository.annotation.Id; public class User { @Id private String username; private Integer age; @Field("fName") private String firstName; @Field("lName") private String lastName; public User() {} public User(String username, Integer age, String firstName, String lastName) { this.username = username; this.age = age; this.firstName = firstName; this.lastName = lastName; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Override public String toString() { return "User [username=" + username + ", age=" + age + ", firstName=" + firstName + ", lastName=" + lastName + "]"; } } |
La anotación @Id es obligatoria y define el campo que se utilizará como clave de su documento. Puede utilizar la anotación @Field para cambiar el nombre de las propiedades en el documento JSON resultante. Tenga en cuenta también que el campo @Id no estará en el JSON.
Esta clase de entidad debe utilizarse con la clase DocumentoEntidad. Su uso es muy similar al del otras implementaciones de documentos disponible con el SDK. El siguiente código creará un documento JSON en Couchbase:
|
1 2 3 4 5 |
User user = new User("ldoguin", 31, "Laurent", "Doguin"); EntityDocument<User> document = EntityDocument.create(user); Repository repo = CouchbaseCluster.create().openBucket().repository(); repo.upsert(document); |
Que crean el siguiente documento:
Clave: ldoguin
Valor: { "lName": "Doguin", "age": 31, "fName": "Laurent" }
Este código es actualmente bloqueante y síncrono. ¿Es esto malo? Depende :) Si estás ejecutando código bloqueante en un hilo no bloqueante, como harías si se estuviera ejecutando en lugar del hola mundo anterior, entonces estás bloqueando todo lo demás. Y sí, eso sería algo malo. Porque este hilo gestiona el bucle de eventos que maneja todas las peticiones. He encontrado esta entrada del blog bastante útil para entender esto.
Uno de los objetivos de Ratpack's es ayudarle a utilizar código de bloqueo en un asíncrono, no bloqueante manipulador. Ratpack utiliza Promesa como una forma de gestionar el código asíncrono. Y también te da una primitiva de Bloqueo que toma una función como argumento y devuelve una promesa. La función ejecutará el código de bloqueo. Aquí tengo un ejemplo que crea un usuario cuando se pulsa la URL localhost:5050/create y lo devuelve cuando se pulsa la URL localhost:5050/get:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public static void main(String... args) throws Exception { RatpackServer.start(server -> server.handlers(chain -> chain.path("create", ctx -> { Blocking.get(() -> { EntityDocument<User> document = EntityDocument.create(new User("ldoguin", 31, "Laurent", "Doguin")); Repository repo = CouchbaseCluster.create().openBucket().repository(); return repo.upsert(document); }).then(entityDoc -> ctx.render("OK")); }).path("get", ctx -> { Blocking.get(() -> { Repository repo = CouchbaseCluster.create().openBucket().repository(); EntityDocument<User> ldoguin = repo.get("ldoguin", User.class); return ldoguin; }).then(user -> ctx.render(user.content().toString())); }) )); } |
Dejando a un lado el hecho de que esta es probablemente la API peor diseñada de la historia, esto debería darte una idea de cómo puedes ejecutar código síncrono bloqueante en un manejador asíncrono con Ratpack. Que es el primer paso para migrar una aplicación existente a Ratpack.
Pero este ejemplo no muestra todas las bondades de nuestro SDK basado en RxJava. Así que en el próximo post te mostraré cómo utilizar RxJava y Ratpack junto con la versión Async del Repositorio.