2. • Member of the Ratpack core team
• Work at Netflix
• Writing Learning Ratpack for O’Reilly
• Follow me on Twitter: @danveloper
3. • A full-stack, high throughput, non-blocking
web framework
• Built entirely on Java 8
• Specialized support for writing apps in Groovy
4. • Groovy has come a long way over the last few
years…
• Static compilation and (optional) compile time type
checking
• Excellent language for writing concise DSLs
• DSLs can be statically compiled!
Why Groovy?
5. • Groovy’s three compilation modes give
applications a lot of flexibility
• Parallels Java performance using Invoke Dynamic
or Static Compilation
• Pick-and-choose static or dynamic, depending on
the use-case!
Why Groovy?
6. • Requires Groovy 2.3.6+
• Utilize advanced static compilation and type
checking features of Groovy, like @DelegatesTo
• Allows you to define your application structure in a
ratpack.groovy file
Why Groovy?
8. • There are no plugins in Ratpack, instead the
framework provides functionality through modules
• Modules are built in Guice*
• DI is an abstraction in Ratpack, so even though the
framework uses Guice, you don’t have to
*
h$ps://github.com/google/guice
9. • A set of composable libraries for building
unopinionated, rich web applications
• Pick-and-choose what aspects of the
framework you want
• No lock-in to a single way of doing things
10. • Emphasis on Performance and Efficiency in both applications
and development experience
• Hot reloading is available during development time
• Beautiful development-time error pages
• Extreme emphasis on developer testing, especially functional
and integration
• Support for mocking nearly all aspects of the framework
12. • Convention over configuration taken too far
doesn’t let you wire things together
• Ratpack makes wiring modules a “one-liner”, so
there’s not a lot of work to get new features
integrated
• Out of the box support for Guice and Spring
(Spring Boot) DI
13. • Gradle integration makes building robust
projects easy to do
• Simple apps can be run entirely through
the Groovy command-line
14. buildscript {
repositories {
jcenter()
maven { url "http://oss.jfrog.org/oss-snapshot-local" }
maven { url "http://clinker.netty.io/nexus/content/repositories/snapshots" }
}
dependencies {
classpath 'io.ratpack:ratpack-gradle:0.9.13-SNAPSHOT'
}
}
apply plugin: 'io.ratpack.ratpack-java'
Simple Gradle Build Script*
* Ratpack 0.9.13 will be released on Feb 1, 2014
15. • Any non-trivial application needs build and
packaging capabilities
• The Ratpack Gradle plugin provides all the
fixtures to support building web
application projects
16. • Ratpack is built on Netty, which provides
the infrastructure for highly performant,
non-blocking networking
17. • Netty is an extremely mature, super low-level
networking library
• Excellent documentation, very involved
community
• Heavy emphasis on high throughput and
performance
18. • Netty provides the infrastructure for non-
blocking networking, but doesn’t help
much in the way of web application
structure
• Provides an asynchronous API for working
with network data (including HTTP)
19. • Computers “think” very good in asynchronous
terms; humans do not…
• Without app structure, programming toward this
paradigm is difficult
• Ratpack ensures the most performant structures of
Netty are being utilized, while providing a sensible
app structure and programming model
20. • Ratpack provides applications with a Netty-based, non-
blocking HTTP client
• Can be used to integrate with external RESTful services
• Utilizes the same event loop as the server, so resources
are appropriately utilized
• Provides a robust API for programmatically crafting
requests
23. • The JVM doesn’t have any concept of continuations, so
the only option we have is asynchronous APIs
• Async APIs allow callers to provide a completion
handler (or callback), which is to be invoked when some
processing has finished
• There is no temporal relationship between the caller
and the invocation of the callback
24. handler { req, res ->!
... (1) do some work ...!
!
async {!
... (2) do some async work ...!
}!
async {!
... (3) do some more async ...!
}!
!
... (4) send response ...!
}!
(1) – Definitely finishes first
(2) – May finish before or after (3)
and (4)
(3) – May finish before or after (2) or
(4)
(4) – May finish before or after (2) or
(3)
Async APIs create non-determinism in control flow
25. • The temporal disconnect creates non-determinism
in request processing
• This is a big problem for web applications, because
they demand a deterministic control flow
• Ratpack provides the concept of an Execution,
which creates determinism in asynchronous
processing
26. handler { req, res ->!
... (1) do some work ...!
!
async {!
... (3) do some async work ...!
}!
async {!
... (4) do some more async ...!
}!
!
... (2) send response ...!
}!
(1) – Definitely finishes first
(3) – Definitely finishes third
(4) – Definitely finishes fourth
(2) – Definitely finishes second
Ratpack creates a deterministic control flow
27. • Through Ratpack’s promise API, the Execution
is able to schedule async segments to be
invoked after the handler code has finished
• Promise API ensures that developers can work
with asynchronous processing in a sensible
way
28. • In non-blocking, it is of paramount importance
to not block the thread
• Since a single thread is handling processing
for many clients, blocking one blocks all
• Need to adopt libraries that are non-blocking
or provide async APIs
• This is not always practical…
29. • Not all interfaces are able to be non-blocking or
asynchronous
• Most non-blocking frameworks force you to be either
entirely async or wholesale define your handlers as
blocking
• This is limiting and makes it difficult to work with legacy
APIs or those not crafted for non-blocking work
• Ratpack gives you a mechanism to define blocking
execution segments in your handler code
30. handler(r -> (ctx) -> {
ModelDAO dao = ctx.get(ModelDAO.class);
ctx.blocking(() ->
dao.load(ctx.getPathTokens().get("id")))
.then(model -> {
... do some work with the data ...
ctx.render(model);
});
})
Handler with blocking operation
31. • Blocking operations are scheduled to an I/O
bound thread pool
• Once the blocking operation is completed,
the data is then returned to the request taking
thread, where processing can finish
• Blocking operations are invoked when they
are subscribed to in the request-taking thread
32. ...
ctx.blocking(() -> {
... do some blocking io ...
return result;
}).then(result -> {
... subscribe to the blocking promise ...
... process in the request-taking thread ...
})
...
Blocking with a subscription
33. • Data is being delivered to web consumers in
an increasingly real time way
• Web applications must be able to support
streaming protocols like SSE and WebSockets
• Ratpack has built-in fixtures that make it easy
to develop real-time web applications
34. • Because of its non-blocking nature, Ratpack can
support a high volume of concurrent real-time
streams
• Valuable in app-to-app communication too, where
a consumer wants to be reactively informed about
some data
• Ratpack’s HTTP client also supports reading
streams of data from external producers, making
stream processing a great integration
35.
36. • Ratpack applications are defined through a
handler chain
• Handler chain is a programmatic construct
for managing the flow of request data
• Handlers can be bound to request path
routes and HTTP verbs
37. import static ratpack.groovy.Groovy.ratpack!
!
ratpack {!
handlers {!
get {!
response.send "I'm the default route!"!
}!
get("route1") {!
response.send "I'm in route1"!
}!
get("route2/:param") {!
response.send "I'm in route2 and received param: ${pathTokens.param}"!
}!
}!
}!
Handler chain in a Ratpack Script-backed app
38. RatpackServer.start(b -> b
.config(ServerConfig.noBaseDir())
.handlers(chain -> chain
.get(ctx -> ctx.getResponse()
.send("I'm in the default route!"))
!
.get("route1", (ctx) -> ctx.getResponse()
.send("I'm in route1!"))
!
.get("route2/:param", (ctx) -> ctx.getResponse()
.send(String
.format(” received param: %s",
ctx.getPathTokens().get("param"))))
)
);"
Handler chain in using Java 8 APIs
39. • Ratpack applications are defined through a
handler chain
• Handler chain is a programmatic construct
for managing the flow of request data
• Handlers can be bound to request path
routes and HTTP verbs
40. handlers {!
get { ... }!
get("route") { ... }!
!
post { ... }!
post("route") { ... }!
!
put { ... }!
put("route") { ... }!
!
delete { ... }!
delete("route") { ... }!
!
patch { ... }!
patch("route") { ... }!
}!
Semantic handler API for binding to HTTP verbs and routes
41. • Handlers can be nested within a route…
handlers {
prefix("api") { ModelDAO dao ->
get {
blocking { dao.getAll() }.
then { models -> render JsonOutput.toJson(models) }
}
post {
Model model = parse(fromJson(Model))
blocking { dao.save(model) }.
then { m -> render m }
}
put(":id") {
blocking { dao.load(pathTokens.id) }.
map { model -> model.merge(parse(fromJson(Model))) }.
blockingMap { model -> dao.save(model) }.
then { model -> render model }
}
}
}!
42. • There’s a special handler type for serving
static assets from the app’s baseDir!
• Static assets must be defined as the last
handler in the chain
• Can be scoped to routes
43. • Use the assets handler to serve static content
handlers {
prefix("api") {
get {
response.send "some API data"
}
}
assets("public", "index.html”)
}!
44. • You can also use a handler with byMethod to perform common processing
within a route…
handlers {
handler("api") { ModelDAO dao ->
dao.beginTransaction()
byMethod {
get {}
post {}
put {}
delete {}
}
dao.endTransaction()
}!
}!
45. • Can discriminate on content type, allowing you to build HyperMedia APIs…
handlers {
handler {
byContent {
json {
response.send(toJson([msg: "regular json"]))
}
xml {
response.send(...)
}
type("application/vnd.app.org+json;v=1") {
response.send(toJson([newmsg: "hypermedia json"]))
}
}
}
}!
46. • Handlers define the edge of your application
• In Servlet API terms, handlers can be thought
of as a marriage between filters and servlets
• Allows request introspection and
programmatic injection of handler chain
47. • This allows handlers to be constructed and dependency injected, etc…
class UserHandler implements Handler {
private final String message
UserHandler(String message) {
this.message = message
}
@Override
void handle(Context context) !
!throws Exception {
context.response.send(message)
}
}!
ratpack {
bindings {
bindInstance(new UserHandler("user handler"))
}
handlers {
handler { UserHandler userHandler ->
if (request!
.headers.contains("X-Routing-Header")) {
def routingHeader = request!
.headers.get("X-Routing-Header")
if (routingHeader == "user") {
insert(userHandler)
}
}
next()
}
get {
response.send "default system handler"
}
}
}!
New
Handler
is
programma1cally
inserted
into
the
processing
chain
48.
49. • Dependency injection is an abstraction in
Ratpack, through the concept of a Registry
• Components are bound to, and resolvable
from, a Registry instance
• Registries can be backed in a DI framework
50. • Out of the box support for Guice and Spring
Boot
• Registries can be inherited, allowing
components to be resolved in a cascading
manner
• Every request context gets a registry that
components can be extracted from
51. • In a Groovy script that uses closures as
handlers, the variable arguments to that
closure are “injected” from the registry
• In Java 8, they are able to be resolved from
the request context object
52. • You don’t need to use a DI framework to get injection support, you can build
your own registry from objects you construct…
public static void main(String[] args) throws Exception {
Registry registry = Registries.registry()
.add(new ModelDAO()).add(new DB()).build();
RatpackServer.start(spec -> spec
.config(ServerConfig.noBaseDir())
.handlers(chain -> {
chain.register(registry);
chain.handler(":id", ctx -> {
ModelDAO dao = ctx.get(ModelDAO.class);
ctx.blocking(() ->
dao.load(ctx.getPathTokens().get("id")))
.then(ctx::render);
});
})
);
}!
53. • Variables arguments to Groovy closure handlers are resolved from the
registry
def myRegistry = Registries.registry()
.add(new ModelDAO())
.build()
ratpack {
handlers {
register(myRegistry)
prefix("api") {
get(":id") { ModelDAO dao ->
render dao.load(pathTokens.id)
}
}
assets("public", "index.html”)
}
}!
54. • In Groovy, you get a bindings block, which you can use to bind components
to the Registry. With Guice, annotations can be used to Inject
class UserHandler implements Handler {
private final ModelDAO dao
@javax.inject.Inject!
UserHandler(ModelDAO dao) {
this.message = message
}
@Override
void handle(Context ctx) !
!throws Exception {
ctx.blocking {!
dao.getAll()!
} then { ctx.render(it) }
}
}!
ratpack {
bindings {
binder { b ->
b.bind(ModelDAO).in(Scopes.SINGLETON)
b.bind(UserHandler).in(Scopes.SINGLETON)
}
}!
handlers {
handler { UserHandler userHandler ->
if (request!
.headers.contains("X-Routing-Header")) {
def routingHeader = request!
.headers.get("X-Routing-Header")
if (routingHeader == "user") {
insert(userHandler)
}
}
next()
}
get {
response.send "default system handler"
}
}
}!
Can
get
a
handle
on
a
Guice
binder
to
perform
annota1on-‐based
Injec1ng
55. • Likewise, a Guice Module can be “added” to the registry…
ratpack {
bindings {!
// Any Guice module can be added this way!
// this is how Ratpack modules are"
// introduced... "
add(new SqlModule())
}!
handlers {
...!
}
}!
56. • Spring application context can be used to
back a registry using the
ratpack.spring.Spring class
h>p://www.ratpack.io/manual/0.9.13/api/ratpack/spring/Spring.html
57. • Like handlers, registries can be
programmatically registered into the
handler chain based on request data
• Can allow your app to resolve components
specific to the context of a request
60. • Including framework modules in your
Gradle based project is really easy
• Can utilize helper methods from the
Ratpack Gradle plugin to include named
modules
• Allows framework versions to stay in sync
62. • Including framework modules in your
Gradle based project is really easy
• Can utilize helper methods from the
Ratpack Gradle plugin to include named
modules
• Allows framework versions to stay in sync
65. • NetflixOSS Hystrix support, via the ratpack-
hystrix module
• Calls to remote services can be made fault
tolerant
• Ability to stream Hystrix metrics to the Hystrix
Dashboard
Great Support for building MICROSERVICES!!
66. • The ratpack-hikari module uses HikariCP
to create a super fast pooled SQL DataSource
• Can be used in conjunction with Groovy SQL
to query databases
• Configurable using the fixtures from the
ratpack-config module
Great Support for DATABASES!!
67. • The ratpack-jackson module provides
request data parsing and object rendering
from and to JSON
• Data can be worked with in free-form nodes,
or bound to command objects
• Arbitrary models can be rendered as JSON
using simply context.render(obj)!
Great Support for DATA BINDING!!
68. • Ratpack’s Promise API is an implementation of
Reactive Streams Specification
• The ratpack-rxjava module provides a
bridge between a Ratpack Promise and an
RxJava Observable
• The ratpack-reactor module allows data
to be processed using Project Reactor
Great Support for REACTIVE PROGRAMMING!!
69. • View templates can be rendered through a variety
of means
• Support for server-side templating with
Handlebars, Thymeleaf, Groovy Templates, and
Groovy Markup
• Ongoing work to integrate @davydotcom’s asset-
pipeline, which will give robust support for all types
of static content
Great Support for FULL-STACK FEATURES!!
70.
71. • Ratpack has been built from the ground-up with
testing considered at every turn
• Even more-so – considering testing from the
perspective of the developer
• The concept of the Registry gives the framework
control over components
• Makes it easy to provide fixtures for mocking and
stubbing data
72. • The EmbeddedApp class from the ratpack-
test module supports functional and
integration testing
• Can be used to test an application module
or a subset of handlers and functionality
• Can be used outside of Ratpack too!
73. • Spock is the choice framework, though
there’s no strict integration there
• Functional tests are easy to bootstrap and
the TestHttpClient helper makes it easy to
programmatically craft test calls
74. • Great resource for seeing Ratpack’s testing
in action is the example-books project
• Rus Hart keeps this up to date with changes
to the framework.
• https://github.com/ratpack/example-books