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 { repositórios { jcentro() } dependências { caminho de classe "io.ratpack:ratpack-gradle:1.3.3" } } aplicar plug-in: "io.ratpack.ratpack-java" aplicar plug-in: 'eclipse' sourceCompatibility = 1.8 versão = '1.0' frasco { manifesto { atributos 'Implementação-Título': 'Ratpack Couchbase Repositório Amostra', 'Implementação-Versão': versão } } repositórios { mavenCentral() jcentro() } dependências { compilar "com.couchbase.client:java-client:2.2.5" } mainClassName = "org.couchbase.devex.Application" teste { systemProperties 'propriedade': 'valor' } |
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 |
pacote org.couchbase.desenvolver; importação pacote de rato.servidor.RatpackServer; público classe Aplicativo { público estático vazio principal(Cordas... argumentos) lançamentos Exceção { RatpackServer .iniciar(servidor -> servidor.manipuladores(cadeia -> cadeia.caminho("hello", ctx -> ctx.renderizar("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 |
pacote org.couchbase.desenvolver; importação com.couchbase.cliente.java.repositório.anotação.Campo; importação com.couchbase.cliente.java.repositório.anotação.Id; público classe Usuário { @Id privado Cordas nome de usuário; privado Inteiro idade; @Campo("fName") privado Cordas firstName; @Campo("lName") privado Cordas lastName; público Usuário() {} público Usuário(Cordas nome de usuário, Inteiro idade, Cordas firstName, Cordas lastName) { este.nome de usuário = nome de usuário; este.idade = idade; este.firstName = firstName; este.lastName = lastName; } público Cordas getUsername() { retorno nome de usuário; } público vazio setUsername(Cordas nome de usuário) { este.nome de usuário = nome de usuário; } público Inteiro getAge() { retorno idade; } público vazio setAge(Inteiro idade) { este.idade = idade; } público Cordas getFirstName() { retorno firstName; } público vazio setFirstName(Cordas firstName) { este.firstName = firstName; } público Cordas getLastName() { retorno lastName; } público vazio setLastName(Cordas lastName) { este.lastName = lastName; } @Substituir público Cordas toString() { retorno "Usuário [nome de usuário=" + nome de usuário + ", age=" + idade + ", 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 |
Usuário usuário = novo Usuário("ldoguin", 31, "Laurent", "Doguin"); EntidadeDocumento<Usuário> documento = EntidadeDocumento.criar(usuário); Repositório repo = CouchbaseCluster.criar().openBucket().repositório(); repo.upsert(documento); |
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 |
público estático vazio principal(Cordas... argumentos) lançamentos Exceção { RatpackServer.iniciar(servidor -> servidor.manipuladores(cadeia -> cadeia.caminho("create" (criar), ctx -> { Bloqueio.obter(() -> { EntidadeDocumento<Usuário> documento = EntidadeDocumento.criar(novo Usuário("ldoguin", 31, "Laurent", "Doguin")); Repositório repo = CouchbaseCluster.criar().openBucket().repositório(); retorno repo.upsert(documento); }).então(entidadeDoc -> ctx.renderizar("OK")); }).caminho("obter", ctx -> { Bloqueio.obter(() -> { Repositório repo = CouchbaseCluster.criar().openBucket().repositório(); EntidadeDocumento<Usuário> ldoguin = repo.obter("ldoguin", Usuário.classe); retorno ldoguin; }).então(usuário -> ctx.renderizar(usuário.conteúdo().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.