1. Обзор
Ratpack — это набор библиотек на основе JVM
, созданных для современных высокопроизводительных приложений реального времени. Он построен на основе встроенного сетевого механизма Netty
, управляемого событиями, и полностью соответствует шаблону реактивного проектирования.
В этой статье мы узнаем, как использовать Ratpack, и создадим с его помощью небольшое приложение.
2. Почему Ratpack?
Основные преимущества Ratpack:
- это очень легкий, быстрый и масштабируемый
- он потребляет меньше памяти, чем другие фреймворки, такие как DropWizard; интересный результат сравнительного теста можно найти здесь
- поскольку он построен на основе
Netty
, Ratpack полностью управляем событиями и неблокирует по своей природе. - он поддерживает управление зависимостями
Guice
- как и
Spring
Boot
, Ratpack имеет собственные библиотеки тестирования для быстрой настройки тестовых случаев.
3. Создание приложения
Чтобы понять, как работает Ratpack, давайте начнем с создания с его помощью небольшого приложения.
3.1. Зависимости Maven
Во-первых, давайте добавим в наш pom.xml следующие зависимости:
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-core</artifactId>
<version>1.4.5</version>
</dependency>
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-test</artifactId>
<version>1.4.5</version>
</dependency>
Вы можете проверить последнюю версию на Maven Central .
Обратите внимание, что хотя мы используем Maven в качестве нашей системы сборки, в соответствии с рекомендацией Ratpack , лучше использовать Gradle
в качестве инструмента сборки, поскольку Ratpack имеет первоклассную поддержку Gradle, предоставляемую через плагин Ratpack Gradle .
Мы можем использовать следующий скрипт сборки Gradle:
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. Создание приложения
После того, как наше управление сборкой настроено, нам нужно создать класс для запуска встроенного сервера Netty
и создать простой контекст для обработки запросов по умолчанию:
public class Application {
public static void main(String[] args) throws Exception {
RatpackServer.start(server -> server.handlers(chain -> chain
.get(ctx -> ctx.render("Welcome to ForEach ratpack!!!"))));
}
}
Как мы видим, с помощью RatpackServer
мы можем запустить сервер (порт по умолчанию 5050). Метод handlers()
принимает функцию, которая получает объект Chain , который отображает все соответствующие входящие запросы. Этот «Handler Chain API» используется для построения стратегии обработки ответов.
Если мы запустим этот фрагмент кода и нажмем в браузере на http://localhost:5050, «Добро пожаловать в ratpack ForEach!!!» должно отображаться.
Точно так же мы можем сопоставить запрос HTTP POST.
3.3. Обработка параметров пути URL
В следующем примере нам нужно захватить некоторый параметр пути URL в нашем приложении. В Ratpack мы используем PathTokens для их захвата:
RatpackServer.start(server -> server
.handlers(chain -> chain
.get(":name", ctx -> ctx.render("Hello "
+ ctx.getPathTokens().get("name") + " !!!"))));
Здесь мы сопоставляем параметр имени
URL. Всякий раз, когда придет запрос типа http://localhost:5050/John
, ответ будет «Hello John !!!».
3.4. Модификация заголовка запроса/ответа с/без фильтра
Иногда нам нужно изменить встроенный заголовок ответа HTTP в зависимости от наших потребностей. Ratpack имеет MutableHeaders для настройки исходящих ответов.
Например, нам нужно изменить следующие заголовки в ответе: Access-Control-Allow-Origin
, Accept-Language
и Accept-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") + "!!!"))));
Используя MutableHeaders
, мы устанавливаем три заголовка и помещаем их в цепочку
.
Точно так же мы можем проверить и заголовки входящих запросов:
ctx.getRequest().getHeaders().get("//TODO")
То же самое можно сделать, создав фильтр. Ratpack имеет интерфейс Handler
,
который можно реализовать для создания фильтра. Он имеет только один метод handle(),
который принимает текущий контекст
в качестве параметра:
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();
}
}
Мы можем использовать этот фильтр следующим образом:
RatpackServer.start(
server -> server.handlers(chain -> chain
.all(new RequestValidatorFilter())
.get(ctx -> ctx.render("Welcome to foreach ratpack!!!"))));
}
3.5. Парсер JSON
Ratpack внутри использует более быстрый джексон
для парсинга JSON. Мы можем использовать модуль Джексона для анализа любого объекта в JSON.
Давайте создадим простой класс POJO, который будет использоваться для парсинга:
public class Employee {
private Long id;
private String title;
private String name;
// getters and setters
}
Здесь мы создали один простой класс POJO с именем Employee
, который имеет три параметра: id, title
и name
. Теперь мы будем использовать этот объект Employee
для преобразования в JSON и возвращать то же самое при переходе по определенному URL-адресу:
List<Employee> employees = new ArrayList<Employee>();
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)))));
Как мы видим, мы вручную добавляем два объекта Employee
в список и анализируем их как JSON с помощью модуля Джексона .
Как только будет достигнут URL-адрес /data/employees
, будет возвращен объект JSON.
Здесь следует отметить, что мы вообще не используем ObjectMapper ,
поскольку модуль Ratpack Jackson сделает все необходимое на лету.
3.6. База данных в памяти
Ratpack имеет первоклассную поддержку баз данных в памяти. Он использует HikariCP для пула соединений JDBC. Чтобы использовать его, нам нужно добавить зависимость модуля Ratpack HikariCP в pom.xml
:
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-hikari</artifactId>
<version>1.4.5</version>
</dependency>
Если мы используем Gradle
, то же самое нужно добавить в файл сборки Gradle:
compile ratpack.dependency('hikari')
Теперь нам нужно создать файл SQL с операторами таблицы DDL, чтобы таблицы создавались сразу после запуска сервера. Мы создадим файл DDL.sql в каталоге
src/main/resources
и добавим в него несколько операторов DDL.
Поскольку мы используем базу данных H2, мы также должны добавить зависимости для нее.
Теперь, используя HikariModule, мы можем инициализировать базу данных во время выполнения:
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:foreach;INIT=RUNSCRIPT FROM 'classpath:/DDL.sql'");
}))).handlers(...));
4. Тестирование
Как упоминалось ранее, Ratpack имеет первоклассную поддержку тестовых случаев jUnit. Используя MainClassApplicationUnderTest , мы можем легко создавать тестовые случаи и тестировать конечные точки:
@RunWith(JUnit4.class)
public class ApplicationTest {
MainClassApplicationUnderTest appUnderTest
= new MainClassApplicationUnderTest(Application.class);
@Test
public void givenDefaultUrl_getStaticText() {
assertEquals("Welcome to foreach ratpack!!!",
appUnderTest.getHttpClient().getText("/"));
}
@Test
public void givenDynamicUrl_getDynamicText() {
assertEquals("Hello dummybot!!!",
appUnderTest.getHttpClient().getText("/dummybot"));
}
@Test
public void givenUrl_getListOfEmployee()
throws JsonProcessingException {
List<Employee> employees = new ArrayList<Employee>();
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();
}
}
Обратите внимание, что нам нужно вручную завершить работающий экземпляр MainClassApplicationUnderTest
, вызвав метод close()
, так как это может излишне заблокировать ресурсы JVM. Вот почему мы использовали аннотацию @After
для принудительного завершения экземпляра после выполнения тестового примера.
5. Вывод
В этой статье мы увидели простоту использования Ratpack.
Как всегда, полный исходный код доступен на GitHub .