Introdução ao Ratpack
1. Visão geral
Ratpack é um conjunto de bibliotecas baseadas emJVM construídas para aplicativos modernos de alto desempenho em tempo real. Ele é construído com base no mecanismo de rede orientado a eventosNetty incorporado e é totalmente compatível com o padrão de design reativo.
Neste artigo, aprenderemos como usar o Ratpack e construiremos um pequeno aplicativo usando-o.
2. Por que Ratpack?
As principais vantagens do Ratpack:
-
é muito leve, rápido e escalável
-
ele consome menos memória do que outras estruturas, como DropWizard; um resultado de comparação de benchmark interessante pode ser encontradohere
-
uma vez que é construído sobreNetty, o Ratpack é totalmente orientado a eventos e não bloqueia por natureza
-
tem suporte para gerenciamento de dependênciaGuice
-
muito parecido comSpringBoot, Ratpack tem suas próprias bibliotecas de teste para configurar rapidamente casos de teste
3. Criação de um aplicativo
Para entender como o Ratpack funciona, vamos começar criando um pequeno aplicativo com ele.
3.1. Dependências do Maven
Primeiro, vamos adicionar as seguintes dependências em nossopom.xml:
io.ratpack
ratpack-core
1.4.5
io.ratpack
ratpack-test
1.4.5
Observe que, embora estejamos usando o Maven como nosso sistema de compilação, de acordo comRatpack recommendation, é melhor usarGradle como uma ferramenta de compilação, pois o Ratpack tem suporte Gradle de primeira classe fornecido por meio doRatpack’s Gradle plugin
Podemos usar o seguinte script Gradle de compilação:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "io.ratpack:ratpack-gradle:1.4.5"
}
}
apply plugin: "io.ratpack.ratpack-java"
repositories {
jcenter()
}
dependencies {
testCompile 'junit:junit:4.11'
runtime "org.slf4j:slf4j-simple:1.7.21"
}
test {
testLogging {
events 'started', 'passed'
}
}
3.2. Construindo o aplicativo
Depois que nosso gerenciamento de compilação está configurado, precisamos criar uma classe para iniciar o servidorNetty incorporado e construir um contexto simples para lidar com as solicitações padrão:
public class Application {
public static void main(String[] args) throws Exception {
RatpackServer.start(server -> server.handlers(chain -> chain
.get(ctx -> ctx.render("Welcome to example ratpack!!!"))));
}
}
Como podemos ver, usandoRatpackServer agora podemos iniciar o servidor (porta padrão 5050). O métodohandlers() assume uma função que recebe um objetoChain, que mapeia todas as respectivas solicitações de entrada. Essa "API da cadeia de manipulador" é usada para criar a estratégia de manipulação de resposta.
Se executarmos este snippet de código e acessarmos o navegador emhttp://localhost:5050, “Bem-vindo ao exemplo ratpack !!!” deve ser exibido.
Da mesma forma, podemos mapear uma solicitação HTTP POST.
3.3. Tratamento de parâmetros de caminho de URL
No próximo exemplo, precisamos capturar alguns parâmetros de caminho da URL em nosso aplicativo. No Ratpack usamosPathTokens para capturá-los:
RatpackServer.start(server -> server
.handlers(chain -> chain
.get(":name", ctx -> ctx.render("Hello "
+ ctx.getPathTokens().get("name") + " !!!"))));
Aqui, estamos mapeando o parâmetro de URLname. Sempre que uma solicitação comohttp://localhost:5050/John vier, a resposta será “Hello John !!!”.
3.4. Request/Response Header Modification With/Without Filter
Às vezes, precisamos modificar o cabeçalho de resposta HTTP embutido com base em nossa necessidade. Ratpack temMutableHeaders para personalizar as respostas de saída.
Por exemplo, precisamos alterar os seguintes cabeçalhos na resposta:Access-Control-Allow-Origin,Accept-Language eAccept-Charset:
RatpackServer.start(server -> server.handlers(chain -> chain.all(ctx -> {
MutableHeaders headers = ctx.getResponse().getHeaders();
headers.set("Access-Control-Allow-Origin", "*");
headers.set("Accept-Language", "en-us");
headers.set("Accept-Charset", "UTF-8");
ctx.next();
}).get(":name", ctx -> ctx
.render("Hello " + ctx.getPathTokens().get("name") + "!!!"))));
UsandoMutableHeaders, configuramos os três cabeçalhos e os colocamos emChain.
Da mesma maneira, também podemos verificar os cabeçalhos de solicitação recebidos:
ctx.getRequest().getHeaders().get("//TODO")
O mesmo pode ser alcançado através da criação de um filtro. O Ratpack tem uma interfaceHandler, que pode ser implementada para criar um filtro. Ele tem apenas um métodohandle(), que leva oContext atual como parâmetro:
public class RequestValidatorFilter implements Handler {
@Override
public void handle(Context ctx) throws Exception {
MutableHeaders headers = ctx.getResponse().getHeaders();
headers.set("Access-Control-Allow-Origin", "*");
ctx.next();
}
}
Podemos usar esse filtro da seguinte maneira:
RatpackServer.start(
server -> server.handlers(chain -> chain
.all(new RequestValidatorFilter())
.get(ctx -> ctx.render("Welcome to example ratpack!!!"))));
}
3.5. Analisador JSON
Ratpack usa internamentefaster-jackson para análise JSON. Podemos usar o móduloJackson para analisar qualquer objeto para JSON.
Vamos criar uma classe POJO simples que será usada para análise:
public class Employee {
private Long id;
private String title;
private String name;
// getters and setters
}
Aqui, criamos uma classe POJO simples chamadaEmployee, que tem três parâmetros:id, title ename. Agora usaremos este objetoEmployee para converter em JSON e retornar o mesmo quando determinado URL for atingido:
List employees = new ArrayList();
employees.add(new Employee(1L, "Mr", "John Doe"));
employees.add(new Employee(2L, "Mr", "White Snow"));
RatpackServer.start(
server -> server.handlers(chain -> chain
.get("data/employees",
ctx -> ctx.render(Jackson.json(employees)))));
Como podemos ver, estamos adicionando manualmente dois objetosEmployee em uma lista e os analisamos como JSON usando o móduloJackson. Assim que o URL/data/employees for atingido, o objeto JSON será retornado.
O ponto a ser observado aqui é quewe are not using ObjectMapper at all, já que o módulo Jackson do Ratpack fará o necessário na hora.
3.6. Banco de dados na memória
O Ratpack possui suporte de primeira classe para bancos de dados em memória. Ele usaHikariCP para o pool de conexão JDBC. Para usá-lo, precisamos adicionarHikariCP module dependency do Ratpack nopom.xml:
io.ratpack
ratpack-hikari
1.4.5
Se estivermos usandoGradle, o mesmo precisa ser adicionado ao arquivo de compilação do Gradle:
compile ratpack.dependency('hikari')
Agora, precisamos criar um arquivo SQL com instruções DDL da tabela para que as tabelas sejam criadas assim que o servidor estiver em funcionamento. Vamos criar o arquivoDDL.sql no diretóriosrc/main/resources e adicionar algumas instruções DDL a ele.
Como estamos usando o banco de dados H2, temos que adicionar dependências para ele também.
Agora, usando o HikariModule, podemos inicializar o banco de dados no tempo de execução:
RatpackServer.start(
server -> server.registry(Guice.registry(bindings ->
bindings.module(HikariModule.class, config -> {
config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
config.addDataSourceProperty("URL",
"jdbc:h2:mem:example;INIT=RUNSCRIPT FROM 'classpath:/DDL.sql'");
}))).handlers(...));
4. Teste
Como mencionado anteriormente, o Ratpack possui suporte de primeira classe para os casos de teste do jUnit. UsandoMainClassApplicationUnderTest, podemos facilmente criar casos de teste e testar os endpoints:
@RunWith(JUnit4.class)
public class ApplicationTest {
MainClassApplicationUnderTest appUnderTest
= new MainClassApplicationUnderTest(Application.class);
@Test
public void givenDefaultUrl_getStaticText() {
assertEquals("Welcome to example ratpack!!!",
appUnderTest.getHttpClient().getText("/"));
}
@Test
public void givenDynamicUrl_getDynamicText() {
assertEquals("Hello dummybot!!!",
appUnderTest.getHttpClient().getText("/dummybot"));
}
@Test
public void givenUrl_getListOfEmployee()
throws JsonProcessingException {
List employees = new ArrayList();
ObjectMapper mapper = new ObjectMapper();
employees.add(new Employee(1L, "Mr", "John Doe"));
employees.add(new Employee(2L, "Mr", "White Snow"));
assertEquals(mapper.writeValueAsString(employees),
appUnderTest.getHttpClient().getText("/data/employees"));
}
@After
public void shutdown() {
appUnderTest.close();
}
}
Observe que precisamos encerrar manualmente a instânciaMainClassApplicationUnderTest em execução chamando o métodoclose(), pois pode bloquear desnecessariamente os recursos JVM. É por isso que usamos a anotação@After para forçar o encerramento da instância assim que o caso de teste for executado.
5. Conclusão
Neste artigo, vimos a simplicidade do uso do Ratpack.
Como sempre, o código-fonte completo está disponívelover on GitHub.