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 { repositorios { jcenter() } dependencias { ruta de clase "io.ratpack:ratpack-gradle:1.3.3" } } aplicar plugin: "io.ratpack.ratpack-java" aplicar plugin: 'eclipse' fuenteCompatibilidad = 1.8 versión = '1.0' tarro { manifestar { atributos 'Aplicación-Título': 'Ratpack Couchbase Repositorio Muestra', 'Aplicación-Versión': versión } } repositorios { mavenCentral() jcenter() } dependencias { compilar "com.couchbase.client:java-client:2.2.5" } mainClassName = "org.couchbase.devex.Application" prueba { systemProperties 'propiedad': 'valor' } |
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 |
paquete org.couchbase.desvelar; importar ratpack.servidor.RatpackServer; público clase Aplicación { público estático void principal(Cadena... args) lanza Excepción { RatpackServer .iniciar(servidor -> servidor.manipuladores(cadena -> cadena.ruta("hola", ctx -> ctx.render("¡Hola Mundo!")))); } } |
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 |
paquete org.couchbase.desvelar; importar com.couchbase.cliente.java.repositorio.anotación.Campo; importar com.couchbase.cliente.java.repositorio.anotación.Id; público clase Usuario { @Id privado Cadena nombre de usuario; privado Entero edad; @Campo("fName") privado Cadena firstName; @Campo("lNombre") privado Cadena apellido; público Usuario() {} público Usuario(Cadena nombre de usuario, Entero edad, Cadena firstName, Cadena apellido) { este.nombre de usuario = nombre de usuario; este.edad = edad; este.firstName = firstName; este.apellido = apellido; } público Cadena getUsername() { devolver nombre de usuario; } público void setUsername(Cadena nombre de usuario) { este.nombre de usuario = nombre de usuario; } público Entero getAge() { devolver edad; } público void setAge(Entero edad) { este.edad = edad; } público Cadena getFirstName() { devolver firstName; } público void setPrimerNombre(Cadena firstName) { este.firstName = firstName; } público Cadena getApellido() { devolver apellido; } público void setApellido(Cadena apellido) { este.apellido = apellido; } @Anular público Cadena toString() { devolver "Usuario [username=" + nombre de usuario + ", age=" + edad + ", firstName=" + firstName + ", lastName=" + apellido + "]"; } } |
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 |
Usuario usuario = nuevo Usuario("ldoguin", 31, "Laurent", "Doguin"); DocumentoEntidad<Usuario> documento = DocumentoEntidad.crear(usuario); Repositorio repo = CouchbaseCluster.crear().openBucket().repositorio(); repo.upsert(documento); |
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 |
público estático void principal(Cadena... args) lanza Excepción { RatpackServer.iniciar(servidor -> servidor.manipuladores(cadena -> cadena.ruta("crear", ctx -> { Bloqueo.consiga(() -> { DocumentoEntidad<Usuario> documento = DocumentoEntidad.crear(nuevo Usuario("ldoguin", 31, "Laurent", "Doguin")); Repositorio repo = CouchbaseCluster.crear().openBucket().repositorio(); devolver repo.upsert(documento); }).entonces(entityDoc -> ctx.render("OK")); }).ruta("get", ctx -> { Bloqueo.consiga(() -> { Repositorio repo = CouchbaseCluster.crear().openBucket().repositorio(); DocumentoEntidad<Usuario> ldoguin = repo.consiga("ldoguin", Usuario.clase); devolver ldoguin; }).entonces(usuario -> ctx.render(usuario.contenido().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.