Loading...

May 30, 2016

Ratpacked: Using Spring As Component Registry

Usually in our Ratpack application we use a registry to store components that we want to use in our application code. The calling code for the registry doesn't need to know how the registry is implemented. Ratpack support Google Guice for example, but Spring is also supported. This means we can define the components for our registry using Spring and we only have to tell Ratpack where to look for the Spring configuration files. Ratpack provides for us the ratpack.spring.Spring class with the static method spring. This method returns a Ratpack registry implementation we can use in our application.

To enable Spring support we must add the Ratpack spring-boot dependency to our build file:

// File: build.gradle
...
dependencies {
    compile ratpack.dependency('spring-boot')
}
...

In our first example we first write a simple Spring configuration file. In the file we also use Spring's profile support, so depending on the active profile different beans are defined:

// File: src/main/groovy/mrhaki/ratpack/spring/MessageConfig.groovy
package mrhaki.spring

import groovy.transform.CompileStatic
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile

@CompileStatic
@Configuration
class MessageConfig {
    
    @Bean
    Message messageDefault() {
        new MessageImpl('DEFAULT')
    }
    
    @Bean 
    @Profile('dev')
    Message messageDevelopment() {
        new MessageImpl('DEV')
    }

    @Bean
    @Profile('prod')
    Message messageProduction() {
        new MessageImpl('PROD')
    }

}

@CompileStatic
interface Message {
    String message()
}

@CompileStatic
class MessageImpl implements Message {

    private final String env

    MessageImpl(final String env) {
        this.env = env
    }

    @Override
    String message() {
        return "Hello from Spring! - running in $env"
    }

}

To turn this Spring configuration into a Ratpack registry we simply use the static spring method of the ratpack.spring.Spring class:

import mrhaki.spring.Message
import mrhaki.spring.MessageConfig
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import ratpack.spring.Spring

import static ratpack.groovy.Groovy.ratpack

ratpack {
    handlers {
        // Use Spring Application context as registry
        // for components to be used in our Ratpack application.
        // With the register() method we make the Registry
        // available for all handlers.
        register Spring.spring(new AnnotationConfigApplicationContext(MessageConfig))
        
        get { Message message ->
            render message.message()
        }
    }
}

If we make a request for the URL http://localhost:5050/ we get the following output: Hello from Spring! - running in DEFAULT.

In this example we use the spring method that accepts a ListableBeanFactory implementation to create the new registry. But actually we can also provide one or more Spring configuration classes directly to the spring method. We can use Spring Boot configuration to define our registry. So we can rely on all the auto configuration features of Spring Boot to create a new registry in our Ratpack application. We can also pass arguments with the spring method that normally would come from the command line for a Spring application. Let's rewrite our previous example and use the configuration class with an argument to select a Spring profile:

import mrhaki.spring.Message
import mrhaki.spring.MessageConfig
import ratpack.server.ServerConfig
import ratpack.spring.Spring

import static ratpack.groovy.Groovy.ratpack

ratpack {
    handlers {
        // Instead of the register() method we use all()
        // together with next() method to define registry
        // for the handlers.
        all {
            // Use Spring configuration as registry
            // for components to be used in our Ratpack application.
            // We pass extra arguments to select a Spring profile.
            final Boolean devMode = get(ServerConfig).development
            next(Spring.spring(MessageConfig, "--spring.profiles.active=${devMode ? 'dev' : 'prod'}"))
        }

        get { Message message ->
            render message.message()
        }        
    }
}

So if we start the Ratpack application in development mode we get a different implementation for the Message interface than if we would start in non-development mode, because the value of spring.profiles.active determines which profile is active. Let's open http://localhost:5050/ when we run in development mode and we get the following output: Hello from Spring! - running in DEV.

Finally we can use a SpringApplicationBuilder object with the spring method. We can for example turn of the banner or disable the logging of startup information:

import mrhaki.spring.Message
import mrhaki.spring.MessageConfig
import org.springframework.boot.Banner
import org.springframework.boot.builder.SpringApplicationBuilder
import ratpack.spring.Spring

import static ratpack.groovy.Groovy.ratpack

ratpack {
    handlers {
        // Using SpringApplicationBuilder for further customization.
        // We can add multiple configuration sources to the constructor.
        final SpringApplicationBuilder springApp = 
                new SpringApplicationBuilder(MessageConfig)
                    .bannerMode(Banner.Mode.OFF)
                    .logStartupInfo(true)
        
        // We could also pass arguments like in the previous 
        // example using this spring() method implementation.
        register Spring.spring(springApp)
        
        get { Message message ->
            render message.message()
        }        
    }
}

We start our Ratpack application and we get the following logging on our console:

[main] INFO ratpack.server.RatpackServer - Starting server...
[main] INFO ratpack.server.RatpackServer - Building registry...
[2016-05-30 07:12:21.161] - 61301 INFO [main] --- ratpack.groovy.GroovyRatpackMain: Starting GroovyRatpackMain on mrhaki-laptop with PID 61301 (/Users/mrhaki/.gradle/caches/modules-2/files-2.1/io.ratpack/ratpack-groovy/1.3.3/34230f9bf9c4e1b0e8c0fd253b9b4f62ef154096/ratpack-groovy-1.3.3.jar started by mrhaki in /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/ratpack/spring)
[2016-05-30 07:12:21.164] - 61301 INFO [main] --- ratpack.groovy.GroovyRatpackMain: No profiles are active
[2016-05-30 07:12:21.190] - 61301 INFO [main] --- org.springframework.context.annotation.AnnotationConfigApplicationContext: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5ca1f591: startup date [Mon May 30 07:12:21 CEST 2016]; root of context hierarchy
[2016-05-30 07:12:21.275] - 61301 INFO [main] --- org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
[2016-05-30 07:12:21.325] - 61301 INFO [main] --- ratpack.groovy.GroovyRatpackMain: Started GroovyRatpackMain in 0.314 seconds (JVM running for 1.874)
[main] INFO ratpack.server.RatpackServer - Ratpack started (development) for http://localhost:5050

Written with Ratpack 1.3.3