1. 概要

Ratpack は、リアクティブ、非同期、および非ブロッキング機能を備えたスケーラブルなHTTPアプリケーションを構築するための軽量Javaライブラリのセットです。

さらに、Ratpackは、 Google Guice、 Spring Boot RxJava Hystrixなどのテクノロジーやフレームワークとの統合も提供します。

このチュートリアルでは、GroovyでRatpackを使用する方法について説明します。

2. なぜGroovy?

Groovy は、JVMで実行される強力で動的な言語です。

したがって、Groovyを使用すると、スクリプトとドメイン固有言語が非常に簡単になります。 Ratpackを使用すると、これによりWebアプリケーションの迅速な開発が可能になります。

Ratpackは、ratpack-groovyおよびratpack -groovy-testライブラリを介してGroovyと簡単に統合できます。

3. Groovyスクリプトを使用したRatpackアプリケーション

Ratpack Groovy API はJavaで構築されているため、JavaアプリケーションとGroovyアプリケーションの両方と簡単に統合できます。 これらは、ratpack.groovyパッケージで入手できます。

実際、Groovyのスクリプト機能とGrapeの依存関係管理を組み合わせることで、Ratpackを利用したWebアプリケーションをわずか数行ですばやく作成できます。

@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リクエストを処理します。 私たちがしなければならなかったのは、Ratpackサーバーを有効にするためにいくつかの基本的なDSLを追加することでした。

これを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. Gradle

build.gradle にGroovy用のRatpackのGradleプラグインを追加することで、ratpack-groovy統合を有効にできます。

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

5. GroovyのRatpackハンドラー

ハンドラーは、Web要求と応答を処理する方法を提供します。 このクロージャーでは、要求オブジェクトと応答オブジェクトにアクセスできます。

GETやPOSTなどのHTTPメソッドを使用してWebリクエストを処理できます

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

このWebリクエストは次の方法でテストできます http:// localhost:5050 / greet /

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

ハンドラーのコードでは、ctxContextレジストリオブジェクトであり、パス変数、要求および応答オブジェクトへのアクセスを許可します。

ハンドラーは、Jacksonを介したJSONの処理もサポートしています。

Groovyマップから変換されたJSONを返しましょう。

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. GroovyでのRatpackの約束

ご存知のように、Ratpackはアプリケーションで非同期機能と非ブロッキング機能を有効にします。 これは、 RatpackPromisesで実装されています。

PromiseはJavaScriptで使用されるものと似ており、Java Futureに少し似ています。 Promise は、将来利用できる値の表現と考えることができます。

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

ここでの最後のアクションはthenアクションであり、最終値をどう処理するかを決定します。 この場合、POSTへの応答として返します。

このコードをもっと詳しく理解しましょう。 ここで、 Jackson.fromJson は、 ObjectMapper Userを使用してリクエスト本文のJSONを解析します。 次に、組み込みのContextparseメソッドは、それをPromiseオブジェクトにバインドします。

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ライブラリは非常に豊富で、mapflatMapなどの関数を使用してアクションをチェーンできることに注意してください。

7. データベースとの統合

ハンドラーがサービスを待機する必要がある場合は、非同期ハンドラーを使用すると最も効果的です。 RatpackアプリケーションをH2データベースと統合して、これを実証しましょう。

HikariCPJDBC接続プールの拡張であるRatpackのHikariModuleクラス、またはデータベース統合用のGroovy Sqlのいずれかを使用できます。

7.1. HikariModule

HikariCPサポートを追加するには、最初に次のHikariおよびH2Maven依存関係を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"
}

ここで、接続プールのbindingsクロージャーの下で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 Class

Groovy Sql を使用すると、rowsexecuteInsertなどのメソッドを使用して、データベースをすばやく操作できます。

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

Sqlを使用してHTTPPOSTの例を書いてみましょう。

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アプリケーションをテストするためのratpack-groovy-testライブラリも提供します。

これを使用するには、pom.xmlにMaven依存関係として追加できます。

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

または、build.gradleに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/baeldung/Ratpack.groovy");
        def shell = new GroovyShell()  
        shell.evaluate(file)
    }
}

GroovyテストをJUnitテストとして実行すると、クラスはGroovyShellを使用してRatpack.groovyスクリプトを呼び出します。次に、テストのためにRatpackサーバーを起動します。

それでは、Groovyテストクラス RatpackGroovySpec.groovy と、RatpackGroovyAppを介してRatpackサーバーを起動するコードを記述しましょう。

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

次に、 fetchUsersgetハンドラーの応答を検証する別のテストを作成しましょう。

@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. 結論

この記事では、Groovyを使用してRatpackのHTTPハンドラーを作成するいくつかの方法を見てきました。 また、Promisesとデータベースの統合についても検討しました。

Groovyクロージャ、DSL、およびGroovyの Sql によって、コードが簡潔で効率的で読みやすくなることを確認しました。 同時に、Groovyのテストサポートにより、ユニットテストと統合テストが簡単になります。

これらの手法を使用すると、Groovyの動的言語機能と表現力豊かなAPIを使用して、Ratpackを使用して高性能でスケーラブルなHTTPアプリケーションを迅速に開発できます。

いつものように、サンプルコードはGitHubにあります。