Recentemente, comecei a brincar com Ratpack. 
É um conjunto de bibliotecas Java para a criação de aplicativos HTTP modernos.
Para ir além dessa definição simples, o Ratpack é não bloqueador, assíncrono e baseado em rede. Um pouco como o nosso Java SDK. Portanto, ele é um candidato natural para uma estrutura de aplicativo leve.
Uma das primeiras coisas que você pode fazer ao testar uma nova estrutura da Web é criar uma API básica. E a criação de um CRUD simples é natural quando você trabalha com um banco de dados de documentos como o Couchbase. Portanto, este post é uma introdução básica ao Ratpack, apresentando a abstração do repositório de entidades do Couchbase disponível com nosso Java SDK. Não vou me concentrar na API em si, mas mais no mecanismo usado no Ratpack e no repositório do Couchbase.
Criando um projeto Java do Ratpack
A maioria dos usuários do Ratpack que conheço tende a programar com Groovy. Normalmente, estou codificando em Java, portanto, é isso que vou usar hoje. O início rápido No entanto, os guias que você encontrará para o Ratpack terão Groovy. O modelo lazybones é um modelo em Groovy, por exemplo. Então, comecei gerando um projeto Java Gradle simples em meu IDE favorito. Em seguida, abri o arquivo build.gradle e o completei com as dependências corretas. Adicionei o arquivo jcentro uma dependência do ratpack-gradle 1.3.3 e me certifiquei de que estava usando o repositório io.ratpack.ratpack-java plugin. E não vamos nos esquecer da dependência do 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' } |
A próxima etapa é iniciar o aplicativo com um simples Hello World. Tenho uma classe Application, como você pode ver no arquivo de compilação acima, com a classe mainClassName opção. Tudo o que farei aqui para começar é ativar o servidor Ratpack e registrar o caminho de URL '/hello' para retornar uma mensagem Hello World.
O Ratpack tem Manipuladores objetos. Um manipulador é uma função com um contexto como parâmetro. Você pode associar um manipulador a um caminho. Portanto, aqui vou associar uma função Handler ao caminho '/hello'. Para entender o Ratpack, é preciso entender os manipuladores, pois você os usará em todos os seus aplicativos.
O ponto de entrada em um aplicativo Ratpack é a classe RatpackServer. Nessa classe, você pode usar o método estático iniciar. Ele usa uma função com um objeto RatpackServerSpec como parâmetro. Esse objeto fornece a você uma API fluente para configurar o servidor. A única coisa que preciso fazer aqui é adicionar meu manipulador. O manipuladores recebe como parâmetro uma função com uma cadeia de manipuladores. A partir dessa cadeia, posso criar meu manipulador:
|
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!")))); } } |
Quando inicio o aplicativo e vou para localhost:5050/hello, recebo minha mensagem hello. Passando para a parte do Couchbase.
O modelo de entidade do Couchbase
O modelo de entidade apareceu com a versão 2.2.x. Você pode declarar a entidade facilmente usando a anotação @Id do Couchbase. Também é necessário certificar-se de que sua classe de entidade tenha um construtor público com zero arg e que o setter e o getter estejam disponíveis para os campos que você deseja no documento JSON resultante. Para modelar um usuário com nome de usuário, idade, nome e sobrenome, uso a seguinte classe 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 + "]"; } } |
A anotação @Id é obrigatória e define o campo que será usado como chave do seu documento. Você pode usar a anotação @Field para alterar o nome das propriedades em seu documento JSON resultante. Observe também que o campo @Id não estará no JSON.
Essa classe de entidade deve ser usada com a classe EntidadeDocumento. Seu uso é muito semelhante ao do outras implementações de documentos disponível com o SDK. O código a seguir criará um documento JSON no 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 criam o seguinte documento:
Legenda: ldoguin
Valor: { "lName": "Doguin", "age": 31, "fName": "Laurent" }
No momento, esse código está bloqueando e é síncrono. Isso é ruim? Depende :) Se você estiver executando um código bloqueante em uma thread não bloqueante, como faria se ele estivesse sendo executado no lugar do hello world acima, então você estará bloqueando todo o resto. E sim, isso seria uma coisa ruim. Porque esse thread gerencia o loop de eventos que trata de todas as solicitações. Eu encontrei esta postagem do blog bastante útil para entender isso.
Um dos objetivos do Ratpack&apos é ajudá-lo a usar o código de bloqueio em uma assíncrono, sem bloqueio manipulador. O Ratpack usa Promessa como uma forma de gerenciar o código assíncrono. E ele também oferece uma primitiva de bloqueio que recebe uma função como argumento e retorna uma promessa. A função executará o código de bloqueio. Aqui tenho um exemplo que cria um usuário ao acessar o URL localhost:5050/create e o retorna ao acessar o 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())); }) )); } |
Deixando de lado o fato de que essa é provavelmente a API mais mal projetada de todos os tempos, isso deve lhe dar uma ideia de como você pode executar código síncrono e de bloqueio em um manipulador assíncrono com o Ratpack. Esse é o primeiro passo para migrar um aplicativo existente para o Ratpack.
Mas esse exemplo não mostra realmente todas as vantagens do nosso SDK baseado em RxJava. Portanto, na próxima postagem, mostrarei como usar o RxJava e o Ratpack junto com a versão Async do Repository.