Ratpack com Groovy

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.