Search

Dark theme | Light theme

October 26, 2021

Spocklight: Adjusting Time With MutableClock

Testing classes that work with date calculations based on the current date and time (now) can be difficult. First of all we must make sure our class under test accepts a java.time.Clock instance. This allows us to provide a specific Clock instance in our tests where we can define for example a fixed value, so our tests don't break when the actual date and time changes. But this can still not be enough for classes that will behave different based on the value returned for now. The Clock instances in Java are immutable, so it is not possible to change the date or time for a Clock instance.

In Spock 2.0 we can use the new MutableClock class in our specifications to have a Clock that can be used to go forward or backward in time on the same Clock instance. We can create a MutableClock and pass it to the class under test. We can test the class with the initial date and time of the Clock object, then change the date and time for the clock and test the class again without having to create a new instance of the class under test. This is handy in situations like a queue implementation, where a message delivery date could be used to see if messages need to be delivered or not. By changing the date and time of the clock that is passed to the queue implementation we can write specifications that can check the functionality of the queue instance.

The MutableClock class has some useful methods to change the time. We can for example use the instant property to assign a new Instant. Or we can add or subtract a duration from the initial date and time using the + and - operators. We can specify the temporal amount that must be applied when we use the ++ or -- operators. Finally, we can use the adjust method with a single argument closure to use a TemporalAdjuster to change the date or time. This last method is useful if we want to specify a date adjustment that is using months and years.

In the following example Java code we have the class WesternUnion that accepts letters with a delivery date and message. The letter is stored and when the deliver method is we remove the letter from our class if the delivery date is after the current date and time.

package mrhaki;

import java.time.Clock;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

/**
 * Class to mimic a letter delivery service, 
 * that should deliver letters after the letter
 * delivery data has passed.
 */
public class WesternUnion {
    private final Clock clock;
    private final List<Letter> letters = new ArrayList<>();

    /**
     * Default constructor that uses a default Clock with UTC timezone.
     */
    public WesternUnion() {
        this(Clock.systemUTC());
    }

    /**
     * Constructor that accepts a clock, very useful for testing.
     * 
     * @param clock Clock to be used in this class for date calculations.
     */
    WesternUnion(final Clock clock) {
        this.clock = clock;
    }

    /**
     * Store accepted letter.
     * 
     * @param deliveryDate Date the letter should be deliverd.
     * @param message Message for the letter.
     */
    public int acceptLetter(Instant deliveryDate, String message) {
        final Letter letter = new Letter(deliveryDate, message);
        letters.add(letter);
        return letters.size();
    }

    /**
     * "Deliver" letters where the delivery date has passed.
     */
    public int deliver() {
        Instant now = Instant.now(clock);
        letters.removeIf(letter -> letter.getDeliveryDate().isBefore(now));
        return letters.size();
    }

    /**
     * Simple record for a "letter" which a delivery date and message.
     */
    private record Letter(Instant deliveryDate, String message) {
        private Instant getDeliveryDate() {
            return deliveryDate;
        }
    }
}

In order to test this class we want pass a Clock instance where we can mutate the clock, so we can test if the deliver method works if we invoke it multiple times. In the next specification we test this with different usages of MutableClock. Each specification method uses the MutableClock in a different way to test the WesternUnion class:

package mrhaki

import spock.lang.Specification
import spock.lang.Subject
import spock.util.time.MutableClock

import java.time.Duration
import java.time.Instant
import java.time.Period
import java.time.ZoneId
import java.time.ZonedDateTime

class WesternUnionSpec extends Specification {

    // Default time zone used for letter dates.
    private static final ZoneId TZ_HILL_VALLEY = ZoneId.of("America/Los_Angeles")
    
    // Constants used for letter to be send.
    private static final Instant DELIVERY_DATE = 
        ZonedDateTime
            .of(1955, 11, 12, 0, 0, 0, 0, TZ_HILL_VALLEY)
            .toInstant()
    private static final String MESSAGE = 
      'Dear Marty, ... -Your friend in time, "Doc" Emmett L. Brown'

    @Subject
    private WesternUnion postOffice

    void "deliver message after delivery date"() {
        given: "Set mutable clock instant to Sept. 1 1885"
        def clock = new MutableClock(
          ZonedDateTime.of(1885, 9, 1, 0, 0, 0, 0, TZ_HILL_VALLEY))

        and:
        postOffice = new WesternUnion(clock)

        expect:
        postOffice.acceptLetter(DELIVERY_DATE, MESSAGE) == 1

        when:
        int numberOfLetters = postOffice.deliver()

        then: "Delivery date has not passed so 1 letter"
        numberOfLetters  == 1

        when: "Move to delivery date of letter + 1 day and try to deliver"
        // We can change the clock's Instant property directly to 
        // change the date.
        clock.instant = 
            ZonedDateTime
                .of(1955, 11, 13, 0, 0, 0, 0, TZ_HILL_VALLEY)
                .toInstant()
        int newNumberOfLetters = postOffice.deliver()

        then: "Delivery date has passed now so 0 letters left"
        newNumberOfLetters == 0
    }
    
    void "deliver message after adjusting MutableClock using adjust"() {
        given: "Set mutable clock instant to Sept. 1 1885"
        def clock = new MutableClock(
            ZonedDateTime.of(1885, 9, 1, 0, 0, 0, 0, TZ_HILL_VALLEY))
        
        and:
        postOffice = new WesternUnion(clock)

        expect:
        postOffice.acceptLetter(DELIVERY_DATE, MESSAGE) == 1

        when:
        int numberOfLetters = postOffice.deliver()
        
        then: "Delivery date has not passed so 1 letter"
        numberOfLetters  == 1

        when: "Move clock forward 70 years, 2 months and 12 days and try to deliver"
        // To move the clock forward or backward by month or years we need
        // the adjust method. The plus/minus/next/previous methods are applied
        // to the Instant property of the MutableClock and with Instant
        // we can not use months or years.
        clock.adjust {t -> t + (Period.of(70, 2, 12)) }
        int newNumberOfLetters = postOffice.deliver()
        
        then: "Delivery date has passed now so 0 letters left"
        newNumberOfLetters == 0
    }

    void "deliver message after adding amount to MutableClock"() {
        given: "Set mutable clock instant to Oct. 26 1955"
        def clock = new MutableClock(
            ZonedDateTime.of(1955, 10, 26, 0, 0, 0, 0, TZ_HILL_VALLEY))

        and:
        postOffice = new WesternUnion(clock)

        expect:
        postOffice.acceptLetter(DELIVERY_DATE, MESSAGE) == 1

        when:
        int numberOfLetters = postOffice.deliver()

        then: "Delivery date has not passed so 1 letter"
        numberOfLetters  == 1

        and: "Move clock forward by given amount (18 days) and try to deliver"
        // The +/- operators are mapped to plus/minus methods. 
        // Amount cannot be months or years, then we need the adjust method.
        clock + Duration.ofDays(18)

        when: "Try to deliver now"
        int newNumberOfLetters = postOffice.deliver()

        then: "Delivery date has passed now so 0 letters left"
        newNumberOfLetters == 0
    }
    
    void "deliver message with fixed change amount"() {
        given: "Set mutable clock instant to Oct. 26 1955"
        def defaultTime = 
            ZonedDateTime.of(1955, 10, 26, 0, 0, 0, 0, TZ_HILL_VALLEY)
        // We can set the changeAmount property of MutableClock 
        // in the constructor or by setting the changeAmount property. 
        // Now when we invoke next/previous (++/--)
        // the clock moves by the specified amount.
        def clock = 
            new MutableClock(
                defaultTime.toInstant(), 
                defaultTime.zone, 
                Duration.ofDays(18))

        and:
        postOffice = new WesternUnion(clock)

        expect:
        postOffice.acceptLetter(DELIVERY_DATE, MESSAGE) == 1

        when:
        int numberOfLetters = postOffice.deliver()

        then: "Delivery date has not passed so 1 letter"
        numberOfLetters  == 1

        and: "Move clock forward by given amount (18 days) and try to deliver"
        // The ++/-- operators are mapped to next/previous. 
        // Amount cannot be months or years, then we need the adjust method.
        clock++

        when: "Try to deliver now"
        int newNumberOfLetters = postOffice.deliver()

        then: "Delivery date has passed now so 0 letters left"
        newNumberOfLetters == 0
    }
}

Written with Spock 2.0.

September 27, 2021

Clojure Goodness: Create All Parent Directories For A File

The Clojure namespace clojure.java.io contains useful functions to work with files. One of those functions is make-parents. We can pass a java.io.File instance as arguments or use the same arguments that can be passed to the file function that is also in this namespace. The function will create all parent directories for the file. The return result is true if the directories are created (they didn't exist before) and false when the directories didn't have to be created (already exist).

In the following example we see an example of usage of the make-parents function:

(ns mrhaki.io.make-parents
  (:require [clojure.java.io :refer [make-parents file]]
            [clojure.test :refer [is]]))

;; make-parents will create the parents directories for a file 
;; The function returns true if the directories are created, 
;; false if the directories already exist.
(let [file (file "tmp" "clojure" "sample.txt")]
  (is (true? (make-parents file)) "All parent directories are created")
  (is (false? (make-parents file)) "Second time the directory already exists"))

;; make-parents uses the same arguments as the clojure.java.io.file function
(is (true? (make-parents "tmp" "clj" "sample.txt")) "Directories tmp/clj are created")

Written with Clojure 1.10.3.

September 8, 2021

PlantUML Pleasantness: Change Look-and-feel With Themes

When we convert our PlantUML diagrams we get a default look-and-feel for our diagrams that is mostly red and yellow based. We can alter the style and color of individual elements using the skinparam command. But we can also apply so-called themes to our diagram that will alter the look-and-feel of our diagram with a simple theme reference.

We can define the name of the theme in our diagram markup using the !theme directive. Or we can use the command-line option -theme when we generate a diagram from our markup. We can even combine both methods where the end result is also a combination of both themes.

Let's start with simple PlantUML markup to test some themes:

@startuml

' Elements
actor "Application User" as User
[Mail server] as Mail <<Mail>>

package "Sample Application" {
    [Controller] <<Spring REST controllers>>
    [Service] <<Spring service>>
}

' Connections
User --> Controller
Controller --> Service
Service --> Mail

@enduml

Without themes we get the following diagram with the default look-and-feel:

First we use the !theme directive and apply the cerulean theme. Later we will how we can check which themes are part of your PlantUML version.

@startuml

' Set theme
!theme cerulean

' Elements
actor "Application User" as User
[Mail server] as Mail <<Mail>>

package "Sample Application" {
    [Controller] <<Spring REST controllers>>
    [Service] <<Spring service>>
}

' Connections
User --> Controller
Controller --> Service
Service --> Mail

@enduml

The resulting diagram now has a different look-and-feel:

Instead of adding the !theme directive we can also the command-line tool for PlantUML and specify a theme with the option -theme. For our example we could run $ plantuml diagram.puml -theme crt-green and we get the following result:

If the PlantUML markup contains the !theme directive and we use the command-line -theme option the end result will be a combination. For example if we set the cerulean with the !theme directive and specify sketchy-outline with the -theme option we get:

If we want to know which themes are included with our PlantUML version we can use the command help themes in our PlantUML markup and the resulting diagram shows all themes available.

@startuml
help themes
@enduml

For an overview of themes we can check out the theme gallery.
More information is also available in the PlantUML documentation.

Written with PlantUML 1.2021.10.

August 30, 2021

Turn Backspace Key Into Delete On MacOS

Simply hold the fn key while pressing the backspace key to turn the backspace into a Del key. Depending on the position of the cursor in a piece of text we can simply choose to either use backspace or Del.

March 24, 2021

Java Joy: Run Action When Optional Value Present Or Not

If we have an Optional instance we can consume the value if it is present using the ifPresent method. Since Java 9 the method ifPresentOrElse has been added to the Optional class. The first argument is of type Consumer and is invoked when there is an optional value. The second argument is of type Runnable and is executed when the the optional is empty. The method in the Consumer and Runnable implementations does not return a type but returns void. Therefore we should use ifPresentOrElse when we need a conditional side effect for an Optional instance.

In the following example we have a method handleName that will update a list if an optional value is present or increases a counter when the optional value is empty:

package mrhaki.optional;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

public class IfPresentOrElse {

    // Store names.
    private static List<String> names = new ArrayList<>();
    
    // Counter for counting empty or null names.
    private static AtomicInteger emptyCounter = new AtomicInteger();

    public static void main(String[] args) {
        // Optional name property will have a value.
        handleName(new User("mrhaki"));

        assert "MRHAKI".equals(names.get(0));
        assert 0 == emptyCounter.get();
        
        // Reset properties
        names.clear();
        emptyCounter.set(0);
        
        // Optional name property is empty.
        handleName(new User());

        assert names.isEmpty();
        assert 1 == emptyCounter.get();
    }

    private static void handleName(User user) {
        user.getName().ifPresentOrElse(
                // If present add name in uppercase to names.
                s -> names.add(s.toUpperCase(Locale.ROOT)),
                // If not present increment the counter.
                emptyCounter::incrementAndGet);
    }

    // Simple class with an optional property name.
    private static class User {
        private final String name;

        private User() {
            this(null);
        }

        private User(final String name) {
            this.name = name;
        }

        Optional<String> getName() {
            return Optional.ofNullable(name);
        }
    }
}

Written with Java 16.

March 22, 2021

Clojure Goodness: Turning Map Values To Map Keys

In the clojure.set namespace we can find the function map-invert. This function returns a new map where the values are keys with the appropriates keys of the original map assigned as value. If the original map has duplicate values than the latest key for the duplicate value will be the value of the new key.

In the following example code we see the result of using map-invert:

(ns mrhaki.set.map-invert
  (:require [clojure.set :refer [map-invert]]
            [clojure.test :refer [is]]))

(is (= {"mrhaki" :alias "Clojure" :language}
       (map-invert {:alias "mrhaki" :language "Clojure"})))

;; With duplicate values only one will be key.
(is (= {1 :c 2 :b 3 :d}
       (map-invert {:a 1 :b 2 :c 1 :d 3})))

Written with Clojure 1.10.1.

March 19, 2021

Java Joy: Getting Multiple Results From One Stream With Teeing Collector

If we want to get two types of information from a Stream of objects we can consume the Stream twice and collect the results. But that is not very efficient, especially when the stream has a lot of objects. Since Java 12 we can use the teeing method of the java.util.stream.Collectors class to get multiple results while consuming the stream of objects only once. The teeing method takes two collectors as argument each returning a separate result for the stream items. As third argument we must pass a function that will merge the results of the two collectors into a new object.

In the following code we have two example use cases that use the teeing method to get multiple results while consuming a Stream of objects only one time:

package mrhaki.stream;

import java.util.List;
import java.util.Locale;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;

public class Teeing {
    public static void main(String[] args) {
        // Simple list of language names.
        List<String> languageNames = List.of("Java", "Clojure", "Kotlin", "Groovy", "Scala");

        // Predicate for String value that has the letter j or J.
        Predicate<String> containsJ = s -> s.toLowerCase(Locale.ROOT).contains("j");

        // Collector to apply two filters for one stream process and combine the result.
        Collector<String, ?, List<List<String>>> splitOnLetterJ =
                Collectors.teeing(
                        // Filter on language name with a j or J.
                        Collectors.filtering(containsJ, Collectors.toList()),
                        // Filter on language name withtout a j or J.
                        Collectors.filtering(containsJ.negate(), Collectors.toList()),
                        // Combine both results into a  new List with two items.
                        (withJ, withoutJ) -> List.of(withJ, withoutJ));

        List<List<String>> split = languageNames.stream().collect(splitOnLetterJ);

        assert List.of("Java", "Clojure").equals(split.get(0));
        assert List.of("Kotlin", "Groovy", "Scala").equals(split.get(1));


        // List of language records with a name and 
        // boolean to indicate the language is dynamic or not. 
        List<Language> languages =
                List.of(new Language("Clojure", true),
                        new Language("Java", false),
                        new Language("Groovy", true),
                        new Language("Scala", false),
                        new Language("Kotlin", false));

        // Filter for dynamic languages and transform to list.
        Collector<Language, ?, List<Language>> filteringDynamicLanguages =
                Collectors.filtering(Language::dynamic, Collectors.toUnmodifiableList());
        
        // Make a list with the language names in upper case.
        Collector<Language, ?, List<String>> upperCaseLanguageNames =
                Collectors.mapping(language -> language.name.toUpperCase(Locale.ROOT),
                                   Collectors.toUnmodifiableList());

        // Function to merge both list into a list with first item the result
        // of filteringDynamicLanguages and the second item the resul
        // of upperCaseLanguageNames.
        final BiFunction<List<Language>, List<String>, List<List<?>>> mergeLists =
                (dynamicLanguages, upperCaseLanguages) -> List.of(dynamicLanguages, upperCaseLanguages);

        List<List<?>> result = languages
                .stream()
                .collect(
                        Collectors.teeing(
                                filteringDynamicLanguages,
                                upperCaseLanguageNames,
                                mergeLists));

        assert List.of(new Language("Clojure", true), new Language("Groovy", true)).equals(result.get(0));
        assert List.of("CLOJURE", "JAVA", "GROOVY", "SCALA", "KOTLIN").equals(result.get(1));
    }

    // Record to store language name and if the language is dynamic.
    record Language(String name, boolean dynamic) {}
}

Written with Java 16.

March 18, 2021

Clojure Goodness: Create New Instance Of Java Class

Working with Java classes from Clojure code is easy. If we want to invoke methods or access fields on instances of Java classes we must first create an instance by invoking the constructor. In Clojure we can do that using the special form new or using a dot (.) notation. The new special form has the class name of the Java class we want to create an instance of as argument followed by arguments for the Java class constructor. With the dot notation we place a . after the class name followed by arguments for the class constructor to create a new instance.

In the following example we see several ways to create an instance of a Java class.

(ns mrhaki.java.new-instance
  (:require [clojure.test :refer [is]])
  (:import (java.net URI)
           (java.util Map TreeMap)))

;; Using a dot after the class name to invoke the constructor.
(is (instance? String (String.)))

;; Or using the new special form to invoke the constructor.
(is (instance? String (new String)))

;; Constructor arguments can be used.
(is (instance? URI (URI. "https://www.mrhaki.com")))
(is (instance? URI (new URI "https" "www.mrhaki.com" "/" "")))

;; We can use Clojure data structures in constructors.
(is (instance? Map (TreeMap. {:language "Clojure"})))

Written with Clojure 1.10.1.

March 16, 2021

Gradle Goodness: Create Properties File With WriteProperties Task

If we need to create a Java properties file in our build we could create a custom task and use the Properties class to store a file with properties. Instead of writing our custom task we can use the task WriteProperties that is already part of Gradle. Using this task Gradle will not add a timestamp in the comment to the properties file. Also the properties are sorted by name, so we know the properties are always in the same order in the output file. Finally, a fixed line separator is used, which is \n by default, but can be set via the task property lineSeparator.

To define the properties that need to be in the output file we can use the property method for a single property, or we can use properties method with a Map argument to set multiple properties.

In the following build file we define a new task projectProps of task type WriteProperties and we use task output as dependency for the processResources task. This way our new task will be executed, if there are changes, when the processResources task is executed:

// File: build.gradle.kts
plugins {
    java
}

version = "1.0.0"

tasks {
    val projectProps by registering(WriteProperties::class) {
        description = "Write project properties in a file."

        // Set output file to build/project.properties
        outputFile = file("${buildDir}/project.properties")
        // Default encoding is ISO-8559-1, here we change it.
        encoding = "UTF-8"
        // Optionally we can specify the header comment.
        comment = "Version and name of project"

        // Define property.
        property("project.version", project.version)

        // Define properties using a Map.
        properties(mapOf("project.name" to project.name))
    }

    processResources {
        // Depend on output of the task to create properties,
        // so the properties file will be part of the Java resources.
        from(projectProps)
    }
}

When we invoke the classes task we can see that our new task is executed:

$ gradle --console plain classes
> Task :compileJava UP-TO-DATE
> Task :projectProps
> Task :processResources
> Task :classes

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

$ cat build/project.properties
#Version and name of project
project.name=write-properties-sample
project.version=1.0.2

Without any changes we see our task was up-to-date and doesn’t have to run:

$ gradle --console plain classes
> Task :compileJava UP-TO-DATE
> Task :projectProps UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE

BUILD SUCCESSFUL in 596ms
3 actionable tasks: 3 up-to-date

Written with Gradle 6.8.3

March 11, 2021

Java Joy: Formatting A String Value With Formatted Method

Java 15 introduced the multi-line string value referred to as a text block. With this introduction also the formatted method was added to the String class. The method can be invoked on a String value directly and function exactly as the static String.format method. The nice thing is that now we directly can use a method on the value instead of having to use a static method where the value is passed as argument.

In the following example we use the formatted method for a normal String value and a text block:

package mrhaki.string;

public class Formatted {
    public static void main(String[] args) {
        String text = "Hi, %s, thank you for reading %d blogs".formatted("mrhaki", 2);
        assert "Hi, mrhaki, thank you for reading 2 blogs".equals(text);

        String email = """
        Hello %s,

        thank you for reading %d blogs.
        """.formatted("Hubert", 2);

        assert """
                Hello Hubert,

                thank you for reading 2 blogs.
                """.equals(email);
    }
}

Written with Java 15.