October 3, 2018

Micronaut Mastery: Configuration Property Name Is Lowercased And Hyphen Separated

In Micronaut we can inject configuration properties in different ways into our beans. We can use for example the @Value annotation using a string value with a placeholder for the configuration property name. If we don't want to use a placeholder we can also use the @Property annotation and set the name attribute to the configuration property name. We have to pay attention to the format of the configuration property name we use. If we refer to a configuration property name using @Value or @Property we must use lowercased and hyphen separated names (also known as kebab casing). Even if the name of the configuration property is camel cased in the configuration file. For example if we have a configuration property sample.theAnswer in our application.properties file, we must use the name sample.the-answer to get the value.

In the following Spock specification we see how to use it in code. The specification defines two beans that use the @Value and @Property annotations and we see that we need to use kebab casing for the configuration property names, even though we use camel casing to set the configuration property values:

package mrhaki

import groovy.transform.CompileStatic
import io.micronaut.context.ApplicationContext
import io.micronaut.context.annotation.Property
import io.micronaut.context.annotation.Value
import io.micronaut.context.exceptions.DependencyInjectionException
import spock.lang.Shared
import spock.lang.Specification

import javax.inject.Singleton

class ConfigurationPropertyNameSpec extends Specification {

    // Create application context with two configuration 
    // properties: reader.maxFileSize and reader.showProgress.
    @Shared
    private ApplicationContext context = 
            ApplicationContext.run('reader.maxFileSize': 1024, 
                                   'reader.showProgress': true)

    void "use kebab casing (hyphen-based) to get configuration property value"() {
        expect:
        with(context.getBean(FileReader)) {
            maxFileSize == 1024   
            showProgress == Boolean.TRUE
        }
    }

    void "using camel case to get configuration property should throw exception"() {
        when:
        context.getBean(InvalidFileReader).maxFileSize

        then:
        final dependencyException = thrown(DependencyInjectionException)
        dependencyException.message == """\
            |Failed to inject value for parameter [maxFileSize] of method [setMaxFileSize] of class: mrhaki.InvalidFileReader
            |
            |Message: Error resolving property value [\${reader.maxFileSize}]. Property doesn't exist
            |Path Taken: InvalidFileReader.setMaxFileSize([Integer maxFileSize])""".stripMargin()
    }
}

@CompileStatic
@Singleton
class FileReader {
    
    private Integer maxFileSize
    private Boolean showProgress
    
    // Configuration property names 
    // are normalized and 
    // stored lowercase hyphen separated (= kebab case).
    FileReader(
            @Property(name ='reader.max-file-size') Integer maxFileSize,
            @Value('${reader.show-progress:false}') Boolean showProgress) {
        
        this.maxFileSize = maxFileSize
        this.showProgress = showProgress
    }
    
    Integer getMaxFileSize() {
        return maxFileSize
    }
    
    Boolean showProgress() {
        return showProgress
    }
}

@CompileStatic
@Singleton
class InvalidFileReader {
    // Invalid reference to property name,
    // because the names are normalized and
    // stored lowercase hyphen separated.
    @Value('${reader.maxFileSize}')
    Integer maxFileSize
}

Written with Micronaut 1.0.0.RC1.

October 2, 2018

Micronaut Mastery: Consuming Server-Sent Events (SSE)

Normally we would consume server-sent events (SSE) in a web browser, but we can also consume them in our code on the server. Micronaut has a low-level HTTP client with a SseClient interface that we can use to get server-sent events. The interface has an eventStream method with different arguments that return a Publisher type of the Reactive Streams API. We can use the RxSseClient interface to get back RxJava2 Flowable return type instead of Publisher type. We can also use Micronaut's declarative HTTP client, which we define using the @Client annotation, that supports server-sent events with the correct annotation attributes.

In our example we first create a controller in Micronaut to send out server-sent events. We must create method that returns a Publisher type with Event objects. These Event objects can contains some attributes like id and name, but also the actual object we want to send:

// File: src/main/java/mrhaki/ConferencesController.java
package mrhaki;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.sse.Event;
import io.reactivex.Flowable;

import java.util.concurrent.TimeUnit;

@Controller("/conferences")
public class ConferencesController {

    private final ConferenceRepository repository;

    public ConferencesController(final ConferenceRepository repository) {
        this.repository = repository;
    }

    /**
     * Send each second a random Conference.
     * 
     * @return Server-sent events each second where the event is a randomly
     * selected Conference object from the repository.
     */
    @Get("/random")
    Flowable<Event<Conference>> events() {
        final Flowable<Long> tick = Flowable.interval(1, TimeUnit.SECONDS);
        final Flowable<Conference> randomConferences = repository.random().repeat();

        return tick.zipWith(randomConferences, this::createEvent);
    }

    /**
     * Create a server-sent event with id, name and the Conference data.
     * 
     * @param counter Counter used as id for event.
     * @param conference Conference data as payload for the event.
     * @return Event with id, name and Conference object.
     */
    private Event<Conference> createEvent(Long counter, final Conference conference) {
        return Event.of(conference)
                    .id(String.valueOf(counter))
                    .name("randomEvent");
    }

}

Notice how easy it is in Micronaut to use server-sent events. Let's add a declarative HTTP client that can consume the server-sent events. We must set the processes attribute of the @Get annotation with the value text/event-stream. This way Micronaut can create an implementation of this interface with the correct code to consume server-sent events:

// File: src/main/java/mrhaki/ConferencesClient.java
package mrhaki;

import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.http.sse.Event;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;

@Client("/conferences")
interface ConferencesSseClient {

    /**
     * Return Publisher with SSE containing Conference data.
     * We must set the processes attribute with the value
     * text/event-stream so Micronaut can generate an implementation
     * to support server-sent events.
     * We could also return Publisher implementation class
     * like Flowable or Flux, Micronaut will do the conversion.
     * 
     * @return Publisher with Event objects with Conference data.
     */
    @Get(value = "/random", processes = MediaType.TEXT_EVENT_STREAM)
    Publisher<Event<Conference>> randomEvents();

    /**
     * Here we use a Publisher implementation Flux. Also we don't
     * add the Event in the return type: Micronaut will leave out
     * the event metadata and we get the data that is part of
     * the event as object.
     *
     * @return Flux with Conference data.
     */
    @Get(value = "/random", processes = MediaType.TEXT_EVENT_STREAM)
    Flux<Conference> randomConferences();

}

Next we create a Spock specification to test our controller with server-sent events. In the specification we use the low-level HTTP client and the declarative client:

// File: src/test/groovy/mrhaki/ConferencesControllerSpec.groovy
package mrhaki

import io.micronaut.context.ApplicationContext
import io.micronaut.http.client.sse.RxSseClient
import io.micronaut.http.client.sse.SseClient
import io.micronaut.http.sse.Event
import io.micronaut.runtime.server.EmbeddedServer
import io.reactivex.Flowable
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

class ConferencesControllerSpec extends Specification {

    @Shared
    @AutoCleanup
    private EmbeddedServer server = ApplicationContext.run(EmbeddedServer)

    /**
     * Low level client to interact with server
     * that returns server side events, that supports
     * RxJava2.
     */
    @Shared
    @AutoCleanup
    private RxSseClient sseLowLevelClient =
            server.applicationContext
                  .createBean(RxSseClient, server.getURL())

    /**
     * Declarative client for interacting
     * with server that send server side events.
     */
    @Shared
    private ConferencesSseClient sseClient =
            server.applicationContext
                  .getBean(ConferencesSseClient)

    void "test event stream with low level SSE client"() {
        when:
        // Use eventStream method of RxSseClient to get SSE
        // and convert data in event to Conference objects by 
        // setting second argument to Conference.class.
        final List<Event<Conference>> result =
                sseLowLevelClient.eventStream("/conferences/random", Conference.class)
                                 .take(2)
                                 .toList()
                                 .blockingGet()
        
        then:
        result.name.every { name -> name == "randomEvent" }
        result.id == ["0", "1"]
        result.data.every { conference -> conference instanceof Conference }
    }

    void "test event stream with declarative SSE client"() {
        when:
        // Use declarative client (using @Client)
        // with SSE support.
        List<Event<Conference>> result =
                Flowable.fromPublisher(sseClient.randomEvents())
                        .take(2)
                        .toList()
                        .blockingGet();

        then:
        result.name.every { name -> name == "randomEvent" }
        result.id == ["0", "1"]
        result.data.every { conference -> conference instanceof Conference }
    }

    void "test conference stream with declarative SSE client"() {
        when:
        // Use declarative client (using @Client)
        // with built-in extraction of data in event.
        List<Conference> result =
                sseClient.randomConferences()
                         .take(2)
                         .collectList()
                         .block();

        then:
        result.id.every(Closure.IDENTITY) // Check every id property is set.
        result.every { conference -> conference instanceof Conference }
    }
}

Written with Micronaut 1.0.0.RC1.

September 30, 2018

Micronaut Mastery: Running Code On Startup

When our Micronaut application starts we can listen for the ServiceStartedEvent event and write code that needs to run when the event is fired. We can write a bean that implements the ApplicationEventListener interface with the type ServiceStartedEvent. Or we can use the @EventListener annotation on our method with code we want to run on startup. If the execution of the code can take a while we can also add the @Async annotation to the method, so Micronaut can execute the code on a separate thread.

In our example application we have a reactive repository for a Mongo database and we want to save some data in the database when our Micronaut application starts. First we write a bean that implements the ApplicationEventListener:

// File: src/main/java/mrhaki/Dataloader.java
package mrhaki;

import io.micronaut.context.annotation.Requires;
import io.micronaut.context.env.Environment;
import io.micronaut.context.event.ApplicationEventListener;
import io.micronaut.discovery.event.ServiceStartedEvent;
import io.micronaut.scheduling.annotation.Async;
import io.reactivex.Flowable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Singleton;

@Singleton
@Requires(notEnv = Environment.TEST) // Don't load data in tests.
public class DataLoader implements ApplicationEventListener<ServiceStartedEvent> {

    private static final Logger log = LoggerFactory.getLogger(DataLoader.class);

    /**
     * Reactive repository for Mongo database to store
     * Conference objects with an id and name property.
     */
    private final ConferenceRepository repository;

    public DataLoader(final ConferenceRepository repository) {
        this.repository = repository;
    }

    @Async
    @Override
    public void onApplicationEvent(final ServiceStartedEvent event) {
        log.info("Loading data at startup");

        // Transform names to Conferences object and save them.
        Flowable.just("Gr8Conf", "Greach", "JavaLand", "JFall", "NextBuild")
                .map(name -> new Conference(name))
                .forEach(this::saveConference);
    }

    /**
     * Save conference in repository.
     * 
     * @param conference Conference to be saved.
     */
    private void saveConference(Conference conference) {
        repository
                .save(conference)
                .subscribe(
                        saved -> log.info("Saved conference {}.", saved),
                        throwable -> log.error("Error saving conference.", throwable));
    }
}

Alternatively we could have used the @EventListener annotation on a method with an argument of type ServiceStartedEvent:

// File: src/main/java/mrhaki/Dataloader.java
package mrhaki;

import io.micronaut.context.annotation.Requires;
import io.micronaut.context.env.Environment;
import io.micronaut.context.event.ApplicationEventListener;
import io.micronaut.discovery.event.ServiceStartedEvent;
import io.micronaut.runtime.event.annotation.EventListener;
import io.micronaut.scheduling.annotation.Async;
import io.reactivex.Flowable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Singleton;

@Singleton
@Requires(notEnv = Environment.TEST) // Don't load data in tests.
public class DataLoader {

    private static final Logger log = LoggerFactory.getLogger(DataLoader.class);

    /**
     * Reactive repository for Mongo database to store
     * Conference objects with an id and name property.
     */
    private final ConferenceRepository repository;

    public DataLoader(final ConferenceRepository repository) {
        this.repository = repository;
    }

    @EventListener
    @Async
    public void loadConferenceData(final ServiceStartedEvent event) {
        log.info("Loading data at startup");

        // Transform names to Conferences object and save them.
        Flowable.just("Gr8Conf", "Greach", "JavaLand", "JFall", "NextBuild")
                .map(name -> new Conference(name))
                .forEach(this::saveConference);
    }

    /**
     * Save conference in repository.
     * 
     * @param conference Conference to be saved.
     */
    private void saveConference(Conference conference) {
        repository
                .save(conference)
                .subscribe(
                        saved -> log.info("Saved conference {}.", saved),
                        throwable -> log.error("Error saving conference.", throwable));
    }
}

When we start our Micronaut application we can see in the log messages that our conference data is created:

22:58:17.343 [pool-1-thread-1] INFO  mrhaki.DataLoader - Loading data at startup
22:58:17.343 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 1230ms. Server Running: http://localhost:9000
22:58:17.573 [Thread-11] INFO  mrhaki.DataLoader - Saved conference Conference{id=5bb134f505d4feefa74d19c7, name='JFall'}.
22:58:17.573 [Thread-8] INFO  mrhaki.DataLoader - Saved conference Conference{id=5bb134f505d4feefa74d19c3, name='Gr8Conf'}.
22:58:17.573 [Thread-10] INFO  mrhaki.DataLoader - Saved conference Conference{id=5bb134f505d4feefa74d19c5, name='Greach'}.
22:58:17.573 [Thread-9] INFO  mrhaki.DataLoader - Saved conference Conference{id=5bb134f505d4feefa74d19c8, name='NextBuild'}.
22:58:17.573 [Thread-6] INFO  mrhaki.DataLoader - Saved conference Conference{id=5bb134f505d4feefa74d19c6, name='JavaLand'}.

Written with Micronaut 1.0.0.RC1.

September 4, 2018

Spring Sweets: Dockerize Spring Boot Application With Jib

Jib is an open-source Java library from Google for creating Docker images for Java applications. Jib can be used as Maven or Gradle plugin in our Spring Boot project. One of the nice feature of Jib is that it adds layers with our classes, resources and dependency libraries for the Docker image. This means that when only class files have changed, the classes layer is rebuild, but the others remain the same. Therefore the creation of a Docker image with our Spring Boot application is also very fast (after the first creation). Also the Maven and Gradle plugins have sensible defaults, like using the project name and version as image name, so we don't have to configure anything in our build tool. Although Jib provides options to configure other values for the defaults, for example to change the JVM options passed on to the application.

Let's see Jib in action for a simple Spring Boot application. In our example we use Gradle as build tool with the following Spring Boot application:

// File: src/main/java/mrhaki/spring/sample/SampleApplication.java
package mrhaki.spring.sample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
@SpringBootApplication
public class SampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }

    @GetMapping("/message")
    Mono<String> message() {
        return Mono.just("Spring Boot sample application.");
    }
    
}

Next we add the Jib plugin to our Gradle build file:

// File: build.gradle
...
plugins {
    id 'com.google.cloud.tools.jib' version '0.9.10'
}
...

With the Gradle task jibDockerBuild we can create a Docker image for our local Docker. Our project is called springboot-sample with version 1.0.0-SNAPSHOT, so we get the Docker image springboot-sample:1.0.0-SNAPSHOT:

$ ./gradlew jibDockerBuild
Tagging image with generated image reference springboot-sample:1.0.0-SNAPSHOT. If you'd like to specify a different tag, you can set the jib.to.image parameter in your build.gradle, or use the --image=<MY IMAGE> commandline flag.
warning: Base image 'gcr.io/distroless/java' does not use a specific image digest - build may not be reproducible

Containerizing application to Docker daemon as springboot-sample:1.0.0-SNAPSHOT...

Getting base image gcr.io/distroless/java...
Building dependencies layer...
Building resources layer...
Building classes layer...
Finalizing...
Loading to Docker daemon...

Container entrypoint set to [java, -cp, /app/resources/:/app/classes/:/app/libs/*, mrhaki.spring.sample.SampleApplication]

Built image to Docker daemon as springboot-sample:1.0.0-SNAPSHOT

BUILD SUCCESSFUL in 3s
3 actionable tasks: 1 executed, 2 up-to-date
$

Notice that the default image our Docker image is build on is gcr.io/distroless/java, but we can change that in our Gradle build file via the jib configuration block.

Our image is available so we can run a Docker container based on our image and check the URL of our application:

$ docker run -d --name springboot-sample -p 8080:8080 springboot-sample:1.0.0-SNAPSHOT
5d288cbe4ed606760a51157734349135d4d4562072e1024f4585dff370ac6f99
$ curl -X GET http://localhost:8080/message
Spring Boot sample application.
$

In the following example we add some configuration for Jib in our Gradle build file:

// File: build.gradle
...
jib {
    to {
        image = "springboot-mrhaki:${project.version}"
    }

    container {
        // Set JVM options.
        jvmFlags = ['-XX:+UnlockExperimentalVMOptions', '-XX:+UseCGroupMemoryLimitForHeap', '-Dserver.port=9000']
        // Expose different port.
        ports = ['9000']
        // Add labels.
        labels = [maintainer: 'mrhaki']
    }

}
...

When we run the jibDockerBuild task a new Docker image is build:

$ warning: Base image 'gcr.io/distroless/java' does not use a specific image digest - build may not be reproducible

Containerizing application to Docker daemon as springboot-mrhaki:1.0.0-SNAPSHOT...

Getting base image gcr.io/distroless/java...
Building dependencies layer...
Building resources layer...
Building classes layer...
Finalizing...
Loading to Docker daemon...

Container entrypoint set to [java, -XX:+UnlockExperimentalVMOptions, -XX:+UseCGroupMemoryLimitForHeap, -Dserver.port=9000, -cp, /app/resources/:/app/classes/:/app/libs/*, mrhaki.spring.sample.SampleApplication]

Built image to Docker daemon as springboot-mrhaki:1.0.0-SNAPSHOT

BUILD SUCCESSFUL in 3s
3 actionable tasks: 1 executed, 2 up-to-date
$ docker run -d --name springboot-mrhaki -p 9000:9000 springboot-mrhaki:1.0.0-SNAPSHOT
6966ff3b5ca8dae658d59e39c0f26c11d22af7e04e02ad423516572eb5e0e0bd
$ curl -X GET http://localhost:9000/message
Spring Boot sample application.
$

Jib also adds the jib task to deploy a Docker image to a registry instead of the local Docker daemon. Jib can use several command-line applications to authenticate with registries or we can set authentication information in the Gradle build file. Check Jib on Github for more documentation of the options and features.

Written with Spring Boot 2.0.4.RELEASE and Jib Gradle plugin 0.9.10.

September 2, 2018

Awesome Asciidoctor: Document Attributes With Styling

Document attributes in Asciidoctor are very powerful. We can assign values to a document attributes and reference the attribute name in our document enclosed between curly braces. Asciidoctor will fill in the value when the document is transformed. Instead of a plain value we can also use styling markup in the document attribute definition. We must use the passthrough macro and allow for quote substitution.

In the following example document we define three document attributes: cl-added, cl-updated and cl-changed. We use the passthrough macro, quotes substation to assign CSS classes:

= Attributes with styles
// Include contents of docinfo.html
// in HTML head with CSS style
// definitions for .label.added,
// .label.changed and .label.updated
// used in the document attributes
// cl-added, cl-changed and cl-updated.
:docinfo1:

// Document attribues with styling,
// using the passthrough macro
// and quotes subsitution.
// We can use quotes or the short-hand q.
:cl-added: pass:quotes[[.label.added]#Added:#]
:cl-changed: pass:q[[.label.changed]#Changed:#]
:cl-updated: pass:q[[.label.updated]#Updated:#]


== Sample section

* {cl-added} Document attributes for document.
* {cl-changed} Definition of attributes to include
 more options.
* {cl-updated} New version of Asciidoctor.

Notice we need a file docinfo.html with the CSS style definitions:

<style>
.label {
    color: #fff;
    padding: .2em .6em .3em;
    font-weight: 700;
    border-radius: .25em;
    font-size: 90%;
}

.added {background-color: #007700;}
.changed {background-color: #088;}
.updated {background-color: #3344bb;}
</style>

When run Asciidoctor to get HTML output we see the following:

Written with Aciidoctor 1.5.7.1.

August 31, 2018

Micronaut Mastery: Change The Default Package For Generated Classes

When we use the Micronaut command line commands to generate controllers, beans and more, the classes will have a default package name. We can use a fully qualified package name for the class we want to generate, but when we only define a class name, Micronaut will use a default package name automatically. We can set the default package for an application when we first create an application with the create-app command. But we can also alter the default package name for an existing Micronaut application.

To set the default package name when we create a new application we must include the package name in our application name. For example when we want to use the default package name mrhaki.micronaut for an application that is called book-service we must use the following create-app command:

$ mn create-app mrhaki.micronaut.book-service
| Generating Java project.....
| Application created at /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/micronaut/book-service
$ tree src
src
├── main
│   ├── java
│   │   └── mrhaki
│   │       └── micronaut
│   │           └── Application.java
│   └── resources
│       ├── application.yml
│       └── logback.xml
└── test
    └── java
        └── mrhaki
            └── micronaut

9 directories, 3 files

Notice the tree structure reflects the structure for the package mrhaki.micronaut.
When we invoke the create-controller book command the generated files use the package name mrhaki.micronaut:

$ mn create-controller book
| Rendered template Controller.java to destination src/main/java/mrhaki/micronaut/BookController.java
| Rendered template ControllerTest.java to destination src/test/java/mrhaki/micronaut/BookControllerTest.java
$

We can change the default package name for an existing application as well. We must edit the file micronaut-cli.yml in the root of our project and change the property defaultPackage. In the following example we use the sed command on MacOS to replace the current default package with com.mrhaki.microanut, but we could of course also use any text editor to make the change:

$ cat micronaut-cli.yml
profile: service
defaultPackage: mrhaki.micronaut
---
testFramework: junit
sourceLanguage: java
$ sed -E -i '' 's/(defaultPackage:) .*/\1 com\.mrhaki\.micronaut/g' micronaut-cli.yml
$ cat micronaut-cli.yml
profile: service
defaultPackage: com.mrhaki.micronaut
---
testFramework: junit
sourceLanguage: java

Let's generate a new controller and see if our new default package name for the application is used:

$ mn create-controller author
| Rendered template Controller.java to destination src/main/java/com/mrhaki/micronaut/AuthorController.java
| Rendered template ControllerTest.java to destination src/test/java/com/mrhaki/micronaut/AuthorControllerTest.java
$

Written with Micronaut 1.0.0.M4.

August 30, 2018

Micronaut Mastery: Use Micronaut Beans In Spring Applications

We can add Micronaut beans to the application context of a Spring application. Micronaut has a MicronautBeanProcessor class that we need to register as Spring bean in the Spring application context. We can define which Micronaut bean types need to be added to the Spring application context via the constructor of the MicronautBeanProcessor class. This way we can use Micronaut from inside a Spring application. For example we can use the declarative HTTP client of Micronaut in our Spring applications.

First we need to add dependencies on Micronaut to our Spring application. In this example we will use the Micronaut HTTP client in our Spring application and use Gradle as build tool. We must add the following dependencies:

// File: build.gradle
...
dependencyManagement {
    imports {
        mavenBom 'io.micronaut:bom:1.0.0.M4'
    }
}
...
dependencies {
    ...
    annotationProcessor "io.micronaut:inject-java"
    compile "io.micronaut:http-client"
    compile "io.micronaut:spring"
    ...
}
...

Next we register a MicronautBeanProcessor bean in the Spring application context. We specify in the constructor that Micronaut beans annotated with @Client must be added to the Spring application context:

// File: src/main/java/mrhaki/micronaut/SampleApplication.java
package mrhaki.micronaut;

import io.micronaut.http.client.Client;
import io.micronaut.spring.beans.MicronautBeanProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class SampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }

    @Bean
    public MicronautBeanProcessor httpClientMicronautBeanProcessor() {
        // Register beans with @Client annotation.
        // We could for example also use @Singleton
        // and others, because the constructor has
        // a variable number of arguments.
        return new MicronautBeanProcessor(Client.class);
    }

}

We add the source for our Micronaut declarative HTTP client, where we access httpbin.org as a remote webservice:

// File: src/main/java/mrhaki/micronaut/HttpBinClient.java
package mrhaki.micronaut;

import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.client.Client;

@Client("http://httpbin.org")
interface HttpBinClient {
    
    @Get("/uuid")
    ResponseUuidData uuid();
    
    @Post("/anything")
    ResponseData data(String message);
    
}

Inside a Spring controller we use the client as Spring bean:

// File: src/main/java/mrhaki/micronaut/HttpBinController.java
package mrhaki.micronaut;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;

@RestController
@RequestMapping("/sample")
public class HttpBinController {

    private final HttpBinClient client;

    // Inject Micronaut HTTP client via implicit
    // constructor injection.
    public HttpBinController(final HttpBinClient client) {
        this.client = client;
    }

    @GetMapping(value = "/uuid", produces = TEXT_PLAIN_VALUE)
    Mono<String> uuid() {
        return Mono.fromCallable(() -> client.uuid().getUuid().toString());
    }

    @GetMapping(value = "/data")
    Mono<ResponseData.MessageResponseData> data() {
        return Mono.fromCallable(() -> client.data("Micronaut rocks").getJson());
    }

}

Finally we have some POJO classes with the results from the remote web service calls:

// File: src/main/java/mrhaki/micronaut/ResponseUuidData.java
package mrhaki.micronaut;

import java.util.UUID;

public class ResponseUuidData {
    private UUID uuid;
    public UUID getUuid() { return uuid; }
    public void setUuid(final UUID uuid) { this.uuid = uuid; }
}
// File: src/main/java/mrhaki/micronaut/ResponseData.java
package mrhaki.micronaut;

public class ResponseData {
    private MessageResponseData json;
    public MessageResponseData getJson() { return json; }
    public void setJson(final MessageResponseData json) { this.json = json; }
    
    static class MessageResponseData {
        private String message;
        public String getMessage() { return message; }
        public void setMessage(final String message) { this.message = message; }
    }
}

It is time to start our Spring application and invoke /sample/uuid and /sample/data URLs to see the results:

$ curl -X GET http://localhost:8080/sample/uuid
7149a954-da9a-4bfb-ba04-4b9f814698fa
$ curl -X GET http://localhost:8080/sample/data
{"message":"Micronaut rocks"}

Written with Micronaut 1.0.0.M4 and Spring Boot 2.0.4.RELEASE.

August 29, 2018

Micronaut Mastery: Adding Custom Info To Info Endpoint

In a previous post we learned how to add build information to the /info endpoint in our application. We can add custom information to the /info endpoint via our application configuration or via a bean by implementing the InfoSource interface. Remember we need to add the io.micronaut:management dependency to our application for this feature.

Any configuration property that is prefixed with info will be exposed via the /info endpoint. We can define the properties in configuration files, using Java system properties or system environment variables. In the following example configuration file we add the property info.sample.message:

// File: src/main/resources/application.yml
...
info:
  sample:
    message: Micronaut is awesome

Another option is to create a new class that implements the InfoSource interface. We need to override the getSource method to return a Publisher with a PropertySource. In our example application we have a ConferenceRepository bean that can access a database with data about conferences. In the following example InfoSource implementation we want to return the number of conferences that are stored in the database:

package mrhaki.micronaut;

import io.micronaut.context.annotation.Requires;
import io.micronaut.context.env.MapPropertySource;
import io.micronaut.context.env.PropertySource;
import io.micronaut.core.async.SupplierUtil;
import io.micronaut.management.endpoint.info.InfoEndpoint;
import io.micronaut.management.endpoint.info.InfoSource;
import io.micronaut.runtime.context.scope.Refreshable;
import io.reactivex.Flowable;
import org.reactivestreams.Publisher;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

// Component is refreshable via a refresh event
@Refreshable
// Only create this component when the following 
// requirements are met:
@Requires(beans = InfoEndpoint.class)
@Requires(property = "endpoints.info.config.enabled", notEquals = "false")
public class DatabaseInfoSource implements InfoSource {

    /**
     * Repository to fetch count of conferences in the database.
     */
    private final ConferenceRepository conferenceRepository;

    /**
     * Supplier for returning result.
     */
    private final Supplier<PropertySource> supplier;

    public DatabaseInfoSource(final ConferenceRepository conferenceRepository) {
        this.conferenceRepository = conferenceRepository;
        
        // Cache result from database.
        // This component is refreshable via 
        // a refresh event to get the latest value
        // from the database.
        this.supplier = SupplierUtil.memoized(this::retrieveDatabaseInfo);
    }

    /**
     * Return information.
     * 
     * @return Information about the database.
     */
    @Override
    public Publisher<PropertySource> getSource() {
        return Flowable.just(supplier.get());
    }

    /**
     * Get information from the database via the {@link #conferenceRepository} instance.
     * 
     * @return Number of conferences in the database.
     */
    private MapPropertySource retrieveDatabaseInfo() {
        final Map<String, Long> databaseInfo = new HashMap<>();
        databaseInfo.put("db.conferences.count", conferenceRepository.count());
        return new MapPropertySource("database", databaseInfo);
    }

}

Let's run our Micronaut application and invoke the /info endpoint. We see the following response if we have 42 conference records in our database:

{
    "db": {
        "conferences": {
            "count": 42
        }
    },
    "sample": {
        "message": "Micronaut is awesome"
    }
}

Written with Micronaut 1.0.0.M4.

August 28, 2018

Micronaut Mastery: Using Specific Configuration Properties For HTTP Client

One of the (many) great features of Micronaut is the HTTP client. We use the @Client annotation to inject a low-level HTTP client. Or we define a declarative HTTP client based on an interface, for which Micronaut will generate an implementation. The @Client annotation supports the configuration parameter to reference a configuration class with configuration properties for the HTTP client. The configuration class extends HttpClientConfiguration to support for example the configuration of timeouts and connection pooling. We can add our own configuration properties as well and use them in our application.

In the following example we want to access the OpenWeatherMap API using a declarative HTTP client. First we write a class that extends HttpClientConfiguration. This gives us HTTP client configuration properties and we also add some properties to define the OpenWeatherMap URI, path and access key we need to invoke the REST API. Finally we add configuration properties for a @Retryable annotation we want to use for our HTTP client.

// File: src/main/java/mrhaki/micronaut/WeatherClientConfiguration.java
package weather;

import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.http.client.HttpClientConfiguration;
import io.micronaut.runtime.ApplicationConfiguration;

import java.net.URI;
import java.time.Duration;

import static weather.WeatherClientConfiguration.PREFIX;

/**
 * Custom HTTP client configuration set via application
 * properties prefixed with "weather.client".
 */
@ConfigurationProperties(PREFIX)
public class WeatherClientConfiguration extends HttpClientConfiguration {

    public static final String PREFIX = "weather.client";

    /**
     * HTTP client connection pool configuration.
     */
    private final WeatherClientConnectionPoolConfiguration connectionPoolConfiguration;

    /**
     * OpenWeatherMap URI.
     */
    private URI url;

    /**
     * Path for requests sent to OpenWeatherMap.
     */
    private String path;
    
    /** 
     * Key needed to access OpenWeatherMap API.
     */
    private String apiKey;

    public WeatherClientConfiguration(
            final ApplicationConfiguration applicationConfiguration,
            final WeatherClientConnectionPoolConfiguration connectionPoolConfiguration) {
        super(applicationConfiguration);
        this.connectionPoolConfiguration = connectionPoolConfiguration;
    }

    public URI getUrl() {
        return url;
    }

    public void setUrl(final URI url) {
        this.url = url;
    }

    public String getPath() {
        return path;
    }

    public void setPath(final String path) {
        this.path = path;
    }

    public String getApiKey() {
        return apiKey;
    }

    public void setApiKey(final String apiKey) {
        this.apiKey = apiKey;
    }

    @Override
    public ConnectionPoolConfiguration getConnectionPoolConfiguration() {
        return connectionPoolConfiguration;
    }
    
    @ConfigurationProperties(ConnectionPoolConfiguration.PREFIX)
    public static class WeatherClientConnectionPoolConfiguration extends ConnectionPoolConfiguration {
    }

    /**
     * Extra configuration propertie to set the values
     * for the @Retryable annotation on the WeatherClient.
     */
    @ConfigurationProperties(WeatherClientRetryConfiguration.PREFIX)
    public static class WeatherClientRetryConfiguration {
        
        public static final String PREFIX = "retry";
        
        private Duration delay;
        
        private int attempts;

        public Duration getDelay() {
            return delay;
        }

        public void setDelay(final Duration delay) {
            this.delay = delay;
        }

        public int getAttempts() {
            return attempts;
        }

        public void setAttempts(final int attempts) {
            this.attempts = attempts;
        }
    }
}

Next we write the declarative HTTP client as Java interface with the @Client annotation. We refer to our custom configuration and use the configuration properties to set the URI and path for accessing the OpenWeatherMap API.

// File: src/main/java/mrhaki/micronaut/WeatherClient.java
package weather;

import io.micronaut.http.annotation.Get;
import io.micronaut.http.client.Client;
import io.micronaut.retry.annotation.Retryable;
import io.reactivex.Single;

import java.util.Map;

// Declarative HTTP client with URL and path
// fetched from the application configuration.
// HTTP client configuration like pooled connections,
// timeouts are defined using WeatherClientConfiguration.
@Client(
        value = "${weather.client.url}",
        path = "${weather.client.path}",
        configuration = WeatherClientConfiguration.class)
// Retry accessing OpenWeatherMap REST API if error occurs.
@Retryable(
        attempts = "${weather.client.retry.attempts}",
        delay = "${weather.client.retry.delay}")
interface WeatherClient {

    /**
     * Get weather description for the town of Tilburg, NL. 
     * The APPID query parameter is filled in with the apiKey
     * argument value.
     *
     * @param apikey OpenWeatherMap API key to access REST API.
     * @return Response data from REST API.
     */
    @Get("weather?q=Tilburg,nl&APPID={apikey}")
    Single<Map<String, Object>> tilburg(String apikey);

}

Finally we write a controller that uses the declarative HTTP client WeatherClient to get a weather description for the town of Tilburg in The Netherlands:

// File: src/main/java/mrhaki/micronaut/WeatherController.java
package weather;

import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.reactivex.Single;

import java.util.List;
import java.util.Map;

/**
 * Controller to expose data from the 
 * OpenWeatherMap REST API.
 */
@Controller("/weather")
public class WeatherController {
    
    private final WeatherClient client;
    private final WeatherClientConfiguration configuration;

    public WeatherController(
            final WeatherClient client, 
            final WeatherClientConfiguration configuration) {
        this.client = client;
        this.configuration = configuration;
    }

    /**
     * Get weather data for town Tilburg, NL and get the
     * weather description to return.
     * 
     * @return Weather description as text.
     */
    @Get(value = "/tilburg", produces = MediaType.TEXT_PLAIN)
    public Single<String> weatherInTilburg() {
        return client.tilburg(configuration.getApiKey())
                     .map(response -> getWeatherDescription(response));
    }

    /**
     * Get weather description from response data.
     * 
     * @param data Response data from OpenWeatherMap API.
     * @return Textual description of weather.
     */
    private String getWeatherDescription(final Map<String, Object> data) {
        final List<Object> weatherList = (List<Object>) data.get("weather");
        final Map<String, Object> weather = (Map<String, Object>) weatherList.get(0);
        final String description = (String) weather.get("description");
        return description;
    }
    
}

In the application.yml configuration file we can set the values for the configuration properties:

# File: src/main/resources/application.yml
...
weather:
  client:
    url: http://api.openweathermap.org/
    path: /data/2.5/
    api-key: 39caa...
    read-timeout: 500ms
    retry:
      attempts: 2
      delay: 5s

When we run our application and access the URL http://localhost:8080/weather/tilburg using HTTPie we get the weather description:

$ http :8080/weather/tilburg
HTTP/1.1 200 OK
Content-Length: 13
Content-Type: text/plain;charset=UTF-8

moderate rain

Written with Micronaut 1.0.0.M4.

August 23, 2018

Micronaut Mastery: Using Stubs For Testing

Writing tests is always a good idea when developing an application. Micronaut makes it very easy to write tests. Using the @Client annotation we can generate a client for our REST resources that uses HTTP. Starting up a Micronaut application is so fast we can run our actual application in our tests. And using dependency injection we can replace components from the production application with stubs in our tests.

Let's show how we can use stub in our tests for an example application. In the example application we have controller ConferenceController that returns Conference objects. These objects are fetched from a simple data repository ConferenceDataRepository. When we write a test we want to replace the ConferenceDataRepository with a stub, so we can return the appropriate Conference objects for our tests.

First we take a look at the Conference class:

package mrhaki.micronaut;

public class Conference {
    
    private final String name;
    private final String location;

    public Conference(String name, String location) {
        this.name = name;
        this.location = location;
    }

    public String getName() {
        return name;
    }

    public String getLocation() {
        return location;
    }
    
}

We add an interface that describe the functionality we expect for getting Conference objects in our application:

// File: src/main/java/mrhaki/micronaut/ConferenceService.java
package mrhaki.micronaut;

import io.reactivex.Maybe;
import io.reactivex.Single;

import java.util.List;

interface ConferenceService {

    Single<List<Conference>> all();

    Maybe<Conference> findByName(final String name);
    
}

For our example the implementation of the ConferenceService is simple, but in a real world application the code would probably access a database to get the results:

// File: src/main/java/mrhaki/micronaut/ConferenceDataRepository.java
package mrhaki.micronaut;

import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.Single;

import javax.inject.Singleton;
import java.util.Arrays;
import java.util.List;

@Singleton
public class ConferenceDataRepository implements ConferenceService {

    private final static List<Conference> CONFERENCES =
            Arrays.asList(new Conference("Gr8Conf EU", "Copenhagen"),
                          new Conference("Gr8Conf US", "Minneapolis"),
                          new Conference("Greach", "Madrid"));

    public Single<List<Conference>> all() {
        return Single.just(CONFERENCES);
    }

    public Maybe<Conference> findByName(final String name) {
        return all()
                .flatMapObservable(conferences -> Observable.fromIterable(conferences))
                .filter(conference -> name.equals(conference.getName()))
                .singleElement();
    }

}

Finally our controller to return conference data via HTTP REST that uses via dependency injection the ConferenceDataRepository implementation of the ConferenceService interface:

// File: src/main/java/mrhaki/micronaut/ConferenceController.java
package mrhaki.micronaut;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.reactivex.Maybe;
import io.reactivex.Single;

import java.util.List;

@Controller("/conference")
public class ConferenceController {

    private final ConferenceService conferenceService;

    public ConferenceController(final ConferenceService conferenceService) {
        this.conferenceService = conferenceService;
    }

    @Get("/")
    public Single<List<Conference>> all() {
        return conferenceService.all();
    }

    @Get("/{name}")
    public Maybe<Conference> findByName(final String name) {
        return conferenceService.findByName(name);
    }
    
}

To add a stub for the ConferenceService in our test we must write a new implementation of the ConferenceService that is available on the test class path and not on the production code class path. In our test code directory (src/test/{java|groovy|kotlin}) we write our stub implementation. We use the @Primary annotation to instruct Micronaut to use this implementation of the ConferenceService interface. If we leave out the @Primary annotation we get an error, because we have two implementations of the interface on our classpath, ConferenceDataRepository and ConferenceServiceStub, and Micronaut doesn't know which one to use. By using @Primary we tell Micronaut to use the stub implementation.

// File: src/test/groovy/mrhaki/micronaut/ConferenceServiceStub.groovy
package mrhaki.micronaut

import io.micronaut.context.annotation.Primary
import io.reactivex.Maybe
import io.reactivex.Single

import javax.inject.Singleton

@Singleton
@Primary
class ConferenceServiceStub implements ConferenceService {

    Single<List<Conference>> all() {
        return Single.just([new Conference("Gr8Conf", "Copenhagen")])
    }

    Maybe<Conference> findByName(final String name) {
        if (name == 'Gr8Conf') {
            return Maybe.just(new Conference("Gr8Conf", "Copenhagen"))
        } else {
            return Maybe.empty()
        }
    }

}

In our test directory we also add a declarative HTTP client to invoke the REST resource. This client is only used for testing and makes invoking the REST resource very easy:

// File: src/test/groovy/mrhaki/micronaut/ConferenceClient.groovy
package mrhaki.micronaut

import io.micronaut.http.annotation.Get
import io.micronaut.http.client.Client
import io.reactivex.Maybe
import io.reactivex.Single

@Client("/conference")
interface ConferenceClient {

    @Get("/")
    Single<List<Conference>> all()

    @Get("/{name}")
    Maybe<Conference> findByName(final String name)

}

We write a Spock specification to test our REST resource and it will use the stub code as implementation of ConferenceService:

// File: src/test/groovy/mrhaki/micronaut/ConferenceContollerSpec.groovy
package mrhaki.micronaut

import io.micronaut.context.ApplicationContext
import io.micronaut.runtime.server.EmbeddedServer
import io.reactivex.Maybe
import io.reactivex.Single
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

class ConferenceContollerSpec extends Specification {
    
    @Shared
    @AutoCleanup
    private EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer)

    @Shared
    private ConferenceClient client = embeddedServer.applicationContext.getBean(ConferenceClient)

    void 'get all conferences'() {
        when:
        final Single<List<Conference>> result = client.all()
        
        then:
        final conference = result.blockingGet().first() 
        with(conference) {
            name == 'Gr8Conf'
            location == 'Copenhagen'
        }
    }
    
    void 'find conference by name'() {
        when:
        final Maybe<Conference> result = client.findByName('Gr8Conf')
        
        then:
        final conference = result.blockingGet()
        with(conference) {
            name == 'Gr8Conf'
            location == 'Copenhagen'
        }
    }
    
    void 'return empty when conference cannot be found by name'() {
        when:
        final Maybe<Conference> result = client.findByName('JavaOne')

        then:
        result.isEmpty().blockingGet()
    }
}

Written with Micronaut 1.0.0.M4.