Spring Boot: A bit more cool with Kotlin

In the context of my favorite framework, Spring Boot, I have recently started to explore the effect of using the Kotlin 1.1 programming language (as an alternative to Java 8). This post describes a few language features that may be interesting to a typical Java 8 developer. Also, I hope you will see that:

Spring Boot is a bit more cool with Kotlin

ūüôā

I have created an example application on GitHub. That application contains all the code that you see here. It is based on Spring Boot 2.0 and Kotlin 1.1.

The example contains: a JPA entity, a JPA repository, a REST controller, a REST client and 2 x integration tests. The database is H2 and the schema is created using a single Flyway migration.

It is going to get messy now: I’ll just pick and choose Kotlin features, in the context of that example, that I find interesting. Hang tight ūüôā

JPA – Kotlin style

Here’s the JPA entity:

import javax.persistence.Entity
import javax.persistence.Id
import javax.validation.constraints.NotNull

@Entity
class Message(@Id val id: String, @NotNull val text: String)

A one liner. It has two properties: id and text. It uses standard JPA annotations. So that’s one of Kotlin’s nice features: properties – see [kotlin-reference]. Another, is the possibility to develop extremely compact class definitions.

A JPA repository that can be used to perform CRUD operations on the entity:

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface MessageRepository : JpaRepository<Message, String>

This is a Spring Data powered JPA repository in action. To be honest it’s quite similar to the same if it was written Java. Just wanted to show it for completeness.

REST controller – Kotlin style

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import javax.annotation.PostConstruct

@RestController
@RequestMapping("/messages")
class MessageController(val messageRepository: MessageRepository) {

    @PostConstruct
    fun postConstruct() = messageRepository.save(
            listOf(Message("1", "Hello World"), Message("2", "Hej Verden")))

    @GetMapping
    fun list() = messageRepository.findAll()

    @GetMapping("/{id}")
    fun get(@PathVariable id: String) = messageRepository.findOne(id)

}

This is a normal Spring MVC REST controller. It uses constructor injection (see the class header), defines two HTTP GET methods and a lifecycle method.

The methods are one liners: In Kotlin, functions doesn’t have to supply a body.

Notice that the methods doesn’t explicitly declare any return types. That’s an example of Kotlin’s advanced type inference. You are definitely allowed to declare the types also. Being a die hard Java developer, I may argue that it could add clarity to the reader – but mostly for more advanced examples (fx functions with bodies having x amount of code).

Together these features reduce the typical Java ceremonies a lot.

REST client – Kotlin style

Here’s a rather funny REST client (it’s just a client to the controller you saw above):

import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.runBlocking
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.stereotype.Component
import java.util.concurrent.TimeUnit

@Component
class MessageClient(templateBuilder: RestTemplateBuilder, @Value("\${server.port}") val port: Int) {

    val restTemplate = templateBuilder.rootUri("http://localhost:$port").build()

    fun getMessages(): List<Message> {

        fun asyncGetForObject(id: Int) = async(CommonPool) {
            TimeUnit.SECONDS.sleep(4)
            restTemplate.getForObject("/messages/$id/", Message::class.java)
        }

        val messages = listOf(asyncGetForObject(1), asyncGetForObject(2))

        return runBlocking { messages.map { it.await() } }

    }

}

This class has two properties: port and restTemplate. The latter is initialized in the class body. Take a look at that initialization code: it uses Kotlin’s string template support for string interpolation [kotlin-stringtemplates]. I know it’s small stuff – but thats really a neat little feature. Also – you know what? Kotlin even supports multiline strings ūüôā

Take a close look at the getMessages function. It has a nested function called asyncGetForObject. At first this may seem nasty to a Java developer. But to be honest, after having given it a bit of thought, I think it can be okay. In this case I think it is, because: the function is only supposed to be used from within the getMessages function and it is rather small.

Also, getMessages uses the Kotlin 1.1 experimental “coroutine” functionality [kotlin-coroutines]. The coroutine is the async(CommonPool){} block. Invoking that, as we do twice in the val messages = … line is non-blocking (hence the async hint :)). At that very moment 2 x REST requests run in parallel. The last block runBlocking{} is where we await the results and return them when they are ready.

There is a lot more to the coroutine story. And remember it is experimental in Kotlin 1.1. But still: That’s damn interesting in my opinion :).

Did you notice the collection map functionality?? The messages.map { it.await() } code. (We pass a lambda to the List.map() method). In Kotlin single-argument lambdas, we can just reference the it variable. Also, no collect() call there. That’s a really nice lambda functionality in Kotlin, right? ūüôā

Tests – Kotlin style

Here’s a Spring Boot integration test of the REST client:

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT
import org.springframework.test.context.junit4.SpringRunner
import kotlin.system.measureTimeMillis

@RunWith(SpringRunner::class)
@SpringBootTest(webEnvironment = DEFINED_PORT, value = "server.port=8090")
class MessageClientIntegrationTests {

    @Autowired
    lateinit var messageClient: MessageClient

    @Test
    fun `getMessages() should fetch messages in parallel`() {

        val durationInMilliseconds = measureTimeMillis {

            val messages = messageClient.getMessages()

            assertThat(messages.size).isEqualTo(2)
            assertThat(messages).extracting { it.id }.contains("1", "2")
            assertThat(messages).extracting { it.text }.contains("Hello World", "Hej Verden")

        }

        assertThat(durationInMilliseconds).isLessThan(5000)

    }

}

The test function name is more pleasant to read than a typical Java based test.

Notice Kotlin’s measureTimeMillis function. For Java developers that may look like a build in language construct. Like the Java synchronized( this ) {} blocks fx. It is a function actually: In Kotlin, when a lambda is the last argument, then it may be supplied after the function call (measureTimeMillis() {} or measureTimeMillis {}).

The measureTimeMillis function itself – that’s not bad at all either I think. Here it is super handy and nevertheless easy to use.

There are many more cool features in Kotlin. That’s subject for another post though.

I told you it was going to get messy ūüôā

References

[kotlin-reference] : Kotlin Reference
https://kotlinlang.org/docs/reference/

[kotlin-coroutines] : Kotlin Reference : Coroutines
https://kotlinlang.org/docs/reference/coroutines.html

[kotlin-stringtemplates] : Kotlin Reference : String templates
https://kotlinlang.org/docs/reference/basic-types.html#string-templates

Tagged with: ,
Posted in Kotlin, Spring

Java EE: Hello World, Kotlin

Are you a savvy Java EE 7 application developer? My bet is then, that you are using Java 7/8 for developing your favorite components (EJBs, CDI beans etc). In this post I am going to show how you can develop a Java EE 7 application using Kotlin 1.1 instead. It is very very (!) easy: leverage your existing Java EE 7 knowledge while learning one of the most powerful and beautiful JVM languages we have right now. Very opinionated of course ūüėČ

The example code in this post can be found in its entirety and real context on GitHub.

The application consists of the following: an EJB, a JAX-RS resource, two arquillian integration tests and a Gradle script. CDI is used – but only for injecting the EJB into the JAX-RS resource. Everything is fuelled by a WildFly 10.1 application server. Not WildFly Swarm; vanilla Java EE 7 here.

Just want to see the code?

1 of 4: The EJB

import javax.ejb.Stateless

@Stateless
class HelloBean {

    fun sayHello(caller: String) = "Hello, $caller"

}

This is a stateless session bean with a “no-interface” view.

It has a single method: sayHello. Kotlin can infer the return type here from the expression: an ordinary String. Also note how Kotlin offers string interpolation – the caller parameter is seamlessly used in the message: No String.format(“Hello, %s”, caller) necessary anymore.

Are you wondering about the weird method syntax? It looks like a variable assignment perhaps? Well, in Kotlin, a method (or function) may or may not have a body. If it doesn’t have a body, then you use the “=” character and specify the return value directly. If you do provide a body…then it will look much like traditional Java code.

No semicolons :).

2 of 4: The JAX-RS resource

import javax.inject.Inject
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.PathParam

@Path("/hello")
class HelloResource @Inject constructor(val helloBean: HelloBean) {

    @GET
    @Path("/{caller}")
    fun get(@PathParam("caller") caller: String) = helloBean.sayHello(caller)

}

A bit weird :). But awesome when your synapses start to learn what it means.

Class HelloResource is a JAX-RS resource that has a primary constructor [kotlin-primaryconstructor] and a property: helloBean. It’s part of the class header. Normally you wouldn’t write constructor in the class header. But when you want to apply an annotation to it…then it becomes mandatory. The annotation used here, is the CDI @Inject annotation.

In short: HelloResource uses CDI to perform “constructor injection” in order to obtain a reference to the HelloBean EJB. You can also do field injection etc. – but that’s for your own exercise ūüôā

The JAX-RS resource defines a single method that responds to “HTTP GET” requests: get(…). Notice how the JAX-RS annotations are used as you are used to from Java.

Before the resource is deployed in the application server, recall that in Java EE 7, you need to wrap up the trivial JAX-RS Application as well:

import javax.ws.rs.ApplicationPath
import javax.ws.rs.core.Application

@ApplicationPath("/api")
class HelloJaxRsApplication : Application() {

    override fun getClasses() = mutableSetOf(HelloResource::class.java)

}

Kotlin inheritance right there! In Kotlin you use “:” for covering the typical Java extends and implements keywords.

Take a look at the getClasses method: it uses the mutableSetOf function. What’s up with that?

Well, in Kotlin you can have package level functions – functions that don’t live in a class. Some functions, such as mutableSetOf, is visible to you without the need for importing them. Just like you can use java.lang.* in Java land.

3 of 4: The Arquillian integration tests

An integration test of the EJB:

import org.jboss.arquillian.container.test.api.Deployment
import org.jboss.arquillian.junit.Arquillian
import org.jboss.shrinkwrap.api.ShrinkWrap
import org.jboss.shrinkwrap.api.asset.EmptyAsset
import org.jboss.shrinkwrap.api.spec.WebArchive
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import java.io.File
import javax.inject.Inject
import kotlin.reflect.KClass

@RunWith(Arquillian::class)
class HelloBeanIntegrationTests {

    @Inject
    lateinit var helloBean: HelloBean

    @Test
    fun sayHello_whenInvokedWithDuke_thenReturnsHelloDuke() {

        // Given
        val caller = "Duke"

        // When
        val message = helloBean.sayHello(caller)

        // Then
        assertEquals("Hello, Duke", message)

    }

    companion object {

        @JvmStatic
        @Deployment
        fun createDeployment() = ShrinkWrap.create(WebArchive::class.java)
                .addPackage(HelloBean::class.java.`package`)
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
                .addAsLibraries(File(KClass::class.java.protectionDomain.codeSource.location.file))

    }

}

Notice the injection of helloBean: Keyword var. Properties declared like this are mutable – you can change them at will in your code. Kotlin also offers val. This is like using the final modifier in Java: you can only set them once.

In this case we are forced to use the val keyword because Arquillian performs dependency injection of the helloBean field, after the class has been constructed. For that to work we also need to use Kotlin’s lateinit modifier. Without it the code wouldn’t compile. You may find it overly annoying here. But you will probably be happy to hear that it is caused by another Kotlin feature that is insanely cool: Kotlin offers null safety [kotlin-nullsafety].

The actual @Test method itself: Not much to say here. It’s the ordinary server-side Arquillian test. When executed, then it runs inside the application server process.

In Java, Arquillian needs a static method annotated with @Deployment that produces a Java EE archive (EAR, JAR or WAR) containing the components to be tested. Kotlin doesn’t have static methods at all – so that’s a problem. Luckily Kotlin offers companion objects [kotlin-companionobjects] and the @JvmStatic annotation [kotlin-jvmstatic]. Suffice to say: This cocktail solves the Java interoperability “issue” without further ado. Arquillian cannot tell the difference ūüôā

Notice the library being added to the Arquillian archive: We need to bundle some Kotlin runtime classes with the application. What you see in this example, is a good old dirty Java trick allowing you to locate the actual JAR file from which the specified class is loaded. Perhaps you can find a Shrinkwrap Gradle/Maven resolver for a more viable alternative [shrinkwrap-resolver].

A client-side integration test of the JAX-RS resource:

import org.jboss.arquillian.container.test.api.Deployment
import org.jboss.arquillian.container.test.api.RunAsClient
import org.jboss.arquillian.junit.Arquillian
import org.jboss.arquillian.test.api.ArquillianResource
import org.jboss.shrinkwrap.api.ShrinkWrap
import org.jboss.shrinkwrap.api.asset.EmptyAsset
import org.jboss.shrinkwrap.api.spec.WebArchive
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import java.io.File
import java.net.URI
import javax.ws.rs.client.ClientBuilder
import kotlin.reflect.KClass

@RunWith(Arquillian::class)
class HelloResourceIntegrationTests {

    @ArquillianResource
    lateinit var url: URI

    @Test @RunAsClient
    fun get_whenInvokedWithDuke_thenReturnsHelloDuke() {

        // Given
        val caller = "Duke"

        // When
        val message = ClientBuilder.newClient().target(url)
                .path("/api/hello/$caller")
                .request()
                .get(String::class.java)

        // Then
        assertEquals("Hello, Duke", message)

    }

    companion object {

        @JvmStatic
        @Deployment
        fun createDeployment() = ShrinkWrap.create(WebArchive::class.java)
                .addPackage(HelloResource::class.java.`package`)
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
                .addAsLibraries(File(KClass::class.java.protectionDomain.codeSource.location.file))

    }

}

Kotlin-wise, not so much to remark.

The test is an Arquillian client side test. That is enforced via the @RunAsClient annotation on the test method.

Also, I thought it could be fun to use the JAX-RS client side API to test the resource. So that’s what you see there: Vanilla Java EE API use.

4 of 4: The Gradle script

Many Java EE developers are happy Maven users. I have been so too for years. Today I am a happy Gradle user. So for this example, I have used Gradle to take care of the build, packaging and dependency management:

buildscript {
    ext {
        kotlinVersion = '1.1.1'
        wildflyVersion = '10.1.0.Final'
        wildflyHome = "${rootDir}/build/unpacked/dist/wildfly-${wildflyVersion}"
    }
    repositories {
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
        classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion"
        classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlinVersion"
    }
}

configurations {
    install
}

apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: "kotlin-allopen"
apply plugin: "kotlin-noarg"
apply plugin: 'war'

allOpen {
    annotation("javax.ejb.Stateless")
    annotation("javax.ws.rs.Path")
}

noArg {
    annotation("javax.ws.rs.Path")
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
    maven { url 'https://repository.jboss.org/nexus/content/groups/public-jboss' }
    maven { url 'https://repository.jboss.org/nexus/content/repositories' }
    maven { url 'https://repository.jboss.org/nexus/content/repositories/thirdparty-releases' }
}

dependencies {
    providedCompile 'javax:javaee-api:7.0'

    compile("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
    compile("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")

    testCompile 'junit:junit:4.12'
    testCompile 'org.jboss.arquillian:arquillian-bom:1.1.12.Final'
    testCompile 'org.jboss.arquillian.junit:arquillian-junit-container:1.1.12.Final'

    testRuntime "org.wildfly.arquillian:wildfly-arquillian-container-managed:2.0.2.Final"
    testRuntime 'org.jboss.logging:jboss-logging:3.1.4.GA'
    testRuntime 'org.jboss.resteasy:resteasy-client:3.1.1.Final'

    install "org.wildfly:wildfly-dist:${wildflyVersion}@zip"
}

test {
    environment 'JBOSS_HOME', rootProject.wildflyHome
    systemProperty 'java.util.logging.manager', 'org.jboss.logmanager.LogManager'
}

task unzipWildFlyAppServer(type: Copy) {
    from zipTree(configurations.install.singleFile)
    into file("${buildDir}/unpacked/dist")
    tasks.test.dependsOn unzipWildFlyAppServer
}

To build Kotlin source code you need the Gradle “kotlin” plugin. You apply that as with the normal “java” plugin.

In Kotlin, classes are per default “final” (in Java terminology). But the Java EE application server vendor needs to subclass our components in order to provide security, transaction support, threadsafety, scope management and much more. You could write “open” in front of all your Kotlin classes. But the Kotlin guys have made us an offer we cannot refuse: the kotlin-allopen compiler plugin. It is declared in the buildscript{} block – and used in the allOpen{} block: simply list the standard Java EE annotations (or your own custom ones) that should trigger a class to be “open” (meaning that it can be inherited from by another class).

Some Java frameworks wants a noarg constructor in Java classes. JAX-RS resources must provide one as well. But recall the example code: there isn’t one. Because we use CDI to perform constructor injection of the EJB. Again, Kotlin comes with another compiler plugin: kotlin-noarg. In the noArg{} block we list those annotations that should trigger classes to always have a noarg constructor. So, basically the same as for the allopen functionality.

In addition to that, don’t forget to add the kotlin-stdlib and kotlin-reflect Kotlin libraries to the compile classpath.

Sidetrack: The unzipWildFlyAppServer Gradle task has nothing to do with Kotlin. It just ensures that WildFly AS 10.1 is downloaded and extracted – so that it can be used from the Arquillian tests.

Conclusion

I haven’t even shown all the cool Kotlin language features in this post. There are many many super cool features that Kotlin offers you. Fx properties, default parameter values, named parameters, data classes (!!), extension functions and much more.

But I hope that I succeeded in showing you how to use Kotlin with Java EE. I feel that it is largely painless. There are a few interoperability tricks that we need to perform – but I guess they become “the usual suspects”.

You can use this new modern language right now. But you don’t have to switch paradigm, leave the JVM – heck; you can even continue mastering your favorite Java framework. Here, in this post, I showcased Java EE 7. But the same applies for Spring Framework applications (Spring Boot flavors also), Vert.x applications and much more.

At the time of writing this post I haven’t adopted Kotlin on real-world projects (on the job). But it is very likely to happen on the next.

You can wait for Java to adopt modern programming language features. Or you can use Kotlin right now. A little flirt with Kotlin doesn’t mean you are divorcing Java ūüôā

References

[kotlin-primaryconstructor] Kotlin Reference : Constructors
https://kotlinlang.org/docs/reference/classes.html#constructors

[kotlin-nullsafety] Kotlin Reference : Null safety
https://kotlinlang.org/docs/reference/null-safety.html

[kotlin-companionobjects] Kotlin Reference : Companion objects
https://kotlinlang.org/docs/reference/classes.html#companion-objects

[kotlin-jvmstatic] Kotlin Reference : Generating REAL static methods
https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#static-methods

[shrinkwrap-resolver] Shrinkwrap Resolvers
https://github.com/shrinkwrap/resolver

Tagged with: , , , , ,
Posted in Java EE, Kotlin, WildFly

Spring Boot: Hello World, Kotlin

In this post I show how you can create a Spring Boot 1.5 application using Kotlin 1.1 (as opposed to typically Java 8 in these times).

The example I’ve created is a typical “Hello World” example. I have chosen to implement a Spring MVC controller – and an awesome Spring Boot integration test. The Gradle build script uses Kotlin as well (that’s pretty awesome). You can find the example project in its entirety and real context¬†on GitHub.

In this post I also explain some of the Kotlin specifics worth noticing to a typical (Spring) Java developer. If you are a Kotlin savvy developer and find that my explanations are wrong or misleading Рthen please leave a comment, so that I can fix them. I am by no means a Kotlin specialist (yet!).

Just want to see the code?

1 of 4: The Spring Boot application

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
class Application

fun main(args: Array<String>) {
    SpringApplication.run(Application::class.java, *args)
}

Notice that we activate Spring Boot using the normal @SpringBootApplication annotation. The fun part here, if you will, is that in Kotlin classes doesn’t even need to have a body. I guess many of them will have one – but here it is simply not necessary.

The main method in Kotlin is a package level function (it is not embedded inside a class).

We pass Application::class.java to the run method. That is not a typo Рand it is not a Java source file reference :). It is still the Class object that represents class Application. If you just pass Application::class Рthen you would pass an object of type KClass Рthat is Kotlins own representation of what you know as the Class type from Java!

The weird *args is not a pointer Рbut rather Kotlins spread operator (the run method declares a vararg parameter).

( Also: Semicolons are not used – no biggie for me though )

2 of 4: The controller

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController

@RestController
class GreetingController {

 @GetMapping("/hello/{name}")
 fun get(@PathVariable name: String) = "Hello, $name"

}

This is a typical Spring MVC controller. I bet you can recognise @RestController, @GetMapping and @PathVariable (if you are an experienced Spring Java developer).

Notice that the function¬†is prefixed with¬†fun here. It looks like a variable assignment, but it isn’t. What you see there is an implicit return statement of the string “Hello, $name”. The function could have had a body and a¬†return statement instead – but it isn’t necessary in this case.

Notice that the the function doesn’t have an explicit return type. You could have put “: String” just before the equals sign. But Kotlin¬†can infer it – so not necessary in this case either.

The $name argument is replaced with the contents of the name parameter Рa demonstration of Kotlins support for string interpolation. This, almost insignificant feature, would have a huge effect on many of the Java projects I have seen!

3 of 4: The integration test

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.client.TestRestTemplate
import org.springframework.test.context.junit4.SpringRunner

@RunWith(SpringRunner::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class GreetingControllerIntegrationTests {

    @Autowired
    lateinit var restTemplate: TestRestTemplate

    @Test
    fun `GET when given Duke then returns "Hello, Duke"`() {

        // Given
        val name = "Duke"

        // When
        val body = restTemplate.getForObject("/hello/{name}", String::class.java, name)

        // Then
        assertThat(body).isEqualTo("Hello, $name")

    }

}

Worth noticing here is the weird lateinit modifier [lateinit]. So this is the deal: Kotlin normally assume that the member is non-null and therefore it must be assigned explicitly in the constructor to a non-null value. But since the Spring TestContext Framework takes care of the injection after the constructor has run, then we need to allow it explicitly, using the lateinit modifier.

Another interesting element is the @Test function name: I actually started with the name get_whenInvokedWithDuke_thenReturnsHelloDuke. But I updated the example after a recommendation from Tyler (see the comments below): Using backticks around function names allow us to provide sentence-like function names. Super nice for naming tests if you ask me.

In the @Test itself: notice the use of¬†val. It’s like Java’s final¬†modifier (in context of variables). Kotlin’s type inference means that we aren’t¬†forced to write down the type (String).

So…. I think this seems super nice. The code is not overly verbose with type information. And at least for this Hello World case I think it is perfectly fine. I’m also pretty happy with the string interpolation again ūüôā … (in Java land I tend to use String.format(“stuff…%s”, varhere) – rather annoying).

4 of 4: The Gradle script

I have been using Gradle as a drop-in replacement for Maven for the last 1.5 year. For that period¬†I’ve always used Groovy as the programming language in my Gradle scripts. And I kind of like that – but also, I must admit that the IDE assistance isn’t super optimal¬†(even in IntelliJ which I use now).

Then as I was browsing Kotlin features etc, I found out that the Gradle guys are working on supporting Kotlin as another programming language in scripts [gradlekotlin]. And not only that: they are working on making Kotlin the language of choice for developing Gradle plugins [gradlescriptkotlin] !

Okay – the code:

buildscript {

    val springBootVersion = "1.5.2.RELEASE"
    var kotlinVersion: String by extra
    kotlinVersion = "1.1.0"

    repositories {
        mavenCentral()
    }

    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
        classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion")
    }

}

val kotlinVersion: String by extra

apply {
    plugin("kotlin")
    plugin("kotlin-spring")
    plugin("org.springframework.boot")
}

repositories {
    mavenCentral()
}

dependencies {
    compile("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
    compile("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
    compile("org.springframework.boot:spring-boot-starter-web")
    testCompile("org.springframework.boot:spring-boot-starter-test")
}

Looks like the ordinary Groovy based script in my opinion. That’s nice – so no big re-adjustment.

I am quite certain that you should use an up-to-date version of IntelliJ to get a decent IDE experience with Kotlin based build scripts. For me it seems okay (IntelliJ IDEA 2016.3.5) Рbut the content assist is extremely slow. I guess that will be nailed properly at some point. Also I still think I get a bunch of weird suggestions in my content assist in the different blocks.

Motivation: why Kotlin?

I love Java for it’s simplicity – and I know it by heart.

But, to be honest I feel there are a bunch of nice features in most of the other popular languages today that Java simply doesn’t have. Kotlin is one of these new JVM languages – and it has a truly remarkable feature list. Head over to the reference manual and browse through it [kotlinreference]; I promise that you will be clapping your hands as you read through the individual features.

Lately I’ve noticed how Pivotal embraces Kotlin – through examples,¬†public demo sessions (fx by¬†our favorite rockstar, Starbuxman [starbuxman]) and nonetheless by making the core Spring Framework seamless to use from Kotlin [springkotlin]. Being an avid Spring Boot fan that got my attention.

I haven’t tried Kotlin on a real world project yet. But I definitely hope that I will get the chance very soon.

References

[kotlinreference] : Kotlin Reference
https://kotlinlang.org/docs/reference/

[starbuxman] : Spring Tips: The Kotlin Programming language
https://spring.io/blog/2016/10/19/spring-tips-the-kotlin-programming-language

[springkotlin] : Introducing Kotlin support in Spring Framework 5.0
https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0

[lateinit]: The Kotlin lateinit modifier:
https://kotlinlang.org/docs/reference/properties.html#late-initialized-properties

[gradlekotlin]: Kotlin Meets Gradle
https://blog.gradle.org/kotlin-meets-gradle

[gradlescriptkotlin]: Gradle Script Kotlin – FAQ
https://github.com/gradle/gradle-script-kotlin/wiki/Frequently-Asked-Questions#in-what-language-should-i-develop-my-plugins

 

 

Tagged with:
Posted in Kotlin, Spring

Spring Boot: Prometheus actuator endpoint

In this post I show how you can add support for Prometheus in Spring Boot applications.

Step 1 of 2: Add the Prometheus Spring Boot Starter

You can get Prometheus support in Spring Boot applications via a so-called “starter”. Here is how to get that…

It’s a one liner in Gradle:

compile 'com.moelholm:prometheus-spring-boot-starter:1.0.1'

And a five liner in Maven:

<dependency>
  <groupId>com.moelholm</groupId>
  <artifactId>prometheus-spring-boot-starter</artifactId>
  <version>1.0.1</version>
</dependency>

That’s it. Ready for testing it?

Step 2 of 2: Test it

Now you have a new Spring Boot Actuator endpoint. If you haven’t changed the management port and default application port – then you can find it here:

http://localhost:8080/prometheus

That will give you output like this:

# HELP httpsessions_max httpsessions_max
# TYPE httpsessions_max gauge
httpsessions_max -1.0
# HELP httpsessions_active httpsessions_active
# TYPE httpsessions_active gauge
httpsessions_active 0.0
# HELP mem mem
# TYPE mem gauge
mem 368223.0
# HELP mem_free mem_free
# TYPE mem_free gauge
mem_free 226605.0
... ( and so on and so forth forever ) ...

I have created a working example on GitHub – consult that to see the starter used and validated from an integration test.

Optional configuration

The Prometheus endpoint can be configured via 2 properties:

  • endpoints.prometheus.path
  • endpoints.prometheus.sensitive

The first one defaults to /prometheus. The second one defaults to false. Feel free to change any one of them by setting the properties in your Spring Boot application.properties file (or YAML if you are into that stuff).

About the Spring Boot Starter

I recently had to add Prometheus support to some of our Spring Boot applications at my work.

I searched (a bit) and did find another nice starter by Thomas Darimont [1]. The problem with this starter, for us, was that it:

  • Wasn’t in the Maven Central
  • Didn’t add the Prometheus endpoint as a real Spring Boot Actuator endpoint

I also found another starter by Oleg Vyukov [2]. But I found the implementation a bit complicated – at least compared with other material on how to manually activate a Prometheus endpoint. That made me a bit nervous…

So I figured: why not…create another starter myself :). That is the one you see referenced in the above example. You can find the source code for it on GitHub:

https://github.com/nickymoelholm/prometheus-spring-boot-starter

References

[1] Thomas Darimont’s starter:
https://github.com/thomasdarimont/prometheus-spring-boot-starter

[2] Oleg Vyukov’s starter:
https://github.com/akaGelo/spring-boot-starter-prometheus

Tagged with: ,
Posted in Spring

Spring Boot: Controlling timezones with Hibernate

In this post I show how you, from a Spring Boot application, can control the timezone used by Hibernate when it communicates timestamps with the database.

This post is inspired by a very similar recent post by Vlad Mihalcea [1], Hibernate Developer Advocate at Redhat.

It’s basically the same subject – but with a Spring Boot focus. So read on, if you want¬†to get a “ready-to-use” recipe for¬†controlling the timezones used by JPA/Hibernate in your Spring Boot applications.

I will show the technique in the context of a Spring Boot 1.4.2 application.

Just looking for the solution? Then scroll down to Solution 2: Configuring Hibernate to control the timezone.

Motivation

On the job I was recently developing a JPA 2 powered database integration: I decided that the application should store timestamps in UTC. Effectively the same end result as deciding that it should run in the GMT timezone. UTC is strictly speaking not a timezone Рif you want to be nitty-gritty [2].

It turns out that this is a very annoying situation. Until very recently, with Hibernate,¬†you basically only had one solution: Configuring the JVM’s default timezone. More about that next.

Solution 1: Configuring the JVM’s default timezone

Here is one way to fix the problem: Put¬†the JVM’s default timezone into UTC. Like this, if you launch¬†a Spring Boot runnable JAR:

java -Duser.timezone=UTC -jar build/libs/springboot-hibernate-timezones.jar

Or from inside your Spring Boot application, like this:

@SpringBootApplication
public class Application {

  @PostConstruct
  void started() {
    TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
  }

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

}

You could also put the TimeZone code into the main method. But putting the TimeZone code into a @PostConstruct callback like this, ensures that it is also effective when you run tests or wrap up the application as a Web ARchive (WAR). Be careful about competing with other initializer methods that use JPA during startup.

In fact, as of writing this post (November 2016), setting the default timezone is the only truly portable solution for vanilla JPA solutions. JPA doesn’t allow you to control this.

If you use another JPA provider (than Hibernate) then this is still the way to go.

This solution works. But it is very invasive and has a few potential drawbacks.

Firstly, what if your JVM cannot be put into the timezone you desire for your database? Perhaps another default timezone is required by other parts of your application. Perhaps it is outside your control. That could easily be the case if your application is colocated with other applications in a shared application server for example.

Secondly, what if you need to access different databases – and they, for whatever reason, need timestamps to be stored in different timezones?

Solution 2: Configuring Hibernate to control the timezone

The previous solution is like solving the problem with a bazooka. Luckily, Vlad Mihalcea recently updated Hibernate itself to support this usecase. Even better:

The Hibernate specific configuration solution is a much more powerful solution

It is more powerful because you can choose to configure a specific SessionFactory (think JPA¬†EntityManagerFactory) and even a specific¬†Session (think JPA¬†EntityManager).¬†What a fantastic update! Find more details in Vlad Mihalcea’s post¬†[1].

I have prepared a working Spring Boot example on GitHub – consult that to see the code in its entirety and true surroundings. These are the 2 steps you need to complete…

Step 1 of 2: Upgrade Hibernate

The Hibernate version shipped with Spring Boot 1.4.2 is 5.0.11.Final. That’s not new enough. So start by upgrading Spring Boot to use Hibernate¬†5.2.3.Final or newer.

From the example on GitHub Рin Gradle:

...

// So we get Hibernates time zone configuration option (released October 2016):
ext['hibernate.version'] = '5.2.3.Final'

...

That’s how Hibernate get’s upgraded.

Step 2 of 2: Configure Hibernate to control the timezone

With Spring Boot this is a breeze. Add a property in application.properties:

spring.jpa.properties.hibernate.jdbc.time_zone = UTC

Done.

By the way: The example on GitHub uses a YAML file instead. It looks slightly different – but same principle in another syntax.

Test it

The example on GitHub includes a minimal test of the Hibernate configuration.

Firstly, a JPA entity:

@Entity
class Message {

  @Id // Don't do this at home...
  private String message;

  @Column
  private LocalDateTime created;

  // ... Getters and setters && a few constructors

}

Secondly, a JPA repository – Spring Data style:

interface MessageRepository extends CrudRepository<Message, String> {
}

And finally, the integration test:

@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaIntegrationTests {

  static {
    TimeZone.setDefault(TimeZone.getTimeZone("GMT+2"));
  }

  @Autowired
  private MessageRepository messageRepository;

  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Autowired
  private Environment e;

  @Test
  public void testJpa() {

    //
    // Given
    //

    // 1) This is the datetime we will insert
    LocalDateTime localDateTime = LocalDateTime.of(2017, Month.JANUARY, 1, 0, 0, 0);

    // 2) This is the datetime we expect the database to receive
    LocalDateTime gmtDateTime = LocalDateTime.of(2016, Month.DECEMBER, 31, 22, 0, 0);

    //
    // When
    //

    messageRepository.save(new Message("Hello World from JPA", localDateTime));

    //
    // Then
    //

    Message fromDatabase = messageRepository.findOne("Hello World from JPA");

    // 1) Datetime is unchanged
    assertThat(fromDatabase.getCreated()).isEqualTo(localDateTime);

    // 2) We expect the database to store the datetime in the GMT timezone ( == UTC )
    jdbcTemplate.query("select " + formatDate(e, "created") + " from message", rs -> {
      assertThat(LocalDateTime.parse(rs.getString(1))).isEqualTo(gmtDateTime);
    });

  }

}

First, consider that the default timezone is set to GMT+2 (see the static initializer). Then notice that a LocalDateTime is created in this context. Finally notice the “Then” part: the database receives a LocalDateTime that is 2 hours before the LocalDateTime being inserted.

And there you have it: Hibernate automatically translates the LocalDateTime from GMT+2 into GMT (=UTC) when communicating with the database.

BONUS TIP: Getting the Hibernate configuration to work with MariaDB / MySQL

Well, that didn’t work for me.

To begin with at least. It turns out that MariaDB / MySQL by default runs in a useLegacyDatetimeCode=true mode. You need to deactivate it for Hibernate to be able to do it’s magic. Luckily that’s easy, just configure it in the JDBC connection URL:

jdbc:mysql://localhost:3306/mysql?useLegacyDatetimeCode=false

Why? I debugged the driver to find out what was happening. It turns out that the Calendar objects Hibernate send to the JDBC API gets completely ignored. When the driver runs in the useLegacyDatetimeCode=true mode, which is default. The driver get’s the argument – and then simply choose to make another Calendar like this: Calendar.getInstance(). The effect of that, is that the driver uses the timezone that is default to the JVM.

Notice that the MariaDB / MySQL JDBC drivers also support another argument: serverTimezone=TIMEZONEHERE. Set that to GMT, skip the Hibernate configuration, and you have another solution. This time, however, tightly bound to the specific driver.

How Hibernate controls the timezone

Hibernate controls the timezone on the JDBC API layer.

The example I created used a LocalDateTime. The JDBC API doesn’t support LocalDateTime, so Hibernate translates that into a java.sql.Timestamp before it sends it to the PreparedStatement.setTimestamp(int parameterIndex, Timestamp x, Calendar cal) method.

Notice the Calendar argument. In Java Calendars are TimeZone aware. The Calendar Hibernate sends into the method is in the timezone that you specify.

Likewise, when Hibernate reads from the database again it sends the Calendar into ResultSet.getTimestamp(int columnIndex, Calendar cal).

I have illustrated this in a simulation – find it on GitHub too:

@RunWith(SpringRunner.class)
@SpringBootTest
public class JdbcIntegrationTests {

  static {
    TimeZone.setDefault(TimeZone.getTimeZone("GMT+2"));
  }

  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Autowired
  private Environment e;

  @Test
  public void testJdbc() {

    //
    // Given
    //

    // 1) This Calendar ensures that Timestamps are stored in the specified timezone:
    //    > Ignored when using useLegacyDatetimeCode=true (which is default for MariaDB) <
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

    // 2) This is the datetime we will insert
    LocalDateTime localDateTime = LocalDateTime.of(2017, Month.JANUARY, 1, 0, 0, 0);

    // 3) This is the datetime we expect the database to receive
    LocalDateTime gmtDateTime = LocalDateTime.of(2016, Month.DECEMBER, 31, 22, 0, 0);

    //
    // When
    //

    jdbcTemplate.update("insert into message values (?, ?)", ps -> {
      ps.setString(1, "Hello World from JDBC");
      ps.setTimestamp(2, Timestamp.valueOf(localDateTime), cal);
    });

    //
    // Then
    //

    // 1) We expect to get the datetime back in the JVM's timezone:
    jdbcTemplate.query("select * from message", rs -> {
      assertThat(rs.getTimestamp("created", cal).toLocalDateTime()).isEqualTo(localDateTime);
    });

    // 2) We expect the database to store the datetime in the GMT timezone ( == UTC )
    jdbcTemplate.query("select " + formatDate(e, "created") + " from message", rs -> {
      assertThat(LocalDateTime.parse(rs.getString(1))).isEqualTo(gmtDateTime);
    });

  }


}

The test is almost the same as the JPA version. This time it uses raw JDBC to document the machinery.

Reflection

I am actually puzzled that the configuration now offered by Hibernate hasn’t already been baked into the JPA specification. I suggested it on Twitter for some of the top Hibernate/JPA influencers and it seems like they could see the point in that [3].

Who knows, one day we may have this feature in JPA?

References

[1] How to store timestamps in UTC using the new hibernate.jdbc.time_zone configuration property:
http://in.relation.to/2016/09/12/jdbc-time-zone-configuration-property/

[2] UTC ‚Äď The World’s Time Standard:
https://www.timeanddate.com/time/aboututc.html

[3] Twitter – Why didn’t this feature go into JPA?
https://twitter.com/moelholm/status/794876824618749952

Tagged with: , , ,
Posted in Hibernate, Spring

Spring: Bean qualification

In this post I present how you can control a situation in which there are multiple beans that qualifies for an injection point in Spring.

The material here has been tested with Spring Framework 4.3.

The problem

Let’s first take a look at the problem.¬†Imagine you have this¬†interface:

public interface BusinessEventLogger {
}

And two different implementations of this. First this:

@Repository
public class DatabaseBusinessEventLogger implements BusinessEventLogger {
}

And then this:

@Repository
public class WebServiceBusinessEventLogger implements BusinessEventLogger {
}

Now, when you try to inject by the interface…

@Service
public class GreeterService {

  @Autowired
  private BusinessEventLogger businessEventLogger;

  public String sayHello(String caller) {
    businessEventLogger.log("Sending hello message to %s", caller);
    return String.format("Hello World, %s", caller);
  }

}

… then Spring will fail with a¬†NoUniqueBeanDefinitionException. That is because Spring¬†cannot decide which repository implementation is appropriate. After all, there are¬†two equally valid bean candidates here.

Techniques that you can use to get back in control:

  • Static¬†decisions:
    • Use the bean class
    • Use the injection target member name
    • Use the¬†@Qualifier annotation
    • Use a custom qualifier annotation
    • Use @Primary
  • Runtime decisions:
    • Use @Profile
    • Use @Profile and @Primary

That should be enough to get you started. Notice the grouping. Some of the techniques require you at development time to know which bean is relevant. Other techniques allow you to defer the decision until runtime. The latter group is appropriate if the actual decision is environment specific for example.

I have prepared an example on GitHub that shows the problem and each of the solutions. It is based on Spring Framework 4.3 (via Spring Boot 1.4.1). And it is a multi module Gradle project. The problem scenario itself is illustrated in module 00_TheProblem. The solution scenarios are illustrated in the modules named something similar to 0x_TheSolution_abc.

Solution 01: Use the bean class

This technique is applicable if you, at development time, already know the specific bean you need:

@Service
public class GreeterService {

  @Autowired
  private WebServiceBusinessEventLogger businessEventLogger;

  public String sayHello(String caller) {
    businessEventLogger.log("Sending hello message to %s", caller);
    return String.format("Hello World, %s", caller);
  }

}

Notice that the class WebServiceBusinessEventLogger has been hardcoded here.

Solution 02: Use the injection target member name

This technique is applicable if you, at development time, already know the specific bean you need:

@Service
public class GreeterService {

  @Autowired
  private BusinessEventLogger webServiceBusinessEventLogger;

  public String sayHello(String caller) {
    webServiceBusinessEventLogger.log("Sending hello message to %s", caller);
    return String.format("Hello World, %s", caller);
  }

}

Notice the name of the member field being injected into: webServiceBusinessEventLogger. This name happens to match the bean name of the bean that  is-a WebServiceBusinessEventLogger.

A bit fragile huh? One day another developer may drop by and refactor the name to something else. If that happens – then the application won’t start anymore.

Solution 03: Use @Qualifier

This technique is applicable if you, at development time, already know the specific bean you need:

@Service
public class GreeterService {

  @Autowired @Qualifier("webServiceBusinessEventLogger")
  private BusinessEventLogger businessEventLogger;

  public String sayHello(String caller) {
    businessEventLogger.log("Sending hello message to %s", caller);
    return String.format("Hello World, %s", caller);
  }

}

Notice the use of Spring’s¬†@Qualifier annotation. It tells Spring exactly which bean is needed – by specifying its name.

I think this solution is more robust than solution 02. But it is still a bit fragile. What if somebody decides to refactor the name of the WebServiceBusinessEventLogger class?

Solution 04: Use a custom qualifier annotation

This technique is applicable if you, at development time, already know the specific bean you need:

@Service
public class GreeterService {

  @Autowired @WebServiceStrategy
  private BusinessEventLogger businessEventLogger;

  public String sayHello(String caller) {
    businessEventLogger.log("Sending hello message to %s", caller);
    return String.format("Hello World, %s", caller);
  }

}

Notice the use of the @WebServiceStrategy annotation. This is a custom qualifier annotation. It tells Spring what bean is used. To make this work though, you first have to define the annotation:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface WebServiceStrategy {
}

(Notice the use of Spring’s @Qualifier here. Now it is being used as a meta-annotation)

And you will also have to add it to the bean:

@Component
@WebServiceStrategy
public class WebServiceBusinessEventLogger implements BusinessEventLogger {
}

You also need to do the same for the database logger.

This solution is as type safe as it gets. But at the expense of additional ceremony. It can make sense, if the abstraction of the qualifier annotation names is good enough.

Solution 05: Use @Primary

This technique is applicable if you, at development time, already know the specific bean you need. You put Spring’s @Primary annotation on the “primary” bean:

@Component
@Primary
public class WebServiceBusinessEventLogger implements BusinessEventLogger {
}

This will make the WebServiceBusinessEventLogger bean “win”.

Notice the difference from the previous solutions:

Using Spring’s @Primary annotation on a bean is a global decision: every injection point will get the chosen¬†bean.

This may seem like a weird solution. And in production code Рusing it like this Рperhaps it is. But this is not where @Primary shines. Rather:

The @Primary bean feature is interesting when used together with @Profile to activate the primary bean at runtime.

OR:

The @Primary bean feature is interesting when used for testing purposes.

If you haven’t thought about this¬†before, then now is the time to read that again :).

For testing purposes you can use @Primary to make a test bean “win” over a business bean. Take a look at Baeldung’s article for further inspiration [1]. In that¬†article @Primary is used with a @Bean factory method and @Profile to make a test bean have precedence¬†over a business bean. @Profile¬†for this scenario isn’t¬†necessary – unless the test bean also gets¬†picked up¬†when you run the application normally. That is typically an IDE configuration issue: when your test *.class¬†files are colocated with the normal business *.class files (I’ve seen that in Eclipse once or twice).

Solution 06: Use @Profile

This technique is applicable if you want to choose the bean at runtime.

If you are using Spring Boot you could, as an example, start the application with:

--spring.profiles.active=webservice 

… to activate the webservice¬†profile. Or you could also do it from a test, like this:

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("webservice")
public class GreeterServiceIntegrationTests {
}

Notice the use of @ActiveProfiles – that tells Spring to run the ApplicationContext with the webservice profile activated.

In order for this to work, you also need to tell the conflicting beans what profile they belong to. You use the @Profile annotation for that. Here’s how you would do it for the WebServiceBusinessEventLogger class:

@Component
@Profile("webservice")
public class WebServiceBusinessEventLogger implements BusinessEventLogger {
}

Notice¬†@Profile. That annotation tells Spring to only add the bean to the ApplicationContext¬†if the webservice profile is active.¬†@ActiveProfiles is different: it’s a test specific annotation that you put on test classes to tell Spring what profiles to activate when running the tests.

Unless you do anything else: You still need to put the @Profile annotation on the database logger too. Otherwise there will still be a conflict when you run with the webservice profile active (since, now there are two active beans candidates). If you have more candidates Рthen you will have to put @Profile on them too.

Solution 07: Use @Profile and @Primary

This technique is applicable if you¬†want to choose the bean at runtime. It is 90% similar to solution 06. But this solution doesn’t require you to put @Profile on all candidates¬†– only on the one that you also choose to be @Primary:

@Component
@Profile("webservice")
@Primary
public class WebServiceBusinessEventLogger implements BusinessEventLogger {
}

That’s it.¬†Nothing else.¬†If you have more candidates – then you will have to put @Profile¬†and @Primary on them too.

The difference to solution 06, is that in this solution, one bean acts as a default bean: namely the one without @Profile.

Conclusion

This post described different techniques to solving the situation where you have multiple bean candidates for an injection point. Some of them are appropriate if you can make the decision at development time. Others are appropriate if you would like the flexibility at runtime. All of them have been possible since Spring Framework 3.x.

I haven’t included:

  • XML¬†examples
  • Standards based qualification examples (JSR330 standard annotations [2])
  • @Bean examples

They do provide some further alternatives to fulfil the mission. That is an exercise for the motivated reader :).

I would love to hear from you, if you believe that I have forgotten some other obvious techniques. Just leave a comment before you leave.

References

[1] Injecting Mockito Mocks into Spring Beans :
http://www.baeldung.com/injecting-mocks-in-spring

[2] What is the relation between JSR-299 and JSR-330 in Java EE 6? Do we need two DI APIS?:
http://www.adam-bien.com/roller/abien/entry/what_is_the_relation_between

Tagged with: , ,
Posted in Spring

Spring Boot and Gradle: Separating tests

In this post I will present 4 different approaches to separating unit tests from integration tests, so that they¬†can be¬†run¬†independently of each other. Here’s the 4 different approaches:

  • Separation based on name patterns
  • Separation based on JUnit categories
  • Separation based on Spring’s¬†@IfProfileValue
  • Separation based on different source¬†directories

These approaches can easily be extended to apply to other test types as well (performance tests for example). Also, please note that:

Only one approach is specific to using Spring.

The remaining 3 approaches can just as well be used without Spring.

For each approach you will find a reference to a super simple GitHub based project. Consult the projects there to see the source code in its entirety and true surroundings. All projects are based on JUnit 4, Spring Boot 1.4 and Gradle.

Example code

This is the class being tested:

@Service
public class GreeterService {

  public String sayHello(String caller) {
    return String.format("Hello World, %s", caller);
  }

}

The unit test class instantiates it directly:

public class GreeterServiceTests {

  @Test
  public void sayHello_whenInvokedWithDuke_thenSaysHelloWorldDuke() {

    // Given
    GreeterService greeterService = new GreeterService();

    // When
    String greeting = greeterService.sayHello("Duke");

    // Then
    assertThat(greeting).isEqualTo("Hello World, Duke");

  }

}

Unit tests (such as the above) can be run from Gradle like this: ./gradlew test.

The integration test class uses Spring Boot like this:

@RunWith(SpringRunner.class)
@SpringBootTest
public class GreeterServiceIntegrationTests {

  @Autowired
  private GreeterService greeterService;

  @Test
  public void sayHello_whenInvokedWithDuke_thenSaysHelloWorldDuke() {

    // When
    String greeting = greeterService.sayHello("Duke");

    // Then
    assertThat(greeting).isEqualTo("Hello World, Duke");

  }

}

Integration tests (such as the above) can be run from Gradle like this: ./gradlew integrationTest.

Separation based on name patterns

Find the GitHub project here.

This approach expects you to partition tests using different names. I have chosen these patterns:

  • Unit test classes are¬†suffixed with¬†Tests
  • Integration test classes are suffixed with IntegrationTests

There are no changes to the test classes you¬†have already seen.¬†Gradle takes care of that. Here’s the relevant part:

...
apply plugin: 'java'
apply plugin: 'spring-boot'
...

test {
    useJUnit {
        exclude '**/*IntegrationTests.class'
    }
}

task integrationTest(type: Test) {
    useJUnit {
        include '**/*IntegrationTests.class'
    }
}

The important thing to remember here is that the patterns must end with¬†.class. I hope you won’t fall into the trap of forgetting that detail now…

So, this is easy. All driven from Gradle. However, if¬†developers¬†uses an invalid¬†suffix by mistake, then please note that this will¬†result in the test classes’ test cases not being run at all. A bit dangerous.

Separation based on JUnit categories

Find the GitHub project here.

This approach expects you to use JUnit annotations on the test classes. Firstly, create interfaces representing the test types:

public interface IntegrationTest {
}

And:

public interface UnitTest {
}

Then annotate your tests using the JUnit¬†@Category annotation. Here’s the unit test:

@Category(UnitTest.class)
public class GreeterServiceTests {

  @Test
  public void sayHello_whenInvokedWithDuke_thenSaysHelloWorldDuke() {
  ...
  }

}

Here’s the integration test:

@SpringBootTest
@RunWith(SpringRunner.class)
@Category(IntegrationTest.class)
public class GreeterServiceIntegrationTests {

  @Autowired
  private GreeterService greeterService;

  @Test
  public void sayHello_whenInvokedWithDuke_thenSaysHelloWorldDuke() {
  ...
  }

}

Lastly, tell Gradle when to run the tests:

...
apply plugin: 'java'
apply plugin: 'spring-boot'
...

test {
    useJUnit {
        includeCategories 'com.moelholm.UnitTest'
    }
}

task integrationTest(type: Test) {
    useJUnit {
        includeCategories 'com.moelholm.IntegrationTest'
    }
}

So, this is easy as well. And it is type safe Рso it is not brittle with respect to different test class names. Although not super elegant: Now you have to declare weird marker interfaces Рand remember to annotate your test cases accordingly by pointing to them from the @Category annotation.

For more information about JUnit categories – see [1].

Separation based on Spring’s @IfProfileValue

Find the GitHub project here.

This approach expects you to consistently use a custom annotation plus Spring’s¬†SpringRunner on all classes – even unit tests.

Here’s how the unit test class looks:

@RunWith(SpringRunner.class)
@UnitTest
public class GreeterServiceTests {

  @Test
  public void sayHello_whenInvokedWithDuke_thenSaysHelloWorldDuke() {
  ...
  }

}

Here’s how the integration test class looks:

@RunWith(SpringRunner.class)
@IntegrationTest
public class GreeterServiceIntegrationTests {

  @Autowired
  private GreeterService greeterService;

  @Test
  public void sayHello_whenInvokedWithDuke_thenSaysHelloWorldDuke() {
  ...
  }

}

In addition to that you must also implement the annotations – they are simple though. Here’s the @UnitTest annotation:

@Retention(RetentionPolicy.RUNTIME)
@IfProfileValue(name="testprofile", value="unittest")
public @interface UnitTest {
}

Notice the @IfProfileValue annotation [2]. Read it like this: if there is a system property defined and it has value unittest, then it means that the test is enabled.

Here’s the @IntegrationTest annotation:

@Retention(RetentionPolicy.RUNTIME)
@IfProfileValue(name="testprofile", value="integrationtest")
@SpringBootTest
public @interface IntegrationTest {
}

Again you see the¬†@IfProfileValue annotation. This time ¬†the value is different though:¬†integrationtest. Also notice how the¬†@SpringBoot test annotation is used here as a meta-annotation. Having it here means that we¬†don’t have to use it on the test classes also (in addition to the @IntegrationTest annotation and the @RunWith annotation).

The Gradle configuration is simple too:

...
apply plugin: 'java'
apply plugin: 'spring-boot'
...

test {
    useJUnit {
        systemProperty "testprofile", "unittest"
    }
}

task integrationTest(type: Test) {
    useJUnit {
        systemProperty "testprofile", "integrationtest"
    }
}

Notice how a system property is passed to the JVM Рeffectively activating either the @UnitTest or the @IntegrationTest annotations.

This approach is kind of like the one based on JUnit categories. But I think the test classes look a bit leaner. One minor issue, if at all, is that Spring is used for running the unit tests also. It means a minor initial overhead of a few seconds at most.

Separation based on different source directories

Find the GitHub project here.

This approach expects you to place unit tests in src/test/java and integration tests in src/integrationTest/java. No modifications to the test classes at all Рno custom annotations, no categories.

Here’s¬†how it is defined with Gradle:

...
apply plugin: 'java'
apply plugin: 'spring-boot'
...

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    // So that we can use JUnit and the test dependencies pulled in by Spring Boot
    // from 'src/test/java' as well as 'src/integrationTest/java':
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

sourceSets {
    // So that we can place source code in 'src/integrationTest/java':
    integrationTest {
        java {

            // So that we can compile against the business classes (GreeterService, ...):
            compileClasspath += main.output
            // So that we can compile against the libs used by the business classes:
            compileClasspath += main.compileClasspath

            // So that we can compile against the unit test classes (custom utilities fx):
            compileClasspath += test.output
            // So that we can compile against the libs used by the unit test classes (JUnit, Spring test support, ...):
            compileClasspath += test.compileClasspath

            // So that test- and business classes can be used at runtime:
            runtimeClasspath += test.runtimeClasspath

        }
    }
}

task integrationTest(type: Test) {

    // So that Gradle knows where the integration test classes are:
    testClassesDir = sourceSets.integrationTest.output.classesDir

    // So that Gradle knows which runtime class path to use:
    classpath = sourceSets.integrationTest.runtimeClasspath

}

Notice the comments – they highlight the relevant parts for getting it right.

This approach introduces another source directory layout and hence forces you to physically separate integration test classes from unit test classes.¬†From a conceptual level I think this is the nicest model. But to be completely honest: getting the Gradle script “right” wasn’t super easy. And I bet¬†you can find variants of this out there that does something slightly different. Or at least looks different.

In retrospective

There are at least these 4 ways that you can choose between. Each of them works fine Рso choose the one that is most meaningful to you and your team.

References

[1] JUnit 4 Categories:
https://github.com/junit-team/junit4/wiki/categories

[2] @IfProfileValue JavaDoc:
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/annotation/IfProfileValue.html

Tagged with: , , , , , ,
Posted in Spring