Ratpack dengan Groovy

1. Ikhtisar

Ratpack adalah sekumpulan pustaka Java ringan untuk membangun aplikasi HTTP yang dapat diskalakan dengan fitur reaktif, asinkron, dan non-pemblokiran.

Selain itu, Ratpack juga menyediakan integrasi dengan teknologi dan kerangka kerja seperti Google Guice, Spring Boot, RxJava, dan Hystrix.

Dalam tutorial ini, kita akan menjelajahi cara menggunakan Ratpack dengan Groovy .

2. Mengapa Groovy?

Groovy adalah bahasa yang kuat dan dinamis yang berjalan di JVM.

Oleh karena itu, Groovy membuat scripting dan Domain Specific Languages ​​menjadi sangat mudah. Dengan Ratpack, ini menyediakan pengembangan aplikasi web yang cepat.

Ratpack menyediakan integrasi yang mudah dengan Groovy melalui perpustakaan ratpack-groovy dan ratpack -groovy-test .

3. Aplikasi Ratpack Menggunakan Groovy Script

Ratpack Groovy API dibuat di Java sehingga dapat dengan mudah diintegrasikan dengan aplikasi Java dan Groovy. Mereka tersedia dalam paket ratpack.groovy .

Sebenarnya, dalam kombinasi dengan kemampuan skrip Groovy dan manajemen ketergantungan Grape, kita dapat dengan cepat membuat aplikasi web yang didukung Ratpack hanya dalam beberapa baris:

@Grab('io.ratpack:ratpack-groovy:1.6.1') import static ratpack.groovy.Groovy.ratpack ratpack { handlers { get { render 'Hello World from Ratpack with Groovy!!' } } }

Ini adalah penangan pertama kami, menangani permintaan GET. Yang harus kami lakukan adalah menambahkan beberapa DSL dasar untuk mengaktifkan server Ratpack.

Sekarang mari kita jalankan ini sebagai skrip Groovy untuk memulai aplikasi. Secara default, aplikasi akan tersedia di // localhost: 5050 :

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

Kami juga dapat mengkonfigurasi port menggunakan ServerConfig :

ratpack { serverConfig { port(5056) } }

Ratpack juga menyediakan fitur hot reload , yang berarti kita dapat mengubah Ratpack.groovy , dan kemudian melihat perubahannya saat aplikasi melayani permintaan HTTP kita berikutnya.

4. Manajemen Ketergantungan Ratpack-Groovy

Ada beberapa cara untuk mengaktifkan dukungan ratpack-groovy .

4.1. Anggur

Kita dapat menggunakan Grape manajer ketergantungan tertanam Groovy.

Ini sesederhana menambahkan anotasi ke skrip Ratpack.groovy kami :

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

4.2. Ketergantungan Maven

Untuk membangun di Maven, yang kita butuhkan hanyalah menambahkan dependensi untuk library ratpack-groovy :

 io.ratpack ratpack-groovy ${ratpack.version}  

4.3. Gradle

Kita dapat mengaktifkan integrasi ratpack-groovy , dengan menambahkan plugin Gradle Ratpack untuk Groovy di build.gradle :

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

5. Penangan Ratpack di Groovy

Penangan menyediakan cara untuk menangani permintaan dan tanggapan web . Objek permintaan dan respons dapat diakses di closure ini.

Kami dapat menangani permintaan web menggunakan metode HTTP seperti GET dan POST :

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

Kami dapat menguji permintaan web ini melalui // localhost: 5050 / greet / :

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

Dalam kode penangan, ctx adalah objek registri Konteks yang memberikan akses ke variabel jalur, objek permintaan dan respons.

Penangan juga mendapat dukungan untuk menangani JSON melalui Jackson.

Mari kembalikan JSON, yang diubah dari peta Groovy:

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

Di sini, Jackson.json digunakan untuk melakukan konversi.

6. Janji Ratpack di Groovy

Seperti yang kita ketahui, Ratpack mengaktifkan fitur asynchronous dan non-blocking pada aplikasi. Ini diimplementasikan dengan Ratpack Promises.

Promises mirip dengan yang digunakan di JavaScript dan mirip dengan Java Future . Kita dapat menganggap Janji sebagai representasi dari nilai yang akan tersedia di masa depan:

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

Tindakan terakhir di sini adalah tindakan kemudian , yang menentukan apa yang harus dilakukan dengan nilai akhir. Dalam kasus ini, kami mengembalikannya sebagai respons terhadap POST.

Let's understand this code in more detail. Here, Jackson.fromJson parses the JSON of the request body using the ObjectMapperUser. Then, the inbuilt Context.parse method binds it to the Promise object.

The promise operates asynchronously. When the then operation is eventually performed, the response is returned:

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

We should note that the Promise library is quite rich, allowing us to chain actions using functions like map and flatMap.

7. Integration with a Database

Having asynchronous handlers is of most benefit when our handlers have to wait for services. Let's demonstrate this by integrating our Ratpack application with an H2 database.

We can either use the Ratpack's HikariModule class which is an extension of HikariCP JDBC connection pool, or Groovy Sql for database integration.

7.1. HikariModule

To add HikariCP support, let's first add the following Hikari and H2 maven dependencies in our pom.xml:

 io.ratpack ratpack-hikari ${ratpack.version}   com.h2database h2 ${h2.version} 

Or, we can add the following dependencies to our build.gradle:

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

Now, we'll declare HikariModule under the bindings closure for the connection pool:

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

Finally, we're all set to use it for simple database operations using Java's Connection and 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) } 

Let's check that the handler works as expected:

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

7.2. Groovy Sql Class

We can use Groovy Sql for quick database operations, through methods like rows and 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"}]

Let's write an HTTP POST example with 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"}' \ //localhost:5050/addUser User Jiney Weiber inserted

8. Unit Testing

8.1. Setting up the Tests

As discussed, Ratpack also provides the ratpack-groovy-test library for testing a ratpack-groovy application.

To use it, we can add it as Maven dependency in our pom.xml:

 io.ratpack ratpack-groovy-test 1.6.1 

Alternatively, we can add the Gradle dependency in our build.gradle:

testCompile ratpack.dependency('groovy-test')

Then we need to create a Groovy main class RatpackGroovyApp.groovy to let us test the Ratpack.groovy script.

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

When running Groovy tests as JUnit tests, the class will invoke the Ratpack.groovy script using GroovyShell. In turn, it will start the Ratpack server for testing.

Now, let's write our Groovy Test class RatpackGroovySpec.groovy along with the code to start the Ratpack server through the RatpackGroovyApp:

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

Ratpack provides MainClassApplicationUnderTest to mock the application class for starting the server.

8.2. Writing Our Tests

Let's write our tests, starting with a very basic test to check if the application can start:

@Test void "test if app is started"() { when: get("") then: assert response.statusCode == 200 assert response.body.text == "Hello World from Ratpack with Groovy!!" }

Let's now write another test to verify the response of the fetchUsers get handler:

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

The Ratpack test framework takes care of starting and stopping the server for us.

9. Conclusion

In this article, we’ve seen a few ways to write HTTP handlers for Ratpack using Groovy. We also explored Promises and Database integration.

We've seen how Groovy closures, DSLs, and Groovy's Sql make our code concise, efficient and readable. At the same time, Groovy's test support makes unit and integration testing straightforward.

With these techniques, we can use Groovy's dynamic language features, and expressive APIs, to rapidly develop high-performance, scalable HTTP applications with Ratpack.

Seperti biasa, kode contoh dapat ditemukan di GitHub.