Search

Dark theme | Light theme

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.

March 10, 2021

Gradle Goodness: Add Support For "Scratch" Files To Java Project

When working on a Java project, we might want to have a place where we can just play around with the code we write. We need a "scratch" file where we can access the Java classes we write in our main sourceset. The scratch file is actually a Java source file with a main method where we can create instances of the Java code we write and invoke methods on them. This gives back a fast feedback loop, and we can use it to play around with our Java classes without the need to write a test for it. It gives great flexiblity during development. We must make sure the scratch file will not be packed in the JAR file with our production code.

To support this in our Gradle build file we can add a new sourceset that can access all classes we write in the main sourceset. Also we want to have new configurations for this sourceset so we can add dependencies that are only used by our scratch file. And finally we want a new task to run our scratch file. By default our scratch file will not be part of the JAR file with the classes from the main sourceset.

In the following example build script we first define the common configuration for a Java project with a dependency on the Log4j2 library. Notice we use the toolchain feature of Gradle to use Java 15 to compile and run our Java code. Using the toolchain definition Gradle will look for a Java 15 JDK on our computer and if it cannot find one can even download it automatically.

Next we define a new sourceset dev so we can create a Scratch.java file in the directory src/dev/java and we define the compile and runtime classpath to be dependent on the main source set output. As a bonus we also can use the src/dev/resources directory for resource files we want to have in the classpath when we run our Scratch.java file.

If we want to define dependencies that are only used by our Scratch class file we must add extra configurations: devImplementation and devRuntimeOnly. These configurations extend from the implementation and runtimeOnly configurations added by the java-library plugin. So all dependencies needed by classes in the main sourceset will also be available in the configurations for the dev sourceset.

Finally, we add a new task runDev that executes the main method in the Scratch.java file in the src/dev/java directory.

// File: build.gradle.kts
plugins {
    `java-library`
}

repositories {
    mavenCentral()
}

dependencies {
    implementation(platform("org.apache.logging.log4j:log4j-bom:2.14.0"))
    implementation("org.apache.logging.log4j:log4j-api")
    implementation("org.apache.logging.log4j:log4j-core")
}

java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(15))
    }
}

//------------------------------------------------------------------------------
// Configure "dev" sourceset for running Scratch class
//------------------------------------------------------------------------------

// Create new dev sourceset with a compile and runtime classpath dependency
// on the main sourceset. This allows us to use the classes we create in
// the main sourceset in our dev sourceset.
// The directories src/dev/java and src/dev/resources are recognized
// this sourceset.
val dev: SourceSet by sourceSets.creating {
    compileClasspath += sourceSets.main.get().output
    runtimeClasspath += sourceSets.main.get().output
}

// Create implementation and runtimeOnly configurations for the dev sourceset.
// These configurations can be used to define dependencies that only
// apply for the source files in the dev sourceset.
val devImplementation: Configuration by configurations.getting {
    extendsFrom(configurations.implementation.get())
}
val devRuntimeOnly: Configuration by configurations.getting {
    extendsFrom(configurations.runtimeOnly.get())
}

// Create a new task "runDev" that will run the compiled Scratch.java file
// in the root of src/dev/java. The classpath will contains all dependencies
// from the devImplementation and devRuntimeOnly configurations.
val runDev by tasks.registering(JavaExec::class) {
    description = "Run Scratch file."
    group = "dev"
    classpath = dev.runtimeClasspath
    mainClass.set("Scratch")
}

dependencies {
    // Here we add an extra dependency only for the dev sourceset.
    devImplementation("org.apache.commons:commons-lang3:3.12.0")
}

Now we have our build file with scratch file support so it is time to have some sample code.

First we create a simple Java file in our main sourceset together with a Log4j2 configuration properties file:

// File: src/main/java/mrhaki/Sample.java
package mrhaki;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Sample {
    private static Logger log = LogManager.getFormatterLogger(Sample.class);

    public String sayHello(String name) {
        log.info("sayHello(name=%s)", name);
        return "Hello %s".formatted(name);
    }
}
# File: src/main/resource/log4j2.properties
appender.console.type=Console
appender.console.name=STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %m%n

rootLogger.level=ERROR
rootLogger.appenderRef.stdout.ref=STDOUT

To play around with our Sample class we add a scratch file and also an extra Log4j2 configuration properties file to change the configuration when we run our scratch file:

// File: src/dev/java/Scratch.java
import mrhaki.Sample;
import org.apache.commons.lang3.SystemUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Scratch {
    private static Logger log = LogManager.getFormatterLogger(Scratch.class);

    public static void main(String[] args) {
        log.info("Running dev with Java %s.", SystemUtils.JAVA_VERSION);
        Sample sample = new Sample();
        sample.sayHello("mrhaki");
    }
}
# File: src/dev/resources/log4j2.properties
rootLogger.level=DEBUG

To execute our scratch file we invoke the runDev task from the command-line:

$ gw runDev

> Task :runDev
Running dev with Java 15.0.2.
sayHello(name=mrhaki)

BUILD SUCCESSFUL in 1s
5 actionable tasks: 5 executed

Written with Gradle 6.8.3.