Search

Dark theme | Light theme

November 27, 2022

Gradle Goodness: Add Extra Dependencies For Running Tests Using JVM Test Suite Plugin

The JVM Test Suite plugin adds an extension to our build that allows us to configure test tasks. We always can access the default test task and for example specify the test framework we want to use. Gradle will then automatically add the dependencies of that test framework to the testImplementation configuration. If we want to add more dependencies to the testImplementation configuration we don’t have to do that by explicitly mentioning the testImplementation configuration. Instead we can also use a dependencies block from within the JvmTestSuite extension. Any extra dependencies we need to run our tests can be added using the configuration names without a test prefix. Gradle will automatically add them to the correct test configuration for us so the dependencies are available when we compile and run our tests. This will also work for any other new test type we add to the test suites, e.g. for integration tests.

In the following example Gradle build file we configure two extra dependencies for our test related tasks. We want to use AssertJ and Datafaker in our tests so we add them as dependencies:

plugins {
    java
}

repositories {
    mavenCentral()
}

// Using JVM Test Suite feature to configure our test task.
testing {
    suites {
        val test by getting(JvmTestSuite::class) {
            // We set the Junit version explicit using the version catalog.
            useJUnitJupiter(libs.versions.junit)
            dependencies {
                // We add extra dependencies needed for our tests.
                // We don't use the test prefix here as we are already
                // in a "test" context.
                // Other configurations like annotationProcessor, compileOnly
                // can be used here as well.
                // Here we refer to dependencies using the version catalog,
                // but we could use string values for example.
                implementation(libs.assertj.core)
                implementation(libs.datafaker)
            }
        }
    }
}

The version catalog looks as follows:

# File: gradle/libs.versions.toml
[versions]
junit = "5.9.1"

[libraries]
assertj-core = "org.assertj:assertj-core:3.23.1"
datafaker = "net.datafaker:datafaker:1.6.0"

We can use the dependencies task to see what our test runtime classpath looks like. And we see all our newly defined dependencies as well:

$ gw dependencies --configuration testRuntimeClasspath

> Task :dependencies

------------------------------------------------------------
Root project 'testsuite'
------------------------------------------------------------

testRuntimeClasspath - Runtime classpath of source set 'test'.
+--- org.junit.jupiter:junit-jupiter:5.9.1
|    +--- org.junit:junit-bom:5.9.1
|    |    +--- org.junit.jupiter:junit-jupiter:5.9.1 (c)
|    |    +--- org.junit.jupiter:junit-jupiter-api:5.9.1 (c)
|    |    +--- org.junit.jupiter:junit-jupiter-engine:5.9.1 (c)
|    |    +--- org.junit.jupiter:junit-jupiter-params:5.9.1 (c)
|    |    +--- org.junit.platform:junit-platform-commons:1.9.1 (c)
|    |    \--- org.junit.platform:junit-platform-engine:1.9.1 (c)
|    +--- org.junit.jupiter:junit-jupiter-api:5.9.1
|    |    +--- org.junit:junit-bom:5.9.1 (*)
|    |    +--- org.opentest4j:opentest4j:1.2.0
|    |    \--- org.junit.platform:junit-platform-commons:1.9.1
|    |         \--- org.junit:junit-bom:5.9.1 (*)
|    +--- org.junit.jupiter:junit-jupiter-params:5.9.1
|    |    +--- org.junit:junit-bom:5.9.1 (*)
|    |    \--- org.junit.jupiter:junit-jupiter-api:5.9.1 (*)
|    \--- org.junit.jupiter:junit-jupiter-engine:5.9.1
|         +--- org.junit:junit-bom:5.9.1 (*)
|         +--- org.junit.platform:junit-platform-engine:1.9.1
|         |    +--- org.junit:junit-bom:5.9.1 (*)
|         |    +--- org.opentest4j:opentest4j:1.2.0
|         |    \--- org.junit.platform:junit-platform-commons:1.9.1 (*)
|         \--- org.junit.jupiter:junit-jupiter-api:5.9.1 (*)
+--- org.assertj:assertj-core:3.23.1
|    \--- net.bytebuddy:byte-buddy:1.12.10
\--- net.datafaker:datafaker:1.6.0
     \--- com.github.mifmif:generex:1.0.2
          \--- dk.brics.automaton:automaton:1.11-8

(c) - dependency constraint
(*) - dependencies omitted (listed previously)

A web-based, searchable dependency report is available by adding the --scan option.

BUILD SUCCESSFUL in 709ms
1 actionable task: 1 executed
$

Written with Gradle 7.6.

November 24, 2022

DataWeave Delight: Turn String Into Snake Case With underscore

In a previous post we learned we can turn a string into a string with kebab casing using dasherize from the dw::core::Strings module. If we want to turn a string into a string with camel casing we can use the underscore function. The underscore function will replace spaces, dashes and camel-casing with underscores, which makes the result snake-casing. Any uppercase characters are transformed to lowercase characters.

In the following example we use the underscore function with different input string arguments:

Soure

%dw 2.0
import underscore from dw::core::Strings
    
output application/json
---
{
    // Replaces camel casing with underscores.
    camelCase: underscore("stringInCamelCase"), // string_in_camel_case
    
    // Replaces dashes with underscores.
    kebab_case: underscore("string-with-dashes"), // string_with_dashes
    
    // Replaces spaces with underscores.
    spaces: underscore("string with spaces"), // string_with_spaces
    
    // Uppercase is transformed to lowercase.
    upper: underscore("STRING_WITH_UPPERCASE") // string_with_uppercase
}

Output

{
  "camelCase": "string_in_camel_case",
  "kebab_case": "string_with_dashes",
  "spaces": "string_with_spaces",
  "upper": "string_with_uppercase"
}

Written with DataWeave 2.4.

November 23, 2022

Change Log Level For Events In Keycloak

When running Keycloak it can be useful to log events like a login or logout event using the jboss-logging event listener. This is a built-in event listener that will use JBoss logging to log events. For example we can configure our JBoss logging to log to the standard output streams and when we run Keycloak we can see in the console output details about events when they happen. The event listener jboss-logging is enabled by default for a realm. We can check it from the Events Config page where it is listed in the Event Listeners field.

By default the log level of error events is set to warn and for successful events to log level debug. If we want to change the log level we can use two configuration properties to achieve this:

  • spi-events-listener-jboss-logging-success-level to set the log level for events that are a success. The default value is debug, but we can change it to for example info. Valid values for the log levels are fatal, error, warn, info, debug and trace.
  • spi-events-listener-jboss-logging-error-level to set the log level for events that went wrong. The default value is warn. We can any of the valid log level values here as well.

To pass the configuration property with a value to Keycloak we can use the command-line version where we for prefix the property with two dashes (--). For example:

$ kc.sh start \
 --spi-events-listener-jboss-logging-success-level=info \
 --spi-events-listener-jboss-logging-error-level=error

Or we can use the environment variables KC_SPI_EVENTS_LISTENER_JBOSS_LOGGING_SUCCESS_LEVEL and KC_SPI_EVENTS_LISTENER_JBOSS_LOGGING_ERROR_LEVEL and given them the correct log level values. In the following example we use the environment variables:

$ KC_SPI_EVENTS_LISTENER_JBOSS_LOGGING_SUCCESS_LEVEL=info \
 KC_SPI_EVENTS_LISTENER_JBOSS_LOGGING_ERROR_LEVEL=error \
 kc.sh start

Written with Keycloak 18.0.2.

November 22, 2022

Gradle Goodness: Using Spock With JVM Test Suite Plugin

Spock is an awesome test framework for testing our Java or Groovy code. Spock itself is written with Groovy and provides a nice syntax to define our tests, or specifications in Spock terminology. To configure support for using Spock in our Gradle build is very easy with the JVM Test Suite plugin (included with the Java plugin). The plugin gives us a nice syntax to define different types of tests, for example integration tests, with their own source set, dependencies and configuration. To use Spock as testing framework we only have to use the method useSpock within a test configuration. The default version of Spock that is used is 2.1-groovy-3.0 when we use Gradle 7.6. If we want to use another version we can use a String parameter when we use the useSpock method with the version we want to use.

In the following example we use the default Spock version defined by Gradle 7.6:

// File: build.gradle.kts 
plugins {
    // We need the Groovy plugin to run our
    // Spock specifications. 
    // As it includes the Java plugin it also
    // includes the JVM Test Suite plugin.
    groovy 
}

repositories {
    // We need a repository with the Spock dependencies.
    mavenCentral()
}

testing {
    suites {
        val test by getting(JvmTestSuite::class) {
            // Define we want to use Spock.
            useSpock()
        }
    }
}    

When we check the dependencies in the testCompileClasspath configuration we see the following output:

$ gradle dependencies --configuration testCompileClasspath
...
> Task :dependencies

------------------------------------------------------------
Root project 'spock-testsuite' - Sample project for using Spock
------------------------------------------------------------

testCompileClasspath - Compile classpath for source set 'test'.
\--- org.spockframework:spock-core:2.1-groovy-3.0
        +--- org.codehaus.groovy:groovy:3.0.9
        +--- org.junit:junit-bom:5.8.1
        |    +--- org.junit.platform:junit-platform-engine:1.8.1 (c)
        |    \--- org.junit.platform:junit-platform-commons:1.8.1 (c)
        +--- org.junit.platform:junit-platform-engine -> 1.8.1
        |    +--- org.junit:junit-bom:5.8.1 (*)
        |    +--- org.opentest4j:opentest4j:1.2.0
        |    +--- org.junit.platform:junit-platform-commons:1.8.1
        |    |    +--- org.junit:junit-bom:5.8.1 (*)
        |    |    \--- org.apiguardian:apiguardian-api:1.1.2
        |    \--- org.apiguardian:apiguardian-api:1.1.2
        \--- org.hamcrest:hamcrest:2.2

(c) - dependency constraint
(*) - dependencies omitted (listed previously)
...
$

We can also specify another version we want to use. In the following example we refer to a version in the version catalog when we use the useSpock method:

# File: gradle/libs.versions.toml
[versions]
# We define the Spock version in the version catalog.
spock = "2.3-groovy-4.0"
// File: build.gradle.kts 
plugins {
    // We need the Groovy plugin to run our
    // Spock specifications. 
    // As it includes the Java plugin it also
    // includes the JVM Test Suite plugin.
    groovy 
}

repositories {
    // We need a repository with the Spock dependencies.
    mavenCentral()
}

testing {
    suites {
        val test by getting(JvmTestSuite::class) {
            // Define we want to use Spock
            // and specify a non-default version 
            // using version catalog.
            useSpock(libs.versions.spock)
        }
    }
}    

Let's check the testCompileClasspath configuration again and we see this time our Spock version is 2.3-groovy-4:

$ gradle dependencies --configuration testCompileClasspath
...
> Task :dependencies

------------------------------------------------------------
Root project 'spock-testsuite' - Sample project for using Spock
------------------------------------------------------------

testCompileClasspath - Compile classpath for source set 'test'.
\--- org.spockframework:spock-core:2.3-groovy-4.0
        +--- org.apache.groovy:groovy:4.0.4
        |    \--- org.apache.groovy:groovy-bom:4.0.4
        |         \--- org.apache.groovy:groovy:4.0.4 (c)
        +--- org.junit:junit-bom:5.9.0
        |    +--- org.junit.platform:junit-platform-engine:1.9.0 (c)
        |    \--- org.junit.platform:junit-platform-commons:1.9.0 (c)
        +--- org.junit.platform:junit-platform-engine -> 1.9.0
        |    +--- org.junit:junit-bom:5.9.0 (*)
        |    +--- org.opentest4j:opentest4j:1.2.0
        |    +--- org.junit.platform:junit-platform-commons:1.9.0
        |    |    +--- org.junit:junit-bom:5.9.0 (*)
        |    |    \--- org.apiguardian:apiguardian-api:1.1.2
        |    \--- org.apiguardian:apiguardian-api:1.1.2
        \--- org.hamcrest:hamcrest:2.2

(c) - dependency constraint
(*) - dependencies omitted (listed previously)
...
$

Written with Gradle 7.6.

November 21, 2022

Gradle Goodness: Set Test Framework Version Using Version Catalog With JVM Test Suite Plugin

Since Gradle 7.3 we can use the JVM Test Suite plugin to define in a declarative way tests for our build. For example adding integration tests with a new source set and dependencies becomes easier with this plugin. The plugin is automatically part of the Java plugin so we don't have to define it explicitly in our build. Configuring the default test task can also be done using the syntax of the JVM TestSuite plugin. We can use several methods from the JvmTestSuite class in our configuration. For example if we want to use Spock as testing framework we can simply add the method useSpock in our build script. Or if we want to use the JUnit 5 Jupiter engine we can use useJUnitJupiter. These methods will add dependencies in the testImplementation configuration. There is a default version for the dependencies if we use the method without arguments. But we can also define the version as String argument for these methods. The version catalog for our project is the place to store version for our dependencies, so it would be nice if we could use the version defined in our version catalog as argument for the use<TestFramework> methods. We can reference the version very simple by using libs.versions.<version-key>. This will return the value we defined as version in our version catalog.

In the following example build script we want to use JUnit Jupiter version 5.9.1, instead of the default version of 5.8.2. We use the version as it is defined in our version catalog file libs.versions.toml:

// File: build.gradle.kts
plugins {
    java // The Java plugin also includes the JVM Test Suite plugin.
}

// We need to define a repository so JUnit dependencies can be downloaded.
repositories {
    mavenCentral()
}

testing {
    suites {
        // Using JVM Test Suite feature to configure our test task.
        val test by getting(JvmTestSuite::class) {
            // For JUnit 5 we need to enable JUnit Jupiter.
            // If we don't specify a version the default
            // version is used, which is 5.8.2 with Gradle 7.6.
            // We can use a version as String as argument, but it is even
            // better to refer to a version from the version catalog,
            // so all versions for our dependencies are at the
            // single location of the version catalog.
            // We define the version in libs.versions.toml.
            useJUnitJupiter(libs.versions.junit)
        }
    }
}

In the file gradle/libs.versions.toml we have the following definition:

# gradle/libs.versions.toml
[versions]
junit = "5.9.1"    

When we check our dependencies we see the correct dependencies for JUnit:

$ gradle dependencies --configuration testCompileClasspath
...
------------------------------------------------------------
Root project 'testsuite'
------------------------------------------------------------

testCompileClasspath - Compile classpath for source set 'test'.
\--- org.junit.jupiter:junit-jupiter:5.9.1
     +--- org.junit:junit-bom:5.9.1
     |    +--- org.junit.jupiter:junit-jupiter:5.9.1 (c)
     |    +--- org.junit.jupiter:junit-jupiter-api:5.9.1 (c)
     |    +--- org.junit.jupiter:junit-jupiter-params:5.9.1 (c)
     |    \--- org.junit.platform:junit-platform-commons:1.9.1 (c)
     +--- org.junit.jupiter:junit-jupiter-api:5.9.1
     |    +--- org.junit:junit-bom:5.9.1 (*)
     |    +--- org.opentest4j:opentest4j:1.2.0
     |    +--- org.junit.platform:junit-platform-commons:1.9.1
     |    |    +--- org.junit:junit-bom:5.9.1 (*)
     |    |    \--- org.apiguardian:apiguardian-api:1.1.2
     |    \--- org.apiguardian:apiguardian-api:1.1.2
     \--- org.junit.jupiter:junit-jupiter-params:5.9.1
          +--- org.junit:junit-bom:5.9.1 (*)
          +--- org.junit.jupiter:junit-jupiter-api:5.9.1 (*)
          \--- org.apiguardian:apiguardian-api:1.1.2

(c) - dependency constraint
(*) - dependencies omitted (listed previously)
...

Written with Gradle 7.6.

November 20, 2022

DataWeave Delight: Turn String Into Kebab Casing With dasherize

The dw::core::Strings module has useful functions for working with string values. One of the functions is dasherize. The function takes a string argument and replaces spaces, underscores and camel-casing into dashes. The resulting string value with hyphens is also called kebab-casing. The dasherize function also turns any uppercase character to lowercase.

In the following example we have different input arguments with camel-casing, underscores and spaces:

Source

%dw 2.0
import dasherize from dw::core::Strings

output application/json
---
{
    // Replaces camel casing with dashes.
    camelCase: dasherize("stringInCamelCase"), // string-in-camel-case

    // Replaces underscores with dashes.
    snake_case: dasherize("string_with_underscores"), // string-with-underscores

    // Replaces spaces with dashes.
    spaces: dasherize("string with spaces"), // string-with-spaces

    // Uppercase is transformed to lowercase.
    upper: dasherize("STRING_WITH_UPPERCASE") // string-with-uppercase
}

Output

{
  "camelCase": "string-in-camel-case",
  "snake_case": "string-with-underscores",
  "spaces": "string-with-spaces",
  "upper": "string-with-uppercase"
}

Written with DataWeave 2.4.

November 18, 2022

Mastering Maven: Exclude Modules From Build

When we are working with a multi-module project in Maven we might want to exclude a module when we invoke a build command. We might only be interested in partially building some modules. We can use the command line option -pl or --projects to specify a list of modules that need to be in our build. But we can also use ! followed by the module name to exclude modules from our build.

In the following example we have multi-module project with 4 modules: root with name parent-project, core, service and webapp:

$ pwd
parent-project
$ tree -L 1
.
├── core
├── pom.xml
├── service
└── webapp
$

First we run verify for the complete project and we see in the output that the reactor build order contains all modules:

$ mvn verify
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] parent-project                                                     [pom]
[INFO] core                                                               [jar]
[INFO] service                                                            [jar]
[INFO] webapp                                                             [war]
[INFO]
...
$

Suppose we want to exclude our webapp module than we use $ mvn -pl '!webapp' verify. In the output we see the reactor build order doesn't include our webapp module:

$ mvn verify -pl '!webapp'
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] parent-project                                                     [pom]
[INFO] core                                                               [jar]
[INFO] service                                                            [jar]
[INFO]
...
$

As our list of modules is not so big in this sample project we could also have used the option -pl to only specify the modules we want to build separated by a comma. We must use . to indicate our root module:

$ mvn verify -pl '.,core,service'
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] parent-project                                                     [pom]
[INFO] core                                                               [jar]
[INFO] service                                                            [jar]
[INFO]
...
$

Written with Maven 3.8.6.

November 13, 2022

Gradle Goodness: Grouping Version Catalog Dependencies Into Bundles

The version catalog in Gradle is very useful to define a list of dependencies in one single place. In our build script we references dependencies from the version catalog using type safe accessors when we define a dependency for a configuration. Sometimes multiple dependencies belong to each other and are used in combination with each other. In the version catalog we can define bundles of such dependency groups. Instead of referencing each dependency individually we can reference a bundle from the version catalog in our build script. This keeps our build script cleaner and updating a bundle only needs a change in the version catalog.

In the following example version catalog we have several Log4j2 dependencies. We create two bundles that each contain a set of the dependencies for Log4j2:

# File: gradle/libs.versions.toml
[versions]
# We define the log4j2 version for our dependencies.
log4j2 = "2.19.0"

[libraries]
# The api and core modules from log4j2 we need in our project.
# We can use version.ref to refer to version defined in the [versions] section.
log4j2-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j2" }
log4j2-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j2" }

# Bridge to have Java Util Logging (JUL) processed by log4j2.
log4j2-jul = { module = "org.apache.logging.log4j:log4j-jul", version.ref = "log4j2" }
# JSON layout following ECS (Elastic Search) standard.
log4j2-ecs-layout = "co.elastic.logging:log4j2-ecs-layout:1.5.0"
# Support async logging for log4j2.
disruptor = "com.lmax:disruptor:3.4.4"

[bundles]
# Bundle with log4j2 dependencies needed to compile.
log4j2-impl = ["log4j2-api", "log4j2-core"]

# Bundle with log4j2 dependencies only needed at runtime.
log4j2-runtime = ["log4j2-jul", "log4j2-ecs-layout", "disruptor"]    

With the version catalog in place we can now reference the bundles from our build script. In the following example build script we use both bundles for the configurations implementation and runtimeOnly:

plugins {
    java
}

repositories {
    mavenCentral()
}

dependencies {
    // Use log4j2-impl bundle from version catalog.
    implementation(libs.bundles.log4j2.impl)

    // Use log4j2-runtime bundle from version catalog.
    runtimeOnly(libs.bundles.log4j2.runtime)
}

Bundles are very useful to define dependencies that are used together. We can reference than multiple dependencies with one bundle reference in our build scripts.

Written with Gradle 7.5.1.

November 8, 2022

Gradle Goodness: Defining Plugin Versions Using Version Catalog

A version catalog in Gradle is a central place in our project where we can define dependency references with their version or version rules. A dependency reference is defined using an identifier with a corresponding dependency definition containing the coordinates of the dependency. Now we can reference the dependency using the identifier in for example a dependency configuration, e.g. implementation(libs.spring.core). If there is a version change we want to apply we only have to make the change in our version catalog. An added bonus is that Gradle generates type safe accessors for the identifier we use in our version catalog, so we can get code completion in our IntelliJ IDEA when we want to reference a dependency from the version catalog.

Besides dependencies we need to build and test our software we can also include definitions for Gradle plugins including their version. Normally we reference a Gradle plugin using the id and version. For example in the following code block we include 4 Gradle plugins of which 3 are identified by an id:

plugins {
	`java` // Default Gradle Java plugin
	
	// Include 3 third-party Gradle plugins
	id("org.springframework.boot") version "2.7.5"
	id("io.spring.dependency-management") version "1.0.15.RELEASE"
	id("org.asciidoctor.jvm.convert") version "3.2.0"
}

We can replace these plugin references with version catalog defined values. First we must create the file gradle/libs.version.toml in our project directory. We might already have such file with definitions for the dependencies we use in our build and tests. Next we must add a section [plugins] where we can define our plugin dependencies. We can use the full power of the version catalog here, the only thing we need to remember is to use the id property of we use the longer notation option. With the shorthand notation we can simply define a string value with the id of the plugin, a colon (:) and the version.

In the following example libs.versions.toml file we defined our 3 third-party plugins using several notations:

# File: gradle/libs.versions.toml
[versions]
# Define version we can use as version.ref in [plugins]
asciidoctor = "3.2.0" 

[plugins]
# We can use shorthand notation with the plugin id and version.
spring-boot = "org.springframework.boot:2.7.5"

# We can use the longer notation option where we set 
# the id and version for the plugin.
spring-dep-mgmt = { id = "io.spring.dependency-management", version = "1.0.15.RELEASE" }

# Here we use the longer notation and version.ref to reference
# the version defined in the [versions] section.
asciidoctor-jvm = { id = "org.asciidoctor.jvm.convert", version.ref = "asciidoctor" }

We only have to change our plugins block in our build file. We use the method alias to reference our version catalog definitions. In IntelliJ IDEA we even get code completion when start typing. The following code shows how we include the plugins:

plugins {
	`java` // Default Gradle Java plugin
	
	// Using alias we can reference the plugin id and version
	// defined in the version catalog.
	// Notice that hyphens (-) used as separator in the identifier
	// are translated into type safe accessors for each subgroup.
	alias(libs.plugins.spring.boot)
	alias(libs.plugins.spring.dep.mgmt)
	alias(libs.plugins.asciidoctor.jvm)
}

The version catalog is a powerful feature of Gradle. It allows to have a single place in our project where we define dependency coordinates and we get type safe accessors methods to have code completion in IntelliJ IDEA.

Written with Gradle 7.5.1.

November 5, 2022

Add Import Statement Automatically For Unambiguous Java And Kotlin Classes In IntelliJ IDEA

I mostly use IntelliJ IDEA for my Java development work. Just the other day I got a tip from my colleague Willem Cheizoo I didn't use yet. In IntelliJ we can set the configuration option Add unambiguous imports on the fly. When we enable this option IntelliJ will automatically add an import statement for a Java class when the class name is only available from one package. The option is available from Preferences | Editor | General | Auto import:

The same dialog also has this setting for Kotlin source files.

In the following screenshot we see that IntelliJ provides the context option to add an import> for the class name Asciidoctor when the option Add unambiguous imports on the fly is disabled:

When we enable the option Add unambiguous imports on the fly the import statement is automatically added to our source file:

Written IntellIJ IDEA 2022.2.3.