Loading...

March 17, 2017

Spring Sweets: Access Application Arguments With ApplicationArguments Bean

When we start a Spring Boot application and pass arguments to the application, Spring Boot will capture those arguments and creates a Spring bean of type ApplicationArguments and puts it in the application context. We can use this Spring bean to access the arguments passed to the application. We could for example auto wire the bean in another bean and use the provided argument values. The ApplicationArguments interface has methods to get arguments values that are options and plain argument values. An option argument is prefixed with --, for example --format=xml is a valid option argument. If the argument value is not prefixed with -- it is a plain argument.

In the following example application we have a Spring Boot application with a Spring bean that implements CommandLineRunner. When we define the CommandLineRunner implementation we use the ApplicationArguments bean that is filled by Spring Boot:

package mrhaki.springboot;

import org.jooq.DSLContext;
import org.jooq.Record1;
import org.jooq.Result;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.List;

import static mrhaki.springboot.sql.Tables.USERS;

@SpringBootApplication
public class SampleApp {

    /**
     * Run the application, let Spring Boot
     * decide the exit code and use the
     * exit code value for {@code System.exit()} method.
     */
    public static void main(String[] args) {
        SpringApplication.run(SampleApp.class, args);
    }

    /**
     * Find users based on user input given via application arguments.
     * Example usage: {@code java -jar sample.jar --format=csv mrhaki}
     * 
     * @param dslContext JOOQ context to access data.
     * @param applicationArguments Bean filled by Spring Boot 
     *                             with arguments used to start the application
     * @return Bean to run at startup
     */
    @Bean
    CommandLineRunner showUsers(
            final DSLContext dslContext, 
            final ApplicationArguments applicationArguments) {
        
        return (String... arguments) -> {
            // Get arguments passed to the application  that are
            // not option arguments from ApplicationArguments bean.
            // In the following example: java -jar sample.jar mrhaki
            // mrhaki is the non option argument.
            final String name = applicationArguments.getNonOptionArgs().get(0);
            final Result<Record1<String>> result =
                    dslContext.select(USERS.NAME)
                              .from(USERS)
                              .where(USERS.NAME.likeIgnoreCase(name))
                              .fetch();

            // Check if option argument --format is used.
            // In the following example: java -jar sample.jar --format=xml --format=csv
            // format is the option argument with two values: xml, csv.
            if (applicationArguments.containsOption("format")) {
                // Get values for format option argument.
                final List<String> format = applicationArguments.getOptionValues("format");
                if (format.contains("xml")) {
                    result.formatXML(System.out);
                }
                if (format.contains("json")) {
                    result.formatJSON(System.out);
                }
                if (format.contains("html")) {
                    result.formatHTML(System.out);
                }
                if (format.contains("html")) {
                    result.formatCSV(System.out);
                }
            } else {
                result.format(System.out);
            }
        };
    }

}

We have an alternative if we want to use the ApplicationArguments bean in a CommandlineRunner implementation. Spring Boot offers the ApplicationRunner interface. The interface has the method run(ApplicationArguments) we need to implement and gives us directly access to the ApplicationArguments bean. In the next example we refactor the application and use ApplicationRunner:

package mrhaki.springboot;

import org.jooq.DSLContext;
import org.jooq.Record1;
import org.jooq.Result;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.List;

import static mrhaki.springboot.sql.Tables.USEaRS;

@SpringBootApplication
public class SampleApp {

    /**
     * Run the application, let Spring Boot
     * decide the exit code and use the
     * exit code value for {@code System.exit()} method.
     */
    public static void main(String[] args) {
        SpringApplication.run(SampleApp.class, args);
    }

    /**
     * Find users based on user input given via application arguments.
     * Example usage: {@code java -jar sample.jar --format=csv mrhaki}
     *
     * @param dslContext JOOQ context to access data.
     * @return Bean to run at startup
     */
    @Bean
    ApplicationRunner showUsers(final DSLContext dslContext) {
        return (ApplicationArguments arguments) -> {
            // Get arguments passed to the application that are
            // not option arguments. 
            // In the following example: java -jar sample.jar mrhaki
            // mrhaki is the non option argument.
            final String name = arguments.getNonOptionArgs().get(0);
            final Result<Record1<String>> result =
                    dslContext.select(USERS.NAME)
                              .from(USERS)
                              .where(USERS.NAME.likeIgnoreCase(name))
                              .fetch();

            // Check if option argument --format is used.
            // In the following example: java -jar sample.jar --format=xml --format=csv
            // format is the option argument with two values: xml, csv.
            if (arguments.containsOption("format")) {
                // Get values for format option argument.
                final List<String> format = arguments.getOptionValues("format");
                if (format.contains("xml")) {
                    result.formatXML(System.out);
                } 
                if (format.contains("json")) {
                    result.formatJSON(System.out);
                }
                if (format.contains("html")) {
                    result.formatHTML(System.out);
                }
                if (format.contains("html")) {
                    result.formatCSV(System.out);
                }
            } else {
                result.format(System.out);
            }
        };
    }

}

Written with Spring Boot 1.5.2.RELEASE.

March 15, 2017

Spring Sweets: Custom Exit Code From Exception

When we write a Spring Boot application a lot of things are done for us. For example when an exception in the application occurs when we start our application, Spring Boot will exit the application with exit code 1. If everything goes well and the we stop the application the exit code is 0. When we use the run method of SpringApplication and an exception is not handled by our code, Spring Boot will catch it and will check if the exception implements the ExitCodeGenerator interface. The ExitCodeGenerator interface has one method getExitCode() which must return a exit code value. This value is used as input argument for the method System.exit() that is invoke by Spring Boot to stop the application.

In the following example application we write a Spring Boot command line application that can throw an exception on startup. The exception class implements the ExitCodeGenerator interface:

// File: src/main/java/mrhaki/springboot/SampleApp.java
package mrhaki.springboot;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.Random;

import static org.springframework.boot.SpringApplication.run;

@SpringBootApplication
public class SampleApp {

    /**
     * Run the application.
     */
    public static void main(String[] args) {
        run(SampleApp.class, args);
    }

    /**
     * Very simple {@code CommandLineRunner} to get a
     * random {@code Integer} value and throw exception if value
     * is higher than or equal to 5. This code is executed
     * when we run the application.
     *
     * @return Simple application logic.
     */
    @Bean
    CommandLineRunner randomException() {
        return args -> {
            final Integer value = new Random().nextInt(10);
            if (value >= 5) {
                throw new TooHighException(String.format("Value %d is too high", value));
            } else {
                System.out.println(value);
            }
        };
    }

    /**
     * Exception when a Integer value is too high.
     * We implement the {@code ExitCodeGenerator} interface and
     * annotate this class as a Spring component. Spring Boot
     * will look for classes that implement {@code ExitCodeGenerator}
     * and use them to get a exit code.
     */
    static class TooHighException extends Exception implements ExitCodeGenerator {
        TooHighException(final String message) {
            super(message);
        }

        /**
         * @return Always return 42 as exit code when this exception occurs.
         */
        @Override
        public int getExitCode() {
            return 42;
        }
    }

}

But there is also an exit method available in the SpringApplication class. This method determines the exit code value by looking up beans in the application context that implement the ExitCodeExceptionMapper interface. The ExitCodeExceptionMapper interface has the method getExitCode(Throwable). We can write an implementation that has logic to return different exit codes based on characteristics of the given Throwable. This is especially useful when we want to have an exit code for exceptions from third party libraries.

In the following example we use the exit method of SpringApplication and a Spring bean exitCodeExceptionMapper that implements the ExitCodeExceptionMapper interface using a Java 8 lambda expression:

// File: src/main/java/mrhaki/springboot/SampleApp.java
package mrhaki.springboot;

import org.jooq.DSLContext;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeExceptionMapper;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.dao.DataAccessResourceFailureException;

import static mrhaki.springboot.sql.Tables.USERS;
import static org.springframework.boot.SpringApplication.exit;
import static org.springframework.boot.SpringApplication.run;

@SpringBootApplication
public class SampleApp {

    /**
     * Run the application, let Spring Boot
     * decide the exit code and use the
     * exit code value for {@code System.exit()} method.
     */
    public static void main(String[] args) {
        System.exit(exit(run(SampleApp.class, args)));
    }

    /**
     * Very simple {@code CommandLineRunner} to get a
     * a list of users from a Postgresql database 
     * using JOOQ.
     *
     * @return Simple application logic.
     */
    @Bean
    CommandLineRunner showUsers(final DSLContext dslContext) {
        return args -> dslContext.select(USERS.NAME)
                                 .from(USERS)
                                 .fetch()
                                 .forEach(name -> System.out.println(name.get(USERS.NAME)));
    }

    /**
     * Spring bean that implements the {@code ExitCodeExceptionMapper}
     * interface using a lambda expression.
     */
    @Bean
    ExitCodeExceptionMapper exitCodeExceptionMapper() {
        return exception -> {
            // Using ExitCodeExceptionMapper we can set
            // the exit code ourselves, even if we didn't
            // write the exception ourselves.
            if (exception.getCause() instanceof DataAccessResourceFailureException) {
                return 42;
            }
            return 1;
        };
    }

}

Written with Spring Boot 1.5.2.RELEASE.

March 8, 2017

Ratpacked: Override Registry Objects With Mocks In Integration Specifications

Testing a Ratpack application is not difficult. Ratpack has excellent support for writing unit and integration tests. When we create the fixture MainClassApplicationUnderTest we can override the method addImpositions to add mock objects to the application. We can add them using the ImpositionsSpec object. When the application starts with the test fixture the provided mock objects are used instead of the original objects. For a Groovy based Ratpack application we can do the same thing when we create the fixture GroovyRatpackMainApplicationUnderTest.

We start with a simple Java Ratpack application. The application adds an implementation of a NumberService interface to the registry. A handler uses this implementation for rendering some output.

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

import ratpack.registry.Registry;
import ratpack.server.RatpackServer;

public class RatpackApplication {
    public static void main(String[] args) throws Exception {
        RatpackServer.start(server -> server
                // Add a implementation of NumberService interface
                // to the registry, so it can be used by the handler.
                .registry(Registry.single(NumberService.class, new NumberGenerator()))
                
                // Register a simple handler to get the implementation
                // of the NumberService interface, invoke the give() method
                // and render the value.
                .handler(registry -> ctx -> ctx
                        .get(NumberService.class).give()
                        .then(number -> ctx.render(String.format("The answer is: %d", number)))));
    }
}

The NumberService interface is not difficult:

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

import ratpack.exec.Promise;

public interface NumberService {
    Promise<Integer> give();
}

The implementation of the NumberService interface returns a random number:

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

import ratpack.exec.Promise;

import java.util.Random;

public class NumberGenerator implements NumberService {
    @Override
    public Promise<Integer> give() {
        return Promise.sync(() -> new Random().nextInt());
    }
}

To test the application we want to use a mock for the NumberService interface. In the following specification we override the addImpositions method of the MainClassApplicationUnderTest class:

// File: src/test/groovy/mrhaki/ratpack/RatpackApplicationSpec.groovy
package mrhaki.ratpack

import ratpack.exec.Promise
import ratpack.impose.ImpositionsSpec
import ratpack.impose.UserRegistryImposition
import ratpack.registry.Registry
import ratpack.test.MainClassApplicationUnderTest
import ratpack.test.http.TestHttpClient
import spock.lang.AutoCleanup
import spock.lang.Specification

/**
 * Integration test for the application.
 */
class RatpackApplicationSpec extends Specification {
    
    /**
     * Mock implementation for the {@link NumberService}.
     */
    private final NumberService mockNumberService = Mock()

    /**
     * Setup {@link RatpackApplication} for testing and provide
     * the {@link #mockNumberService} instance to the Ratpack registry.
     */
    @AutoCleanup
    private aut = new MainClassApplicationUnderTest(RatpackApplication) {
        @Override
        protected void addImpositions(final ImpositionsSpec impositions) {
            // Set implementation of NumberService interface to
            // our mock implementation for the test.
            impositions.add(
                    UserRegistryImposition.of(
                            Registry.single(NumberService, mockNumberService)))
        }
    }

    /**
     * Use HTTP to test our application.
     */
    private TestHttpClient httpClient = aut.httpClient
    
    void 'render output with number'() {
        when:
        final response = httpClient.get()
        
        then:
        // Our mock should get invoked once and we return 
        // the fixed value 42 wrapped in a Promise.
        1 * mockNumberService.give() >> Promise.sync { 42 }

        and:
        response.statusCode == 200
        response.body.text == 'The answer is: 42'
    }
}

Written with Ratpack 1.4.5.

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.

March 7, 2017

Ratpacked: Type Check And Static Compilation For Groovy DSL

One of the very nice features of Ratpack is the Groovy DSL to define our application. We get a nice DSL to set up the registry, to define handlers and more. Because of clever use of the @DelegateTo annotation we get good code completion in our IDE. We can also add static compilation of our Groovy DSL when we start our Ratpack application. With static compilation the script is type checked at compile time so we get earlier feedback on possible errors in the script. To configure static compilation we must invoke the app method of the Groovy.Script class with the argument true.

We start with a Groovy DSL for an application that serves recipes. Notice the Closure arguments are all typed, so with type checking there are no errors.

// File: src/ratpack/ratpack.groovy
import mrhaki.ratpack.Recipe
import mrhaki.ratpack.RecipeRenderer
import mrhaki.ratpack.RecipeRepository
import mrhaki.ratpack.RecipeRequest
import mrhaki.ratpack.RecipesList

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        add new RecipeRenderer()
        add RecipeRepository, new RecipesList()
    }

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

Next we create a class with a main method that will be the starting point of our application. We need to run this class to start our Ratpack application. The example class is a Java class, but could also be written with Groovy:

// File: src/main/java/mrhaki/sample/GroovyCompileStaticRatpackMain.java
package mrhaki.ratpack;

import ratpack.groovy.Groovy;
import ratpack.server.RatpackServer;

import java.util.Optional;

public class GroovyCompileStaticRatpackMain {
    
    public static void main(String[] args) throws Exception {
        RatpackServer.start(Groovy.Script.app(true /* compileStatic */));
    }
}

When we use the Gradle Ratpack plugin we use this class as the main class:

// File: build.gradle
...
mainClassName = 'mrhaki.ratpack.GroovyCompileStaticRatpackMain'
...

Now we can still use the run task to start our Ratpack application.

Written with Ratpack 1.4.5.

Gradle Goodness: Create Shortcut Key To Refresh Gradle Projects In IntellIJ IDEA

We can open a Gradle project in IntelliJ IDEA and get support for Gradle inside IntelliJ. Sometimes we need to refresh the project in IntelliJ IDEA, for example when we add a new dependency or plugin in our Gradle build file. We need to refresh the Gradle project so IntelliJ IDEA can work with the changes. The Gradle tool window has an icon to Refresh all Gradle projects. But this means a mouse action and we want to have a shortcut key so we can leave our hands on the keyboard.

The action Refresh all Gradle projects is actually the action Refresh all external projects. We can add keyboard shortcut key via Preferences | Keymap. We use the search box to search for Refresh all external projects.

We can right click on the found action and select Add Keyboard Shortcut to define a new shortcut key:

Now we simply use the shortcut to refresh our Gradle project when we have made a change in the Gradle build file that IntelliJ IDEA should know about.

Besides have the action in the Gradle tool window we can also add it to the main toolbar. We right click on the main toolbar and select the option Customize Menus and Toolbars.... We can add the action Refresh all external projects here to the toolbar:

Written with Gradle 3.4.1 and IntelliJ IDEA 2016.3.4.

Ratpacked: Implement A Custom Request Parser

Ratpack has parsers to parse a request with a JSON body or a HTML form. We simply use the parse method of Context and Ratpack will check if there is a compliant parser in the registry. If there is a parser for that type available then Ratpack will parse the request and return a Promise with the value. To write a new parser we need to implement the Parser interface. The easiest way to implement this interface is by writing a class that extends ParserSupport. Using the ParserSupport class we can also work with an options object that a user can pass on to the parse method of Context. If we don't need options we can also extend the NoOptParserSupport class.

Let's write a custom parser that can parse a request with a hex or base64 encoded value. The parser returns a String object with the decoded value. In our example we also want the user to provide an optional options object of type StringParserOpts which denotes the type of decoding:

// File: src/main/groovy/mrhaki/sample/StringParser.groovy
package mrhaki.ratpack

import ratpack.handling.Context
import ratpack.http.TypedData
import ratpack.parse.Parse
import ratpack.parse.ParserSupport
import ratpack.util.Types

/**
 * Parser to decode hex or base64 values send 
 * in the body of a request. 
 */
class StringParser extends ParserSupport<StringParserOpts> {
    
    @Override
    def <T> T parse(
            final Context context,
            final TypedData body,
            final Parse<T, StringParserOpts> parse) throws Exception {

        // Check if type to be parsed can be handled by
        // this parser. We can also create a check based 
        // on content type of the body for example.
        if (supportsType(parse.type)) {
            // Get request body that is either hex or 
            // base64 encoded.
            final String bodyText = body.text

            // Get optional options. If the options are not set
            // a default instance is given. 
            final StringParserOpts opts = parse.opts.orElse(StringParserOpts.hex())
            
            // Check the options to see if hex or base64 decoding is needed.
            if (opts.hex) {
                return Types.cast(new String(bodyText.decodeHex()))
            } else if (opts.base64) {
                return Types.cast(new String(bodyText.decodeBase64()))    
            }
        }

        // Cannot handle the type to be parsed. 
        // Ratpack will try to find another match.
        return null
    }

    /**
     * Support String parsing.
     * 
     * @param typeToken Type defined to be parsed.
     * @return True if type is String, false if not.
     */
    private boolean supportsType(final typeToken) {
        typeToken.rawType == String
    }

}

/**
 * Class with options used to decode a value. 
 * A user can provide an instance of this class using the 
 * {@link Context#parse(java.lang.Class, java.lang.Object)} method.
 */
class StringParserOpts {
    
    private static enum Decoders { HEX, BASE64 }
    
    private Decoders decoder
    
    private StringParserOpts(final Decoders decoder) {
        this.decoder = decoder
    }

    static StringParserOpts hex() {
        new StringParserOpts(Decoders.HEX)
    }
    
    boolean isHex() {
        decoder == Decoders.HEX
    }

    static StringParserOpts base64() {
        new StringParserOpts(Decoders.BASE64)
    }
    
    boolean isBase64() {
        decoder == Decoders.BASE64
    }
    
}

We have the implementation of our parser, so now we write a specification to test it. We test the parser with a simple handler implementation that uses the parse method and then simply renders the resulting String value. In our specification we use RequestFixture to invoke the handler and inspect the result:

// File: src/test/groovy/mrhaki/ratpack/StringParserSpec.groovy
package mrhaki.ratpack

import ratpack.handling.Handler
import ratpack.test.handling.HandlingResult
import ratpack.test.handling.RequestFixture
import spock.lang.Specification

class StringParserSpec extends Specification {

    void 'parse value in request body with StringParser using default decoder'() {
        given:
        final String content = 'Ratpack is gr8!'.bytes.encodeHex().toString()
        
        and:
        final Handler handler = { context ->
            context.parse(String)
                   .then(context.&render)
        }

        when:
        final HandlingResult result = RequestFixture.handle(handler) { fixture ->
            fixture.body(content, 'text/plain')
                    // Add StringParser to registry, so it can be used by Ratpack.
                   .registry { registry -> registry.add(new StringParser()) }
        }

        then:
        result.rendered(String) == 'Ratpack is gr8!'
    }

    void 'parse hex value in request body with StringParser'() {
        given:
        final String content = 'Ratpack is gr8!'.bytes.encodeHex().toString()

        and:
        final Handler handler = { context ->
            // Parse and set options for hex decoding.
            context.parse(String, StringParserOpts.hex())
                   .then(context.&render)
        }

        when:
        final HandlingResult result = RequestFixture.handle(handler) { fixture ->
            fixture.body(content, 'text/plain')
                    // Add StringParser to registry, so it can be used by Ratpack.
                   .registry { registry -> registry.add(new StringParser()) }
        }

        then:
        result.rendered(String) == 'Ratpack is gr8!'
    }
    
    void 'parse base64 value in request body with StringParser'() {
        given:
        final String content = 'Ratpack is gr8!'.bytes.encodeBase64().toString()

        and:
        final Handler handler = { context ->
            // Parse and set options for base64 decoding.
            context.parse(String, StringParserOpts.base64())
                   .then(context.&render)
        }

        when:
        final HandlingResult result = RequestFixture.handle(handler) { fixture ->
            fixture.body(content, 'text/plain')
                    // Add StringParser to registry, so it can be used by Ratpack.
                   .registry { registry -> registry.add(new StringParser()) }
        }

        then:
        result.rendered(String) == 'Ratpack is gr8!'
    }
    
}

Written with Ratpack 1.4.5.

Ratpacked: Implement Custom Rendering With Renderable Interface

Ratpack uses renderers to render output. We can create our own renderer by implementing the Renderer interface. The renderer class needs to implement a render method that has the object we want to render as argument. Alternatively we can add the logic to render a object to the class definition of that object. So instead of having a separate renderer class for a class, we add the render logic to the class itself. To achieve this we must implement the Renderable interface for our class. Ratpack provides a RenderableRenderer in the registry that knows how to render classes that implement the Renderable interface.

In the following example we have a Recipe class that implements the Renderable interface:

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

import ratpack.handling.Context;
import ratpack.render.Renderable;

import static ratpack.jackson.Jackson.json;

public class Recipe implements Renderable {
    
    private final String name;

    public Recipe(final String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    /**
     * Render object as JSON.
     * 
     * @param context Ratpack context.
     */
    @Override
    public void render(final Context context) throws Exception {
        context.byContent(content -> content
                .plainText(() -> context.render(this.toString()))
                .json(() -> context.render(json(this))));
    }
    
    public String toString() {
        return String.format("Recipe::name=%s", this.name);
    }
}

Let's write a specification to test how the Recipe is rendered:

// File: src/test/groovy/mrhaki/ratpack/RecipeRenderableSpec.groovy
package mrhaki.ratpack

import groovy.json.JsonSlurper
import ratpack.test.embed.EmbeddedApp
import spock.lang.Specification

class RecipeRenderableSpec extends Specification {
    
    def app = EmbeddedApp.fromHandler { ctx ->
        ctx.render(new Recipe('macaroni'))
    }
    
    def httpClient = app.httpClient
    
    void 'render Recipe as plain text'() {
        when:
        def response = httpClient.requestSpec { request -> 
            request.headers.set 'Accept', 'text/plain'
        }.get()
        
        then:
        response.statusCode == 200
        
        and:
        response.body.text == 'Recipe::name=macaroni'
    }

    void 'render Recipe as JSON'() {
        when:
        def response = httpClient.requestSpec { request ->
            request.headers.set 'Accept', 'application/json'
        }.get()

        then:
        response.statusCode == 200

        and:
        def recipe = new JsonSlurper().parseText(response.body.text)
        recipe.name == 'macaroni'
    }
}

Written with Ratpack 1.4.5.

March 6, 2017

Ratpacked: Render Optional Type Instance

Ratpack uses renderers to render objects with the render method of the Context class. Ratpack has several renderers that are available automatically. One of those renderers is the OptionalRenderer. When we want to render an Optional object this renderer is selected by Ratpack. If the Optional instance has a value the value is passed to the render method. If the value is not present a 404 client error is returned.

In the following example application we have a RecipeRepository class with a findRecipeByName method. This method returns Promise<Optional<Recipe>>:

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

import ratpack.exec.Promise;

import java.util.Optional;

public interface RecipeRepository {
    Promise<Optional<Recipe>> findRecipeByName(final String name);
}

We have a Handler that will use the findRecipeByName method and then render the Optional<Recipe> object. The following example application shows the handler implementation:

// 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));
    }

}

The application also uses a custom RecipeRenderer. This renderer is used when the Optional<Recipe> has a value:

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

import ratpack.handling.Context;
import ratpack.render.RendererSupport;

import static ratpack.jackson.Jackson.json;

public class RecipeRenderer extends RendererSupport<Recipe> {
    @Override
    public void render(final Context ctx, final Recipe recipe) throws Exception {
        ctx.render(json(recipe));
    }
}

Let's write a specification where we can test that a client error with status code 404 is returned when the Optional is empty. Otherwise the actual value is rendered:

// File: src/test/groovy/mrhaki/ratpack/ApplicationSpec.groovy
package mrhaki.ratpack

import groovy.json.JsonSlurper
import ratpack.exec.Promise
import ratpack.http.MediaType
import ratpack.impose.ImpositionsSpec
import ratpack.impose.UserRegistryImposition
import ratpack.registry.Registry
import ratpack.test.MainClassApplicationUnderTest
import spock.lang.Specification
import spock.lang.Subject

import static groovy.json.JsonOutput.toJson

class ApplicationSpec extends Specification {
    
    private RecipeRepository recipeMock = Mock()
    
    @Subject
    private aut = new MainClassApplicationUnderTest(Application) {
        @Override
        protected void addImpositions(final ImpositionsSpec impositions) {
            // Add mock for RecipeRepository.
            impositions.add(UserRegistryImposition.of(Registry.of { registry ->
                registry.add(RecipeRepository, recipeMock)
            }))
        }
    }
    
    private httpClient = aut.httpClient
    
    void 'response status 404 when Optional<Recipe> is empty'() {
        when:
        def response = httpClient.requestSpec { requestSpec ->
            requestSpec.headers.set 'Content-type', MediaType.APPLICATION_JSON
            requestSpec.body { body ->
                body.text(toJson(name: 'sushi'))
            }
        }.post('recipe')
        
        then:
        1 * recipeMock.findRecipeByName('sushi') >> Promise.value(Optional.empty())
        
        and:
        response.statusCode == 404
    }

    void 'render Recipe when Optional<Recipe> is not empty'() {
        when:
        def response = httpClient.requestSpec { requestSpec ->
            requestSpec.headers.set 'Content-type', MediaType.APPLICATION_JSON
            requestSpec.body { body ->
                body.text(toJson(name: 'macaroni'))
            }
        }.post('recipe')

        then:
        1 * recipeMock.findRecipeByName('macaroni') >> Promise.value(Optional.of(new Recipe('macaroni')))

        and:
        response.statusCode == 200
        
        and:
        def recipe = new JsonSlurper().parseText(response.body.text)
        recipe.name == 'macaroni'
    }

}

Written with Ratpack 1.4.5.

March 2, 2017

Ratpacked: Using Spring Cloud Contract As Client

In a previous post we learned about Spring Cloud Contract. We saw how we can use contracts to implement the server side of the contract. But Spring Cloud Contract also creates a stub based on the contract. The stub server is implemented with Wiremock and Spring Boot. The server can match incoming requests with the contracts and send back the response as defined in the contract. Let's write an application that is invoking HTTP requests on the server application we wrote before. In the tests that we write for this client application we use the stub that is generated by Spring Cloud Contract. We know the stub is following the contract of the actual server.

First we create the stub in our server project with the Gradle task verifierStubsJar. The tests in the client application need these stub and will fetch it as dependency from a Maven repository or the local Maven repository. For our example we use the local Maven repository. We add the maven-publish plugin to the server project and run the task publishToMavenLocal.

We create a new Gradle project for our Ratpack application that is invoking requests on the pirate service. The following Gradle build file sets all dependencies for the application and plugins to run and test the application:

plugins {
    id 'groovy'
    id 'project-report'
    id 'io.ratpack.ratpack-java' version '1.4.5'
    id 'com.github.johnrengelman.shadow' version '1.2.4'
    id 'io.spring.dependency-management' version '1.0.0.RELEASE'
}

group = 'mrhaki.ratpack.pirate.client'
version = '0.0.1'

repositories {
    jcenter()
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.SR5"
        mavenBom "org.springframework.boot:spring-boot-starter-parent:1.5.1.RELEASE"
    }
    dependencies {
        dependency 'com.google.guava:guava:19.0'
    }
}

dependencies {
    runtime 'org.slf4j:slf4j-simple:1.7.24'
    
    testCompile 'org.codehaus.groovy:groovy-all:2.4.9'
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'org.spockframework:spock-spring:1.0-groovy-2.4'

    testCompile 'org.springframework.boot:spring-boot-starter-web', {
        exclude module: 'logback-classic'
    }
    testCompile 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner', {
        exclude module: 'logback-classic'
    }
    testRuntime 'javax.servlet:javax.servlet-api:3.1.0'
}

mainClassName = 'mrhaki.sample.TavernApp'

assemble.dependsOn shadowJar

The code for the application can be found in Github. We write a test for our application and use the Spring Cloud Contract generated stub server. In our application we use the HttpClient class from Ratpack to invoke the pirate service. These calls will be send to the stub server in the specification. To start the stub we use the JUnit rule StubRunnerRule. We configure it to use the Maven local repository and define the dependency details. The stub server starts using a random port and we can get the address with the method findStubUrl. In our Ratpack application we have the address of the pirate service in the registry. We use impositions in our tests to replace that address with the stub server address:

package mrhaki.sample

import org.junit.ClassRule
import org.springframework.cloud.contract.stubrunner.junit.StubRunnerRule
import ratpack.impose.ImpositionsSpec
import ratpack.impose.UserRegistryImposition
import ratpack.registry.Registry
import ratpack.test.MainClassApplicationUnderTest
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

class BartenderSpec extends Specification {

    @ClassRule
    @Shared
    private StubRunnerRule mockServer =
            new StubRunnerRule()
                    .downloadStub('mrhaki.ratpack.pirate.service', 'pirate-service', '0.0.2', 'stubs')
                    .workOffline(true)  // Use Maven local repo

    @AutoCleanup
    @Shared
    private app = new MainClassApplicationUnderTest(TavernApp) {
        @Override
        protected void addImpositions(final ImpositionsSpec impositions) {
            final mockUrl = mockServer.findStubUrl('mrhaki.ratpack.pirate.service', 'pirate-service')
            impositions.add(UserRegistryImposition.of(Registry.of { registry -> registry.add(URL, mockUrl)}))
        }
    }

    void 'ask for a drink'() {
        when:
        def response = app.httpClient.post('bartender/ask')

        then:
        response.statusCode == 200
        response.body.text == 'Hi-ho, mrhaki, ye like to drink some spiced rum!'
    }
    
    void 'tell story'() {
        when:
        def response = app.httpClient.get('bartender/story')

        then:
        response.statusCode == 200
        response.body.text == 'Ay, matey, mrhaki, walk the plank!'
    }
}

Spring Cloud Contract gives us a stub server that is compliant with the contract and even gives back responses based on matched requests from the contracts.

Written with Ratpack 1.4.5 and Spring Cloud Contract 1.0.3.RELEASE.