Ratpack com Groovy
1. Visão geral
Ratpack é um conjunto de bibliotecas Java leves parabuilding scalable HTTP applications com recursos reativos, assíncronos e sem bloqueio.
Além disso, o Ratpack também fornece integração com tecnologias e estruturas comoGoogle Guice,Spring Boot,RxJavaeHystrix.
Neste tutorial, vamos explorarhow to use Ratpack with Groovy.
2. Por que Groovy?
Groovy é uma linguagem poderosa e dinâmica que é executada na JVM.
Portanto, o Groovy facilita muito os scripts e os idiomas específicos do domínio. Com o Ratpack, isso proporciona um rápido desenvolvimento de aplicativos da web.
Ratpack fornece integração fácil com Groovy por meio das bibliotecasratpack-groovyeratpack-groovy-test.
3. Aplicativo Ratpack usando Groovy Script
OsRatpack Groovy APIs são construídos em Java para que possam se integrar facilmente com aplicativos Java e Groovy. Eles estão disponíveis naratpack.groovy package.
Na verdade, em combinação com as habilidades de script do Groovy e gerenciamento de dependência Grape, podemos criar rapidamente um aplicativo da web baseado em Ratpack em apenas algumas linhas:
@Grab('io.ratpack:ratpack-groovy:1.6.1')
import static ratpack.groovy.Groovy.ratpack
ratpack {
handlers {
get {
render 'Hello World from Ratpack with Groovy!!'
}
}
}
This is our first handler, tratando de uma solicitação GET. Tudo o que precisamos fazer foi adicionar um DSL básico para ativar o servidor Ratpack.
Vamos agora executar isso como um script Groovy para iniciar o aplicativo. Por padrão, o aplicativo estará disponível emhttp://localhost:5050:
$ curl -s localhost:5050
Hello World from Ratpack with Groovy!!
Também podemos configurar a porta usandoServerConfig:
ratpack {
serverConfig {
port(5056)
}
}
Ratpack also provides a hot reloading feature, o que significa que podemos alterarRatpack.groovy e, em seguida, ver as alterações no momento em que o aplicativo atende a nossa próxima solicitação HTTP.
4. Gerenciamento de Dependências Ratpack-Groovy
Existem várias maneiras de habilitar o suporteratpack-groovy.
4.1. Uva
Podemos usar o Grape do gerenciador de dependência incorporado do Groovy.
É tão simples quanto adicionar uma anotação ao nosso scriptRatpack.groovy:
@Grab('io.ratpack:ratpack-groovy:1.6.1')
import static ratpack.groovy.Groovy.ratpack
4.2. Dependência do Maven
Para construir no Maven, tudo o que precisamos é adicionar a dependência parathe ratpack-groovy library:
io.ratpack
ratpack-groovy
${ratpack.version}
4.3. Gradle
Podemos habilitar a integração deratpack-groovy, adicionando o plugin Gradle do Ratpack para Groovy embuild.gradle:
plugins {
id 'io.ratpack.ratpack-groovy' version '1.6.1'
}
5. Manipuladores Ratpack em Groovy
Handlers provide a way to handle web requests and responses. Os objetos de solicitação e resposta podem ser acessados neste fechamento.
Podemos lidar com solicitações da web usando métodos HTTP como GET e POST:
handlers {
get("greet/:name") { ctx ->
render "Hello " + ctx.getPathTokens().get("name") + " !!!"
}
}
Podemos testar essa solicitação da web por meio dehttp://localhost:5050/greet/<name>:
$ curl -s localhost:5050/greet/Norman
Hello Norman!!!
No código do manipulador,ctx é o objeto de registroContext que concede acesso a variáveis de caminho, objetos de solicitação e resposta.
Manipuladores também têm suporte para lidar com JSON através de Jackson.
Vamos retornar JSON, convertido de um mapa Groovy:
get("data") {
render Jackson.json([title: "Mr", name: "Norman", country: "USA"])
}
$ curl -s localhost:5050/data
{"title":"Mr","name":"Norman","country":"USA"}
Aqui,Jackson.json é usado para fazer a conversão.
6. Ratpack promete em Groovy
Como sabemos, o Ratpack habilita os recursos assíncronos e sem bloqueio do aplicativo. Isso é implementado comRatpack Promises.
As promessas são semelhantes às usadas em JavaScript e são um pouco como JavaFuture. Podemos pensar emPromise como a representação de um valor que estará disponível no futuro:
post("user") {
Promise user = parse(Jackson.fromJson(User))
user.then { u -> render u.name }
}
A última ação aqui é a açãothen, que determina o que fazer com o valor final. Nesse caso, retornamos como resposta ao POST.
Vamos entender esse código com mais detalhes. Aqui,Jackson.fromJson analisa o JSON do corpo da solicitação usandoObjectMapperUser. Em seguida, o métodoContext.parse embutido o vincula ao objetoPromise.
A promessa opera de forma assíncrona. Quando a operaçãothen é finalmente realizada, a resposta é retornada:
curl -X POST -H 'Content-type: application/json' --data \
'{"id":3,"title":"Mrs","name":"Jiney Weiber","country":"UK"}' \
http://localhost:5050/employee
Jiney Weiber
Devemos notar que a biblioteca Promise é bastante rica, permitindo-nos encadear ações usando funções comomapeflatMap.
7. Integração com um banco de dados
Ter manipuladores assíncronos é mais benéfico quando nossos manipuladores precisam esperar pelos serviços. Vamos demonstrar isso integrando nosso aplicativo Ratpack com um banco de dados H2.
Podemos usar a classeHikariModule do Ratpack, que é uma extensão do pool de conexão JDBCHikariCP, ou GroovySql para integração de banco de dados.
7.1. HikariModule
Para adicionar suporte a HikariCP, vamos primeiro adicionar as seguintes dependências de mavenHikari eH2 em nossopom.xml:
io.ratpack
ratpack-hikari
${ratpack.version}
com.h2database
h2
${h2.version}
Ou podemos adicionar as seguintes dependências ao nossobuild.gradle:
dependencies {
compile ratpack.dependency('hikari')
compile "com.h2database:h2:$h2.version"
}
Agora, declararemosHikariModule sob o fechamentobindings para o pool de conexão:
import ratpack.hikari.HikariModule
ratpack {
bindings {
module(HikariModule) { config ->
config.dataSourceClassName = 'org.h2.jdbcx.JdbcDataSource'
config.addDataSourceProperty('URL',
"jdbc:h2:mem:devDB;INIT=RUNSCRIPT FROM 'classpath:/User.sql'")
}
}
}
Finalmente, estamos prontos para usá-lo para operações simples de banco de dados usandoConnection ePreparedStatement Java:
get('fetchUserName/:id') { Context ctx ->
Connection connection = ctx.get(DataSource.class).getConnection()
PreparedStatement queryStatement =
connection.prepareStatement("SELECT NAME FROM USER WHERE ID=?")
queryStatement.setInt(1, Integer.parseInt(ctx.getPathTokens().get("id")))
ResultSet resultSet = queryStatement.executeQuery()
resultSet.next()
render resultSet.getString(1)
}
Vamos verificar se o manipulador funciona conforme o esperado:
$ curl -s localhost:5050/fetchUserName/1
Norman Potter
7.2. GroovySql Class __
Podemos usar GroovySql para operações rápidas de banco de dados, por meio de métodos comorowseexecuteInsert:
get('fetchUsers') {
def db = [url:'jdbc:h2:mem:devDB']
def sql = Sql.newInstance(db.url, db.user, db.password)
def users = sql.rows("SELECT * FROM USER");
render(Jackson.json(users))
}
$ curl -s localhost:5050/fetchUsers
[{"ID":1,"TITLE":"Mr","NAME":"Norman Potter","COUNTRY":"USA"},
{"ID":2,"TITLE":"Miss","NAME":"Ketty Smith","COUNTRY":"FRANCE"}]
Vamos escrever um exemplo HTTP POST comSql:
post('addUser') {
parse(Jackson.fromJson(User))
.then { u ->
def db = [url:'jdbc:h2:mem:devDB']
Sql sql = Sql.newInstance(db.url, db.user, db.password)
sql.executeInsert("INSERT INTO USER VALUES (?,?,?,?)",
[u.id, u.title, u.name, u.country])
render "User $u.name inserted"
}
}
$ curl -X POST -H 'Content-type: application/json' --data \
'{"id":3,"title":"Mrs","name":"Jiney Weiber","country":"UK"}' \
http://localhost:5050/addUser
User Jiney Weiber inserted
8. Teste de Unidade
8.1. Configurando os testes
Conforme discutido, Ratpack também fornecethe ratpack-groovy-test library paratesting a ratpack-groovy application.
Para usá-lo, podemos adicioná-lo como dependência Maven em nossopom.xml:
io.ratpack
ratpack-groovy-test
1.6.1
Como alternativa, podemos adicionar a dependência Gradle em nossobuild.gradle:
testCompile ratpack.dependency('groovy-test')
Em seguida, precisamos criar uma classe principal do GroovyRatpackGroovyApp.groovy para nos permitir testar o scriptRatpack.groovy.
public class RatpackGroovyApp {
public static void main(String[] args) {
File file = new File("src/main/groovy/com/example/Ratpack.groovy");
def shell = new GroovyShell()
shell.evaluate(file)
}
}
Ao executar testes Groovy como testes JUnit,the class will invoke the Ratpack.groovy script using GroovyShell. Por sua vez, ele iniciará o servidor Ratpack para teste.
Agora, vamos escrever nossa classe de teste GroovyRatpackGroovySpec.groovy junto com o código para iniciar o servidor Ratpack por meio deRatpackGroovyApp:
class RatpackGroovySpec {
ServerBackedApplicationUnderTest ratpackGroovyApp =
new MainClassApplicationUnderTest(RatpackGroovyApp.class)
@Delegate TestHttpClient client =
TestHttpClient.testHttpClient(ratpackGroovyApp)
}
Ratpack forneceMainClassApplicationUnderTest para simular a classe de aplicativo para iniciar o servidor.
8.2. Escrevendo nossos testes
Vamos escrever nossos testes, começando com um teste muito básico para verificar se o aplicativo pode iniciar:
@Test
void "test if app is started"() {
when:
get("")
then:
assert response.statusCode == 200
assert response.body.text ==
"Hello World from Ratpack with Groovy!!"
}
Vamos agora escrever outro teste para verificar a resposta dofetchUsers get handler:
@Test
void "test fetchUsers"() {
when:
get("fetchUsers")
then:
assert response.statusCode == 200
assert response.body.text ==
'[{"ID":1,"TITLE":"Mr","NAME":"Norman Potter","COUNTRY":"USA"},{"ID":2,"TITLE":"Miss","NAME":"Ketty Smith","COUNTRY":"FRANCE"}]'
}
A estrutura de teste Ratpack cuida de iniciar e parar o servidor para nós.
9. Conclusão
Neste artigo, vimos algumas maneiras de escrever manipuladores HTTP para o Ratpack usando o Groovy. Também exploramos a integração de promessas e bancos de dados.
Vimos como os fechamentos do Groovy, DSLs eSql do Groovy tornam nosso código conciso, eficiente e legível. Ao mesmo tempo, o suporte de teste do Groovy torna os testes de unidade e integração simples.
Com essas técnicas, podemos usar os recursos de linguagem dinâmica do Groovy e APIs expressivas para desenvolver rapidamente aplicativos HTTP escalonáveis e de alto desempenho com o Ratpack.
Como de costume, o código de exemplo pode ser encontradoover on GitHub.