Перейти к основному содержимому

Ratpack с Groovy

· 7 мин. чтения

1. Обзор

Ratpack — это набор облегченных библиотек Java для создания масштабируемых HTTP-приложений с реактивными, асинхронными и неблокирующими функциями.

Кроме того, Ratpack также обеспечивает интеграцию с такими технологиями и фреймворками, как Google Guice, Spring Boot , RxJava и Hystrix .

В этом руководстве мы рассмотрим, как использовать Ratpack с Groovy .

2. Почему Groovy?

Groovy — это мощный динамический язык, работающий в JVM.

Таким образом, Groovy упрощает создание сценариев и предметно-ориентированных языков. С Ratpack это обеспечивает быструю разработку веб-приложений.

Ratpack обеспечивает простую интеграцию с Groovy через библиотеки ratpack-groovy и ratpack -groovy-test .

3. Приложение Ratpack с использованием скрипта Groovy

API -интерфейсы Ratpack Groovy созданы на языке Java, поэтому их можно легко интегрировать как с приложениями Java, так и с приложениями Groovy. Они доступны в пакете ratpack.groovy .

На самом деле, в сочетании с возможностями сценариев Groovy и управлением зависимостями Grape мы можем быстро создать веб-приложение на основе Ratpack всего за несколько строк:

@Grab('io.ratpack:ratpack-groovy:1.6.1')
import static ratpack.groovy.Groovy.ratpack

ratpack {
handlers {
get {
render 'Hello World from Ratpack with Groovy!!'
}
}
}

Это наш первый обработчик, обрабатывающий запрос GET. Все, что нам нужно было сделать, это добавить базовый DSL, чтобы включить сервер Ratpack.

Давайте теперь запустим это как сценарий Groovy, чтобы запустить приложение. По умолчанию приложение будет доступно по адресу http://localhost:5050 :

$ curl -s localhost:5050
Hello World from Ratpack with Groovy!!

Мы также можем настроить порт с помощью ServerConfig :

ratpack {
serverConfig {
port(5056)
}
}

Ratpack также предоставляет функцию горячей перезагрузки , что означает, что мы можем изменить Ratpack.groovy , а затем увидеть изменения в тот момент, когда приложение обработает наш следующий HTTP-запрос.

4. Управление зависимостями Ratpack-Groovy

Есть несколько способов включить поддержку ratpack-groovy .

4.1. Виноград

Мы можем использовать встроенный диспетчер зависимостей Groovy Grape.

Это так же просто, как добавить аннотацию к нашему скрипту Ratpack.groovy :

@Grab('io.ratpack:ratpack-groovy:1.6.1')
import static ratpack.groovy.Groovy.ratpack

4.2. Зависимость от Maven

Для сборки в Maven нам нужно всего лишь добавить зависимость для библиотеки ratpack- groovy :

<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-groovy</artifactId>
<version>${ratpack.version}</version>
</dependency>

4.3. Грейдл

Мы можем включить интеграцию ratpack-groovy , добавив плагин Ratpack Gradle для Groovy в build.gradle :

plugins { 
id 'io.ratpack.ratpack-groovy' version '1.6.1'
}

5. Обработчики Ratpack в Groovy

Обработчики обеспечивают способ обработки веб-запросов и ответов . В этом закрытии можно получить доступ к объектам запроса и ответа.

Мы можем обрабатывать веб-запросы, используя методы HTTP, такие как GET и POST :

handlers { 
get("greet/:name") { ctx ->
render "Hello " + ctx.getPathTokens().get("name") + " !!!"
}
}

Мы можем протестировать этот веб-запрос через http://localhost:5050/greet/<name> :

$ curl -s localhost:5050/greet/Norman
Hello Norman!!!

В коде обработчика ctx — это объект реестра Context , который предоставляет доступ к переменным пути, объектам запросов и ответов.

Обработчики также поддерживают работу с JSON через Jackson.

Вернем JSON, преобразованный из карты Groovy:

get("data") {
render Jackson.json([title: "Mr", name: "Norman", country: "USA"])
}
$ curl -s localhost:5050/data
{"title":"Mr","name":"Norman","country":"USA"}

Здесь для преобразования используется Jackson.json .

6. Ratpack Promises в Groovy

Как мы знаем, Ratpack включает асинхронные и неблокирующие функции в приложении. Это реализовано с помощью Ratpack Promises .

Промисы аналогичны тем, что используются в JavaScript, и чем-то напоминают Future в Java . Мы можем думать о Promise как о представлении значения, которое будет доступно в будущем:

post("user") {
Promise<User> user = parse(Jackson.fromJson(User))
user.then { u -> render u.name }
}

Последним действием здесь является действие then , которое определяет, что делать с окончательным значением. В этом случае мы возвращаем его как ответ на POST.

Давайте разберемся с этим кодом подробнее. Здесь Jackson.fromJson анализирует JSON тела запроса с помощью ObjectMapper User . Затем встроенный Context . parse привязывает его к объекту Promise .

Обещание работает асинхронно. Когда операция then выполняется, возвращается ответ:

curl -X POST -H 'Content-type: application/json' --data \
'{"id":3,"title":"Mrs","name":"Jiney Weiber","country":"UK"}' \
http://localhost:5050/employee

Jiney Weiber

Следует отметить, что библиотека Promise довольно богата, что позволяет нам связывать действия с помощью таких функций, как map и flatMap .

7. Интеграция с базой данных

Наличие асинхронных обработчиков наиболее полезно, когда нашим обработчикам приходится ждать обслуживания. Давайте продемонстрируем это, интегрировав наше приложение Ratpack с базой данных H2.

Мы можем либо использовать класс HikariModule Ratpack , который является расширением пула соединений HikariCP JDBC, либо Groovy S ql для интеграции с базой данных.

7.1. ХикариМодуль

Чтобы добавить поддержку HikariCP, давайте сначала добавим следующие зависимости Hikari и H2 maven в наш pom.xml :

<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-hikari</artifactId>
<version>${ratpack.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
</dependency>

Или мы можем добавить следующие зависимости в наш build.gradle :

dependencies {
compile ratpack.dependency('hikari')
compile "com.h2database:h2:$h2.version"
}

Теперь мы объявим HikariModule под закрытием привязки для пула соединений:

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'")
}
}
}

Наконец, мы готовы использовать его для простых операций с базой данных с помощью Java Connection и PreparedStatement :

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)
}

Давайте проверим, что обработчик работает как положено:

$ curl -s localhost:5050/fetchUserName/1
Norman Potter

7.2. Класс Groovy Sql ``

Мы можем использовать Groovy Sql для быстрых операций с базой данных с помощью таких методов, как rows и executeInsert :

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"}]

Давайте напишем пример HTTP POST с Sql :

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. Модульное тестирование

8.1. Настройка тестов

Как уже говорилось, Ratpack также предоставляет библиотеку ratpack -groovy -test для тестирования приложения ratpack-groovy .

Чтобы использовать его, мы можем добавить его как зависимость Maven в наш pom.xml :

<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-groovy-test</artifactId>
<version>1.6.1</version>
</dependency>

В качестве альтернативы мы можем добавить зависимость Gradle в наш build.gradle :

testCompile ratpack.dependency('groovy-test')

Затем нам нужно создать основной класс Groovy RatpackGroovyApp.groovy , чтобы мы могли протестировать сценарий Ratpack.groovy .

public class RatpackGroovyApp {
public static void main(String[] args) {
File file = new File("src/main/groovy/com/foreach/Ratpack.groovy");
def shell = new GroovyShell()
shell.evaluate(file)
}
}

При запуске тестов Groovy в качестве тестов JUnit класс будет вызывать сценарий Ratpack.groovy с помощью GroovyShell . В свою очередь, он запустит сервер Ratpack для тестирования.

Теперь давайте напишем наш класс Groovy Test RatpackGroovySpec.groovy вместе с кодом для запуска сервера Ratpack через RatpackGroovyApp :

class RatpackGroovySpec {
ServerBackedApplicationUnderTest ratpackGroovyApp =
new MainClassApplicationUnderTest(RatpackGroovyApp.class)
@Delegate TestHttpClient client =
TestHttpClient.testHttpClient(ratpackGroovyApp)
}

Ratpack предоставляет MainClassApplicationUnderTest для имитации класса приложения для запуска сервера.

8.2. Написание наших тестов

Давайте напишем наши тесты, начиная с очень простого теста, чтобы проверить, может ли приложение запускаться:

@Test
void "test if app is started"() {
when:
get("")

then:
assert response.statusCode == 200
assert response.body.text ==
"Hello World from Ratpack with Groovy!!"
}

Давайте теперь напишем еще один тест для проверки ответа обработчика получения fetchUsers :

@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"}]'
}

Платформа тестирования Ratpack позаботится о запуске и остановке сервера.

9. Заключение

В этой статье мы рассмотрели несколько способов написания обработчиков HTTP для Ratpack с помощью Groovy. Мы также изучили интеграцию Promises и базы данных.

Мы видели, как замыкания Groovy, DSL и Sql Groovy делают наш код кратким, эффективным и читабельным. В то же время поддержка тестов Groovy упрощает модульное и интеграционное тестирование.

С помощью этих методов мы можем использовать динамические языковые функции Groovy и выразительные API для быстрой разработки высокопроизводительных масштабируемых HTTP-приложений с помощью Ratpack.

Как обычно, код примера можно найти на GitHub .