When we want to debug our Ratpack application written in Java we can simply use the Debug action on the main application class. When we described the application with the Groovy DSL we must add a Groovy runtime configuration to our project in IntelliJ IDEA to support debugging of the (R|r)atpack.groovy
script file.
First we select Run | Edit configurations.... IntelliJ IDEA opens a new dialog window where we can add or modify run/debug configurations. We select the option New configuration and choose the option Groovy:
Continue reading →
It is actually very easy to run a Ratpack application in the Groovy Console. The Groovy Console is a GUI application that is distributed with Groovy and allows us to write and run Groovy scripts. We start the Groovy Console with the groovyConsole
command: $ groovyConsole
. To run a Ratpack application we only have to add a dependency to Ratpack using the @Grab
annotation. We can write an application with the Groovy DSL and select Script | Run from the menu. If we make a change in the script file we invoke the Run command again. The Ratpack application restarts with our changes. This is very useful for trying out some Ratpack features without much hassle.
The following screenshot shows a simple Ratpack application. At the bottom we see the logging output of the running application:
Continue reading →
To set the base directory for serving static files in a Ratpack application we can use the baseDir
method of the ServerConfigBuilder
class. We must provide a Path
or File
to this method. If we want to serve files from the class path, for example a JAR file or directory, we can use the find
method of the class BaseDir
. The find
method will search the class path for a marker file with the name .ratpack
. If the file is found then the directory or JAR file it is found in is used as the root of the file system. Normally the root of the class path is searched, but we can change the search path with an argument for the find
method.
In the following sample Ratpack application we use the value web-resources/.ratpack.base.dir
for the BaseDir.find
method. So in our class path we must have a web-resources
directory with the file .ratpack.base.dir
that will serve as the root file system for serving static files.
Continue reading →
We learned about externalised configuration in a previous blog post. Ratpack provides support out of the box for several formats and configuration sources. For example we can use files in YAML, properties or JSON format, arguments passed to the application, system properties and environment variables. We can add our own configuration source by implementing the ratpack.config.ConfigSource
interface. We must override the method loadConfigData
to load configuration data from a custom source and convert it to a format that can be handled by Ratpack.
We are going to write a custom ConfigSource
implementation that will get configuration data from a database. We assume the data is in a table with the name CONFIGURATION
and has the columns KEY
and VALUE
. The format of the key is the same as for Java properties files.
Continue reading →
In a previous post we learned about the get
and getAll
methods to get objects from the registry. Ratpack also provides the first
method to get objects from the registry. This method accepts a Function
that is applied to the elements of a given type. The first element where the Function
returns a non null value is returned encapsulated in an Optional
object. If the Function
returns a null value for all elements than Optional.empty()
is returned.
package com.mrhaki.ratpack
import ratpack.registry.Registry
import ratpack.registry.RegistrySpec
import spock.lang.Specification
class FindFirstRegistrySpec extends Specification {
Registry registry
def setup() {
// Setup registry with two objects of type User.
registry = Registry.of { RegistrySpec registrySpec ->
registrySpec.add(new User(username: 'mrhaki'))
registrySpec.add('hubert')
registrySpec.add(new User(username: 'hubert'))
registrySpec.add('mrhaki')
}
}
def "find User where username starts with mr"() {
when:
// First element that returns a non null value
// is return encapsulated in an Optional.
final Optional user = registry.first(User) { user ->
// If username property starts with
// "mr" than return user as non null value.
user.username.startsWith("mr") ? user : null
}
then:
user.get().username == 'mrhaki'
}
}
Continue reading →
To get objects from the registry or context we specify the type of the object we want. Ratpack will find the object(s) that match the given type. If we use the get
method then the last object added to the registry with the given type is returned. To get multiple objects we use the getAll
method. The methods returns an Iterable
with the found objects where the last added objects are returned as first elements.
In the following example specification we have a Registry
with some objects, of which two are of type User
. Next we use the get
and getAll
methods to get the objects.
Continue reading →
We can use the wiretap
method of the Promise
interface to listen in on results. We write an Action
implementation which has the result of a Promise
encapsulated in a Result
object. The wiretap
method can be used to do something with a Promise
value without interrupting a method chain.
In the following example we tap in on Promise
results:
Continue reading →
In our Ratpack application we can have handlers that need to be invoked for every request. For example the handler needs to set a response header and will use the Context.next()
method to continue with the rest of the handlers. When we have such a handler we can use the all
method of a Chain
instance. This happens in the handlers
section of our application definition. We can also register such handlers directly in the registry. We can even use a Module
to register the handler implementation. To register a handler in the registry we must use the HandlerDecorator
interface. The interface has a method prepend
that will encapsulate a Handler
implementation and makes it available before any other handlers.
In the following sample we have a Handler
implementation that checks if the RequestId
object is available. If so then the value is set as a response header:
Continue reading →
A client of our Ratpack application can send a HTTP Accept header to indicate the type of response the client expects or can handle. We can restrict our application to certain types with the accepts
method of the Handlers
class. We can define one or more String
values that denote the types that a client can use and our application responds to. If the value for the Accept header is one of the given values then the next handlers are invoked otherwise a client error 406 Not Acceptable
is returned.
In the following example Ratpack application definition we use the accepts
method so only values of application/json
and application/xml
:
Continue reading →
If we write our own handler class it is not so difficult to get objects from the registry and use them in the code. In our handler we have a handle
method and we get a Context
object as argument. From the Context
we get access to objects in the registry. Instead of writing code to get the objects from the registry we can use the InjectionHandler
as superclass for our handler. The InjectionHandler
class has a handle
method that will look for a handle
method in our implementation class with a first argument of type Context
. Then at least one other argument must be defined in the method signature. The types of the other arguments are used to get the corresponding objects from the registry. We don't write the code to get the object from the registry ourselves, but rely on the implementation in the InjectionHandler
class.
The following example handler class extends from the InjectionHandler
. We write a handle
method and from the signature we see that we have a dependency on the type Messages
. In the implementation of the handle
method we can rely on the InjectionHandler
class to get an object from the registry with type Messages
.
Continue reading →
The logging framework SLF4J supports Mapped Diagnostic Context (MDC). With MDC we can use a logging context that can be identified by something unique. This is useful, because then we can distinguish log messages from a big logging stream by something unique. Normally MDC is implemented on a per thread basis, but that is not useful in a Ratpack application. Ratpack provides the MDCInterceptor
class to use SLF4J's MDC support in a Ratpack application. We must register an instance of MDCInterceptor
with the registry. We can use the static method instance
to create a new instance. With the method withInit
we can define an action to be executed for the initialisation of the instance. An Execution
parameter is used with the action and we can use it to check for objects in the registry.
In the following example we initialise the MDCInterceptor
and check if there is a RequestId
object in the registry (as described in the Javadoc of MDCInterceptor
). If the RequestId
object is available we set the MDC logging context variable requestId
with the value of the RequestId
object. Later in our logging configuration we can use this value so we can distinguish all logging statements belonging to a single request (with the given identifier).
Continue reading →
Ratpack has the ratpack.server.Service
interface with the methods onStart
and onStop
. If we write an implementation class for the Service
interface and register it with the Ratpack registry, then Ratpack will invoke the onStart
method when the application starts and the onStop
method when the application stops. The methods take an event object as argument and we can use the event object to access the registry if we need to. Writing an implementation for the Service
interface can be useful for example to bootstrap the application with initial data or do other things.
In the following example implementation we log when the application starts and stops. In the onStart
method we also display a Ratpack banner on the console.
Continue reading →