Search

Dark theme | Light theme

November 16, 2023

Spring Sweets: Spring Boot 3 With Gradle In IntelliJ

Spring Boot 3 requires at least Java 17, but that also means the Java version used by Gradle must also be at least 17. Otherwise we will get the following error message when we build our Spring Boot project in IntelliJ using Gradle:

A problem occurred configuring root project 'springboot'.
> Could not resolve all files for configuration ':classpath'.
   > Could not resolve org.springframework.boot:spring-boot-gradle-plugin:3.1.5.
     Required by:
         project : > org.springframework.boot:org.springframework.boot.gradle.plugin:3.1.5
      > No matching variant of org.springframework.boot:spring-boot-gradle-plugin:3.1.5 was found. The consumer was configured to find a library for use during runtime, compatible with Java 11, packaged as a jar, and its dependencies declared externally, as well as attribute 'org.gradle.plugin.api-version' with value '8.4' but:
          - Variant 'apiElements' capability org.springframework.boot:spring-boot-gradle-plugin:3.1.5 declares a library, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares a component for use during compile-time, compatible with Java 17 and the consumer needed a component for use during runtime, compatible with Java 11
              - Other compatible attribute:
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')
          - Variant 'javadocElements' capability org.springframework.boot:spring-boot-gradle-plugin:3.1.5 declares a component for use during runtime, and its dependencies declared externally:
              - Incompatible because this component declares documentation and the consumer needed a library
              - Other compatible attributes:
                  - Doesn't say anything about its target Java version (required compatibility with Java 11)
                  - Doesn't say anything about its elements (required them packaged as a jar)
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')
          - Variant 'mavenOptionalApiElements' capability org.springframework.boot:spring-boot-gradle-plugin-maven-optional:3.1.5 declares a library, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares a component for use during compile-time, compatible with Java 17 and the consumer needed a component for use during runtime, compatible with Java 11
              - Other compatible attribute:
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')
          - Variant 'mavenOptionalRuntimeElements' capability org.springframework.boot:spring-boot-gradle-plugin-maven-optional:3.1.5 declares a library for use during runtime, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares a component, compatible with Java 17 and the consumer needed a component, compatible with Java 11
              - Other compatible attribute:
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')
          - Variant 'runtimeElements' capability org.springframework.boot:spring-boot-gradle-plugin:3.1.5 declares a library for use during runtime, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares a component, compatible with Java 17 and the consumer needed a component, compatible with Java 11
              - Other compatible attribute:
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')
          - Variant 'sourcesElements' capability org.springframework.boot:spring-boot-gradle-plugin:3.1.5 declares a component for use during runtime, and its dependencies declared externally:
              - Incompatible because this component declares documentation and the consumer needed a library
              - Other compatible attributes:
                  - Doesn't say anything about its target Java version (required compatibility with Java 11)
                  - Doesn't say anything about its elements (required them packaged as a jar)
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

The issue is that the Spring Boot Gradle plugin 3.1.5 requires Java 17, but our project is using Java 11. We can fix this by explicitly setting the Java version that Gradle uses in IntelliJ. Go to Settings > Build, Execution, Deployment > Build Tools > Gradle and change the JVM used for Gradle to a JDK version of at least version 17.

Written with Spring Boot 3.1.5 and IntelliJ 2023.2.4.

November 5, 2023

IntelliJ HTTP Client: Re-using Javascript In Pre-Request And Response Handlers

When we use the IntelliJ HTTP Client we can write Javascript for the pre-request and response handlers. The Javascript code must be in between {% …​ %} delimeters. If we want to re-use Javascript functions in the pre-request or response handlers we can store them in an external Javascript file. Then we use the import statement to import either the whole file or specify explicitly the code we want to import. This way we can reuse code for different pre-request and response handlers.

In the following example we import the createUsername function from the external scripts/username.js file and use it in the pre-request handler. In the response handler we import the external file scripts/validate-200-response.js and the code from the file is executed when the response is available.

// File: scripts/username.js
function createUsername() {
    const usernames = ["mrhaki", "hubert"];
    const randomIndex = (Math.floor(Math.random() * 11) % 2);

    // Return mrhaki or hubert as username.
    return usernames[randomIndex];
}

export {createUsername};
// File: scripts/validate-200-response.js
client.test("Response is OK", function () {
    client.assert(response.status === 200);
});
### POST JSON payload to /anything
< {%
    // Refer to createUsername function from external Javascript file.
    import {createUsername} from './scripts/username';

    // Use function createUsername.
    request.variables.set("username", createUsername());
%}

POST https://ijhttp-examples.jetbrains.com/anything

{
  "username": "{{username}}"
}

> {%
    // Validate 200 response from external file. 
    import './scripts/validate-200-response';

    client.test("Response has username set", function () {
        client.log("Username -> " + response.body.json.username);
        client.assert(["mrhaki", "hubert"].includes(response.body.json.username));
    });
 %}

Written with IntelliJ 2023.2.4.

IntelliJ HTTP Client: Using External Files As JSON Payload

The built-in IntelliJ HTTP Client is very useful for testing HTTP requests and responses. We can use it to test for example a REST API that works with JSON data. If an endpoint expects a JSON payload we can specify the payload in our HTTP Client request file. But if we have a lot of endpoints and large payload the request file can get big and messy. Instead of having the payload in the request file directly we can specify an external JSON file with the payload and use it for a request body. We must use the < operator and give the name of the file with our JSON payload. The IntelliJ HTTP Client will read the contents of that file and use it as the request body. The payload may also contain (dynamic) variables and those variables will be replaced with correct values when the request is executed.

In the following example we have a POST request with a JSON payload from an external file. The payload in the file contains variables. We use an endpoint from the test API at https://ijhttp-examples.jetbrains.com that expects a JSON payload. The response will contain the payload with request payload where all variables are replaced in the json property.

### POST to /anything with payload from external file
POST https://ijhttp-examples.jetbrains.com/anything
Content-Type: application/json

< ./sample-payload.json

# Content of sample-playload.json:
#{
#  "id": "{{$random.uuid}}",
#  "username": "{{username}}",
#}

# Content of http-client.env.json with value for variable username:
#{
#  "test": {
#    "username": "mrhaki"
#  }
#}

> {%
    client.test("Response is OK", function () {
        client.assert(response.status === 200);
    });
    client.test("Response has username set", function () {
        client.assert(response.body.json.username === "mrhaki");
    });
    client.test("Response has id with value", function () {
        const regexExp = /^[0-9a-f]{8}\b-[0-9a-f]{4}\b-[0-9a-f]{4}\b-[0-9a-f]{4}\b-[0-9a-f]{12}$/gi;
        client.assert(regexExp.test(response.body.json.id));
    });
%}

Written with IntelliJ IDEA 2023.2.4.

October 23, 2023

jq Joy: Using String Interpolation

jq is a powerful tool to work with JSON from the command-line. The tool has a lot of functions that makes our live easier. With jq we can use expressions in strings that will be evaluated and inserted into the string value. This is called string interpolation. The expression is enclosed by parentheses and the first parenthesis is prefixed with a backslash: \(<expression>). The expression can be any valid jq expression and the result of the expression will be inserted into the string.

In the following example we use string interpolation to print a greeting message and we use a simple property reference, but also a bit more complicated expressions:

$ jq --raw-output --null-input '{"prefix": "Hi", "name": "mrhaki", "visitors": 41} | "\(.prefix),\nwelcome \(.name | ascii_upcase)\nYou are visitor \(.visitors + 1)!"'
Hi,
welcome MRHAKI
You are visitor 42!

Written with jq 1.7.

October 9, 2023

Groovy Goodness: Using NullCheck Annotation To Prevent NullPointerException

In Groovy we can apply the @NullCheck annotation to a class, constructor or method. The annotation is an AST (Abstract Syntax Tree) transformation and will insert code that checks for null values in methods or constructors. If a null value is passed to an annotated method or constructor, it will throw an IllegalArgumentException. Without the annotation we could have a NullPointerException if we try to invoke a method on the value we pass as argument. The annotation has an optional property includeGenerated which by default is false. If we set it to true then the null checks are also applied to generated methods and constructors. This is very useful if we apply other AST transformations to our class that generates additional code.

In the following example we use the @NullCheck annotation for a method and at class level:

import groovy.transform.NullCheck

@NullCheck
String upper(String value) {
    "Upper:" + value.toUpperCase()
}

assert upper("groovy") == "Upper:GROOVY"

try {
    upper(null)
} catch (IllegalArgumentException e) {
    assert e.message == "value cannot be null"
}
import groovy.transform.NullCheck

// Apply null check for all constructors and methods.
@NullCheck
class Language {
    private String name

    Language(String name) {
       this.name = name;
    }

    String upper(String prefix) {
        return prefix + name.toUpperCase();
    }
}


def groovy = new Language("groovy")

assert groovy.upper("Upper:") == "Upper:GROOVY"

// Method arguments are checked.
try {
    groovy.upper(null)
} catch (IllegalArgumentException e) {
    assert e.message == "prefix cannot be null"
}

// Constructor argument is also checked.
try {
    def lang = new Language(null)
} catch (IllegalArgumentException e) {
    assert e.message == "name cannot be null"
}

In the following example we set the includeGenerated property to true to also generate null checks for generated code like the constructor generated by @TupleConstructor:

import groovy.transform.NullCheck
import groovy.transform.TupleConstructor

@NullCheck(includeGenerated = true)
@TupleConstructor
class Language {
    final String name

    String upper(String prefix) {
        return prefix + name.toUpperCase();
    }
}

// Constructor is generated by @TupleConstructor and
// @NullCheck is applied to the generated constructor.
try {
    def lang = new Language(null)
} catch (IllegalArgumentException e) {
    assert e.message == "name cannot be null"
}

Written with Groovy 4.0.13

jq Joy: Using Default Values With The Alternative Operator

jq is a powerful tool to work with JSON from the command-line. The tool has a lot of functions and operators that makes our live easier. One of the operators we can use is the alternative operator // which allows us to specify default values. If the value on the left side of the operator // is empty, null or false then the value on the right side is returned, otherwise the value itself is returned. The operator can be used multiple times if we want to have multiple fallbacks for a value we are checking.

In the following examples we use the // operator in different scenarios:

$ jq --null-input  '[42, "jq joy", null, false] | [.[] | . // "default"]'
[
  42,
  "jq joy",
  "default",
  "default"
]
$ jq --null-input 'empty // "value"'
"value"

We can chain the // operator to specify multiple fallback options:

$ jq --null-input '{"name": "mrhaki"} | {"alias": (.username // .user // "not available") }'
{
  "alias": "not available"
}
$ jq --null-input '{"user": "mrhaki"} | {"alias": (.username // .user // "not available") }'
{
  "alias": "mrhaki"
}

Written with jq 1.7.

October 5, 2023

jq Joy: Reverse An Array

jq is a powerful tool to work with JSON from the command-line. The tool has a lot of functions that makes our live easier. To simply reverse the order of elements in an array we can use the reverse function. The values in the input array are reversed and returned in the output array.

In the following example we use the reverse function for an array:

$ jq --null-input --compact-output '[1, 2, 3, 4, 5] | reverse'
[5,4,3,2,1]

Written with jq 1.7.

October 4, 2023

jq Joy: Checking String Ends Or Starts With Given String

jq is a powerful tool to work with JSON from the command-line. The tool has a lot of functions that makes our live easier. We can check if a string starts or ends with a given string using the startswith and endswith functions. We pass the string value we want to see a string value starts with or ends with as the argument to the functions. The function returns true if the string starts or ends with the given string and false otherwise.

In the following examples we use both functions to check some string values in an array:

$ jq --null-input '["mrhaki", "hubert", "MrHaki"] | [.[] | startswith("mr") or startswith("Mr")]'
[
    true,
    false,
    true
]
$ jq --null-input '["jq!", "JSON"] | [.[] | endswith("!")]'
[
  true,
  false
]

Written with jq 1.7.

October 3, 2023

jq Joy: Sum Of Elements In An Array Or Object

jq is a powerful tool to work with JSON from the command-line. The tool has a lot of functions that makes our live easier. One of the functions is add which adds all elements in an array or values in an object. The function has no arguments. The elements in an array are added together if they are numbers and concatenated if they are strings. If the input is an object then the values are added together. When the input is an empty array or object then null is returned.

In the following examples we see different usages of the add function:

$ jq --null-input '[1, 2, 3, 4,] | add'
10
$ jq --null-input '["a", "b", "c", "d"] | add'
"abcd"
$ jq --null-input '{ "burger": 3.23, "drinks": 2.89 } | add'
6.12
$ jq --null-input '[] | add'
null

Written with jq 1.7.

jq Joy: Flatten Arrays

jq is a powerful tool to work with JSON from the command-line. The tool has a lot of functions that makes our live easier. We can use the flatten function to flatten nested arrays into a single array. If we use the function without an argument the arrays are flattened recursively, resulting in a flat array with all elements. But we can also set the depth we want to flatten the array by passing an integer argument to the flatten function.

In the following example we use the flatten function without arguments to recursively flatten the array:

$ jq --null-input '[1, [2, 3], [[4]], 5] | flatten'
[
  1,
  2,
  3,
  4,
  5
]

We can pass an argument to the flatten function to indicate the depth to flatten the collection. In the following example we want to flatten only one level deep:

$ jq --null-input '[1, [2, 3], [[4]], 5] | flatten(1)'
[
  1,
  2,
  3,
  [
    4
  ],
  5
]

Written with jq 1.7.

October 2, 2023

Awesome AssertJ: Using A Custom Representation For Objects

The assertion error messages from AssertJ will use the toString() method of an object to give more insight about why the assertion could have failed. If an object doesn’t override the toString() method the default implementation is Object#toString(). The default implementation will print out the class name and an hexadecimal value of hashCode separated by a @. This doesn’t give much information about the actual object. For classes we have control over we can always implement a toString() method, but for third party classes we may not be able to do that. In order to customize how an object is represented in assertion error messages, AssertJ allows us to provide a custom representation class for an object. The custom representation class must implement the org.assertj.core.presentation.Representation interface. The interface has one method String toStringOf(Object) that should return a String representation of the object. If we want to keep the default behavior for other classes and only override it for our own class, we can extend the StandardRepresentation class and override the method String fallbackToStringOf(Object).

In the following example we provide a custom representation for the User class that will print the username property in the error messages instead of the default toString() implementation:

package mrhaki;

import org.assertj.core.api.Assertions;
import org.assertj.core.presentation.StandardRepresentation;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class CustomRepresentation {

    // Helper class used in tests.
    private static class User {
        private final String username;

        private User(String username) {
            this.username = username;
        }
    }

    // Custom representation for the User class.
    // Here we can write how we want to represent the User class in
    // assertion error messages.
    private static class UserRepresentation extends StandardRepresentation {
        /**
         * The User class doesn't have a toString implementation, so we write
         * custom code so in assertion error message our object is nicely formatted.
         *
         * @param object the object to represent (never {@code null}
         * @return Custom string representation of User or standard representation for other objects.
         */
        @Override
        protected String fallbackToStringOf(Object object) {
            if (object instanceof User user) {
                return "User(username=%s)".formatted(user.username);
            }
            return super.fallbackToStringOf(object);
        }
    }

    @Test
    void assertUserObject() {
        final User mrhaki = new User("mrhaki");
        try {
            // assert will fail and the assertion error will contain
            // a default representation of the objects.
            assertThat(mrhaki).isEqualTo(new User("mrhaki42"));
        } catch (AssertionError e) {
            // expected: mrhaki.CustomRepresentation$User@126253fd
            // but was: mrhaki.CustomRepresentation$User@7eecb5b8
            assertThat(e).hasMessageContaining("expected: mrhaki.CustomRepresentation$User@")
                         .hasMessageContaining("but was: mrhaki.CustomRepresentation$User@");
        }
    }

    @Test
    void asserUserObjectWithCustomRepresentation() {
        final User mrhaki = new User("mrhaki");
        try {
            // assert will fail and the assertion error will contain
            // a custom representation of the objects only for this assert.
            assertThat(mrhaki).withRepresentation(new UserRepresentation()).isEqualTo(new User("mrhaki42"));
        } catch (AssertionError e) {
            // expected: User(username=mrhaki42)
            // but was: User(username=mrhaki)
            assertThat(e).hasMessageContaining("expected: User(username=mrhaki42)")
                         .hasMessageContaining("but was: User(username=mrhaki)");
        }
    }

    @Test
    void asserUserObjectWithCustomRepresentation2() {
        final User mrhaki = new User("mrhaki");
        try {
            // Set custom representation for User objects for all asserts.
            // This can also be defined at class level.
            Assertions.useRepresentation(new UserRepresentation());

            // assert will fail and the assertion error will contain
            // a custom representation of the objects.
            assertThat(mrhaki).isEqualTo(new User("mrhaki42"));
        } catch (AssertionError e) {
            // expected: User(username=mrhaki42)
            // but was: User(username=mrhaki)
            assertThat(e).hasMessageContaining("expected: User(username=mrhaki42)")
                         .hasMessageContaining("but was: User(username=mrhaki)");
        } finally {
            // Reset custom representation.
            Assertions.useDefaultRepresentation();
        }
    }

    @Test
    void asserUserObjectWithCustomRepresentationSAM() {
        final User mrhaki = new User("mrhaki");
        try {
            // assert will fail and the assertion error will contain
            // a custom representation of the objects for this assert only.
            assertThat(mrhaki).withRepresentation(new UserRepresentation()).isEqualTo(new User("mrhaki42"));
        } catch (AssertionError e) {
            // expected: User(username=mrhaki42)
            // but was: User(username=mrhaki)
            assertThat(e).hasMessageContaining("expected: User(username=mrhaki42)")
                         .hasMessageContaining("but was: User(username=mrhaki)");
        }
    }

    @Test
    void asserUserObjectWithCustomRepresentationSAM() {
        final User mrhaki = new User("mrhaki");
        try {
            // assert will fail and the assertion error will contain
            // a custom representation of the objects implemented
            // with the Representation interface as single abstract method (SAM).
            assertThat(mrhaki).withRepresentation(obj -> {
                if (obj instanceof User user) {
                    return "[User:username=%s]".formatted(user.username);
                } else {
                    return Objects.toIdentityString(obj);
                }
            }).isEqualTo(new User("mrhaki42"));
        } catch (AssertionError e) {
            // expected: [User:username=mrhaki42]
            // but was: [User:username=mrhaki]
            assertThat(e).hasMessageContaining("expected: [User:username=mrhaki42]")
                         .hasMessageContaining("but was: [User:username=mrhaki]");
        }
    }
}

Written with AssertJ 3.24.2

September 26, 2023

jq Joy: Deleting Keys From An Object

jq is a powerful tool to work with JSON from the command-line. The tool has a lot of functions that makes our live easier. If we want to delete keys from an object we can use the del function and pass the key name as argument. The argument is actually a path expression so we can refer to a key name with .key, but also use jq expressions.

In the following examples we use the del function in several use cases:

$ jq --null-input '{
    "username": "mrhaki",
    "firstName": "Hubert",
    "lastName": "Klein Ikkink"
} | del(.username)'
{
  "firstName": "Hubert",
  "lastName": "Klein Ikkink"
}
$ jq --null-input '{
    "username": "mrhaki",
    "firstName": "Hubert",
    "lastName": "Klein Ikkink"
} | del(.username) | del(.lastName)'
{
  "firstName": "Hubert"
}

If we want to remove multiple keys at once we can pass multiple key names separated by comma to the del function:

$ jq --null-input '{
    "username": "mrhaki",
    "firstName": "Hubert",
    "lastName": "Klein Ikkink"
} | del(.username, .lastName)'
{
  "firstName": "Hubert"
}

We can use also jq path expressions for example to delete keys by index instead of key name:

$ jq --null-input '{
    "username": "mrhaki",
    "firstName": "Hubert",
    "lastName": "Klein Ikkink"
    } | del(.[keys_unsorted | .[0,2]])'
{
  "firstName": "Hubert"
}

Written with jq 1.7.

jq Joy: Getting Keys From Object And Indices From Array

jq is a powerful tool to work with JSON from the command-line. The tool has a lot of functions that makes our live easier. For example we can use the keys and keys_unsorted functions to get the keys from an object. The function keys will return the keys in sorted order while keys_unsorted will return them in the original order from the object. With the same functions we can also get the indices of the elements in an array, but there is no sorting involved, so both functions return the same output.

In the following examples we first use keys and then keys_unsorted to get the keys from an object:

$ jq --null-input '{
    "username": "mrhaki",
    "firstName": "Hubert",
    "lastName": "Klein Ikkink"
} | keys'
[
  "firstName",
  "lastName",
  "username"
]
$ jq --null-input '{
    "username": "mrhaki",
    "firstName": "Hubert",
    "lastName": "Klein Ikkink"
} | keys_unsorted'
[
  "username",
  "firstName",
  "lastName"
]

To get the indices of an array we can use the keys function as well:

$ jq --null-input '[1, 10, 100] | keys'
[
  0,
  1,
  2
]

Written with jq 1.7.

July 22, 2023

Java Joy: Using mapMulti Method Of The Stream API

Since Java 16 we can use the method mapMulti(BiConsumer) of the Stream API. This method allows us to map each element of the stream to multiple elements. We can also do that with the flatMap(Function) method, but if we want to map a limited set of elements, mapMulti is more convenient. Internally a shared stream is used and we don’t have the cost of creating a new stream for each element. Another use case is if the logic to map an element to multiple elements is complex and is hard to implement by returning a stream. Then mapMulti allows us to write that logic in a BiConsumer instead of a Function.

In the following code we use the mapMulti method in several examples:

package mrhaki.stream;

import javax.print.attribute.HashPrintServiceAttributeSet;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class MapMulti {

    public static void main(String[] args) {
        // We want to return a stream of string values 
        // and the uppercase variant
        // if the original element has the letter o.
        assert Stream.of("Java", "Groovy", "Clojure")
                     .mapMulti((language, downstream) -> {
                         if (language.contains("o")) {
                             downstream.accept(language);
                             downstream.accept(language.toUpperCase());
                         }
                     })
                     .toList()
                     .equals(List.of("Groovy", "GROOVY", "Clojure", "CLOJURE"));

        // Same logic implemented with flatMap.
        assert Stream.of("Java", "Groovy", "Clojure")
                     .filter(language -> language.contains("o"))
                     .flatMap(language -> Stream.of(language, language.toUpperCase()))
                     .toList()
                     .equals(List.of("Groovy", "GROOVY", "Clojure", "CLOJURE"));


        // Helper record to store a name and set of language names.
        record Developer(String name, List<String> languages) {}

        // Number of sample developers that work with different languages.
        var hubert = new Developer("mrhaki", List.of("Java", "Groovy", "Clojure"));
        var java = new Developer("java", List.of("Java"));
        var clojure = new Developer("clojure", List.of("Clojure"));
        var groovy = new Developer("groovy", List.of("Groovy"));

        record Pair(String name, String language) {}

        // Let's find all developers that have Java in their
        // set of languages and return a new Pair
        // object with the name of the developer and a language.
        assert Stream.of(hubert, java, clojure, groovy)
                     // We can explicitly state the class that will be 
                     // in the downstream of the compiler cannot
                     // deduct it using a <...> syntax.
                     .<Pair>mapMulti((developer, downstream) -> {
                         var languages = developer.languages();
                         if (languages.contains("Java")) {
                             for (String language : developer.languages()) {
                                 downstream.accept(new Pair(developer.name(), language));
                             }
                         }
                     })
                     .toList()
                     .equals(List.of(new Pair("mrhaki", "Java"),
                                     new Pair("mrhaki", "Groovy"),
                                     new Pair("mrhaki", "Clojure"),
                                     new Pair("java", "Java")));

        // Same logic using filter and flatMap.
        assert Stream.of(hubert, java, clojure, groovy)
                     .filter(developer -> developer.languages().contains("Java"))
                     .flatMap(developer -> developer.languages()
                                                    .stream()
                                                    .map(language -> new Pair(developer.name(), language)))
                     .toList()
                     .equals(List.of(new Pair("mrhaki", "Java"),
                                     new Pair("mrhaki", "Groovy"),
                                     new Pair("mrhaki", "Clojure"),
                                     new Pair("java", "Java")));
        
        
        // We want to expand each number to itself and its square root value
        // and we muse mapMultiToInt here.
        var summaryStatistics = Stream.of(1, 2, 3)
                                      .mapMultiToInt((number, downstream) -> {
                                          downstream.accept(number);
                                          downstream.accept(number * number);
                                      })
                                      .summaryStatistics();

        assert summaryStatistics.getCount() == 6;
        assert summaryStatistics.getSum() == 20;
        assert summaryStatistics.getMin() == 1;
        assert summaryStatistics.getMax() == 9;
    }
}

Written with Java 20.

July 17, 2023

Awesome AssertJ: Writing Assertions For Optional

For a lot of types AssertJ has special assertion methods. Also for the type Optional. If we want to assert an Optional value we can use several methods that AssertJ provides. For example to check if an Optional is present we can use isPresent() or the alias isNotEmpty(). To check if the Optional is empty we can use isEmpty() or the alias isNotPresent(). Checking the value of an Optional (if it is indeed set) can be done with hasValue() or contains(). For more fine grained assertions on the value we can use hasValueSatisfying(Condition) or hasValueSatisfying(Consumer). With the map(Function) and flatMap(Function) methods we can map the Optional, if not empty, to another value and assert that value.

In the following example we see assertion methods for an Optional instance:

package mrhaki;

import org.assertj.core.api.Condition;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test;

import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;

class OptionalAssertions {
    
    @Test 
    void checkOptionalPresent(){
        assertThat(Optional.of("Awesome AssertJ"))
                // Check if Optional has a value.
                .isPresent()
                
                // Or we use the alias isNotEmpty().
                .isNotEmpty();
    }
    
    @Test 
    void checkOptionalNotPresent() {
        assertThat(Optional.empty())
                // Check if Optional is empty.
                .isEmpty()
                
                // Or we use the aliase isNotPresent().
                .isNotPresent();
    }
    
    @Test
    void checkValueOfOptional() {
        assertThat(Optional.of("Awesome AssertJ"))
                // We can check the value with hasValue(...).
                // We don't have to call get() ourselves.
                .hasValue("Awesome AssertJ")
                
                // Or we use the alias contains(...).
                .contains("Awesome AssertJ");
    }
    @Test
    void checkGetOfOptional() {
        // We can use get() to chain more assertions,
        // but we can only use generic Object assertions.
        assertThat(Optional.of("Awesome AssertJ"))
                .get()
                // We can only use Object assertions.
                .isEqualTo("Awesome AssertJ");

        // If we use get(InstanceOfAssertFactory) we get
        // back an object to use assertion methods for the 
        // given type.
        // The interface InstanceOfAssertFactories has a lot of 
        // useful constants to force the type returned by the Optional,
        // so we can use the assertions for that type.
        assertThat(Optional.of("Awesome AssertJ"))
                .get(InstanceOfAssertFactories.STRING)
                // Now we can use String assertions:
                .startsWith("Awesome")
                .endsWith("AssertJ");
    }

    @Test
    void checkValueSatisfyingOfConditional() {
        // We can use hasValueSatisfying(Consumer)
        // with the instance that is wrapped in the Optional
        // for more fine grained assertion.
        // Also, we can use assertions for the type wrapped
        // in the Optional.
        assertThat(Optional.of("Awesome AssertJ"))
                .hasValueSatisfying(s -> {
                    assertThat(s)
                            .startsWith("Awesome")
                            .endsWith("AssertJ");
                });
    }
    
    @Test
    void checkObjectValueOfOptionalWithCondition() {
        // Record to store properties of a framework.
        record Framework(String name, boolean awesome) {}

        // We can write a Condition that checks if the awesome
        // property is set to true.
        var isAwesomeFramework = new Condition<Framework>(Framework::awesome, "Framework is awesome!");

        // And with hasValueSatisfying we can use the Condition.
        assertThat(Optional.of(new Framework("AssertJ", true)))
                .hasValueSatisfying(isAwesomeFramework);
    }

    @Test
    void checkTransformedValueOfOptional() {
        // We can use map(...) to transform the value
        // of the optional (if present)
        // and write assertions for the transformed value.
        assertThat(Optional.of("Awesome AssertJ"))
                .map(String::toUpperCase)
                .hasValue("AWESOME ASSERTJ");

        // Record to store framework data with an optional type.
        record Framework(String name, Optional<Boolean> awesome) {}
        
        // With flatMap(...) we can transform the value
        // of the optional (if present) to another Optional
        // and write assertions for this transformed Optinal value.
        assertThat(Optional.of(new Framework("Awesome AssertJ", Optional.of(true))))
                .flatMap(Framework::awesome)
                .isPresent()
                .hasValue(true);
    }

    @Test
    void checkInstanceOfOptional() {
        // With containsInstanceOf we can write an assertion
        // to check the type that is contained in the Optional.
        assertThat(Optional.of("Awesome AssertJ"))
                .containsInstanceOf(String.class);
    }
}

Written with AssertJ 3.24.2.

July 12, 2023

Awesome AssertJ: Assertions For An URL Object

AssertJ has a lot of custom assertion methods for different types. For example to assert an URL object AssertJ gives us some specific methods. We can check for different components of the URL instance with different methods. For example we can check if the protocol is equal to the protocol we expect with hasProtocol(String). Similarly we can write assertions for the host, port, authority, path and anchor. To assert query parameters we can use hasQueryParameter(String) to check if query parameter is set and with hasQueryParameter(String, String) we can check if the query parameter has an expected value. To check the whole query string we can use hasQueryString(String).

Each of the assertion methods also has version to assert a component is not present. For example hasNoQuery() to assert a query is not defined for an URL.

In the following example we use the different assertion methods for an URL instance:

package mrhaki;

import org.junit.jupiter.api.Test;

import java.net.MalformedURLException;
import java.net.URL;

import static org.assertj.core.api.Assertions.assertThat;

class UrlChecks {

    @Test
    void checkUrl() throws MalformedURLException {
        assertThat(new URL("https://www.mrhaki.com:80/blogs/index.html#groovy-goodness"))

                // We can check the protocol.
                .hasProtocol("https")

                // We can check the host name.
                .hasHost("www.mrhaki.com")

                // We can check the port if it is part of the URL.
                .hasPort(80)

                // We can check the authority (host + port).
                .hasAuthority("www.mrhaki.com:80")

                // We can check the path part of the URL.
                .hasPath("/blogs/index.html")

                // We can check the anchor part.
                .hasAnchor("groovy-goodness");
    }

    @Test
    void checkQueryParameters() throws MalformedURLException {
        assertThat(new URL("https://www.mrhaki.com/blogs/?tag=Groovy&text=Goodness"))

                // We can check the full query string.
                .hasQuery("tag=Groovy&text=Goodness")

                // We can check if a query parameter is defined.
                .hasParameter("tag")
                .hasParameter("text")

                // We can check if a query parameter with a given value is set.
                .hasParameter("tag", "Groovy")
                .hasParameter("text", "Goodness")

                // We can verify with another URL after the query parameters are sorted.
                .isEqualToWithSortedQueryParameters(new URL("https://www.mrhaki.com/blogs/?text=Goodness&tag=Groovy"));
    }

    @Test
    void checkUserInfo() throws MalformedURLException {
        assertThat(new URL("https://user:password@www.mrhaki.com/"))

                // We can check the user info part of the URL.
                .hasUserInfo("user:password");
    }

    @Test
    void checkUrlNegated() throws MalformedURLException {
        // We can also check for the non-presence of components of the URL.
        assertThat(new URL("https://www.mrhaki.com"))
                .hasNoPort()
                .hasNoPath()
                .hasNoQuery()
                .hasNoParameters()
                .hasNoParameter("tag")
                .hasNoParameter("tag", "Groovy")
                .hasNoAnchor()
                .hasNoUserInfo();

        assertThat(new URL("file:///Users/mrhaki"))
                .hasNoHost();
    }
}

Written with AssertJ 3.24.2.

July 5, 2023

Awesome AssertJ: Check Base64 Encoded Strings

AssertJ has some nice methods to verify string values. If we want to verify a string value is Base64 encoded we can use the isBase64String() method. We can leave out the padding of the value as it is optional. With the method asBase64Decoded() we can decode the value and write our assertions for the decoded value. The method asBase64Decoded() returns a byte[] object and we can use the asString() to convert it into a string value again.

In the following example we use the isBase64() and asBase64Decoded() methods:

package mrhaki;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

public class StringBase64 {

    @Test
    void checkValueIsBase64Encoded() {
        // We can simply check if the value is Base64 encoded
        // with the method isBase64().
        assertThat("QXNzZXJ0SiBiZSBBd2Vzb21lLCBtYXRleSE=").isBase64();

        // Padding (=) can be omitted from the Base64 encoded value.
        assertThat("QXNzZXJ0SiBiZSBBd2Vzb21lLCBtYXRleSE").isBase64();

        // If we want to check a value is NOT Base64 encoded we
        // need to check an AssertionError is thrown.
        assertThatExceptionOfType(AssertionError.class)
                .isThrownBy(() -> assertThat("AssertJ is Awesome").isBase64())
                .withMessageContaining("Expecting \"AssertJ is Awesome\" to be a valid Base64 encoded string");
    }

    @Test
    void checkBase64EncodedValue() {
        // We can use asBase64Decoded() to get a byte[] result
        // and apply our assertions.
        assertThat("QXNzZXJ0SiBiZSBBd2Vzb21lLCBtYXRleSE=")
                .asBase64Decoded().asString()
                .isEqualTo("AssertJ be Awesome, matey!");

        // Also here the padding (=) can be omitted.
        assertThat("QXNzZXJ0SiBiZSBBd2Vzb21lLCBtYXRleSE")
                .asBase64Decoded().asString()
                .isEqualTo("AssertJ be Awesome, matey!");

        assertThat("QXNzZXJ0SiBiZSBBd2Vzb21lLCBtYXRleSE=")
                .asBase64Decoded().asString()
                .isNotEqualTo("AssertJ is Awesome!");
    }
}

Written with AssertJ 3.24.2.

Awesome AssertJ: Use String Template To Verify String Value

To compare string values we can use the isEqualTo(String) method in AssertJ. But if we want to verify that a string contains a certain variable value we can use string templates. This makes the assertion more readable as we can see what value we expect in the string. To use string templates we must the method isEqualTo(String, Object…​). The first argument is the string template and the following arguments will be the actual values that should be used in the template. Actually the String.format(String, Object…​) method is used behind the scenes to format the string template, but we don’t have to clutter our assertions with that call.

In the following example we see how we can use string templates to verify string values:

package mrhaki;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.assertj.core.api.Assertions.assertThat;

public class StringIsEqualToTemplate {

    @Test
    void verifyStringIsEqualTo() {
        // We can use a template to define the format of the String.
        // If we do actually String.format() is used to create
        // our expected value.
        assertThat("AssertJ is awesome!").isEqualTo("%s is %s!", "AssertJ", "awesome");
    }

    // Using a template can be very useful for example in parameterized tests.
    @ParameterizedTest
    @ValueSource(strings = {"AssertJ", "Spock"})
    void verifyStringFormat(String libraryName) {
        // given
        record Library(String name) {
            @Override
            public String toString() {
                return name;
            }
        }
        var library = new Library(libraryName);

        // expect
        assertThat(library.name() + " is awesome").isEqualTo("%s is awesome", library);
    }

    @Test
    void verifyStringIsNotEqualTo() {
        // There is no method implementation for isNotEqualTo where we can define
        // the format as first argument. But we can still use String.format
        // ourselves to achieve the same functionality.
        assertThat("AssertJ is awesome!").isNotEqualTo(String.format("%s is %s!", "Spock", "awesome"));
    }
}

Written with AssertJ 3.24.2.

July 3, 2023

Awesome AssertJ: Use returns To Verify An Object Using Functions

With the returns method in AssertJ we can verify an object using a function. This allows us to verify an object in a very flexible way. We can chain multiple returns method calls to verify multiple aspects of our object. The first argument of the returns method is the expected value of the function call. And the second argument is a function that calls a method on the object we want to verify. A simple function call would be a method reference using the class of the object. But we can also write our own function, where the argument of the function is actual object we are writing the assertion for. To verify the function doesn’t return an expected value we can use the method doesNotReturn.
We can also pass the function to the from method, available in the Assertions class. It can make the assertion more readeable as we can now read the code as: we expect the following value from calling this function.

In the following example we use the returns and doesNotReturn methods in several use cases:

package mrhaki;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.from;

class Returns {

    // Pirate record we will use for tests.
    record Pirate(String name, String ship, int age) {}

    @Test
    void checkReturnValueMethodReference() {
        // With returns() we can specify the expected value
        // from a function call.
        // We can use a method references to easily
        // get a return value we want to verify.
        assertThat(new Pirate("Jack Sparrow", "Black Pearl", 40))
                .returns("Jack Sparrow", Pirate::name)
                .returns("Black Pearl", Pirate::ship)
                .returns(40, Pirate::age);
    }

    @Test
    void checkValueIsNotReturned() {
        // Instead of using returns() we can use doesNotReturn()
        // to verify the function doesn't return an expected value.
        assertThat(new Pirate("Will Turner", "Flying Dutchman", 35))
                .doesNotReturn("Jack Sparrow", Pirate::name)
                .doesNotReturn("Black Pearl", Pirate::ship)
                .doesNotReturn(40, Pirate::age);
    }

    @Test
    void checkReturnValueFrom() {
        // We can use the Assertions.from() method to add
        // some readability to our assertions.
        // We simply pass the function that should return our
        // expected value to the from() method.
        assertThat(new Pirate("Jack Sparrow", "Black Pearl", 40))
                .returns("Jack Sparrow", from(Pirate::name))
                .returns("Black Pearl", from(Pirate::ship))
                .returns(40, from(Pirate::age));
    }

    @Test
    void checkReturnValueFromFunction() {
        // Instead of using method references as functions
        // like in the first test,
        // we can use custom functions as well.
        // The type of the function argument is the object
        // we write the assertions for.
        assertThat(new Pirate("Jack Sparrow", "Black Pearl", 40))
                .returns(true, p -> p.name().startsWith("Jack"))
                .returns("black-pearl", p -> p.ship().toLowerCase().replace(" ", "-"))
                // We can also from() here.
                .returns(12, from(p -> p.name().length()))
                .returns(false, p -> p.age() > 50);
    }
}

Written with AssertJ 3.24.2.

June 30, 2023

Awesome AssertJ: Check String Starts Or Ends With A Given Value

Writing assertions using the nice fluent API of AssertJ is a joy. Besides some of the basic assertions like isEqualTo AssertJ also has specific assertions for specific types. For example if we want write an assertion to check if a String value starts or ends with an expected value we can use the startsWith(String) or endsWith(String) methods. If we don’t care that a character is upper or lower case we can also use startsWithIgnoringCase(String) or endsWithIgnoringCase(String). Each of the methods also has a counterpart method to check the String value doesn’t start or end with an expected value. For example we can use doesNotStartWith(String) to assert a value does not start with the expected value.

Instead of using the specific startsWith…​(String) or endsWith…​(String) methods we can also use the more generic matches method. Now we need to specify a Pattern or Predicate that matches our requirement. This give some more flexbility but is also more verbose. To test there is not a match we can use the doesNotMatch method.

In the following example we write assertions to check if a String value starts with an expected value:

package mrhaki;

import org.junit.jupiter.api.Test;
import java.util.regex.Pattern;

import static org.assertj.core.api.Assertions.assertThat;

class StringStartsWith {

    @Test
    void testStringStartsWith() {
        assertThat("Awesome AssertJ").startsWith("Awesome");

        // We can also check ignoring the casing of the String value.
        assertThat("Awesome AssertJ").startsWithIgnoringCase("aweSome");
    }

    @Test
    void testStringDoesNotStartWith() {
        // Or we check a String value doesn't start with an expected value.
        assertThat("Amazing Assertj").doesNotStartWith("Awesome");

        // We can also check ignoring the casing of the String value.
        assertThat("Amazing Assertj").doesNotStartWithIgnoringCase("aweSome");
    }

    @Test
    void testStringStartsWithRegex() {
        // We can use regular expressions as well, but it is more verbose.
        assertThat("Awesome AssertJ").matches(Pattern.compile("^Awesome.*"));

        // Also here we can ignore casing.
        assertThat("Awesome AssertJ").matches(Pattern.compile("^awesome.*", Pattern.CASE_INSENSITIVE));

        // And check String value doesn't start with expected value.
        assertThat("Amazing AssertJ").doesNotMatch(Pattern.compile("^Awesome.*"));
    }

    @Test
    void testStringStartsWithPredicate() {
        // We can use a Predicate as well, but it is more verbose.
        assertThat("Awesome AssertJ").matches(s -> s.startsWith("Awesome"));

        // We are more flexible as we can use for example specify an offset.
        assertThat("Awesome AssertJ").matches(s -> s.startsWith("some", 3));

        // If we want to ignore casing we have to first convert our value.
        assertThat("Awesome AssertJ").matches(s -> s.toLowerCase().startsWith("awesome"));

        // And check String value doesn't start with expected value.
        assertThat("Amazing AssertJ").matches(s -> !s.startsWith("Awesome"));
    }
}

And in the next example we write assertions to check if a String value ends with an expected value:

package mrhaki;

import org.junit.jupiter.api.Test;
import java.util.regex.Pattern;

import static org.assertj.core.api.Assertions.assertThat;

class StringEndsWith {

    @Test
    void testStringEndsWith() {
        assertThat("Awesome AssertJ").endsWith("AssertJ");

        // We can also check ignoring the casing of the String value.
        assertThat("Awesome AssertJ").endsWithIgnoringCase("assertJ");
    }

    @Test
    void testStringDoesNotEndWith() {
        // Or we we check our String values doesn't end with an expected value.
        assertThat("Awesome Asciidoc").doesNotEndWith("AssertJ");

        // We can also check ignoring the casing of the String value.
        assertThat("Awesome Asciidoc").doesNotEndWithIgnoringCase("assertJ");
    }

    @Test
    void testStringEndsWithRegex() {
        // We can use regular expressions as well, but it is more verbose.
        assertThat("Awesome AssertJ").matches(Pattern.compile(".*AssertJ$"));

        // Also here we can ignore casing.
        assertThat("Awesome AssertJ").matches(Pattern.compile(".*AssertJ$", Pattern.CASE_INSENSITIVE));

        // And check String value doesn't end with expected value.
        assertThat("Amazing Asciidoc").doesNotMatch(Pattern.compile(".*AssertJ$"));
    }

    @Test
    void testStringEndsWithPredicate() {
        // We can use a Predicate as well, but it is more verbose.
        assertThat("Awesome AssertJ").matches(s -> s.endsWith("AssertJ"));

        // If we want to ignore casing we have to first convert our value.
        assertThat("Awesome AssertJ").matches(s -> s.toLowerCase().endsWith("assertj"));

        // And check String value doesn't end with expected value.
        assertThat("Awesome Asciidoc").matches(s -> !s.endsWith("AssertJ"));
    }
}

Written with AssertJ 3.24.2.

June 29, 2023

Awesome AssertJ: Assert toString Method of Object With hasToString Method

AssertJ has many useful methods to write assertions using a fluid API. If we want to test the toString() method implementation of an object we can of course invoke the toString() method inside an assertThat expression and check with the assert method isEqualTo(String) the value. But AssertJ can make that easier so we don’t have to invoke toString() method ourselves. We can use the assert method hasToString(String) on the object we defined in the assertThat expression and specify our expected value as argument to the method. If we want to assert the toString() method doesn’t return an expected value we can use the assert method doesNotHaveToString(String).

In the following example test we use the methods hasToString and doesNotHaveToString for different

package mrhaki;

import org.junit.jupiter.api.Test;

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

import static org.assertj.core.api.Assertions.assertThat;

class HasToString {

    // Helper class used in tests to check toString().
    private static class User {
        private final String username;

        public User(String username) {
            this.username = username;
        }

        public String toString() {
            return String.format("[User{username=%s}]", username);
        }
    }

    @Test
    void testHasToString() {
        // We can assert the toString() method of arbitrary objects
        // with hasToString(String).
        assertThat(new User("mrhaki")).hasToString("[User{username=mrhaki}]");
    }

    @Test
    void testDoesNotHaveString() {
        // If we want to assert the toString() method doesn't return a value
        // we can use doesNotHaveToString(String).
        assertThat(new User("mrhaki")).doesNotHaveToString("User[user=mrhaki]");
    }

    @Test
    void testIntegerToString() {
        // Simple types are automatically boxed to the object wrapper type
        // and then have a toString() method to assert the value for.
        assertThat(42).hasToString("42");  // int value is boxed to Integer.
        assertThat(Integer.valueOf(42)).hasToString("42");
    }

    @Test
    void testListToString() {
        // We can test toString() on List instances.
        var items = List.of("TDD", "JUnit", "AssertJ");
        assertThat(items).hasToString("[TDD, JUnit, AssertJ]");
        assertThat(items).doesNotHaveToString("[TDD,JUnit,AssertJ");
    }

    @Test
    void testMapToString() {
        // We can test toString() on Map instances.
        var map = Map.of("language", "Java");
        assertThat(map).hasToString("{language=Java}");
        assertThat(map).doesNotHaveToString("[language:Java]");
    }
}

Written with AssertJ 3.24.2.

April 21, 2023

Groovy Goodness: Sorting Data With GINQ

GINQ (Groovy-INtegerate Query) is part of Groovy since version 4. With GINQ we can use SQL-like queries to work with in-memory data collections. If we want to sort the data we can use orderby followed by the property of the data we want to sort just like in SQL we can use order by. By default the sort ordering is ascending and null values are put last. We can change the sort ordering by specifying in desc with the orderby clause. Or to make the ascending order explicitly we use the statement in asc. Each of asc and desc also can take an argument to specify how we want null values to be sorted. The default way is to keep null values last in the ordering. If we want to make this explicit we use nullslast as argument to asc or desc. To have null values in the sorted result first we use the argument nullsfirst.

The following example shows all use cases for using orderby when using GINQ:

import groovy.json.JsonSlurper

// Parse sample JSON with a list of users.
def json = new JsonSlurper().parseText('''[
{ "username": "mrhaki", "email": "mrhaki@localhost" },
{ "username": "mrhaki", "email": "user@localhost" },
{ "username": "hubert", "email": "user@localhost" },
{ "username": "hubert", "email": "hubert@localhost" },
{ "username": "hubert", "email": null }
]''')

// Helper method to return a String
// representation of the user row.
def formatUser(row) {
    row.username + "," + row.email
}

// Default ordering is ascending.
// We specify the field name we want to order on.
assert GQ {
    from user in json
    orderby user.username
    select formatUser(user)
}.toList() == [
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'hubert,null',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost'
]

// We can explicitly set ordering to ascending.
assert GQ {
    from user in json
    orderby user.email in asc
    select formatUser(user)
}.toList() == [
    'hubert,hubert@localhost',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost',
    'hubert,user@localhost',
    'hubert,null'
]

// By default null values are last.
// We can also make this explicit as
// option to in asc() or in desc().
assert GQ {
    from user in json
    orderby user.email in asc(nullslast)
    select formatUser(user)
}.toList() == [
    'hubert,hubert@localhost',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost',
    'hubert,user@localhost',
    'hubert,null'
]

// We can combine multiple properties to sort on.
assert GQ {
    from user in json
    orderby user.username, user.email
    select formatUser(user)
}.toList() == [
    'hubert,hubert@localhost',
    'hubert,user@localhost',
    'hubert,null',
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost'
]

// To order descending we must specify it
// as in desc.
assert GQ {
    from user in json
    orderby user.username in desc
    select formatUser(user)
}.toList() == [
    'mrhaki,mrhaki@localhost',
    'mrhaki,user@localhost',
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'hubert,null'
]

// We can mix the ordering and set it
// differently for each property.
assert GQ {
    from user in json
    orderby user.username in asc, user.email in desc
    select formatUser(user)
}.toList() == [
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'hubert,null',
    'mrhaki,user@localhost',
    'mrhaki,mrhaki@localhost'
]

// By default all null values are last,
// but we can use nullsfirst to have null
// values as first value in the ordering.
assert GQ {
    from user in json
    orderby user.username in asc, user.email in desc(nullsfirst)
    select formatUser(user)
}.toList() == [
    'hubert,null',
    'hubert,user@localhost',
    'hubert,hubert@localhost',
    'mrhaki,user@localhost',
    'mrhaki,mrhaki@localhost'
]

Written with Groovy 4.0.11.

Groovy Goodness: Calculate The Median Of A Collection

Since Groovy 4 we can use SQL like queries on in-memory collections with GINQ (Groovy-Integrated Query). GINQ provides some built-in aggregate functions like min, max, sum and others. One of these functions is median. With median we can get the value that is in the middle of the sorted list of values we want to calculate the median for. If the list has an uneven number of elements the element in the middle is returned, but if the list has an even number of elements the average of the two numbers in the middle is returned.

In the following example we see the use of the median function with GINQ:

// List of uneven number of response times.
def responseTimes = [201, 200, 179, 211, 350]

// Get the median from the list of response times.
// As the list has an uneven number of items
// the median is in the middle of the list after
// it has been sorted.
assert GQ {
    from time in responseTimes
    select median(time)
}.toList() == [201]

// List of even number of response times.
responseTimes = [201, 200, 179, 211, 350, 192]

// 2 numbers are the median so the result
// is the average of the 2 numbers.
assert GQ {
    from time in responseTimes
    select median(time)
}.findResult() == 200.5

// Use the GQ annotation and return a List from the method.
@groovy.ginq.transform.GQ(List)
def medianSize(List<String> values) {
    from s in values
    // We can also use an expression to get the median.
    // Here we take the size of the string values to
    // calculage the median.
    select median(s.size())
}

assert medianSize(["Java", "Clojure", "Groovy", "Kotlin", "Scala"]) == [6]

// Sample data structure where each record
// is structured data (map in this case).
// Could also come from JSON for example.
def data = [
    [test: "test1", time: 200],
    [test: "test1", time: 161],
    [test: "test2", time: 427],
    [test: "test2", time: 411],
    [test: "test1", time: 213]
]

// We want to get each record, but also
// the median for all times belonging to a single test.
// We can use the windowing functions provided by GINQ
// together with median.
def query = GQ {
    from result in data
    orderby result.test
    select result.test as test_name,
           result.time as response_time,
           (median(result.time) over(partitionby result.test)) as median_per_test
}

assert query
        .collect { row -> [name: row.test_name,
                           response: row.response_time,
                           median: row.median_per_test] } ==
[
    [name: "test1", response: 200, median: 200],
    [name: "test1", response: 161, median: 200],
    [name: "test1", response: 213, median: 200],
    [name: "test2", response: 427, median: 419],
    [name: "test2", response: 411, median: 419]
]

Written with Groovy 4.0.11.

April 13, 2023

Groovy Goodness: Using Tuples

Groovy supports a tuple type. A tuple is an immutable object to store elements of potentially different types. In Groovy there is a separate Tuple class based on how many elements we want to store in the tuple. The range starts at Tuple0 and ends with Tuple16. So we can store a maximum of 16 elements in a Groovy tuple.
Each of the classes has a constructor that takes all elements we want to store. But the Tuple class also has static factory methods to create those classes. We can use the tuple method and based on how many elements we provide to this method we get the corresponding Tuple object.

To get the elements from a Tuple instance we can use specific properties for each element. The first element can be fetched using the v1 property, the second element is v2 and so on for each element. Alternatively we use the subscript operator ([]) where the first element is at index 0.

Each Tuple instance also has a subList and subTuple method where we can provide the from and to index values of the elements we want to be returned. The methods return a new Tuple with only the elements we requested.
A Groovy tuple is also a List and that means we can use all collection methods for a List also on a Tuple instance.

In the following example we create some tuples and use different methods:

// Using the constructor to create a Tuple.
def tuple2 = new Tuple2("Groovy", "Goodness")

// We can also use the static tuple method.
// Maximum number of elements is 16.
def tuple3 = Tuple.tuple("Groovy", "is", "great")

assert tuple3 instanceof Tuple3


// We can mix types as each elements can
// have it's own type.
def mixed = Tuple.tuple(30, "minutes")

// We can use the subscript operator ([])
// to get a value.
assert mixed[0] == 30
assert mixed[1] == "minutes"

// Or use the get() method.
assert mixed.get(0) instanceof Integer
assert mixed.get(1) instanceof String

// Or use the getter/property V1/V2.
// For each element in a Tuple we can use that.
// Notice that the first element starts with v1.
assert mixed.v1 == 30
assert mixed.getV2() == "minutes"

// Or use multiple assignments.
def (int minutes, String period) = mixed
assert minutes == 30
assert period == "minutes"


// We can get the size.
assert mixed.size() == 2


// Or transform the elements to an array
// and type information is saved.
assert mixed.toArray() == [30, "minutes"]

assert mixed.toArray()[0].class.name == "java.lang.Integer"
assert mixed.toArray()[1].class.name == "java.lang.String"


// Sample tuple with 4 elements.
Tuple4 tuple4 = Tuple.tuple("Groovy", "rocks", "as", "always")

// We can use subList or subTuple to create a new Tuple
// with elements from the original Tuple.
// We need to specify the "from" and "to" index.
// The "to" index is exclusive.
assert tuple4.subList(0, 2) == Tuple.tuple("Groovy", "rocks")
assert tuple4.subTuple(0, 2) == Tuple.tuple("Groovy", "rocks")

// As Tuple extends from List we can use all
// Groovy collection extensions.
assert tuple4.findAll { e -> e.startsWith("a") } == ["as", "always"]
assert tuple4.collect { e -> e.toUpperCase() } == ["GROOVY", "ROCKS", "AS", "ALWAYS"]


// We can even create an empty Tuple.
assert Tuple.tuple() instanceof Tuple0

Written with Groovy 4.0.11.

April 10, 2023

Spocklight: Testing Asynchronous Code With PollingConditions

In a previous blog post we learned how to use DataVariable and DataVariables to test asynchronous code. Spock also provides PollingConditions as a way to test asynchronous code. The PollingConditions class has the methods eventually and within that accept a closure where we can write our assertions on the results of the asynchronous code execution. Spock will try to evaluate conditions in the closure until they are true. By default the eventually method will retry for 1 second with a delay of 0.1 second between each retry. We can change this by setting the properties timeout, delay, initialDelay and factor of the PollingConditions class. For example to define the maximum retry period of 5 seconds and change the delay between retries to 0.5 seconds we create the following instance: new PollingConditions(timeout: 5, initialDelay: 0.5).
Instead of changing the PollingConditions properties for extending the timeout we can also use the method within and specify the timeout in seconds as the first argument. If the conditions can be evaluated correctly before the timeout has expired then the feature method of our specification will also finish earlier. The timeout is only the maximum time we want our feature method to run.

In the following example Java class we have the methods findTemperature and findTemperatures that will try to get the temperature for a given city on a new thread. The method getTemperature will return the result. The result can be null as long as the call to the WeatherService is not yet finished.

package mrhaki;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

class AsyncWeather {

    private final ExecutorService executorService;
    private final WeatherService weatherService;

    private final Map<String, Integer> results = new ConcurrentHashMap<>();

    AsyncWeather(ExecutorService executorService, WeatherService weatherService) {
        this.executorService = executorService;
        this.weatherService = weatherService;
    }

    // Invoke the WeatherService in a new thread and store result in results.
    void findTemperature(String city) {
        executorService.submit(() -> results.put(city, weatherService.getTemperature(city)));
    }

    // Invoke the WeatherService in a new thread for each city and store result in results.
    void findTemperatures(String... cities) {
        Arrays.stream(cities)
              .parallel()
              .forEach(this::findTemperature);
    }

    // Get the temperature. Value can be null when the WeatherService call is not finished yet.
    int getTemperature(String city) {
        return results.get(city);
    }

    interface WeatherService {
        int getTemperature(String city);
    }
}

To test the class we write the following specification using PollingConditions:

package mrhaki

import spock.lang.Specification
import spock.lang.Subject
import spock.util.concurrent.PollingConditions

import java.util.concurrent.Executors

class AsyncPollingSpec extends Specification {

    // Provide a stub for the WeatherService interface.
    // Return 21 when city is Tilburg and 18 for other cities.
    private AsyncWeather.WeatherService weatherService = Stub() {
        getTemperature(_ as String) >> { args ->
            if ("Tilburg" == args[0]) 21 else 18
        }
    }

    // We want to test the class AsyncWeather
    @Subject
    private AsyncWeather async = new AsyncWeather(Executors.newFixedThreadPool(2), weatherService)

    void "findTemperature and getTemperature should return expected temperature"() {
        when:
        // We invoke the async method.
        async.findTemperature("Tilburg")

        then:
        // Now we wait until the results are set.
        // By default we wait for at  most 1 second,
        // but we can configure some extra properties like
        // timeout, delay, initial delay and factor to increase delays.
        // E.g. new PollingConditions(timeout: 5, initialDelay: 0.5, delay: 0.5)
        new PollingConditions().eventually {
            // Although we are in a then block, we must
            // use the assert keyword in our eventually
            // Closure code.
            assert async.getTemperature("Tilburg") == 21
        }
    }

    void "findTemperatures and getTemperature shoud return expected temperatures"() {
        when:
        // We invoke the async method.
        async.findTemperatures("Tilburg", "Amsterdam")

        then:
        // Instead of using eventually we can use within
        // with a given timeout we want the conditions to
        // be available for assertions.
        new PollingConditions().within(3) {
            // Although we are in a then block, we must
            // use the assert keyword in our within
            // Closure code.
            assert async.getTemperature("Amsterdam") == 18
            assert async.getTemperature("Tilburg") == 21
        }
    }
}

Written with Spock 2.4-groovy-4.0.

Spocklight: Testing Asynchronous Code With DataVariable(s)

Testing asynchronous code needs some special treatment. With synchronous code we get results from invoking method directly and in our tests or specifications we can easily assert the value. But when we don’t know when the results will be available after calling a method we need to wait for the results. So in our specification we actually block until the results from asynchronous code are available. One of the options Spock provides us to block our testing code and wait for the code to be finished is using the classes DataVariable and DataVariables. When we create a variable of type DataVariable we can set and get one value result. The get method will block until the value is available and we can write assertions on the value as we now know it is available. The set method is used to assign a value to the BlockingVariable, for example we can do this in a callback when the asynchronous method support a callback parameter.

The BlockingVariable can only hold one value, with the other class BlockingVariables we can store multiple values. The class acts like a Map where we create a key with a value for storing the results from asynchronous calls. Each call to get the value for a given key will block until the result is available and ready to assert.

The following example code is a Java class with two methods, findTemperature and findTemperatures, that make asynchronous calls. The implementation of the methods use a so-called callback parameter that is used to set the results from invoking a service to get the temperature for a city:

package mrhaki;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;

class Async {

    private final ExecutorService executorService;
    private final WeatherService weatherService;

    Async(ExecutorService executorService, WeatherService weatherService) {
        this.executorService = executorService;
        this.weatherService = weatherService;
    }

    // Find temperature for a city using WeatherService and
    // assign result using callback argument.
    void findTemperature(String city, Consumer<Result> callback) {
        // Here we do an asynchronous call using ExecutorService and
        // a Runnable lambda.
        // We're assigning the result of calling WeatherService using
        // the callback argument.
        executorService.submit(() -> {
            int temperature = weatherService.getTemperature(city);
            var result = new Result(city, temperature);
            callback.accept(result);
        });
    }

    // Find temperature for multiple cities using WeatherService and
    // assign result using callback argument.
    void findTemperatures(List<String> cities, Consumer<Result> callback) {
        cities.forEach(city -> findTemperature(city, callback));
    }

    record Result(String city, int temperature) {
    }

    interface WeatherService {
        int getTemperature(String city);
    }
}

To test our Java class we write the following specification where we use both DataVariable and DataVariables to wait for the asynchronous methods to be finished and we can assert on the resulting values:

package mrhaki

import spock.lang.Specification
import spock.lang.Subject
import spock.util.concurrent.BlockingVariable
import spock.util.concurrent.BlockingVariables

import java.util.concurrent.Executors

class AsyncSpec extends Specification {

    // Provide a stub for the WeatherService interface.
    // Return 21 on the first call and 18 on subsequent calls.
    private Async.WeatherService weatherService = Stub() {
        getTemperature(_ as String) >>> [21, 18]
    }

    // We want to test the class Async
    @Subject
    private Async async = new Async(Executors.newFixedThreadPool(2), weatherService)

    void "findTemperature should return expected temperature"() {
        given:
        // We define a BlockingVariable to store the result in the callback,
        // so we can wait for the value in the then: block and
        // asssert the value when it becomes available.
        def result = new BlockingVariable<Async.Result>()

        when:
        // We invoke the async method and in the callback use
        // our BlockingVariable to set the result.
        async.findTemperature("Tilburg") { Async.Result temp ->
            // Set the result to the BlockingVariable.
            result.set(temp)
        }

        then:
        // Now we wait until the result is available with the
        // blocking call get().
        // Default waiting time is 1 second. We can change that
        // by providing the number of seconds as argument
        // to the BlockingVariable constructor.
        // E.g. new BlockingVariable<Long>(3) to wait for 3 seconds.
        result.get() == new Async.Result("Tilburg", 21)
    }

    void "findTemperatures should return expected temperatures"() {
        given:
        // With type BlockingVariables we can wait for multiple values.
        // Each value must be assigned to a unique key.
        def result = new BlockingVariables(5)

        when:
        async.findTemperatures(["Tilburg", "Amsterdam"]) { Async.Result temp ->
            // Set the result with a key to the BlockingVariables result variable.
            // We can story multiple results in one BlockingVariables.
            result[temp.city()] = temp.temperature()
        }

        then:
        // We wait for the results key by key.
        // We cannot rely that the result are available in the
        // same order as the passed input arguments Tilburg and Amsterdam
        // as the call will be asynchronous.
        // But using BlockingVariables we dont' have to care,
        // we simply request the value for a key and the code will
        // block until it is available.
        result["Amsterdam"] == 18
        result["Tilburg"] == 21
    }
}

Written with Spock 2.3-groovy-4.0.

April 7, 2023

DataWeave Delight: Using The log Function

The log function in the dw::Core module allows to log a value or an expression. The function returns the input unchanged. This means we can wrap our code with the log function and the code is still executed as is, but also logged in a system log. As an extra argument we can specify a String value that will be a prefix to the expression value in the logging output. The fact that the input is also returned makes it very easy to add the log function to our DataWeave expressions.

In the following example we use the log function for several use cases:

Source

%dw 2.0

import upper from dw::core::Strings

// Sample object we want to use for logging.
var user = {
    alias: "mrhaki",
    name: {
        firstName: "Hubert",
        lastName: "Klein Ikkink"
    }
}

output application/json
---
{
    // Log a value.
    logValue: log("DataWeave"),

    // Log expression.
    logUpper: log(upper("DataWeave")),

    // Log object property.
    logExpr: log(user.alias),

    // Log with prefix.
    logExprWithPrefix: log("alias", user.alias),

    // Log object.
    logName: log("name", user.name)
}

Output

{
  "logValue": "DataWeave",
  "logUpper": "DATAWEAVE",
  "logExpr": "mrhaki",
  "logExprWithPrefix": "mrhaki",
  "logName": {
    "firstName": "Hubert",
    "lastName": "Klein Ikkink"
  }
}

We see the output simply returns the original object that we passed to the log function. But when we look at a log viewer, for example the console of a Mule application, we see a representation of the object. And if we specified a prefix the prefix is also visible in the logging output.

The previous code returns the following log output:

"DataWeave"
"DATAWEAVE"
"mrhaki"
alias - "mrhaki"
name - { firstName: "Hubert", lastName: "Klein Ikkink" }

Written with DataWeave 2.4.

April 6, 2023

Mastering Maven: Adding Maven Extensions Using extensions.xml

A .mvn directory in the root of our project can contains some useful extras. For example we can set default Maven options or Java VM options when we run a Maven command. We can also define Maven extensions we want to add to the Maven classpath using the file extensions.xml in the .mvn directory. The Maven extension we want to add can be referenced using a groupId, artifactId and version inside an <extension> element. We can define one or more extensions within the parent element extensions. Once we have defined the extension and run Maven the extension is added to classpath of Maven itself.

In the following example we apply the Maven Enforcer extension to our project. We add the file extensions.xml in the .mvn directory that we first have to create:

<!-- File: .mvn/extensions.xml -->
<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.1.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.1.0 http://maven.apache.org/xsd/core-extensions-1.1.0.xsd">
    <!-- Add Maven Enforcer extension -->
    <extension>
        <groupId>org.apache.maven.extensions</groupId>
        <artifactId>maven-enforcer-extension</artifactId>
        <version>3.3.0</version>
    </extension>
</extensions>

This extension requires an extra configuration file with rules we want to apply to our project. The filename must be enforcer-extension.xml and placed in the .mvn directory as well. In our example we add a rule that checks that the Java version we use to run Maven is at least version 17. And a rule to check that our Maven version is at least 3.9.1:

<!-- File: .mvn/enforcer-extension.xml -->
<extension>
    <executions>
        <execution>
            <id>maven-enforcer-extension</id>
            <phase>validate</phase>
            <configuration>
                <rules>
                    <!-- Our build requires Java 17 or higher -->
                    <requireJavaVersion>
                        <version>17</version>
                    </requireJavaVersion>
                    <!-- Use Maven 3.9.1 or higher -->
                    <requireMavenVersion>
                        <version>3.9.1</version>
                    </requireMavenVersion>
                </rules>
            </configuration>
        </execution>
    </executions>
</extension>

Next we run the package command with Java version 11.0.17 and we see in the output that the Enforcer extension failed because of the required Java version rule:

$ mvn package
...
[INFO] --- enforcer:3.3.0:enforce (maven-enforcer-extension) @ sample ---
[INFO] Rule 1: org.apache.maven.enforcer.rules.version.RequireMavenVersion passed
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.777 s (Wall Clock)
[INFO] Finished at: 2023-04-06T17:19:16+02:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-enforcer-plugin:3.3.0:enforce (maven-enforcer-extension) on project sample:
[ERROR] Rule 0: org.apache.maven.enforcer.rules.version.RequireJavaVersion failed with message:
[ERROR] Detected JDK version 11.0.17 (JAVA_HOME=/Users/mrhaki/.sdkman/candidates/java/11.0.17-tem) is not in the allowed range [17,).
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
$

Written with Maven 3.9.1.

April 4, 2023

Groovy Goodness: Using Subscript Operator With Multiple Fields On Date Related Objects

Since Groovy 4.0.5 we can use a subscript operator that accepts multiple fields on a java.util.Date and java.util.Calendar objects. And Groovy 4.0.6 extended this subscript operator to any java.time.TemporalAccessor instance. Before these Groovy version we could already use the subscript operator, but we could provide only one field we wanted to access. In a previous post we already have seen this. But now we can use multiple fields to get their values with one statement. We simply define the fields we want as arguments to the subscript operator. Under the hood the subscript operator is implemented by a getAt method that is added as an extension to the Date, Calendar and TemporalAccess classes. The return type is java.util.List and we can combine this with the multiple assignment support in Groovy. In other languages it is also called destructurizing. With multiple assignments we can assign the values from a java.util.List directly to variables.

In the following example we see several usages of the subscript operator with multiple fields on Date, Calendar and LocalDateTime objects:

import java.time.LocalDateTime
import java.time.Month
import static java.time.temporal.ChronoField.*
import static java.time.temporal.IsoFields.*

// Create a Date instance.
def date = new Date().parse('yyyy/MM/dd', '1973/07/09')

// Groovy adds the subscript operator for multiple
// fields to the Date class.
def output = date[Calendar.DATE, Calendar.MONTH, Calendar.YEAR]
assert output == [9, 6, 1973]

// The result is a list and we can destructurize it
// to assign values to variables (also called multiple assignments).
def (day, month, year) = date[Calendar.DATE, Calendar.MONTH, Calendar.YEAR]

assert "$day-${month + 1}-$year" == "9-7-1973"


// Create a Calendar instance.
def calendar = date.toCalendar()

// The subscript operator supporting multiple fields
// is also added to the Calendar class.
def (calDay, calMonth, calYear) = calendar[Calendar.DATE, Calendar.MONTH, Calendar.YEAR]

assert "Time to celebrate on $calDay-${calMonth + 1}" == "Time to celebrate on 9-7"


// Create a LocalDateTime instance
def birthDateTime = LocalDateTime.of(1973, Month.JULY, 9, 6, 30, 0);

// Groovy adds the subscript operator with multiple fields
// on any TemporalAccessor instance.
def (dayOfWeek, dayOfYear, quarter, week) = birthDateTime[DAY_OF_WEEK, DAY_OF_YEAR, QUARTER_OF_YEAR, WEEK_OF_WEEK_BASED_YEAR]

assert "Born in week $week on day $dayOfWeek" == "Born in week 28 on day 1"
assert quarter == 3
assert dayOfYear == 190

def (hour, minute) = birthDateTime[HOUR_OF_DAY, MINUTE_OF_HOUR]

assert "Live started at $hour:$minute" == "Live started at 6:30"

Written with Groovy 4.0.11.

Mastering Maven: Setting Default Maven Options With maven.config

In a previous blog we learned about setting default JVM options when we run Maven commands. We can also set default Maven options that we want to apply each time we run a Maven command. All options can be defined in the file maven.config in a .mvn directory in the root of our project. Each option must be defined on a new line. This directory and file can be added to our source control so that all users that have access to the repository will use the same Maven options.

In the following example we have a file .mvn/maven.config where we want to show Maven version information for each run, use a thread per CPU core, always try to build all modules and fail at the end if there are failures and finally we set user properties that are used by the Maven compile plugin:

--show-version
--threads=1C
--fail-at-end
-Dmaven.compiler.source=17
-Dmaven.compiler.target=17
-Dencoding=UTF-8

With the above configuration we can see that options are applied when we run the verify command:

$ mvn verify
...
Apache Maven 3.9.1 (2e178502fcdbffc201671fb2537d0cb4b4cc58f8)
Maven home: /Users/mrhaki/.sdkman/candidates/maven/current
Java version: 19.0.2, vendor: Eclipse Adoptium, runtime: /Users/mrhaki/.sdkman/candidates/java/19.0.2-tem
Default locale: en_NL, platform encoding: UTF-8
OS name: "mac os x", version: "13.2.1", arch: "x86_64", family: "mac"
...
[INFO] Scanning for projects...
[INFO]
[INFO] Using the MultiThreadedBuilder implementation with a thread count of 12
...
$

Written with Maven 3.9.1.

March 30, 2023

Spocklight: Creating Temporary Files And Directories With FileSystemFixture

If we write specification where we need to use files and directories we can use the @TempDir annotation on a File or Path instance variable. By using this annotation we make sure the file is created in the directory defined by the Java system property java.io.tmpdir. We could overwrite the temporary root directory using Spock configuration if we want, but the default should be okay for most situations. The @TempDir annotation can actually be used on any class that has a constructor with a File or Path argument. Since Spock 2.2 we can use the FileSystemFixture class provided by Spock. With this class we have a nice DSL to create directory structures and files in a simple matter. We can use the Groovy extensions to File and Path to also immediately create contents for the files. If we want to use the extensions to Path we must make sure we include org.apache.groovy:groovy-nio as dependency to our test runtime classpath. The FileSystemFixture class also has the method copyFromClasspath that we can use to copy files and their content directory into our newly created directory structure.

In the following example specification we use FileSystemFixture to define a new directory structure in a temporary directory, but also in our project directory:

package mrhaki

import spock.lang.Specification
import spock.lang.Subject
import spock.lang.TempDir
import spock.util.io.FileSystemFixture

import java.nio.file.Path
import java.nio.file.Paths

class FileFixturesSpec extends Specification {

    /**
     * Class we want to test. The class has a method
     * File renderDoc(File input, File outputDir) that takes
     * an input file and stores a rendered file in the given
     * output directory.
     */
    @Subject
    private DocumentBuilder documentBuilder = new DocumentBuilder()

    /**
     * With the TempDir annotation we make sure our directories and
     * files created with FileSystemFixture are deleted after
     * each feature method run.
     */
    @TempDir
    private FileSystemFixture fileSystemFixture

    void "convert document"() {
        given:
        // Create a new directory structure in the temporary directory
        // <root>
        //  +-- src
        //  |    +-- docs
        //  |         +-- input.adoc
        //  |         +-- convert.adoc
        //  +-- output
        fileSystemFixture.create {
            dir("src") {
                dir("docs") {
                    // file(String) returns a Path and with
                    // groovy-nio module on the classpath we can use
                    // extensions to add text to file. E.g. via the text property.
                    file("input.adoc").text = '''\
                    = Sample

                    Welcome to *AsciidoctorJ*.
                    '''.stripIndent()

                    // Copy file from classpath (src/test/resources)
                    // and rename it at the same time.
                    // Without rename it would be
                    // copyFromClasspath("/samples/sample.adoc")
                    copyFromClasspath("/samples/sample.adoc", "convert.adoc")
                }
            }
            dir("output")
        }

        and:
        // Using resolve we get the actual Path to the file
        Path inputDoc = fileSystemFixture.resolve("src/docs/input.adoc")
        Path convertDoc = fileSystemFixture.resolve("src/docs/convert.adoc")

        // We can also use Paths to resolve the actual Path.
        Path outputDir = fileSystemFixture.resolve(Paths.get("output"))

        when:
        File resultDoc = documentBuilder.renderDoc(inputDoc.toFile(), outputDir.toFile())

        then:
        resultDoc.text =~ "<p>Welcome to <strong>AsciidoctorJ</strong>.</p>"

        when:
        File resultConvert = documentBuilder.renderDoc(convertDoc.toFile(), outputDir.toFile())

        then:
        resultConvert.text =~ "<p>Testing <strong>AsciidoctorJ</strong> with Spock 🚀</p>"
    }

    void "convert document from non-temporary dir"() {
        given:
        // Create FileSystemFixture in our project build directory.
        FileSystemFixture fileSystemFixture = new FileSystemFixture(Paths.get("build"))
        fileSystemFixture.create {
            dir("test-docs") {
                dir("src") {
                    dir("docs") {
                        copyFromClasspath("/samples/sample.adoc")
                    }
                }
                dir("output")
            }
        }

        and:
        Path convertDoc = fileSystemFixture.resolve("test-docs/src/docs/sample.adoc")
        Path outputDir = fileSystemFixture.resolve(Paths.get("test-docs/output"))

        when:
        File resultDoc = documentBuilder.renderDoc(convertDoc.toFile(), outputDir.toFile())

        then:
        resultDoc.text =~ "<p>Testing <strong>AsciidoctorJ</strong> with Spock 🚀</p>"

        cleanup:
        // We can delete the test-docs directory ourselves.
        fileSystemFixture.resolve("test-docs").deleteDir()
    }
}

In order to use the Groovy extensions for java.nio.Path we must add the groovy-nio module to the test classpath. For example we can do this if we use Gradle by using the JVM TestSuite plugin extension:

plugins {
    java
    groovy
}

...

repositories {
    mavenCentral()
}

testing {
    suites {
        val test by getting(JvmTestSuite::class) {
            // Define we want to use Spock.
            useSpock("2.3-groovy-4.0")
            dependencies {
                // Add groovy-nio module.
                implementation("org.apache.groovy:groovy-nio")
            }
        }
    }
}

Written with Spock 2.3-groovy-4.0 and Gradle 8.0.2.

March 26, 2023

Mastering Maven: Setting Default JVM Options Using jvm.config

In order to add default JVM options to our Maven mvn command we can define an environment variable MAVEN_OPTS. But we can also create a file jvm.config in the directory .mvn in our project root directory. On each line we define a Java option we want to apply. We can specify JVM options, but also Java system properties we want to apply each time we run the mvn command. This directory and file can be added to our source control so that all users that have access to the repository will use the same JVM options.

The following example defines some Java system properties to change the Maven logging. We also add the JVM option --show-version, so that each time we run mvn we also see the version of Java that is used to run our build. We create the file .mvn/jvm.config and the following content:

-Dorg.slf4j.simpleLogger.showDateTime=true
-Dorg.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss
-Dorg.slf4j.simpleLogger.showThreadName=true
--show-version

Now when we run mvn we see in the output the Java version displayed and each log line has a time and thread name:

$ mvn verify
openjdk 19.0.2 2023-01-17
OpenJDK Runtime Environment Temurin-19.0.2+7 (build 19.0.2+7)
OpenJDK 64-Bit Server VM Temurin-19.0.2+7 (build 19.0.2+7, mixed mode, sharing)
22:12:35 [main] [INFO] Scanning for projects...
...
22:12:39 [main] [INFO] ------------------------------------------------------------------------
22:12:39 [main] [INFO] BUILD SUCCESS
22:12:39 [main] [INFO] ------------------------------------------------------------------------
22:12:39 [main] [INFO] Total time:  3.844 s
22:12:39 [main] [INFO] Finished at: 2023-03-26T22:12:39+02:00
22:12:39 [main] [INFO] ------------------------------------------------------------------------
$

Written with Maven 3.9.1