Ratpack dengan Groovy

1. Gambaran keseluruhan

Ratpack adalah sekumpulan perpustakaan Java ringan untuk membangun aplikasi HTTP berskala dengan fitur reaktif, tak segerak, dan tidak menyekat.

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

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

2. Mengapa Groovy?

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

Oleh itu, Groovy menjadikan skrip dan Bahasa Khusus Domain sangat mudah. Dengan Ratpack, ini menyediakan pengembangan aplikasi web yang pantas.

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

3. Aplikasi Ratpack Menggunakan Skrip Groovy

API Ratpack Groovy dibangun di Java sehingga dapat dengan mudah disatukan dengan aplikasi Java dan Groovy. Mereka boleh didapati dalam pakej ratpack.groovy .

Sebenarnya, dalam kombinasi dengan kemampuan skrip Groovy dan pengurusan ketergantungan Grape, kita dapat dengan cepat membuat aplikasi web bertenaga 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 pengendali pertama kami, menangani permintaan GET. Yang harus kami lakukan hanyalah menambahkan beberapa DSL asas untuk membolehkan pelayan Ratpack.

Mari sekarang jalankan ini sebagai skrip Groovy untuk memulakan aplikasi. Secara lalai, 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 tambah nilai panas , yang bermaksud kita dapat mengubah Ratpack.groovy , dan kemudian melihat perubahannya saat aplikasi melayani permintaan HTTP kami yang berikutnya.

4. Pengurusan Ketergantungan Ratpack-Groovy

Terdapat beberapa cara untuk membolehkan sokongan ratpack-groovy .

4.1. Anggur

Kita boleh menggunakan pengurus pergantungan tertanam Groovy Grape.

Semudah menambahkan anotasi pada skrip Ratpack.groovy kami :

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

4.2. Ketergantungan Maven

Untuk membina di Maven, yang kita perlukan hanyalah menambahkan kebergantungan untuk perpustakaan ratpack-groovy :

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

4.3. Gradle

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

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

5. Penangan Ratpack di Groovy

Pengendali menyediakan cara untuk menangani permintaan dan respons web . Objek permintaan dan respons dapat diakses dalam penutupan ini.

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

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

Kami boleh menguji permintaan web ini melalui // localhost: 5050 / salam / :

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

Dalam kod pengendali, ctx adalah objek registri Konteks yang memberikan akses ke pemboleh ubah jalur, objek permintaan dan respons.

Pengendali juga mempunyai sokongan untuk berurusan dengan JSON melalui Jackson.

Mari kembali JSON, ditukar 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 membuat penukaran.

6. Janji Ratpack di Groovy

Seperti yang kita ketahui, Ratpack membolehkan ciri tidak segerak dan tidak menyekat dalam aplikasi. Ini dilaksanakan dengan Ratpack Promises.

Janji serupa dengan yang digunakan dalam JavaScript dan mirip Java Future . Kita boleh menganggap Janji sebagai representasi dari nilai yang akan tersedia pada masa akan datang:

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 kes ini, kami mengembalikannya sebagai tindak balas kepada 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, kod contoh boleh didapati di GitHub.