Groovyを搭載したRatpack

  • link:/category/programming/ [プログラミング]

  • Groovy

1. 概要

link:/ratpack[Ratpack]は、リアクティブ、非同期、およびノンブロッキング機能を備えた*スケーラブルなHTTPアプリケーションを構築するための軽量Javaライブラリのセットです。
さらに、Ratpackはlink:/ratpack-google-guice[Google Guice、] link:/ratpack-spring-boot[Spring Boot]などのテクノロジーやフレームワークとの統合も提供します、https://www.baeldung.com/ratpack-rxjava [RxJava]およびlink:/ratpack-hystrix[Hystrix]。
このチュートリアルでは、GroovyでRatpackを使用する方法について説明します。

2. なぜGroovyなのか?

link:/groovy-language[Groovy]は、JVMで実行される強力で動的な言語です。
したがって、Groovyはスクリプト作成とドメイン固有言語を非常に簡単にします。 Ratpackを使用すると、これにより迅速なWebアプリケーション開発が可能になります。
  • Ratpackは、ratpack-groovy and _ratpack-groovy-test_ライブラリを介してGroovyと簡単に統合できます。*

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

https://github.com/ratpack/ratpack/tree/master/ratpack-groovy[Ratpack Groovy API]はJavaで構築されているため、JavaおよびGroovyアプリケーションの両方と簡単に統合できます。 それらは__ratpack.groovy __packageで利用可能です。
実際、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リクエストを処理します。 必要なのは、基本的な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でビルドするために必要なのは、https://mvnrepository.com/artifact/io.ratpack/ratpack-groovy [the _ratpack-groovy_ library]の依存関係を追加することだけです。
<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") + " !!!"
    }
}
_http:// localhost:5050 / greet / Norman [http:// localhost:5050 / greet / <name>] _を介してこのWeb要求をテストできます。
$ curl -s localhost:5050/greet/Norman
Hello Norman!!!
ハンドラーのコードでは、_ctx_は_Context_レジストリオブジェクトであり、パス変数、要求、応答オブジェクトへのアクセスを許可します。
ハンドラーは、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はアプリケーションの非同期および非ブロッキング機能を有効にします。 これはlink:/ratpack-http-client[Ratpack Promises]で実装されています。
PromiseはJavaScriptで使用されるものに似ており、Java link:/java-future[_Future_]に少し似ています。 _Promise_は、将来利用できる値の表現と考えることができます。
post("user") {
    Promise<User> user = parse(Jackson.fromJson(User))
    user.then { u -> render u.name }
}
ここでの最後のアクションは_then_アクションであり、最終的な値をどうするかを決定します。 この場合、POSTへの応答として返します。
このコードをさらに詳しく理解しましょう。 ここで、_Jackson.fromJson_は、_ObjectMapper_ _User_を使用して、リクエスト本文のJSONを解析します。 次に、組み込みの_Context _._ parse_メソッドが_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ライブラリは非常に豊富であり、_map_や_flatMap_などの関数を使用してアクションをチェーン化できることに注意してください。

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

非同期ハンドラーを持つことは、ハンドラーがサービスを待機する必要がある場合に最も有益です。 RatpackアプリケーションをH2データベースと統合して、これを実証しましょう。
link:/hikaricp[HikariCP] JDBC接続プールの拡張であるRatpackの_HikariModule_クラス、またはGroovy http://docs.groovy-lang.org/latest/html/apiを使用できます。 /groovy/sql/Sql.html[S__ql__]データベース統合用。

7.1. HikariModule

HikariCPサポートを追加するには、まず次のhttps://search.maven.org/search?q=g:io.ratpack%20AND%20a:ratpack-hikariを追加しましょう
<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_を使用すると、_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"}]
_Sql_を使用してHTTP POSTの例を作成しましょう。
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はhttps://search.maven.org/search?q=g:io.ratpack%20AND%20a: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')
次に、_Ratpack.groovy_スクリプトをテストできるように、Groovyメインクラス_RatpackGroovyApp.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 Testクラス_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!!"
}
_fetchUsers_ getハンドラーの応答を確認する別のテストを作成してみましょう。
@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ハンドラーを作成するいくつかの方法を見てきました。 また、Promiseとデータベースの統合についても調査しました。
Groovyのクロージャー、DSL、Groovyの_Sql_がどのようにコードを簡潔で効率的かつ読みやすくするかを見てきました。 同時に、Groovyのテストサポートにより、単体テストと統合テストが簡単になります。
これらの手法により、Groovyの動的言語機能と表現力豊かなAPIを使用して、Ratpackで高性能でスケーラブルなHTTPアプリケーションを迅速に開発できます。
いつものように、サンプルコードはhttps://github.com/eugenp/tutorials/tree/master/ratpack[over on GitHub]にあります。