Search

Dark theme | Light theme
Showing posts with label Ratpacked:Configuration. Show all posts
Showing posts with label Ratpacked:Configuration. Show all posts

March 8, 2017

Ratpacked: Combine Groovy DSL With RatpackServer Java Configuration

We have several options to define a Ratpack application. We can use a Java syntax to set up the bindings and handlers. Or we can use the very nice Groovy DSL. It turns out we can use both together as well. For example we can define the handlers with the Groovy DSL and the rest of the application definition is written in Java. To combine both we start with the Java configuration and use the bindings and handlers method of the Groovy.Script class to inject the files with the Groovy DSL.

We start with a sample application where we use Java configuration to set up our Ratpack application:

// File: src/main/java/mrhaki/ratpack/Application.java
package mrhaki.ratpack;

import ratpack.func.Action;
import ratpack.handling.Chain;
import ratpack.handling.Handler;
import ratpack.registry.RegistrySpec;
import ratpack.server.RatpackServer;

import java.util.Optional;

public class Application {

    public static void main(String[] args) throws Exception {
        new Application().startServer();
    }

    void startServer() throws Exception {
        RatpackServer.start(server -> server
                .registryOf(registry())
                .handlers(chain()));
    }

    private Action<RegistrySpec> registry() {
        return registry -> registry
                .add(new RecipeRenderer())
                .add(RecipeRepository.class, new RecipesList());
    }

    private Action<Chain> chain() {
        return chain -> chain.post("recipe", recipeHandler());
    }

    private Handler recipeHandler() {
        return ctx -> ctx
                .parse(RecipeRequest.class)
                .flatMap(recipeRequest -> ctx
                        .get(RecipeRepository.class)
                        .findRecipeByName(recipeRequest.getName()))
                .then((Optional<Recipe> optionalRecipe) -> ctx.render(optionalRecipe));
    }

}

We can use the Groovy DSL for the bindings and handlers definitions and use them in our Java class with the Groovy.Script class. First we create the files bindings.groovy and handlers.groovy in the directory src/main/resources so they will be in the class path of the Java application. We can use the Groovy DSL syntax in the files:

// File: src/main/resources/bindings.groovy
import mrhaki.ratpack.RecipeRenderer
import mrhaki.ratpack.RecipeRepository
import mrhaki.ratpack.RecipesList

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        add new RecipeRenderer()
        add RecipeRepository, new RecipesList()
    }   
}
// File: src/main/resources/handlers.groovy
import mrhaki.ratpack.Recipe
import mrhaki.ratpack.RecipeRepository
import mrhaki.ratpack.RecipeRequest

import static ratpack.groovy.Groovy.ratpack

ratpack {
    handlers {
        post('recipe') { RecipeRepository recipeRepository ->
            parse(RecipeRequest)
                    .flatMap { RecipeRequest recipeRequest -> 
                        recipeRepository.findRecipeByName(recipeRequest.name) 
                    }
                    .then { Optional<Recipe> optionalRecipe -> 
                        render(optionalRecipe) 
                    }
        }
    }
}

We have our Groovy DSL files with the definitions. To use them in our Java code to define the Ratpack application we must make sure Ratpack can find them. Therefore we create an empty marker file .ratpack in src/main/resources. With this file in place we can use Ratpack's findBaseDir method to set the base directory for finding external files. It is time to refactor our application:

// File: src/main/java/mrhaki/ratpack/Application.java
package mrhaki.ratpack;

import ratpack.func.Action;
import ratpack.groovy.Groovy;
import ratpack.handling.Chain;
import ratpack.handling.Handler;
import ratpack.registry.RegistrySpec;
import ratpack.server.RatpackServer;

import java.util.Optional;

public class Application {

    public static void main(String[] args) throws Exception {
        new Application().startServer();
    }

    void startServer() throws Exception {
        RatpackServer.start(server -> server
                // Set base dir with directory that
                // contains marker file .ratpack.
                .serverConfig(config -> config.findBaseDir())
                // Use bindings.groovy with static compilation.
                .registry(Groovy.Script.bindings(true))
                // Use handlers.groovy with static compilation.
                .handler(Groovy.Script.handlers(true)));
    }

}

Written with Ratpack 1.4.5.

June 27, 2016

Ratpacked: Using Groovy Configuration Scripts As Configuration Source

Ratpack has a lot of options to add configuration data to our application. We can use for example YAML and JSON files, properties, environment variables and Java system properties. Groovy has the ConfigSlurper class to parse Groovy script with configuration data. It even supports an environments block to set configuration value for a specific environment. If we want to support Groovy scripts as configuration definition we write a class that implements the ratpack.config.ConfigSource interface.

We create a new class ConfigSlurperConfigSource and implement the ConfigSource interface. We must implement the loadConfigData method in which we read the Groovy configuration and transform it to a ObjectNode so Ratpack can use it:

// File: src/main/groovy/mrhaki/ratpack/config/ConfigSlurperConfigSource.groovy
package mrhaki.ratpack.config

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ArrayNode
import com.fasterxml.jackson.databind.node.ObjectNode
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import ratpack.config.ConfigSource
import ratpack.file.FileSystemBinding

import java.nio.file.Path

@CompileStatic
class ConfigSlurperConfigSource implements ConfigSource {
    
    private final String configScript
    
    private final URL scriptUrl
    
    private final String environment
    
    ConfigSlurperConfigSource(final String configScript) {
        this(configScript, '')
    }

    ConfigSlurperConfigSource(final String configScript, final String environment) {
        this.configScript = configScript
        this.environment = environment
    }

    ConfigSlurperConfigSource(final Path configPath) {
        this(configPath, '')
    }

    ConfigSlurperConfigSource(final Path configPath, final String environment) {
        this(configPath.toUri(), environment)
    }

    ConfigSlurperConfigSource(final URI configUri) {
        this(configUri, '')
    }

    ConfigSlurperConfigSource(final URI configUri, final String environment) {
        this(configUri.toURL(), environment)
    }

    ConfigSlurperConfigSource(final URL configUrl) {
        this(configUrl, '')
    }

    ConfigSlurperConfigSource(final URL configUrl, final String environment) {
        this.scriptUrl = configUrl
        this.environment = environment
    }

    @Override
    ObjectNode loadConfigData(
            final ObjectMapper objectMapper, 
            final FileSystemBinding fileSystemBinding) throws Exception {

        // Create ConfigSlurper for given environment.
        final ConfigSlurper configSlurper = new ConfigSlurper(environment)

        // Read configuration.
        final ConfigObject configObject = 
                configScript ? 
                        configSlurper.parse(configScript) : 
                        configSlurper.parse(scriptUrl)
        
        // Transform configuration to node tree
        final ObjectNode rootNode = objectMapper.createObjectNode()
        populate(rootNode, configObject)
        return rootNode
    }

    @CompileDynamic
    private populate(final ObjectNode node, final ConfigObject config) {
        // Loop through configuration.
        // ConfigObject also implements Map interface,
        // so we can loop through key/value pairs.
        config.each { key, value ->
            // Value is another configuration,
            // this means the nested configuration
            // block.
            if (value instanceof Map) {
                populate(node.putObject(key), value)
            } else {
                // If value is a List we convert it to
                // an array node.
                if (value instanceof List) {
                    final ArrayNode listNode = node.putArray(key)
                    value.each { listValue ->
                        listNode.add(listValue)
                    }
                } else {
                    // Put key/value pair in node.
                    node.put(key, value)
                }
            }
        }
    }
}

We have several options to pass the Groovy configuration to the ConfigSlurperConfigSource class. We can use a String, URI, URL or Path reference. Let's create a file with some configuration data.

// File: src/ratpack/application.groovy
app {
    serverPort = 9000
}

environments {
    development {
        app {
            serverName = 'local'
        }
    }
    production {
        app {
            serverName = 'cloud'
            serverPort = 80
        }
    }
}

Next we create a Ratpack application using the Groovy DSL. In the serverConfig section we use our new ConfigSlurperConfigSource:

// File: src/ratpack/ratpack.groovy
import com.google.common.io.Resources
import com.mrhaki.config.ConfigSlurperConfigSource

import static groovy.json.JsonOutput.prettyPrint
import static groovy.json.JsonOutput.toJson
import static ratpack.groovy.Groovy.ratpack

//final Logger log = LoggerFactory.getLogger('ratpack')

ratpack {

    serverConfig {
        // Use Groovy configuration.
        add new ConfigSlurperConfigSource('''\
            app {
                message = 'Ratpack swings!'
            }''')

        // Use external Groovy configuration script file.
        add new ConfigSlurperConfigSource(
                Resources.getResource('application.groovy'), 'development')

        require '/app', SimpleConfig
    }

    handlers {
        get('configprops') { SimpleConfig config ->
            render(prettyPrint(toJson(config)))
        }
    }

}

// Simple configuration.
class SimpleConfig {
    String message
    String serverName
    Integer serverPort
}

Let's check the output of the configprops endpoint:

$ http -b localhost:5050/configprops
{
    "message": "Ratpack swings!",
    "serverName": "local",
    "serverPort": 9000
}

Now we set the environment to production in our Ratpack application:

// File: src/ratpack/ratpack.groovy
...

ratpack {

    serverConfig {
        ...

        // Use external Groovy configuration script file.
        add new ConfigSlurperConfigSource(
                Resources.getResource('application.groovy'), 'production')

       ...
    }

    ...
}

If we check configprops again we see different configuration values:

$ http -b localhost:5050/configprops
{
    "message": "Ratpack swings!",
    "serverName": "cloud",
    "serverPort": 80
}

Written with Ratpack 1.3.3.

June 24, 2016

Ratpacked: Handling Exceptions When Reading Configuration Sources

To define configuration sources for our Ratpack application we have several options. We can set default properties, look at environment variables or Java system properties, load JSON or YAML formatted configuration files or implement our own configuration source. When something goes wrong using one of these methods we want to be able to handle that situation. For example if an optional configuration file is not found, we want to inform the user, but the application must still start. The default exception handling will throw the exception and the application is stopped. We want to customise this so we have more flexibility on how to handle exceptions.

We provide the configuration source in the serverConfig configuration block of our Ratpack application. We must add the onError method and provide an error handler implementation before we load any configuration source. This error handler will be passed to each configuration source and is execute when an exception occurs when the configuration source is invoked. The error handler implements the Action interface with the type Throwable. In our implementation we can for example check for the type of Throwable and show a correct status message to the user.

In the following example application we rely on external configuration source files that are optional. If the file is present it must be loaded, otherwise a message must be shown to indicate the file is missing, but the application still starts:

// File: src/ratpack/ratpack.groovy
import org.slf4j.Logger
import org.slf4j.LoggerFactory

import java.nio.file.NoSuchFileException
import java.nio.file.Paths

import static ratpack.groovy.Groovy.ratpack

final Logger log = LoggerFactory.getLogger('ratpack.server')

ratpack {
    serverConfig {
        // Use custom error handler, when
        // exceptions happen during loading
        // of configuration sources.
        onError { throwable ->
            if (throwable in NoSuchFileException) {
                final String file = throwable.file
                log.info "Cannot load optional configuration file '{}'", file
            } else {
                throw throwable
            }
        }
        
        yaml('application.yml')

        // Optional configuration files
        // to override values in 
        // 'application.yml'. This could
        // potentially give an exception if
        // the files don't exist.
        yaml(Paths.get('conf/application.yml'))
        json(Paths.get('conf/application.json'))
        
        args(args)
        sysProps()
        env()
        
        ...
    }

    ...
}

Next we start the application with the absence of the optional configuration files conf/application.yml and conf/application.json:

$ gradle run
...
:run

12:28:38.887 [main]            INFO  ratpack.server.RatpackServer - Starting server...
12:28:39.871 [main]            INFO  ratpack.server - Cannot load optional configuration file 'conf/application.yml'
12:28:39.873 [main]            INFO  ratpack.server - Cannot load optional configuration file 'conf/application.json'
12:28:40.006 [main]            INFO  ratpack.server.RatpackServer - Building registry...
12:28:40.494 [main]            INFO  ratpack.server.RatpackServer - Ratpack started (development) for http://localhost:5050

Notice that application is started and in the logging we have nice status messages that tell us the files are not found.

Written with Ratpack 1.3.3.

June 14, 2016

Ratpacked: Include Files In The Ratpack Groovy DSL

When we define our Ratpack application using the Groovy DSL in a file ratpack.groovy, we can split up the definition in multiple files. With the include method inside the ratpack configuration closure we can use the file name of the file we want to include. The file that we include also contains a ratpack configuration closure. We can use the same bindings, handlers and serverConfig sections. The bindings configuration is appended to the parent configuration. The handlers and serverConfig configuration is merged with the parent configuration.

In an example project we have the following ratpack.groovy, that includes two extra files: course.groovy and loghandler.groovy:

// File: src/ratpack/ratpack.groovy
import java.nio.file.Paths

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        bindInstance(new Integer(42))
    }

    handlers {
        get { Integer answer ->
            render "The answer is $answer"
        }
    }

    // File name of external file to include.
    include 'course.groovy'
    
    // Argument of the include method can
    // also be a Path.
    include Paths.get('loghandler.groovy')
}

In the course.groovy file we add a CourseService type to the registry and use it in a handler:

// File: src/ratpack/course.groovy
import mrhaki.ratpack.course.CourseService
import mrhaki.ratpack.course.impl.CourseMemoryStore

import static ratpack.groovy.Groovy.ratpack
import static ratpack.jackson.Jackson.json

ratpack {
    bindings {
        bind(CourseService, CourseMemoryStore)
    }
    
    handlers {
        get('course/:courseId') { CourseService courseService ->
            final String id = pathTokens.courseId
            
            courseService
                    .getCourse(id)
                    .then { course -> render(json(course))}
        }
    }
}

We also add a RequestLogger handler in the loghandler.groovy file:

// File: src/ratpack/course.groovy
import ratpack.handling.RequestLogger

import static ratpack.groovy.Groovy.ratpack

ratpack {
    handlers {
        all(RequestLogger.ncsa())
    }
}

Written with Ratpack 1.3.3.

June 13, 2016

Ratpacked: Revisited Using Multiple DataSources

In a previous post we learned how to add an extra DataSource to our Ratpack application. At that time on the Ratpack Slack channel there was a discussion on this topic and Danny Hyun mentioned an idea by Dan Woods to use a Map with DataSource objects. So it easier to add more DataSource and Sql objects to the Ratpack registry. In this post we are going to take a look at a solution to achieve this.

We are going to use the HikariDataSource, because it is fast and low on resources, in our example code. First we create a new class to hold the configuration for multiple datasources. The configuration is a Map where the key is the name of the database and the value an HikariConfig object. The key, the name of the database, is also used for creating the HikariDataSource and Sql objects. And the good thing is that Ratpack uses a Jackson ObjectMapper to construct a configuration object and it understands Map structures as well. In the ratpack.groovy file at the end of this blog post we see how we can have a very clean configuration this way.

// File: src/main/groovy/mrhaki/ratpack/configuration/DataSourcesConfiguration.groovy
package mrhaki.ratpack.configuration

import com.zaxxer.hikari.HikariConfig

class DataSourcesConfig {
    
    @Delegate
    private final Map<String, HikariConfig> configurations = [:]

    /**
     * Extra method to add a HikariConfig to the configurations.
     * Can be used for example in the configuration closure when
     * adding the DataSourcesModule to the Ratpack bindings.
     * 
     * @param name Name of database.
     * @param config Configuration to connect to database.
     * @return This DataSourcesConfig object.
     */
    DataSourcesConfig addHikariConfig(final String name, final HikariConfig config) {
        configurations.put(name, config)
        return this
    }
    
}

Next we create a class that holds a Map of HikariDataSource objects for each database name and HikariConfig object in the DataSourcesConfiguration class:

// File: src/main/groovy/mrhaki/ratpack/configuration/DataSources.groovy
package mrhaki.ratpack.configuration

import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource

class DataSources {
    
    @Delegate
    private final Map<String, HikariDataSource> dataSources = [:]

    DataSources(final DataSourcesConfig config) {
        // Create a new Map with HikariDataSource objects as
        // values and the database name as key.
        dataSources = 
            config.collectEntries { String name, HikariConfig hikariConfig ->
                [(name): new HikariDataSource(hikariConfig)]
            }
    }

}

Like with the default HikariModule we also create a class that implements the ratpack.service.Service interface, so we can close the datasources when Ratpack is stopped:

// File: src/main/groovy/mrhaki/ratpack/configuration/DataSourcesService.groovy
package mrhaki.ratpack.configuration

import com.zaxxer.hikari.HikariDataSource
import ratpack.service.Service
import ratpack.service.StopEvent

class DataSourcesService implements Service {
    
    private final DataSources dataSources

    DataSourcesService(final DataSources dataSources) {
        this.dataSources = dataSources
    }

    DataSources getDataSources() {
        return dataSources
    }

    @Override
    void onStop(final StopEvent event) throws Exception {
        dataSources.each { String name, HikariDataSource dataSource ->
            dataSource.close()
        }
    }
}

Let's put all this together in a ConfigurableModule. In the module we use the @Provides annotation to make the objects available in the Ratpack registry:

// File: src/main/groovy/mrhaki/ratpack/configuration/DataSourcesModule.groovy
package mrhaki.ratpack.configuration

import com.google.inject.Provides
import com.google.inject.Singleton
import ratpack.guice.ConfigurableModule

class DataSourcesModule extends ConfigurableModule<DataSourcesConfig> {
    
    @Override
    protected void configure() {
        // Objects are provided with the @Provides annotation. 
    }

    /**
     * Provide DataSourceService, so Ratpack can use it in the
     * Ratpack application lifecycle.
     * 
     * @param config Configuration for datasources.
     * @return DataSourcesService to close datasources on application stop.
     */
    @Provides
    @Singleton
    DataSourcesService dataSourcesServices(final DataSourcesConfig config) {
        final DataSources dataSources = new DataSources(config)
        new DataSourcesService(dataSources)
    }

    /**
     * DataSources has a Map with database name as key and 
     * HikariDataSource as value.
     * 
     * @param dataSourcesService DataSourcesService has already 
     *                           an instance of DataSources.
     * @return Object that we can use to get a HikariDataSource by name.
     */
    @Provides
    @Singleton
    DataSources dataSources(final DataSourcesService dataSourcesService) {
        dataSourcesService.dataSources
    }
    
}

Finally we create another module that uses the DataSources object to create a Map with Sql instances that can be retrieved by the database name. First the class Sqls that holds the map of database names with a corresponding Sql:

// File: src/main/groovy/mrhaki/ratpack/configuration/Sqls.groovy
package mrhaki.ratpack.configuration

import com.zaxxer.hikari.HikariDataSource
import groovy.sql.Sql

class Sqls {
    
    @Delegate
    private final Map<String, Sql> sqls

    Sqls(final DataSources dataSources) {
        // Create new Map with database name as key
        // and Sql instance as value.
        sqls = 
            dataSources.collectEntries { String name, HikariDataSource dataSource ->
                [(name): new Sql(dataSource)]
            }
    }

}

And the module to make an Sqls object available in the registry:

// File: src/main/groovy/mrhaki/ratpack/configuration/SqlsModule.groovy
package mrhaki.ratpack.configuration

import com.google.inject.AbstractModule
import com.google.inject.Provides
import com.google.inject.Singleton

class SqlsModule extends AbstractModule {
    
    @Override
    protected void configure() {
        // We use @Provides annotation.
    }

    /**
     * Create class with Map containing database names
     * with the corresponding Groovy Sql instance.
     * 
     * @param dataSources Datasources to create Sql objects for.
     * @return Object with reference to Sql instances 
     *         identified by database name.
     */
    @Provides
    @Singleton
    Sqls sqls(final DataSources dataSources) {
        new Sqls(dataSources)
    }
    
}

Finally we change the CustomerSql class we created in the previous blog post. This time we pass the Sqls object in the constructor to get both Sql instances:

// File: src/main/groovy/mrhaki/ratpack/CustomerSql.groovy
package mrhaki.ratpack

import groovy.sql.GroovyRowResult
import groovy.sql.Sql
import mrhaki.ratpack.configuration.Sqls
import ratpack.exec.Blocking
import ratpack.exec.Promise

import javax.inject.Inject

class CustomerSql implements CustomerRepository {

    private final Sql customerSql
    private final Sql locationSql

    /**
     * We are using constructor injection to 
     * get both Sql instances.
     *
     * @param Map with Sql instances.
     */
    @Inject
    CustomerSql(final Sqls sqls) {
        customerSql = sqls.customer
        locationSql = sqls.location
    }

    /**
     * Get customer information with address. We use
     * both databases to find the information 
     * for a customer with the given id.
     * 
     * @param id Identifier of the customer we are looking for.
     * @return Customer with filled properties.
     */
    @Override
    Promise<Customer> getCustomer(final String id) {
        Blocking.get {
            final String findCustomerQuery = '''\
                SELECT ID, NAME, POSTALCODE, HOUSENUMBER
                FROM CUSTOMER
                WHERE ID = :customerId
                '''

            customerSql.firstRow(findCustomerQuery, customerId: id)
        }.map { customerRow ->
            new Customer(
                    id: customerRow.id,
                    name: customerRow.name,
                    address: new Address(
                            postalCode: customerRow.postalcode,
                            houseNumber: customerRow.housenumber))
        }.blockingMap { customer ->
            final String findAddressQuery = '''\
                SELECT STREET, CITY
                FROM address
                WHERE POSTALCODE = :postalCode
                  AND HOUSENUMBER = :houseNumber
                '''

            final GroovyRowResult addressRow =
                    locationSql.firstRow(
                            findAddressQuery,
                            postalCode: customer.address.postalCode,
                            houseNumber: customer.address.houseNumber)

            customer.address.street = addressRow.street
            customer.address.city = addressRow.city
            customer
        }
    }
}

In ratpack.groovy we now use our new modules to work with multiple datasources. Notice that the mapping from properties to a Map with HikariConfig objects in DataSourcesConfiguration works out of the box:

// File: src/ratpack/ratpack.groovy
import mrhaki.ratpack.CustomerHandler
import mrhaki.ratpack.CustomerRepository
import mrhaki.ratpack.CustomerSql
import mrhaki.ratpack.configuration.DataSourcesConfig
import mrhaki.ratpack.configuration.DataSourcesModule
import mrhaki.ratpack.configuration.SqlsModule

import static ratpack.groovy.Groovy.ratpack

ratpack {
    serverConfig {
        props 'dataSources.customer.jdbcUrl': 
              'jdbc:postgresql://192.168.99.100:5432/customer'
        props 'dataSources.customer.username': 'postgres'
        props 'dataSources.customer.password': 'secret'

        props 'dataSources.location.jdbcUrl': 
              'jdbc:mysql://192.168.99.100:3306/location?serverTimezone=UTC&useSSL=false'
        props 'dataSources.location.username': 'root'
        props 'dataSources.location.password': 'secret'
    }

    bindings {
        moduleConfig(DataSourcesModule, serverConfig.get('/dataSources', DataSourcesConfig))
        module(SqlsModule)

        // Alternative way to configure DataSourcesModule:
        //module(DataSourcesModule) { DataSourcesConfig config ->
        //    config.addHikariConfig('customer', serverConfig.get('/dataSources/customer', HikariConfig))
        //    config.addHikariConfig('location', serverConfig.get('/dataSources/location', HikariConfig))
        //}
        
        // CustomerSql uses both Sql objects.
        bind(CustomerRepository, CustomerSql)
    }

    handlers {
        get('customer/:customerId', new CustomerHandler())
    }
}

Written with Ratpack 1.3.3.

June 11, 2016

Ratpacked: Using Multiple DataSources

Recently on our project where we use Ratpack we had to get data from different databases in our Ratpack application. We already used the HikariModule to get a DataSource to connect to one database. Then with the SqlModule we use this DataSource to create a Groovy Sql instance in the registry. In our code we use the Sql object to query for data. To use the second database we used the Guice feature binding annotations to annotate a second DataSource and Sql object. In this post we see how we can achieve this.

UPDATE: In a following blog post we learn another way to add multiple datasources to our Ratpack application.

Interestingly while I was writing this post there was a question on the Ratpack Slack channel on how to use multiple datasources. The solution in this post involves still a lot of code to have a second DataSource. In the channel Danny Hyun mentioned a more generic solution involving a Map with multiple datasources. In a follow-up blog post I will write an implementation like that, so we have a more generic solution, with hopefully less code to write. BTW the Ratpack Slack channel is also a great resource to learn more about Ratpack.

We first take a look at the solution where we actually follow the same module structure as the HikariModule and SqlModule. We also use binding annotations that are supported by Guice. With a binding annotation we can have multiple instances of the same type in our registry, but still can distinguish between them. We write a binding annotation for the specific DataSource and Sql objects we want to provide via the registry:

// File: src/main/groovy/mrhaki/ratpack/configuration/LocationDataSource.java
package mrhaki.ratpack.configuration;

import com.google.inject.BindingAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Binding annotation for DataSource of location database.
 */
@BindingAnnotation
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LocationDataSource {
}
// File: src/main/groovy/mrhaki/ratpack/configuration/LocationSql.java
package mrhaki.ratpack.configuration;

import com.google.inject.BindingAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Binding annotation for Groovy Sql object for location database.
 */
@BindingAnnotation
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LocationSql { }

Instead of writing a specific binding annotation we can use the @Named binding annotation and use a name as the value. We will see an example of this in the LocationHikariModule we write.

We are going to write a new class that extends ConfigurableModule to configure a DataSource with Hikari. We use the same structure as the default HikariModule that is supplied by Ratpack:

// File: src/main/groovy/mrhaki/ratpack/configuration/LocationHikariModule.groovy
package mrhaki.ratpack.configuration

import com.google.inject.Provides
import com.google.inject.Singleton
import com.google.inject.name.Named
import com.zaxxer.hikari.HikariDataSource
import ratpack.guice.ConfigurableModule
import ratpack.hikari.HikariService

import javax.sql.DataSource

class LocationHikariModule extends ConfigurableModule<LocationHikariConfig> {
    
    @Override
    protected void configure() {
        // Add HikariService and DataSource
        // via @Provides annotation in this class.
    }

    /**
     * Create a HikariService instance. This object closes
     * the DataSource when Ratpack stops. The LocationHikariConfig
     * object is created by a @Provides method in the 
     * ConfigurableModule class (which we extend from).
     * 
     * @param config Configuration object with properties for creating a HikariDataSource.
     * @return HikariService object with a binding annotation name locationHikariService.
     */
    @Provides
    @Singleton
    @Named('locationHikariService')
    HikariService locationHikariService(final LocationHikariConfig config) {
        return new HikariService(new HikariDataSource(config))
    }

    /**
     * Create a DataSource object with a binding annotation LocationDataSource.
     * 
     * @param hikariService HikariService with binding 
     *        annotation name locationHikariService to get DataSource.
     * @return New DataSource with binding annotation LocationDataSource.a
     */
    @Provides
    @Singleton
    @LocationDataSource
    DataSource locationDataSource(@Named('locationHikariService') HikariService hikariService) {
        return hikariService.dataSource
    }
    
}

And the configuration class we need:

// File: src/main/groovy/mrhaki/ratpack/configuration/LocationHikariConfig.groovy
package mrhaki.ratpack.configuration

import com.zaxxer.hikari.HikariConfig

/**
 * We need a separate class for the configuration
 * of Hikari to get a specific DataSource.
 * If we would re-use HikariConfig to configure
 * the LocationHikariModule we would only have
 * one instance of HikariConfig, because a
 * ConfigurableModule adds a instance of
 * HikariConfig to the registry. And by type
 * is this instance used again. 
 */
class LocationHikariConfig extends HikariConfig {
}

With the LocationHikiriModule class we provided a HikariService, DataSource and LocationHikariConfig instance for the registry. Now we want to use the DataSource and create a Sql instance. We create a LocationSqlModule class and a LocationSqlProvider to create a Sql instance with the binding annotation LocationSql:

// File: src/main/groovy/mrhaki/ratpack/configuration/LocationSqlModule.groovy
package mrhaki.ratpack.configuration

import com.google.inject.AbstractModule
import com.google.inject.Scopes
import groovy.sql.Sql

class LocationSqlModule extends AbstractModule {
    @Override
    protected void configure() {
        // Bind Groovy Sql to registry,
        // but annotated as LocationSql,
        // so we can have two Sql instances in
        // the registry.
        bind(Sql)
            .annotatedWith(LocationSql)
            .toProvider(LocationSqlProvider)
            .in(Scopes.SINGLETON)
    }
}
// File: src/main/groovy/mrhaki/ratpack/LocationSqlProvider.groovy
package mrhaki.ratpack.configuration

import com.google.inject.Provider
import groovy.sql.Sql

import javax.inject.Inject
import javax.sql.DataSource

class LocationSqlProvider implements Provider<Sql> {
    
    private final DataSource dataSource

    /**
     * Assign DataSource when creating object for this class.
     * 
     * @param dataSource Specific DataSource specified by 
     *        the LocationDataSource binding annotation. 
     */
    @Inject
    LocationSqlProvider(@LocationDataSource final DataSource dataSource) {
        this.dataSource = dataSource
    }

    /**
     * Create new Groovy Sql object with the DataSource set 
     * in the constructor.
     * 
     * @return Groovy Sql instance.
     */
    @Override
    Sql get() {
        return new Sql(dataSource)
    }
    
}

We are ready to write a class that uses two Sql instances. We have a CustomerRepository interface to get a Customer that contains properties that come from two databases:

// File: src/main/groovy/mrhaki/ratpack/CustomerRepository.groovy
package mrhaki.ratpack

import ratpack.exec.Promise

interface CustomerRepository {
    Promise<Customer> getCustomer(final String id)
}
// File: src/main/groovy/mrhaki/ratpack/configuration/CustomerSql.groovy
package mrhaki.ratpack

import groovy.sql.GroovyRowResult
import groovy.sql.Sql
import mrhaki.ratpack.configuration.LocationSql
import ratpack.exec.Blocking
import ratpack.exec.Promise

import javax.inject.Inject

class CustomerSql implements CustomerRepository {

    private final Sql customerSql
    private final Sql locationSql

    /**
     * We are using constructor injection to 
     * get both Sql instances.
     *
     * @param customerSql Sql to access the customer database.
     * @param locationSql Sql to access the location database. Sql instance
     * is indicated by binding annotation LocationSql.
     */
    @Inject
    CustomerSql(final Sql customerSql, @LocationSql final Sql locationSql) {
        this.customerSql = customerSql
        this.locationSql = locationSql
    }

    /**
     * Get customer information with address. We use
     * both databases to find the information 
     * for a customer with the given id.
     * 
     * @param id Identifier of the customer we are looking for.
     * @return Customer with filled properties.
     */
    @Override
    Promise<Customer> getCustomer(final String id) {
        Blocking.get {
            final String findCustomerQuery = '''\
                SELECT ID, NAME, POSTALCODE, HOUSENUMBER
                FROM CUSTOMER
                WHERE ID = :customerId
                '''

            customerSql.firstRow(findCustomerQuery, customerId: id)
        }.map { customerRow ->
            new Customer(
                    id: customerRow.id,
                    name: customerRow.name,
                    address: new Address(
                            postalCode: customerRow.postalcode,
                            houseNumber: customerRow.housenumber))
        }.blockingMap { customer ->
            final String findAddressQuery = '''\
                SELECT STREET, CITY
                FROM address
                WHERE POSTALCODE = :postalCode
                  AND HOUSENUMBER = :houseNumber
                '''

            final GroovyRowResult addressRow =
                    locationSql.firstRow(
                            findAddressQuery,
                            postalCode: customer.address.postalCode,
                            houseNumber: customer.address.houseNumber)

            customer.address.street = addressRow.street
            customer.address.city = addressRow.city
            customer
        }
    }
}

We also write a handler that uses the CustomerSql class:

// File: src/main/groovy/mrhaki/ratpack/configuration/CustomerSql.groovy
package mrhaki.ratpack

import ratpack.handling.Context
import ratpack.handling.InjectionHandler

import static groovy.json.JsonOutput.toJson

class CustomerHandler extends InjectionHandler {

    void handle(
            final Context context,
            final CustomerRepository customerRepository) {

        final String customerId = context.pathTokens.customerId

        customerRepository
                .getCustomer(customerId)
                .then { customer -> context.render(toJson(customer)) }
    }

}

We have created all these new classes it is time to get everything together in our ratpack.groovy file:

// File: src/ratpack/ratpack.groovy
import com.zaxxer.hikari.HikariConfig
import mrhaki.ratpack.CustomerHandler
import mrhaki.ratpack.CustomerRepository
import mrhaki.ratpack.CustomerSql
import mrhaki.ratpack.configuration.LocationHikariConfig
import mrhaki.ratpack.configuration.LocationHikariModule
import mrhaki.ratpack.configuration.LocationSqlModule
import ratpack.groovy.sql.SqlModule
import ratpack.hikari.HikariModule

import static ratpack.groovy.Groovy.ratpack

ratpack {
    serverConfig {
        // HikariConfig properties for customer database.
        props 'customer.jdbcUrl': 
              'jdbc:postgresql://192.168.99.100:5432/customer'
        props 'customer.username': 'postgres'
        props 'customer.password': 'secret'

        // LocationHikariConfig properties for location database.
        props 'location.jdbcUrl': 
              'jdbc:mysql://192.168.99.100:3306/location?serverTimezone=UTC&useSSL=false'
        props 'location.username': 'root'
        props 'location.password': 'secret'
    }

    bindings {
        // Default usage of HikariModule.
        // This module will add a DataSource to the registry.
        // The HikariModule is a ConfigurableModule
        // and this means the configuration class
        // HikariConfig is also added to the registry.
        // Finally a HikariService type is added to the
        // registry, which is a Ratpack service that
        // closes the DataSource when the application stops.
        // We use the configuration properties that start with customer. to
        // fill the HikariConfig object.
        moduleConfig(HikariModule, serverConfig.get('/customer', HikariConfig))
        
        // The default SqlModule will find
        // the DataSource type in the registry
        // and creates a new Groovy Sql object.
        module(SqlModule)
        
        // Custom module to add a second DataSource instance
        // to the registry identified by @LocationDataSource binding annotation.
        // We use the configuration properties that start with location. to
        // fill the LocationHikariConfig object.
        moduleConfig(LocationHikariModule, serverConfig.get('/location', LocationHikariConfig))

        // We create a second Sql instance in the registry,
        // using the DataSource with @LocationDataSource binding annotation.
        // The Sql instance is annotated as @LocationSql.
        module(LocationSqlModule)
        
        // CustomerSql uses both Sql objects.
        bind(CustomerRepository, CustomerSql)
    }

    handlers {
        get('customer/:customerId', new CustomerHandler())
    }
}

Written with Ratpack 1.3.3.

January 19, 2016

Ratpacked: Different Base Directory With Marker File

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.

// File: src/main/java/com/mrhaki/ratpack/Application.java
package com.mrhaki.ratpack;

import com.google.common.collect.ImmutableMap;
import ratpack.groovy.Groovy;
import ratpack.groovy.template.MarkupTemplateModule;
import ratpack.guice.Guice;
import ratpack.server.BaseDir;
import ratpack.server.RatpackServer;

public class Application {

    public static void main(String[] args) throws Exception {
        RatpackServer.start(server ->
            server
                .serverConfig(builder ->
                    builder.baseDir(
                        // Instruct Ratpack to search
                        // class path for file
                        // .ratpack.base.dir in the 
                        // directory web-resources.
                        BaseDir.find("web-resources/.ratpack.base.dir"))
                )
                    
                .registry(Guice.registry(bindings -> 
                    bindings.module(MarkupTemplateModule.class))
                )
                    
                .handlers(chain ->
                    chain
                        .get(context -> 
                            context.render(
                                Groovy.groovyMarkupTemplate(
                                    ImmutableMap.of("title", "My Ratpack App"),
                                    "index.gtpl"))
                        )
                        .files(files ->
                            // The public director is resolved from
                            // the base directory web-resources.    
                            files.dir("public")
                        )
                )
        );    
    }
 
}

Of course we use Gradle as build system. The Ratpack Gradle plugin automatically adds the directory src/ratpack to the main source set as a resources directory. Files in this directory will automatically be added to the class path. So for our example application we create the directory src/ratpack/web-resources and create an empty file .ratpack.base.dir in this directory. Any files and directory we now add to this directory will be accessible from the Ratpack application. Suppose we have the following contents:

$ tree -a src/ratpack
src/ratpack
└── web-resources
    ├── .ratpack.base.dir
    ├── public
    │   ├── images
    │   │   └── favicon.ico
    │   ├── lib
    │   ├── scripts
    │   └── styles
    └── templates
        └── index.gtpl

7 directories, 3 files

Written with Ratpack 1.1.1.

January 14, 2016

Ratpacked: Using Database As Custom Configuration Source

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.

package com.mrhaki.config

import groovy.sql.Sql
import ratpack.config.ConfigSource
import ratpack.config.internal.source.AbstractPropertiesConfigSource

/**
 * {@link ConfigSource} implementation to read configuration 
 * data from a database table. The database must have a table
 * CONFIGURATION with the VARCHAR columns KEY and VALUE.
 * The format of the key matches that of regular Java properties.
 * 
 * E.g. we can insert the configuration key 'app.message' like this:
 * INSERT INTO CONFIGURATION(KEY, VALUE) VALUES('app.message', 'Ratpack rocks');
 * 
 * This class extends {@link AbstractPropertiesConfigSource}, because it supports
 * property key formats like we use in our database.
 */
class JdbcConfigSource extends AbstractPropertiesConfigSource {

    /**
     * Database JDBC url, username, password and driver.
     */
    final Map<String, String> sqlProperties

    JdbcConfigSource(final Map<String, String> sqlProperties) {
        super(Optional.empty())
        this.sqlProperties = sqlProperties
    }

    @Override
    protected Properties loadProperties() throws Exception {
        final Properties properties = new Properties()
        
        Sql.withInstance(sqlProperties) { sql->
            // Get key/value pairs from database.
            sql.eachRow("SELECT KEY, VALUE FROM CONFIGURATION") { row ->
                // Save found data in Properties object.
                properties.setProperty(row.key, row.value)
            } 
        } 

        return properties
    }
}

To use this ConfigSource implementation in our Ratpack application we use the add method of ConfigDataBuilder:

import com.mrhaki.config.JdbcConfigSource
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import ratpack.config.ConfigData

import static groovy.json.JsonOutput.prettyPrint
import static groovy.json.JsonOutput.toJson
import static ratpack.groovy.Groovy.ratpack

class SampleConfig {
    String message
}

ratpack {

    bindings {
        final ConfigData configData = ConfigData.of { builder ->
            // Add our custom JdbcConfigSource as 
            // configuration source.            
            builder.add(
                        new JdbcConfigSource(
                            driver: 'org.postgresql.Driver',
                            url: 'jdbc:postgresql://192.168.99.100:32768/',
                            user: 'postgres',
                            password: 'secret'))
                   .build()
        }

        // Assign all configuration properties from the /app node
        // to the properties in the SampleConfig class.
        bindInstance(SimpleConfig, configData.get('/app', SampleConfig))
    }

    handlers {
        get('configprops') { SampleConfig config ->
            render(prettyPrint(toJson(config)))
        }
    }

}

When we request configprops we get the following result:

$ http -b localhost:5050/configprops
{
    "message": "Ratpack rocks!"
}

$ 

Written with Ratpack 1.1.1.

December 24, 2015

Ratpacked: Execute Code On Start and Stop Application Lifecycle Events

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.

// File: src/main/groovy/com/mrhaki/ratpack/Banner.groovy
package com.mrhaki.ratpack

import com.google.common.io.Resources
import groovy.util.logging.Slf4j
import ratpack.server.Service
import ratpack.server.StartEvent
import ratpack.server.StopEvent
import ratpack.util.RatpackVersion

@Slf4j
class Banner implements Service {

    @Override
    void onStart(StartEvent event) throws Exception {
        log.info('Starting Ratpack application')
        showBanner()        
    }
    
    private void showBanner() {
        // Load banner.txt from the classpath.
        final URL bannerResource = Resources.getResource('banner.txt')
        final String banner = bannerResource.text
        
        // Print banner with version info about Ratpack and JVM.
        println banner
        println ":: Ratpack version : ${RatpackVersion.version}"
        println ":: Java version    : ${System.getProperty('java.version')}"
    }

    @Override
    void onStop(StopEvent event) throws Exception {
        log.info('Stopping Ratpack application')
    }
}

In our Ratpack application we use the Banner class and add it to the registry:

// File: src/ratpack/ratpack.groovy
import com.mrhaki.ratpack.Banner

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        // Register Service implementation in the
        // registry. Ratpack picks it up and executes
        // the onStart and onStop methods.
        bind Banner
    }

    handlers {
        get {
            render 'Ratpack rocks!'
        }
    }
}

We also create a file banner.txt in the src/ratpack directory:

    ____        __                   __  
   / __ \____ _/ /_____  ____ ______/ /__
  / /_/ / __ `/ __/ __ \/ __ `/ ___/ //_/
 / _, _/ /_/ / /_/ /_/ / /_/ / /__/ ,<   
/_/ |_|\__,_/\__/ .___/\__,_/\___/_/|_|  
               /_/                       

When we run the application we see the logging messages and our banner:

$ gradle run
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:configureRun
:run
[main] INFO ratpack.server.RatpackServer - Starting server...
[main] INFO ratpack.server.RatpackServer - Building registry...
[main] INFO ratpack.server.RatpackServer - Initializing 1 services...
[ratpack-compute-1-1] INFO com.mrhaki.ratpack.Banner - Starting Ratpack application
    ____        __                   __  
   / __ \____ _/ /_____  ____ ______/ /__
  / /_/ / __ `/ __/ __ \/ __ `/ ___/ //_/
 / _, _/ /_/ / /_/ /_/ / /_/ / /__/ ,<   
/_/ |_|\__,_/\__/ .___/\__,_/\___/_/|_|  
               /_/                       

:: Ratpack version : 1.1.1
:: Java version    : 1.8.0_66
[main] INFO ratpack.server.RatpackServer - Ratpack started (development) for http://localhost:5050
> Building 83% > :run

Written with Ratpack 1.1.1.

November 16, 2015

Ratpacked: Use Command Line Arguments For Configuration

Ratpack 1.1 introduced a feature to use command line arguments for our application configuration. We must use the args method of the ConfigDataBuilder class. We can define a common prefix for the arguments and the separator between the configuration property and value. If we don't specify any arguments then Ratpack assumes there is no prefix and the separator is the equal sign (=).

In the following example Ratpack application we use the args method and rely on all the default settings:

// File: src/ratpack/Ratpack.groovy
import ratpack.config.ConfigData
import ratpack.config.ConfigDataBuilder

import static groovy.json.JsonOutput.prettyPrint
import static groovy.json.JsonOutput.toJson
import static ratpack.groovy.Groovy.ratpack

class SimpleConfig {
    String message
}

ratpack {

    bindings {
        final ConfigData configData = ConfigData.of { ConfigDataBuilder builder ->
            // We use the args method to use 
            // command line arguments to configure
            // our application. The arguments 
            // must have a key and value separated by
            // an equal sign (=).
            // We pass the argument list (args) which is
            // available in our Ratpack.groovy context.
            builder.args(args)

            builder.build()
        }

        // Assign all configuration properties from the /simple node
        // to the properties in the SimpleConfig class.
        bindInstance(SimpleConfig, configData.get('/simple', SimpleConfig))
    }

    handlers {
        get('configprops') { SimpleConfig config ->
            render(prettyPrint(toJson(config)))
        }
    }

}

If we use Gradle to run our application we can reconfigure the run task to pass command line arguments:

// File: build.gradle
...
run {
    args 'simple.message=Sample message'
}
...

If we run the application with our Gradle build file we can request the configprops path and get the following result:

$ http localhost:5050/configprops
...
{
    "message": "Sample message"
}

$

Written with Ratpack 1.1.1.

November 11, 2015

Ratpacked: Apply Configuration To Configurable Module

In Ratpack we can use Guice modules to organise code to provide objects to the registry. Ratpack adds configurable modules that take an extra configuration object. Values from the configuration object are used to create new objects that are provided to our application. Using the Groovy DSL we have several ways to make a configuration object available to a configurable module.

Let's create a sample configurable module and look at the different ways to give it a configuration object. We create a module, GreetingModule, that provides a Greeting object. The configuration is defined in the class GreetingConfig.

// File: src/main/groovy/com/mrhaki/ratpack/GreetingModule.groovy
package com.mrhaki.ratpack

import com.google.inject.Provides
import groovy.transform.CompileStatic
import ratpack.guice.ConfigurableModule

@CompileStatic
class GreetingModule extends ConfigurableModule<GreetingConfig> {
    
    @Override
    protected void configure() {}
    
    /**
     * Method to create a new Greeting object with values
     * from the GreetingConfig configuration object.
     */
    @Provides
    Greeting greeting(final GreetingConfig config) {
        new Greeting(message: "${config.salutation}, ${config.message}")
    }
    
}
// File: src/main/groovy/com/mrhaki/ratpack/GreetingConfig.groovy
package com.mrhaki.ratpack

import groovy.transform.CompileStatic

/**
 * Configuration properties for creating
 * a {@link Greeting} object using the
 * configurable module {@link GreetingModule}.
 */
@CompileStatic
class GreetingConfig {
    String message
    String salutation
}
// File: src/main/groovy/com/mrhaki/ratpack/Greeting.groovy
package com.mrhaki.ratpack

import groovy.transform.CompileStatic

/**
 * Simple class with a greeting message.
 * The {@link GreetingModule} module creates
 * an instance of this class.
 */
@CompileStatic
class Greeting {
    String message
}

We have our configurable module and the supporting classes. Now we define the configuration and module in the bindings block of our Ratpack Groovy application. We use the ConfigData class to set configuration properties we later bind to the GreetingConfig object. With ConfigData we can define configuration properties from different sources, like files in Yaml, JSON or Java properties format. In our sample we simply define them using a Map. In the first sample we use the module method with a Closure argument to configure the GreetingModule

// File: src/ratpack/Ratpack.groovy
import com.mrhaki.ratpack.Greeting
import com.mrhaki.ratpack.GreetingConfig
import com.mrhaki.ratpack.GreetingModule
import ratpack.config.ConfigData
import ratpack.config.ConfigDataBuilder

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        final ConfigData configData = ConfigData.of { ConfigDataBuilder builder ->
            builder.props(
                    ['greeting.salutation': 'Hi',
                     'greeting.message'   : 'how are you doing?'])
            // We can also use external files, 
            // system properties and environment
            // variables to set configuration properties
            // inside this block.
            builder.build()
        }

        // The module methods allows a Closure argument to
        // set values for the supported configuration class.
        module GreetingModule, { GreetingConfig config ->
            config.salutation = configData.get('/greeting/salutation', String)
            config.message = configData.get('/greeting/message', String)
        }
    }

    handlers {
        // Simple handler using the created Greeting object.
        get { Greeting greeting ->
            render greeting.message
        }
    }
}

In the following sample we create an instance of GreetingConfig using the bindInstance method. The GreetingModule will pick up this instance and use it automatically:

// File: src/ratpack/Ratpack.groovy
import com.mrhaki.ratpack.Greeting
import com.mrhaki.ratpack.GreetingConfig
import com.mrhaki.ratpack.GreetingModule
import ratpack.config.ConfigData
import ratpack.config.ConfigDataBuilder

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        final ConfigData configData = ConfigData.of { ConfigDataBuilder builder ->
            builder.props(
                    ['greeting.salutation': 'Hi',
                     'greeting.message'   : 'how are you doing?'])
            // We can also use external files, 
            // system properties and environment
            // variables to set configuration properties
            // inside this block.
            builder.build()
        }

        // First create an instance of the GreetingConfig class. 
        bindInstance GreetingConfig, configData.get('/greeting', GreetingConfig)
        // With the module method we use the GreetingModule and
        // because there is a GreetingConfig instance available 
        // it is used to configure the module.
        module GreetingModule
    }

    handlers {
        // Simple handler using the created Greeting object.
        get { Greeting greeting ->
            render greeting.message
        }
    }
}

Finally we can use the moduleConfig method. This methods accepts a configuration object as an argument directly. So we can use an instance of GreetingConfig as method argument. We can also combine it with a Closure argument to override configuration properties:

// File: src/ratpack/Ratpack.groovy
import com.mrhaki.ratpack.Greeting
import com.mrhaki.ratpack.GreetingConfig
import com.mrhaki.ratpack.GreetingModule
import ratpack.config.ConfigData
import ratpack.config.ConfigDataBuilder

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        final ConfigData configData = ConfigData.of { ConfigDataBuilder builder ->
            builder.props(
                    ['greeting.salutation': 'Hi',
                     'greeting.message'   : 'how are you doing?'])
            // We can also use external files, 
            // system properties and environment
            // variables to set configuration properties
            // inside this block.
            builder.build()
        }

        // With the moduleConfig method we can pass an instance
        // of GreetingConfig directly.
        moduleConfig GreetingModule, configData.get('/greeting', GreetingConfig)

        // Or we can even combine it with a Closure argument to override a 
        // configuration property.
        //moduleConfig(
        //        GreetingModule, 
        //        configData.get('/greeting', GreetingConfig)) { GreetingConfig config ->
        //    config.message = 'Ratpack rocks!'
        //}
    }

    handlers {
        // Simple handler using the created Greeting object.
        get { Greeting greeting ->
            render greeting.message
        }
    }
}

Written with Ratpack 1.1.1.

November 3, 2015

Ratpacked: Externalized Application Configuration

Ratpack has very useful methods to apply application configuration in our application. We can read configuration properties from files in different formats, like JSON, YAML and Java properties, and the files can be read from different locations, like class path or file system. We can also set configuration properties via Java system properties on the command line or use environment variables.

We use the ratpack.config.ConfigData class with the static of method to add configuration properties to our application. We provide a lambda expression or Groovy closure to the of method to build our configuration. Here we specify external files, locations and other configuration options we want to include for our application. If the same configuration property is defined in multiple configuration sources Ratpack will apply the value that is last discovered. This way we can for example provide default values and allow them to be overridden with environment variables if we apply the environment variables last.

To use the values that are gathered we use the get method of the ConfigData instance. We can apply the configuration properties to the properties of a configuration class that is then automatically instantiated. We add this to the registry so we can use the configuration properties further down in our application.

In the following example Ratpack application we use different ways to apply configuration properties. It is inspired by how Spring Boot reads and applies externalized configuration properties. First we use a simple Map with default values, then the class path is scanned for files with names application.yml, application.json and application.properties in either the root of the class path or a config package. Next the same file names are searched on the file system relative from where the application is started. Next Java system properties starting with sample. are applied to the configuration. And finally environment variables starting with SAMPLE_ are interpreted as configuration properties.

Let's start with the simple configuration class and properties we want to set via Ratpack's configuration ability:

// File: src/main/groovy/com/mrhaki/SampleConfig.groovy
package com.mrhaki

/**
 * Configuration properties for our application.
 */
class SampleConfig {

    /**
     * URL for external service to invoke with HTTP client.
     */
    String externalServiceUrl

    /**
     * URI to access the Mongo database.
     */
    String mongoUri

    /**
     * Indicate if we need to use a HTTP proxy.
     */
    boolean useProxy

    /**
     * Simple message
     */
    String message

}

Next we have a very simple Ratpack application. Here we use ConfigData.of with a lot of helper methods to read in configuration properties from different sources:

// File: src/ratpack/Ratpack.groovy
import com.google.common.io.Resources
import com.mrhaki.SampleConfig
import ratpack.config.ConfigData
import ratpack.config.ConfigDataBuilder

import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths

import static groovy.json.JsonOutput.prettyPrint
import static groovy.json.JsonOutput.toJson
import static ratpack.groovy.Groovy.ratpack

ratpack {

    bindings {
        final ConfigData configData = ConfigData.of { builder ->
            // Set default value, can be overridden by
            // configuration further down the chain.
            // The map must have String values.
            builder.props(['app.useProxy': Boolean.TRUE.toString()])

            loadExternalConfiguration(builder)

            // Look for system properties starting with
            // sample. to set or override configuration properties.
            builder.sysProps('sample.')

            // Look for environment variables starting
            // with SAMPLE_ to set or override configuration properties.
            builder.env('SAMPLE_')

            builder.build()
        }

        // Assign all configuration properties from the /app node
        // to the properties in the SampleConfig class.
        bindInstance(SampleConfig, configData.get('/app', SampleConfig))
    }

    handlers {
        get('configprops') { SampleConfig config ->
            render(prettyPrint(toJson(config)))
        }
    }

}

private void loadExternalConfiguration(final ConfigDataBuilder configDataBuilder) {
    
    final List<String> configurationLocations =
            ['application.yml',
             'application.json',
             'application.properties',
             'config/application.yml',
             'config/application.json',
             'config/application.properties']
    
    configurationLocations.each { configurationLocation ->
        loadClasspathConfiguration(configDataBuilder, configurationLocation)
    }
    
    configurationLocations.each { configurationLocation ->
        loadFileSystemConfiguration(configDataBuilder, configurationLocation)
    }
}

private void loadClasspathConfiguration(
        final ConfigDataBuilder configDataBuilder,
        final String configurationName) {

    try {
        final URL configurationResource = Resources.getResource(configurationName)
        switch (configurationName) {
            case yaml():
                configDataBuilder.yaml(configurationResource)
                break
            case json():
                configDataBuilder.json(configurationResource)
                break
            case properties():
                configDataBuilder.props(configurationResource)
                break
            default:
                break
        }
    } catch (IllegalArgumentException ignore) {
        // Configuration not found.
    }

}

private void loadFileSystemConfiguration(
        final ConfigDataBuilder configDataBuilder,
        final String configurationFilename) {

    final Path configurationPath = Paths.get(configurationFilename)
    if (Files.exists(configurationPath)) {
        switch (configurationFilename) {
            case yaml():
                configDataBuilder.yaml(configurationPath)
                break
            case json():
                configDataBuilder.json(configurationPath)
                break
            case properties():
                configDataBuilder.props(configurationPath)
                break
            default:
                break
        }
    }
}

private def yaml() {
    return hasExtension('yml')
}

private def json() {
    return hasExtension('json')
}

private def properties() {
    return hasExtension('properties')
}

private def hasExtension(final String extension) {
    return { filename -> filename ==~ /.*\.${extension}$/ }
}

Next we create some external configuration files:

# File: src/ratpack/application.yml
---
app:
  mongoUri: mongodb://mongo:27017/test
# File: src/ratpack/application.properties
app.externalServiceUrl = http://remote:9000/api
app.message = Ratpack rules!

Let's run the application and see the output of the configprops endpoint:

$ http localhost:5050/configprops
...
{
    "externalServiceUrl": "http://remote:9000/api",
    "useProxy": true,
    "message": "Ratpack rules!",
    "mongoUri": "mongodb://mongo:27017/test"
}

Next we stop the application and start it with the Java system property -Dsample.app.useProxy=false and the environment variable SAMPLE_APP__MESSAGE='Ratpack rocks!'. We check the configprops endpoint again:

$ http localhost:5050/configprops
...
{
    "externalServiceUrl": "http://remote:9000/api",
    "useProxy": false,
    "message": "Ratpack rocks!",
    "mongoUri": "mongodb://mongo:27017/test"
}

Written with Ratpack 1.0.0.

This is the 1000th blog post on the Messages from mrhaki blog. I've blogged about many different subjects, starting with Apache Cocoon, Netbeans, followed by Groovy and Groovy related technologies like Grails, Gradle. Also about other developer subjects like Asciidoctor and much more. I hope you enjoyed the first 1000, because I am not finished and will continue with more blog posts about great technologies.


October 2, 2015

Ratpacked: Start Ratpack With a Random Port Number

To start our Ratpack application with a random port number we must use the value 0 as port number. The value 0 tells Ratpack to use a random port number (in a safe port range).

In the following example Ratpack application we configure the server port to be random:

import static ratpack.groovy.Groovy.ratpack

ratpack {
    serverConfig {
        // Tell Ratpack to use a 
        // random port number.
        port 0
    }
}

Written with Ratpack 1.0.0.

Ratpacked: Change Server Port With Environment Variable

When we define a Ratpack application we can set a server port in the server configuration code. When we do not define the port number in our code and use the default server configuration we can also set the server port with the environment variables PORT or RATPACK_PORT.

In the following example we use Gradle as build tool to run our Ratpack application. Gradle will pass on environment variables to the run task. We use the environment variable RATPACK_PORT to change the port to 9000:

$ RATPACK_PORT=9000 gradle run
Starting server...
Building registry...
Ratpack started (development) for http://localhost:9000

Alternatively we can define the port number in our configuration, but also add an option to support environment variables to set or override configuration properties. This support is simply added by invoking the env method on the ConfigDataBuilder interface. With this method Ratpack looks for environment variables that start with RATPACK_. The rest of the environment variable is then parsed and transformed to a configuration property. A double underscore is used as separator between sections, a single underscore is used as boundary for camel case fields. For example the environment variable RATPACK_SERVER__PORT transforms to server.port.

import static ratpack.groovy.Groovy.ratpack

ratpack {
    serverConfig {
        // Instruct Ratpack to look for
        // environment variables that
        // start with RATPACK_ as
        // configuration properties.
        env()
    }
}

Let's run our application and use the environment variable RATPACK_SERVER__PORT to change the port number:

$ RATPACK_SERVER__PORT=9000 gradle run
Starting server...
Building registry...
Ratpack started (development) for http://localhost:9000

We can alter the default prefix RATPACK for environment variables. We still use the method env, but this time we specify an argument as the prefix for the environment variables. In the following code we see how to use the prefix COCKTAILS_ for environment variables:

package learning.ratpack;

import ratpack.server.RatpackServer;

public class Main {
    public static void main(String[] args) throws Exception {
        RatpackServer.start(server ->
            server
                .serverConfig(serverConfig ->
                    serverConfig
                        // Define prefix for environment
                        // variables that Ratpack uses.
                        .env("COCKTAILS_")));
    }
}

Let's run our application and use the environment variable COCKTAILS_SERVER__PORT to change the port number:

$ COCKTAILS_SERVER__PORT=9000 gradle run
Starting server...
Building registry...
Ratpack started (development) for http://localhost:9000

Written with Ratpack 1.0.0.

September 30, 2015

Ratpacked: Use Asynchronous Logging

Ratpack is from the ground up build to be performant and asynchronous. Let's add a logging implementation that matches the asynchronous nature of Ratpack. Ratpack uses the SLF4J API for logging and if we write logging statement in our own code we should use the same API. For Groovy developers it is nothing more than adding the @Slf4j AST annotation to our classes. The Logback library has an asynchronous appender which has a queue to store incoming logging events. Then a worker on a different thread will invoke a classic blocking appender, like a file or console appender, to actually log the messages. But in our example we don't use the standard async appender from Logback, but use a asynchronous logbook appender from the Reactor project. Now our queue is backed by a very performant reactor ring buffer implementation.

The following Logback configuration file shows how we can configure the reactor.logback.AsyncAppender:

<!-- File: src/main/resources/logback.xml -->
<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%-30(%d{HH:mm:ss.SSS} [%thread]) %-5level %logger{32} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Create new asynchronous Logback appender backed by Reactor RingBufferProcessor. -->
    <appender name="ASYNC" class="reactor.logback.AsyncAppender">
        <!-- Backlog size for logging events. Change size if they are picked up slowly.
             Default is 1024 * 1024 -->
        <backlog>1048576</backlog>

        <!-- Caller data is relatively slow, so per default disabled -->
        <includeCallerData>false</includeCallerData>

        <!-- Redirect logging messages to STDOUT -->
        <appender-ref ref="STDOUT"/>
    </appender>

    <root level="INFO">
        <appender-ref ref="ASYNC"/>
    </root>

</configuration>

We need to add a runtime dependency for io.projectreactor:reactor-logback in our build.gradle file to use the AsyncAppender:

...
repositories {
    jcenter()
}

dependencies {
    runtime 'ch.qos.logback:logback-classic:1.1.3'
    runtime 'io.projectreactor:reactor-logback:2.0.5.RELEASE'
}
...

Russell Hart mentioned via Twitter that we can also use asynchronous loggers from Log4j 2 as described in Ratpack book examples. First we must add some new runtime dependencies. Log4j 2 has a bridge for SLF4J API so we add that together with other Log4j 2 dependencies. The asynchronous loggers use LMAX disruptor and we need that as a dependency as well. Our build file now looks like this:

...
repositories {
    jcenter()
}

dependencies {
    ext {
        log4jGroup = 'org.apache.logging.log4j'
        log4jVersion = '2.4'
    }

    runtime "$log4jGroup:log4j-slf4j-impl:$log4jVersion"
    runtime "$log4jGroup:log4j-api:$log4jVersion"
    runtime "$log4jGroup:log4j-core:$log4jVersion"
    runtime 'com.lmax:disruptor:3.3.2'
}
...

Next we create a new configuration file, log4j2.xml, which is used by Log4j 2 to configure the loggers. Notice we use <AsyncLogger/> and <AsyncRoot/> to use asynchronous logging. To learn more about the configuration we need to look at the Log4j website.

<?xml version="1.0" encoding="UTF-8"?>
<!-- File: src/main/resources/log4j2.xml -->
<Configuration status="WARN">
    <Appenders>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout pattern="%m%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <AsyncLogger name="ratpack.server.RatpackServer" level="DEBUG" />
        <AsyncRoot level="ERROR">
            <AppenderRef ref="STDOUT"/>
        </AsyncRoot>
    </Loggers>
</Configuration>

Written with Ratpack 1.0.0.

Ratpacked: Default Port Is 5050

Update: Since Ratpack 1.1.0 the port number is always shown on the console, even if we don't add a SLF4J API implementation.

When we use all the defaults for a Ratpack application the default port that is used for listening to incoming requests is 5050. This is something to remember, because we don't see it when we start the application. If we want to show it, for example in the console, we must add a SLF4J Logging API implementation. Ratpack uses SLF4J API for logging and the port number and address that is used for listening to incoming requests are logged at INFO level. We must add a runtime dependency to our project with a SLF4J API implementation. We provide the necessary logging configuration if needed and then when we start our Ratpack application we can see the port number that is used.

In the following example we use the Logback library as a SLF4J API implementation. We add a runtime dependency ch.qos.logback:logback-classic:1.1.3 to our Gradle build file. If we would use another build tool, we can still use the same dependency. And also we add a simple Logback configuration file in the src/main/resources directory.

// File: build.gradle
plugins {
    id 'io.ratpack.ratpack-java' version '1.0.0'
}

mainClassName = 'com.mrhaki.ratpack.Main'

repositories {
    jcenter()
}

dependencies {
    // Here we add the Logback classic
    // dependency, so we can configure the
    // logging in the Ratpack application.
    runtime 'ch.qos.logback:logback-classic:1.1.3'
}

We create a Logback XML configuration file in src/main/resources:

<!-- File: src/main/resources/logback.xml -->
<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%msg%n</pattern>
        </encoder>
    </appender>

    <!-- Log all INFO level messages from RatpackServer -->
    <logger name="ratpack.server.RatpackServer" level="INFO"/>

    <root level="ERROR">
        <appender-ref ref="STDOUT"/>
    </root>

</configuration>

When we start the application with Gradle we can see in our console the logging messages from the RatpackServer. The last line shows the port number of our running Ratpack application:

$ gradle -q run
Starting server...
Building registry...
Ratpack started (development) for http://localhost:5050

Written with Ratpack 1.0.0.