Search

Dark theme | Light theme

December 9, 2022

Kotlin Kandy: Strip Leading Spaces From Multiline Strings

Multiline strings are very useful. But sometimes we want use the multiline string without the leading spaces that are added because of code formatting. To remove leading spaces we can use the trimIndent method. This method will find the least amount of leading spaces and removes that amount of spaces from each line. Also a first and last empty line are removed.

If we want a bit more control we can also add a character to the start of each line to show where the line starts. And then we use the method trimMargin and all spaces before that character are removed. The default character is the pipe symbol, |, but we can also define our own and pass it as argument to the trimMargin method.

In the following example code we use the trimIndent and trimMargin methods:

// trimIndent will remove spaces from the beginning
// of the line based on the least number of spaces.
// The first and last empty line are also removed
// from the string.
fun createText(): String {
    return """
        Multiline string
          with simple 2 spaces
        indentation.
    """.trimIndent()
}

assert(createText() == """Multiline string
  with simple 2 spaces
indentation.""")

// trimMargin will trim all spaces before
// the default margin character |.
val languages = """
    |Kotlin
    |Groovy
    |Clojure
      |Java
""".trimMargin()

assert(languages == """Kotlin
Groovy
Clojure
Java""")

// We can use our own margin character by
// specifying the character as argument
// to the trimMargin method.
val buildTools = """
    >Gradle
    >Maven
      >SBT
    >Leiningen
""".trimMargin(">")

assert(buildTools == """Gradle
Maven
SBT
Leiningen""")

Written with Kotlin 1.7.20.

Kotlin Kandy: Transforming Collection Items With Index

If we want to transform items in a collection we can use the map method. If we also want to use the index of the element in the collection in the transformation we must use the mapIndexed method. We must provide a lambda function with 2 arguments, where the first argument is the index of the element in the collection and the second argument is the element in the collection.

In the following examples we use the mapIndexed and the related mapIndexedNotNull and mapIndexedTo methods:

// With mapIndexed we can use a lambda with 2 arguments:
// the first argument is the index,
// the second argument is the value.
assert(listOf(3, 20, 10, 2, 1).mapIndexed { index, n -> n * index } ==
        listOf(0, 20, 20, 6, 4))

// Instead of using the indices property we can use
// mapIndexed to get all the indices.
assert(listOf(3, 20, 10, 2, 1).mapIndexed { index, _ -> index } ==
        listOf(0, 1, 2, 3, 4))
assert(listOf(3, 20, 10, 2, 1).indices == 0..4)

// mapIndexed also works on ranges.
val lettersIndex = ('a'..'z').mapIndexed { index, letter -> letter to (index + 1) }
assert(lettersIndex.take(3) == listOf('a' to 1, 'b' to 2, 'c' to 3))

// Using toMap we get a nice lookup map to find the position
// of a letter in the alphabet.
assert("kotlin".map { c -> lettersIndex.toMap().get(c) } == listOf(11, 15, 20, 12, 9, 14))


// The mapIndexedNotNull method only returns non-null results.
val others = listOf("Kotlin", "Groovy", "Java", "Clojure")
    .mapIndexedNotNull { index, language -> if (index == 2) null else language }
assert(others == listOf("Kotlin", "Groovy", "Clojure"))


// With mapIndexTo we can add the output of the
// transform lambda function to an existing
// mutable collection.
val storage = mutableListOf(90, 10, 3)
assert(listOf(7, 42, 100)
        .mapIndexedTo(storage) { index, n -> index * n } ==
            listOf(90, 10, 3, 0, 42, 200))

Written with Kotlin 1.7.20.

Kotlin Kandy: Getting The Indices Of A Collection

Kotlin adds a lot of useful extensions to the collection classes. One of them is the indices property. The indices property returns the indices of the elements in the collection as an IntRange.

// With the indices property we get back the
// index values for each element, starting with 0
// as an IntRange.
val list = listOf(3, 20, 10, 2, 1)
assert(list.indices == 0..4)


// Helper function to return the position in the alphabet
// of a given letter.
fun findLetterIndex(c: Char): Int {
    // Range of all letters.
    val alphabet = 'a'..'z'

    // Combine letters in alphabet with their position (zero-based).
    // Result: [(a, 0), (b, 1), (c, 2), ...]
    val alphabetIndices = alphabet.zip(alphabet.toList().indices)

    // Find position, if not found return -1.
    val position = alphabetIndices.find { p -> p.first == c}?.second ?: -1

    // Fix the zero based index values.
    return position + 1
}

val positionInAlphabet = "kotlin".map(::findLetterIndex)
assert(positionInAlphabet == listOf(11, 15, 20, 12, 9, 14))

Written with Kotlin 1.7.20.

December 5, 2022

Gradle Goodness: Configure Test Task With JVM Test Suite

The JVM Test Suite plugin is part of the Java plugin and provides a nice way to configure multiple test types in our build file. Even if we don't have multiple test types we have a default test type, which is used when we run the Gradle test task. Using the test suite DSL we can configure the task of type Test that belongs to a test suite type. The current release of the JVM Test Suite plugin provides a single target for a test suite type with a single Test task. This will probably change in future releases of the plugin so more task of type Test can be created and configured.

We can reference the Test task using the syntax within a JvmTestSuite configuration block:

...
targets {
    all {
        testTask
    }
}
...

Once we have the reference to the Test task we can configure it using all the methods and properties available for this class.

In the following example build script we configure the logging and set a system property for our default Test task:

plugins {
    java
}
    
repositories {
    mavenCentral()
}

testing {
    suites {
        val test by getting(JvmTestSuite::class) {
            useJUnitJupiter()  // We want to use Jupiter engine
            
            targets {
                all {
                    // Here can access the test task for this 
                    // test suite type (we use the default in this example).
                    // The task can be referenced as testTask.
                    // The task is of type Test and we can use all methods
                    // and properties of the Test class.
                    testTask.configure {
                        // We define a system property with key greeting
                        // and value Hello, which can be used in our test code.
                        systemProperties(mapOf("greeting" to "Hello"))
                        
                        // We configure the logging for our tests.
                        testLogging {
                            exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
                            showStandardStreams = true
                        }
                    }
                }
            }
        }
    }
}

Written with Gradle 7.6.

December 2, 2022

Gradle Goodness: Set Project Version In Version Catalog

The version catalog in Gradle is very useful to have one place in our project to define our project and plugin dependencies with their versions. But we can also use it to define our project version and then refer to that version from the version catalog in our build script file. That way the version catalog is our one place to look for everything related to a version. In the version catalog we have a versions section and there we can define a key with a version value. The name of the key could be our project or application name for example. We can use type safe accessors generated by Gradle in our build script to refer to that version.

In the following example build script written with Kotlin we see how we can refer to the version from the version catalog:

// File: build.gradle.kts
description = "Sample project for Gradle version catalog"
    
// Set version using version catalog.
version = libs.versions.app.version.get()
    
// We can use the TaskContainer to keep all
// task related things in one place.
tasks {
    // Register a new task to print out the project version.
    register("projectVersion") {
        doLast {
            println("Project version: " + version)
        }
    }
}

And the version catalog is defined in the following file:

# File: gradle/libs.versions.toml
[versions]
app-version = "2.0.1"

When we run the task projectVersion we see our project version in the output:

$ gradle projectVersion

> Task :projectVersion
Project version: 2.0.1

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

Written with Gradle 7.6.

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.