Search

Dark theme | Light theme

December 20, 2016

Gradle Goodness: Run Task Ignoring Up-to-date Checks

Gradle builds are fast because Gradle supports incremental tasks. This means Gradle can determine if input or output of task has changed, before running the task. If nothing has changed a task is marked a up-to-date and the task is not executed, otherwise the task is executed. If we want execute a task even if it is up-to-date we must use the command line option --rerun-tasks.

In the following example we run the assemble task for a simple Java project, and we see all tasks are executed. When we invoke the assemble task again we see the tasks are all up-to-date:

$ gradle assemble

:compileJava
:processResources
:classes
:jar
:assemble

BUILD SUCCESSFUL

Total time: 1.765 secs
$ gradle assemble

:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:assemble UP-TO-DATE

BUILD SUCCESSFUL

Total time: 0.715 secs
$

To run all tasks without an up-to-date check we use the option --rerun-tasks:

$ gradle --rerun-tasks assemble
:compileJava
:processResources
:classes
:jar
:assemble

BUILD SUCCESSFUL

Total time: 1.037 secs
$

Written with Gradle 3.2.1.

December 19, 2016

Spring Sweets: Add (Extra) Build Information To Info Endpoint

With Spring Boot Actuator we get some useful endpoints in our application to check on our application when it is running. One of the endpoints is the /info endpoint. We can add information about our application if Spring Boot finds a file META-INF/build-info.properties in the classpath of our application. With the Gradle Spring Boot plugin we can generate the build-info.properties file. When we apply the Gradle Spring Boot plugin to our project we get a Gradle extension springBoot in our build file. With this extension we can configure Spring Boot for our project. To generate project information that is used by the /info endpoint we must add the method statement buildInfo() inside the springBoot extension. With this method statement the Gradle Spring Boot plugin generates a file build/main/resources/META-INF/build-info.properties..

// File: build.gradle
plugins {
    id 'org.springframework.boot' version '1.4.2.RELEASE'
}
...
springBoot {
    // This statement tells the Gradle Spring Boot plugin
    // to generate a file 
    // build/resources/main/META-INF/build-info.properties
    // that is picked up by Spring Boot to display
    // via /info endpoint.
    buildInfo()
}
...

Let's run our application and send a request for /info:

$ http -b localhost:8080/info
{
    "build": {
        "artifact": "spring-boot-sample",
        "group": "mrhaki.spring",
        "name": "sample-mrhaki",
        "time": 1482139076000,
        "version": "0.3.0"
    }
}
$

To override the default properties or to add new properties we must provide a configuration closure to the buildInfo method. If we a built-in key as the name of the property it is overridden with a new value, otherwise the key is added as a new property. In the following example we add some extra properties and override the properties time and name:

// File: build.gradle
...
springBoot {
    buildInfo {
        // Generate extra build info.
        additionalProperties = [
                by: System.properties['user.name'],
                operatingSystem: "${System.properties['os.name']} (${System.properties['os.version']})",
                continuousIntegration: System.getenv('CI') ? true: false,
                machine: InetAddress.localHost.hostName,
                // Override buildInfo property time
                time: buildTime(),
                // Override name property
                name: 'sample-springboot-app'
        ]
    }
}

def buildTime() {
    final dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ")
    dateFormat.timeZone = TimeZone.getTimeZone('GMT')
    dateFormat.format(new Date())
}
...

We restart the application and invoke the /info endpoint to get more results for the build:

$ http -b localhost:8080/info
{
    "build": {
        "artifact": "spring-boot-sample",
        "by": "mrhaki",
        "continuousIntegration": "false",
        "group": "mrhaki.spring",
        "machine": "mrhaki-laptop-2015.local",
        "name": "sample-springboot-app",
        "operatingSystem": "Mac OS X (10.12.2)",
        "time": "2016-12-19 09:16:50+0000",
        "version": "0.3.0"
    }
}
$

Written with Spring Boot 1.4.2.RELEASE.

Spring Sweets: Add Git Info To Info Endpoint

With Spring Boot Actuator we get some endpoints that display information about our application. One of the endpoints is the /info endpoint. If our project uses Git we can add information about Git to the /info endpoint. By default Spring Boot will look for a file git.properties in the classpath of our application. The file is a Java properties file with keys that start with git. and have values like the branch name, commit identifier and commit message. Spring Boot uses this information and when we request the /info endpoint we get a response with the information. This can be very useful to check the Git information that was used to build the application. To create the git.properties file we can use a Gradle (or Maven) plugin that will do the work for us.

In the following example we use the Gradle plugin to generate the git.properties file for our project. The Gradle Git properties plugin is added in the plugins configuration block. The plugin adds a Gradle extension gitProperties that can be used to customize the output in git.properties. We could even change the location, but we keep it to the default location which is build/resources/main/git.properties.

// File: build.gradle
plugins {
    id 'org.springframework.boot' version '1.4.2.RELEASE'
    // Add Git properties plugin.
    id 'com.gorylenko.gradle-git-properties' version '1.4.17'
}

// Customize Git properties plugin.
gitProperties {
    // Change date format in git.properties file.
    dateFormat = "yyyy-MM-dd HH:mm:ssZ"
    dateFormatTimeZone = 'GMT'
}

That is all we need to do. The Git properties plugin generates the git.properties file that is need by Spring Boot. Let's run our application and send a request for the /info endpoint:

$ http -b localhost:8080/info
{
    "git": {
        "branch": "feature/dsl-hapi",
        "commit": {
            "id": "511d269",
            "time": "2016-12-15 12:28:20+0000"
        }
    }
}
$

We can even show more information by changing the Spring Boot configuration property management.git.info.mode. The default value is SIMPLE, but we can also use the value FULL. Then we get the Git commit message and user details. We can set the configuration property in different ways for our Spring Boot application. For example in application.yml, Java system property or environment variable. In our example we add a Java system property to the Gradle bootRun task:

// File: build.gradle
...
bootRun {
    systemProperty 'management.info.git.mode', 'FULL'
}
...

We execute the bootRun task and send a new request for the /info endpoint:

$ http -b localhost:8080/info
{
    "git": {
        "branch": "feature/dsl-hapi",
        "commit": {
            "id": "511d2693cbabc34449f838bf856fa2b9989cbca6",
            "id.abbrev": "511d269",
            "message": {
                "full": "Enable Codenarc checks for integration test code",
                "short": "Enable Codenarc checks for integration test code"
            },
            "time": "2016-12-15 12:28:20+0000",
            "user": {
                "email": "mrhaki@server",
                "name": "Hubert Klein Ikkink"
            }
        }
    }
}
$

This time we see more information about Git.

Written with Spring Boot 1.4.2.RELEASE

December 16, 2016

PlantUML Pleasantness: Get PlantUML Definition From PNG

When we generate a PNG version of our PlantUML definition the original definition is stored in the PNG image. We can extract the definition using the command line option -metadata. We need to provide the PNG file and in the output we see the original PlantUML definition.

The following PNG image (activity.png) is created with PlantUML:

Next we run PlantUML from the command line using the option -metadata:

$ java -jar plantuml.jar -metadata activity.png
------------------------
activity.png

@startuml

' Make a dashed line, alternative syntax for ..>
(*) -[dashed]-> "Write outline"

' Make line bold and use color code
"Write outline" -[bold,#008000]-> "Find example"

' Only change the color
"Find example" -[#ff00ff]-> "Write blog"

' Order of line style and color can be reversed
"Write blog" -[#6666ff,dashed]-> "Publish"

' Use dotted line style
"Publish" -[dotted]-> (*)

@enduml

PlantUML version 8051(Thu Dec 01 18:52:05 CET 2016)
(GPL source distribution)
Java Runtime: Java(TM) SE Runtime Environment
JVM: Java HotSpot(TM) 64-Bit Server VM
Java Version: 1.8.0_112-b16
Operating System: Mac OS X
OS Version: 10.12.1
Default Encoding: UTF-8
Language: en
Country: US

------------------------
$

At the top we see the section @startuml..@enduml with the PlantUML syntax that was used to generate the PNG image.

Written with PlantUML 8051.

December 14, 2016

PlantUML Pleasantness: Change Line Style And Color

We can change the line style and color when we "draw" the line in our PlantUML definition. We must set the line style and color between square brackets ([]). We can choose the following line styles: bold, plain, dotted and dashed. The color is either a color name or a hexadecimal RGB code prefixed with a hash (#).

In the following example activity diagram we apply different styles and colors to the lines:

@startuml

' Make a dashed line, alternative syntax for ..>
(*) -[dashed]-> "Write outline"

' Make line bold and use color name
"Write outline" -[bold,#green]-> "Find example"

' Only change the color with hexadecimal RGB code
"Find example" -[#ff00ff]-> "Write blog"

' Order of line style and color can be reversed
"Write blog" -[#6666ff,dashed]-> "Publish"

' Use dotted line style
"Publish" -[dotted]-> (*)

@enduml

When we generate the activity diagram we see the different line styles and colors:

Written with PlantUML 8051.

December 13, 2016

Awesome Asciidoctor: Change Number Style For Ordered Lists

To write a (nested) ordered lists in Asciidoctor is easy. We need to start the line with a dot (.) followed by a space and the list item text. The number of dots reflects the levels of nesting. So with two dots (..) we have a nested list item. By default each nested level has a separate numbering style. The first level has arabic numbering, the second lower case alphanumeric, the third upper case alphanumeric, the fourth lower case roman and the fifth (which is maximum depth of nested levels in Asciidoctor) has style upper case roman. But we can change this by setting a block style for each nested level block. The name of the block style is arabic, loweralpha, upperalpha, lowerromann or upperroman. With the HTML5 backend we can also use decimal and lowergreek.

In the following example we have an ordered list where we set different block styles for the nested level:

= Nested numbering styles

. Services
[arabic]
.. Datastore
.. Mail

. Repositories
// Only HTML backend
[decimal]
.. PostgresDB
.. Redis

. Controllers
// Only HTML backend
[lowergreek]
.. API
.. Admin

. Software systems
// Or use upperalpha for upper case alphanumeric
[loweralpha]
.. OAuth

. Operatings systems
// Or use lowerroman for lower case roman
[upperroman]
.. Linux
[decimal]
... Server A
... Server B
.. macOS
.. Windows server

When we create the HTML output we have the following result:

Written with Asciidoctor 1.5.4.

Gradle Goodness: Passing Environment Variable Via Delegate Run Action In IntelliJ IDEA

IntelliJ IDEA 2016.3 introduced the option to delegate the run action to Gradle. This means when we have a run Configuration for our Java or Groovy classes we can use the Run action and IDEA will use Gradle to run the application. Actually IntelliJ IDEA creates a new task of type JavaExec dynamically for our specific run configuration with the main property set to the class we want to run.

In the Edit Configuration dialog window we can set the command line argument and Java system properties. These are passed on to the dynamically created JavaExec task and are accessible from within the class that runs. The environment variables that can be set in the Edit Configuration dialog windows are not passed to the JavaExec task configuration. But we can do it ourselves in the build script file of our project. We look for the dynamically created task and use the environment method to add a environment variable that can be access in the Java or Groovy class that is executed.

We start our example with a simple Groovy class that can be executed using JavaExec. We simply print out the given input arguments, all Java system properties that start with sampleApp and finally all environment variables that start with SAMPLE_APP:

// File: src/main/groovy/mrhaki/gradle/SampleApp.groovy
package mrhaki.gradle

class SampleApp {

    static void main(final String[] args) {
        final app = new SampleApp()
        app.printArgs(args)
        app.printSystemProperties()
        app.printEnv()
    }

    void printArgs(final String[] args) {
        printSection 'Command line arguments', args
    }

    void printSystemProperties() {
        printSection 'System properties', System.properties.findAll(findNameStartsWith('sampleApp'))
    }

    void printEnv() {
        printSection 'environment', System.getenv().findAll(findNameStartsWith('SAMPLE_APP'))
    }
    
    private void printSection(final String title, final values) {
        println "--- $title ---"
        println values
    }

    private Closure findNameStartsWith(final String search) {
        { name, value -> name.startsWith(search) }
    }
    
}

We create a new Run/Debug Configuration for our SampleApp class:

When we click on OK to save the configuration we are ready to use the Run 'SampleApp' action. When we look at the output we get the following result:

10:54:29: Executing external task 'run SampleApp'...
:compileJava UP-TO-DATE
:compileGroovy 
:processResources UP-TO-DATE
:classes 
:run SampleApp
--- Command line arguments ---
[Execute via IDEA delegate to Gradle]
--- System properties ---
[sampleApp:Gradle Galore]
--- environment ---
[:]

BUILD SUCCESSFUL

Total time: 3.539 secs
10:54:33: External task execution finished 'run SampleApp'.

Notice the Gradle tasks that are invoked and the dynamically created run SampleApp task. We see our Java system property is passed on, together with the program arguments. The environment variable is not passed on. We must add some extra configuration to the dynamically created task run Sample in our build.gradle file:

// File: build.gradle
...
setEnvVarRunConfiguration 'SampleApp', 'SAMPLE_APP', 'Gradle environment variable'

/**
 * Add environment variable to JavaExec task
 * configuration created by IntelliJ IDEA
 * from a Run/Debug configuration.
 * 
 * @param configuration Name of IntelliJ IDEA Run/Debug configuration
 * @param envName Name of environment variable
 * @param envValue Value for environment variable
 */
def setEnvVarRunConfiguration(
        final String configuration, 
        final String envName, 
        final String envValue) {

    // Find task by type JavaExec and matching 
    // task name for configuration name.
    final javaExecRunConfiguration = { task -> 
        task instanceof JavaExec && task.name == "run $configuration"
    }
    
    tasks.matching(javaExecRunConfiguration).all {
        // Add environment variable to JavaExec 
        // task configuration.
        environment envName, envValue
    }
}
...

Now we re-run our application and we get the following output:

11:29:19: Executing external task 'run SampleApp'...
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:run SampleApp
--- Command line arguments ---
[Execute via IDEA delegate to Gradle]
--- System properties ---
[sampleApp:Gradle Galore]
--- environment ---
[SAMPLE_APP:Gradle environment variable]

BUILD SUCCESSFUL

Total time: 0.599 secs
11:29:19: External task execution finished 'run SampleApp'.

Written with IntelliJ IDEA 2016.3 and Gradle 3.2.1.

December 12, 2016

PlantUML Pleasantness: Using Current Date

In PlantUML we can use the special variable %date% to get the current date and time. The default format shows day of the week, date, time and timezone. We can change the date format by specifying our format with the Java SimpleDateFromat symbols. For example to only get the hours and minutes we would write %date[HH:mm]%.

In the following example we use the %date% variable as is and with a custom format:

@startuml
' Use %date% to get date diagram is generated.
title Sample with date - %date%

' We can use Java SimpleDateFormat options
' to format the date. In this example we
' only want the day-month-year of the date.
footer Generated on %date[dd-MM-yyyy]%
@enduml

When we generate a graphical representation we get the following result:

Written with PlantUML 8051.

December 9, 2016

Grails Goodness: Writing Log Messages With Grails 3.2 (Slf4J)

Grails 3.2 changed the logging implementation for the log field that is automatically injected in the Grails artefacts, like controllers and services. Before Grails 3.2 the log field was from Jakarta Apache Commons Log class, but since Grails 3.2 this has become the Logger class from Slf4J API. A big difference is the fact that the methods for logging on the Logger class don't accepts an Object as the first argument. Before there would be an implicit toString invocation on an object, but that doesn't work anymore.

In the following example we try to use an object as the first argument of the debug method in a controller class:

package mrhaki.grails3

class SampleController {

    def index() { 
        log.debug new Expando(action: 'index')
        [:]
    }
    
}

When we invoke the index action we get an exception:

...
2016-12-09 14:59:20.283 ERROR --- [nio-8080-exec-1] o.g.web.errors.GrailsExceptionResolver   : MissingMethodException occurred when processing request: [GET] /sample/index
No signature of method: ch.qos.logback.classic.Logger.debug() is applicable for argument types: (groovy.util.Expando) values: [{action=index}]
Possible solutions: debug(java.lang.String), debug(java.lang.String, [Ljava.lang.Object;), debug(java.lang.String, java.lang.Object), debug(java.lang.String, java.lang.Throwable), debug(org.slf4j.Marker, java.lang.String), debug(java.lang.String, java.lang.Object, java.lang.Object). Stacktrace follows:

java.lang.reflect.InvocationTargetException: null
        at org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:210)
        at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:187)
        at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
        at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
        at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:77)
        at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
Caused by: groovy.lang.MissingMethodException: No signature of method: ch.qos.logback.classic.Logger.debug() is applicable for argument types: (groovy.util.Expando) values: [{action=index}]
Possible solutions: debug(java.lang.String), debug(java.lang.String, [Ljava.lang.Object;), debug(java.lang.String, java.lang.Object), debug(java.lang.String, java.lang.Throwable), debug(org.slf4j.Marker, java.lang.String), debug(java.lang.String, java.lang.Object, java.lang.Object)
        at mrhaki.grails3.SampleController.index(SampleController.groovy:6)
        ... 14 common frames omitted
...

When we change the log statement to log.debug new Expando(action: 'index').toString() it works.

Another time saver and great feature is the use of placeholders {} in the logging message. This allows for late binding of variables that are used in the logging message. Remember when we used the Apache Commons Logging library we had to enclose a logging statement in a if statement to check if logging was enabled. Because otherwise the logging message with variable references was always evaluated, even though the logging was disabled. With Slf4J Logger we don't have to wrap logging statements with an if statement if we use the {} placeholders. Slf4J will first check if logging is enabled for the log message and then the logging message is created with the variables. This allows for much cleaner code.

In the following example we use the placeholders to create a logging message that included the variable id:

package mrhaki.grails3

class SampleController {

    def show(final String id) {
        // Before Grails 3.2 we should write:
        // if (log.debugEnabled) {
        //     log.debug "Invoke show with id [$id]"
        // }
        // With Grails 3.2 it is only the debug method and 
        // String evaluation using {}.
        log.debug 'Invoke show with id [{}]', id
        [id: id]
    }
    
}

Written with Grails 3.2.3

PlantUML Pleasantness: Check If PlantUML Is Up To Date

With the command line option -checkversion we can see if we have the latest PlantUML version. The command prints to the console our current PlantUML version and latest version that is available. Inside a PlantUML definition we can use the command checkversion and generate for example a PNG image with information about our PlantUML version and the latest version that can be downloaded.

First we use the command line option -checkversion for an out-of-date version:

$ plantuml -checkversion
PlantUML version 8048 (Thu Sep 29 19:04:02 CEST 2016)

Last available version for download : 8051

A newer version is available for download.
$

We update our PlantUML and run the command again:

$ plantuml -checkversion
PlantUML version 8051 (Thu Dec 01 18:52:05 CET 2016)

Last available version for download : 8051

Your version is up to date.
$

Now we use a PlantUML definition so we can generate a graphical representation of the information.

@startuml
checkversion
@enduml

First we use the older version of PlantUML to generate a PNG image:

Next we use the latest version that is available:

Written with PlantUML 8051.

PlantUML Pleasantness: Generate Graphical Version Information

If we want to know which version of PlantUML we are using we can use the command line option -version. PlantUML will print the version and also some extra information like the machine name, memory and more. But we can also create a PlantUML definition with the command version and we can transform it to a graphical presentation like a PNG image. This can be handy if we use PlantUML in an environment like Asciidoctor with diagram support and we want to know which version of PlantUML is used.

In our first example we run PlantUML from the command line and use the -version option:

$ plantuml -version
PlantUML version 8051 (Thu Dec 01 18:52:05 CET 2016)
(GPL source distribution)
Java Runtime: Java(TM) SE Runtime Environment
JVM: Java HotSpot(TM) 64-Bit Server VM
Java Version: 1.8.0_112-b16
Operating System: Mac OS X
OS Version: 10.12.1
Default Encoding: UTF-8
Language: en
Country: US
Machine: mrhaki-laptop-2015.fritz.box
PLANTUML_LIMIT_SIZE: 4096
Processors: 8
Max Memory: 3,817,865,216
Total Memory: 257,425,408
Free Memory: 249,050,832
Used Memory: 8,374,576
Thread Active Count: 2

The environment variable GRAPHVIZ_DOT has been set to /usr/local/bin/dot
Dot executable is /usr/local/bin/dot
Dot version: dot - graphviz version 2.38.0 (20140413.2041)
Installation seems OK. File generation OK
$

In the second example we create a file with the following PlantUML definition:

@startuml
version
@enduml

When we generate a PNG from this definition we get the following result:

Written with PlantUML 8051.

December 6, 2016

PlantUML Pleasantness: Create A Sudoku :)

PlantUML has a fun command to create a Sudoku puzzle. We must use sudoku in our PlantUML definition and a random puzzle is generated. We can even give a seed value for a given Sudoku so it is generated again.

In the following example PlantUML definition we use the sudoku command:

@startuml
sudoku
@enduml

We create a PNG file with PlantUML and we get the following result:

To regenerate the same Sudoku we must use the seed value cinnld556e0o:

@startuml
sudoku cinnld556e0o
@enduml

Written with PlantUML 8048.

PlantUML Pleasantness: Include Partial Content From Files

With PlantUML we can include external files in our definition with the !include directive. We specify the file name and the content is included in our PlantUML definition. The included file can also have multiple @startuml ... @enduml sections and we can refer to individual sections with the !include directive. We must append to the include file name an exclamation mark (!) followed by either a number or identifier. If we use a number we specify which section we want to include, where section are numbered starting from 0. So to get the second section from a file commons.puml we would write !include commons.puml!1. Alternatively we can use identifiers in the include file. We append to @startuml an identifier as (id=idValue). Then from the definition that is including the file we refer to the identifier after an exclamation mark (!). If our included file commons.puml has a section with id user then we would include it as !include commons.puml!user.

In the following example PlantUML definition we define the file to be included. We have two sections with @startuml ... @enduml which both have an identifier:

' File: commons.puml

' section with id user to define user actor
@startuml(id=user)
actor "Application User" as User
@enduml

' section with id mail to define mail component
@startuml(id=mail)
[Mail server] as Mail <<Mail>> #ffcc66
@enduml

Next we write a definition where we include commons.puml using a section number and section identifier:

' File: sample.puml
@startuml

' Include first section (0-based index) of commons.puml
!include commons.puml!0

' Include section with id mail of commons.puml
!include commons.puml!mail


[Sample Application] <<Software System>> as SampleApp

User --> SampleApp
SampleApp --> Mail

@enduml

Let's generate a diagram and look at the result where we see our user and mail components from the included file:

Written with PlantUML 8086.

PlantUML Pleasantness: Customize Stereotype Styling

To change the styling of our generated diagrams with PlantUML we can use the skinparam command. We can set for example font size, style, color, background colors and much more. We can change the style for a specific element or for the whole diagram. We can even set specific styling options for stereotypes. The settings for the stereotype are then applied to all elements in our diagram with that stereotype. We must append <<stereotype name>> to the skin parameter name.

In the following example PlantUML description we apply custom background colors to each stereotype:

@startuml

/' Set custom background colors for the default
   component elements and the specific stereotypes. '/
skinparam component {
    BackgroundColor #ff6666
    BackgroundColor<<Database>> #ccff66
    BackgroundColor<<Spring service>> #66ccff
    BackgroundColor<<Spring REST controllers>> #66ffcc
    BackgroundColor<<Spring repository>> #66ffff
    BackgroundColor<<Mail server>> #ffcc66
}

actor User
[Third party application] as ThirdPartyApp

together {
    [PostgreSQL database] as PostgresDB <<Database>>
    [Mail server] as Mail <<Mail server>>
}

package "Spring Boot Application" {
    [APIController] <<Spring REST controllers>>
    [AdminController] <<Spring REST controllers>>

    [AdminService] <<Spring service>>
    [DataStoreService] <<Spring service>>

    [Repository] <<Spring repository>>
}

User --> AdminController
ThirdPartyApp --> APIController

APIController --> DataStoreService
AdminController --> DataStoreService
AdminController --> AdminService

DataStoreService --> Repository
AdminService --> Mail

Repository --> PostgresDB

@enduml

When we generate the diagram we see that each stereotype has it's own background color:

Written with PlantUML 8086.

December 5, 2016

PlantUML Pleasantness: Keeping Elements Together

When we write a PlantUML definition the generated graphical diagram is laid out by PlantUML. In a previous post we learned how to move elements using the length of the connection. But we can also use a together block with all elements that should be at the same level. PlantUML will try to keep the elements together when the diagram is drawn.

In the following sample PlantUML definition we want the PostgresDB and Mail elements to be at the same level, so we group them using a together block:

@startuml

actor User
[Third party application] as ThirdPartyApp

/' Try to keep PostgresDB and Mail together,
   so they are at the same level in the diagram. '/
together {
    [PostgreSQL database] as PostgresDB <<Database>>
    [Mail server] as Mail <<Mail server>>
}

package "Spring Boot Application" {
    [Controllers] <<Spring REST controllers>>
    [DataStoreService] <<Spring service>>
    [Repository] <<Spring repository>>
}

User --> Controllers
ThirdPartyApp --> Controllers

Controllers --> DataStoreService

DataStoreService --> Repository
DataStoreService --> Mail

Repository --> PostgresDB

@enduml

Let's create the diagram and we see the elements are aligned nicely:

Written with PlantUML 8086.

PlantUML Pleasantness: No Shadows In Diagrams

By default elements in a PlantUML generated diagram have shadows. To disable shadows we must set the skin parameter shadowing to the value false. PlantUML will then not create shadows on elements.

In the following sample PlantUML definition we use the value false for the skin parameter shadowing:

@startuml

' Remove shadows
skinparam shadowing false

actor User
[Third party application] as ThirdPartyApp

package "Spring Boot Application" {
    rectangle Controllers <<Spring REST controllers>>
    rectangle DataStoreService <<Spring service>>
    rectangle Repository <<Spring repository>>
}

User --> Controllers
ThirdPartyApp --> Controllers

Controllers --> DataStoreService
DataStoreService --> Repository

@enduml

The generated diagram has no shadows:

Written with PlantUML 8086.

PlantUML Pleasantness: Diagrams In Black And White

The default colors of PlantUML use red and yellow to draw the diagram elements. If we want our diagram to be in black, grey and white we can simply set skin parameter monochrome to true. The generated graphical diagram will now have black, grey and white colors.

In the following sample PlantUML definition we set the diagram skin parameter monochrone to true:

@startuml

' Generated diagram will be in black/grey/white.
skinparam monochrome true

actor User
[Third party application] as ThirdPartyApp

package "Spring Boot Application" {
    rectangle Controllers <<Spring REST controllers>>
    rectangle DataStoreService <<Spring service>>
    rectangle Repository <<Spring repository>>
}

User --> Controllers
ThirdPartyApp --> Controllers

Controllers --> DataStoreService
DataStoreService --> Repository

@enduml

The generated diagram looks like this:

Written with PlantUML 8048.

PlantUML Pleasantness: Align Elements With Line Length

Drawing diagrams with PlantUML is fun and easy. We use text to describe the diagram and the we get a graphical representation. Especially in combination with Asciidoctor with PlantUML integration we have a winning combination to write technical documentation. Because our text is transformed into a graphical format like PNG we don't have much influence on the layout. There are options to indicate positions of elements, but we can also use the length of lines to influence the position of elements.

In the following sample we have a PlantUML diagram description with standard lines. We use two hyphens (--) to define a line:

@startuml
actor User
[Third party application] as ThirdPartyApp

[PostgreSQL database] <<Database>> as PostgresDB
[Mail server] <<Mail server>> as Mail

package "Spring Boot Application" {
    [Controllers] <<Spring REST controllers>>
    [DataStoreService] <<Spring service>>
    [Repository] <<Spring repository>>
}

User --> Controllers
ThirdPartyApp --> Controllers

Controllers --> DataStoreService
DataStoreService --> Repository

DataStoreService --> Mail

Repository --> PostgresDB
@enduml

We create a diagram and we get the following graphical diagram:

The diagram looks fine, but we want to have our mail server at the same level as the PostgreSQL database element. We simply add an extra hyphen (-) to the connection line between DataStoreService and Mail:

@startuml
actor User
[Third party application] as ThirdPartyApp

[PostgreSQL database] <<Database>> as PostgresDB
[Mail server] <<Mail server>> as Mail

package "Spring Boot Application" {
    [Controllers] <<Spring REST controllers>>
    [DataStoreService] <<Spring service>>
    [Repository] <<Spring repository>>
}

User --> Controllers
ThirdPartyApp --> Controllers

Controllers --> DataStoreService
DataStoreService --> Repository

/'Add extra hyphen (-) to put Mail
  add the same level as PostgresDB '/
DataStoreService ---> Mail

Repository --> PostgresDB
@enduml

We regenerate our diagram and we get the following result:

Written with PlantUML 8048.

November 23, 2016

Gradle Goodness: Delegate Build And Run Actions To Gradle In IntelliJ IDEA

IntelliJ IDEA 2016.3 introduces the option to delegate the IDE build and run actions to Gradle. So if we invoke the Build Project action from the Build menu IntelliJ IDEA invokes the correct tasks using Gradle. Also the Run and Debug actions from the Run menu are executed with Gradle.

If we want this behaviour we need to changed the preferences of IntelliJ IDEA. We must open the preferences dialog window and then go to Build, Execution, Deployment | Build Tools | Gradle | Runner. Here we check the option Delegate IDE build/run actions to gradle and we close the window:

Let's open a simple Java project with a Gradle build file in IDEA. Next we invoke the Build Project action with the shortcut key Cmd+F9 (on macOS, other operating systems probably have a different shortcut key). Our code is compiled and we can open the Run view to see the output:

We have a Java class in our project with a main method we want to run. We use the Run action (for example using the shortcut key Ctrl+R on macOS) and IDEA uses Gradle's JavaExec task to run the class. Also this time we can see the output in the Run view:

Written with Gradle 3.2 and IntelliJ IDEA 2016.3.

Grails Goodness: Enabling Grails View In IntelliJ IDEA For Grails 3

IntelliJ IDEA 2016.3 re-introduced the Grails view for Grails 3 applications. Grails 2 applications already were supported with a Grails view in IDEA. Using this view we get an overview of all the Grails artifacts like controller, services, views and more. We can easily navigate to the the class files we need. Now this view is also available for Grails 3 applications.

To enable the view we must click on the view selector in the project view:


We select the Grails option and we get an nice overview of our Grails project in the Grails view:


Also the New action is context sensitive in the Grails view. If we right click on the Services node we can see the option to create a new service class:


If we right click on the root node we get the option to create Grails artifacts:


Written with IntelliJ IDEA 2016.3 and Grails 3.2.2.

November 21, 2016

Gradle Goodness: Creation Rules For Rule Based Model Configuration Using Model DSL

In a previous post we learned how to create a class that extends RuleSource with methods that define rules on how to create and change objects in the Gradle model space. With the model configuration block in a build file we can also define creation rules for Rule based model configuration.

In the following build file we define a model block and define a creation rule for creating the object versionInfo of type VersionFile. Also we add a new task to the tasks object of type ModelMap<Task>. To reference another object from the model space inside the Closure for a creation rule we use the syntax $.<objectName>:

// File: model.gradle
import mrhaki.gradle.VersionFile
import mrhaki.gradle.VersionFileTask

model {
    // Creation rule to create object
    // with name versionInfo (name of the method)
    // and type VersionFile.
    versionInfo(VersionFile) {
        // Set default value for version to project.version.
        version = project.version
        
        // Set default outputFile to 
        // file version.txt in build directory.
        outputFile = project.file("${buildDir}/version.txt")
    }
    
    // tasks is of type ModelMap<Task>.
    tasks {
        // Create task generationVersionFile 
        // with custom task type VersionFileTask.
        create('generateVersionFile', VersionFileTask) {
            // Set properties with values
            // from managed object versionInfo,
            // that we created with the creation rule
            // versionInfo(VersionFile).
            // We use the special $.<name> notation
            // to reference object from the model space.
            version = $.versionInfo.version
            outputFile = $.versionInfo.outputFile
        }
    }
}

The supporting VersionFile class is a Gradle managed object:

// File: buildSrc/src/main/groovy/mrhaki/gradle/VersionFile.groovy
package mrhaki.gradle

import org.gradle.model.Managed

@Managed
interface VersionFile {
    String getVersion() 
    void setVersion(final String version) 

    File getOutputFile() 
    void setOutputFile(final File outputFile) 
}

And the custom task is very straight forward for our example:

// File: buildSrc/src/main/groovy/mrhaki/gradle/VersionFileTask.groovy
package mrhaki.gradle

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction

/**
 * Simple task to save the value for the
 * {@link #version} property in a file.
 * The file is set with the {@link #outputFile}
 * property.
 */
class VersionFileTask extends DefaultTask {

    /**
     * Value for version to be saved.
     */
    @Input
    String version

    /**
     * Output file to store version value in.
     */
    @OutputFile
    File outputFile

    /**
     * Actual task actions to save the value
     * for {@link #version} in {@link #outputFile}.
     */
    @TaskAction
    void generateVersionFile() {
        outputFile.parentFile.mkdirs()
        outputFile.text = version
    }

}

We can use the model rules in our build script when we use apply from: 'model.gradle' in our build script. In our example we also add a model configuration block to configure the versionInfo object:

// File: build.gradle
apply from: 'model.gradle'

model {
    // Configuration rule for the versionInfo
    // object, that is created with the
    // creation rule from 'model.gradle'.
    versionInfo {
        version = '3.0.0.RELEASE'
    }   
}

Let's invoke the model task and check the output to see where are rules are applied:

$ gradle -q model
...
 tasks
      | Type:           org.gradle.model.ModelMap<org.gradle.api.Task>
      | Creator:        Project.<init>.tasks()
      | Rules:
         ⤷ tasks { ... } @ model.gradle line 18, column 5
...
    + generateVersionFile
          | Type:       mrhaki.gradle.VersionFileTask
          | Value:      task ':generateVersionFile'
          | Creator:    create(generateVersionFile, mrhaki.gradle.VersionFileTask) { ... } @ model.gradle line 21, column 9
          | Rules:
             ⤷ copyToTaskContainer
...
+ versionInfo
      | Type:           mrhaki.gradle.VersionFile
      | Creator:        versionInfo(mrhaki.gradle.VersionFile) { ... } @ model.gradle line 8, column 5
      | Rules:
         ⤷ versionInfo { ... } @ build.gradle line 47, column 5
    + outputFile
          | Type:       java.io.File
          | Value:      /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/gradle/versionrule/build/version.txt
          | Creator:    versionInfo(mrhaki.gradle.VersionFile) { ... } @ model.gradle line 8, column 5
    + version
          | Type:       java.lang.String
          | Value:      3.0.0.RELEASE
          | Creator:    versionInfo(mrhaki.gradle.VersionFile) { ... } @ model.gradle line 8, column 5
...
$

Written with Gradle 3.2.

Gradle Goodness: Getting Project Information Into Rule Based Model Configuration

Rule based model configuration in Gradle allows us to have a graph of objects with dependencies that are resolved by Gradle. To make this work Gradle needs to know about the object in this model space. The model space is populated with objects of our own and with objects from Gradle. At time of writing this blog post we can not interact with the Gradle Project object in our rule based model configuration. It is not officially part of the model space. Probably in the future this might change and will the Project object managed by Gradle be part of the model space. Which means we can use then a Project object as input parameter for any rule methods we have. For now the official way to pass project information to the rule based model space is via the model configuration block in our build script. The model configuration block can be used to set properties on objects with values from our Project object.

In the following example we have VersionFile object that is part of the model space.

// File: buildSrc/src/main/groovy/mrhaki/gradle/VersionFile.groovy
package mrhaki.gradle

import org.gradle.model.Managed

@Managed
interface VersionFile {
    String getVersion() 
    void setVersion(final String version) 

    File getOutputFile() 
    void setOutputFile(final File outputFile) 
}

The rules to create the VersionFile object and to use this object to add a new task are defined in the file VersionFileTaskRules:

// File: buildSrc/src/main/groovy/mrhaki/gradle/VersionFileTaskRules.groovy
package mrhaki.gradle

import org.gradle.api.Task
import org.gradle.model.Model
import org.gradle.model.ModelMap
import org.gradle.model.Mutate
import org.gradle.model.RuleSource

class VersionFileTaskRules extends RuleSource {

    @Model
    void versionFile(final VersionFile versionFile) {}

    @Mutate
    void createVersionFileTask(final ModelMap<Task> tasks, final VersionFile versionFile) {
        tasks.create('generateVersionFile', VersionFileTask) { task ->
            task.version = versionFile.version
            task.outputFile = versionFile.outputFile
        }
    }

}

The custom task is not very exiting:

// File: buildSrc/src/main/groovy/mrhaki/gradle/VersionFileTask.groovy
package mrhaki.gradle

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction

/**
 * Simple task to save the value for the
 * {@link #version} property in a file.
 * The file is set with the {@link #outputFile}
 * property.
 */
class VersionFileTask extends DefaultTask {

    /**
     * Value for version to be saved.
     */
    @Input
    String version

    /**
     * Output file to store version value in.
     */
    @OutputFile
    File outputFile

    /**
     * Actual task actions to save the value
     * for {@link #version} in {@link #outputFile}.
     */
    @TaskAction
    void generateVersionFile() {
        outputFile.parentFile.mkdirs()
        outputFile.text = version
    }

}

If we want to set the version property with the project.version value we use the model configuration in our build script:

// File: build.gradle
apply plugin: mrhaki.gradle.VersionFileTaskRules

// Configure model space.
model {

    // Configure VersionFile instance created 
    // by method versionFile() from VersionFileTaskRules.
    versionFile {

        // Set value for version property of VersionFile
        // using the project.version property.
        version =  project.version

        // Set value for outputFile property of VersionFile,
        // using the project.buildDir property and 
        // project.file method.
        outputFile = project.file("${buildDir}/version.txt")
    }

}

// Set project version
version = '1.0.1.RELEASE'

We run the model task to see that our VersionFile object has the right property values:

$ gradle -q model
...
+ versionFile
      | Type:           mrhaki.gradle.VersionFile
      | Creator:        VersionFileTaskRules#versionFile(VersionFile)
      | Rules:
         ⤷ versionFile { ... } @ build.gradle line 9, column 5
    + outputFile
          | Type:       java.io.File
          | Value:      /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/gradle/versionrule/build/version.txt
          | Creator:    VersionFileTaskRules#versionFile(VersionFile)
    + version
          | Type:       java.lang.String
          | Value:      1.0.1.RELEASE
          | Creator:    VersionFileTaskRules#versionFile(VersionFile)
$

Because the Project object is not part of the managed model space, we can not give the version property of VersionFile a default value that is the project.version property value. In our example we can give the version property of the VersionFileTask a default value that is project.version. So if the we don't use the model configuration block the version value of the Project object is used in the task. Unfortunately we cannot see this default from the model task.

There are some internal objects created by Gradle that represent the project build directory and the Project object. The build directory can be accessed as input argument using the syntax @Path('buildDir') File and the project as ProjectIdentifier. But these are for internal use and can be removed or changed without warning. So to have a set of rules for the model space that will be useable in future Gradle versions we must not rely on those objects.

Another workaround, which might not work in the future, would be to rely on the ExtensionContainer object that is available in the model space. Gradle adds this as a hidden object, so also here we need to keep in mind this solution might not work in future Gradle versions. We could write an extension that wraps the Project object and add it to the ExtensionContainer. Next we use the ExtensionContainer as input argument for a rule method to get the extension with the wrapper Project object. The create the extension we use a custom Gradle plugin:

// File: buildSrc/src/main/groovy/mrhaki/gradle/VersionFilePlugin.groovy
package mrhaki.gradle

import groovy.transform.TupleConstructor
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.plugins.ExtensionContainer
import org.gradle.model.Defaults
import org.gradle.model.Model
import org.gradle.model.ModelMap
import org.gradle.model.Mutate
import org.gradle.model.RuleSource

class VersionFilePlugin implements Plugin<Project> {
    
    @Override
    void apply(final Project project) {
        // Create extension 'projectWrapper' and add to ExtensionContainer.
        project.extensions.create('projectWrapper', ProjectWrapper, project)        
    }
    
    // The rules to work with the model space.
    static class VersionTaskRules extends RuleSource {
        @Model
        void versionFile(final VersionFile versionFile) {}

        /**
         * Set default value with {@link VersionFile#setVersion(java.lang.String)} 
         * and {@link VersionFile#setOutputFile(java.io.File)}.
         * 
         * @param versionFile Object to set defaults for is {@link VersionFile}.
         * @param extensionContainer {@link ExtensionContainer} is managed by Gradle. 
         */
        @Defaults
        void setVersionFileDefaults(
                final VersionFile versionFile,
                final ExtensionContainer extensionContainer) {
            
            // Get ProjectWrapper and enclosed Project object.
            final ProjectWrapper projectWrapper = extensionContainer.getByType(ProjectWrapper)
            final Project project = projectWrapper.project
            
            // Set version and outputFile properties with project information.
            versionFile.version = project.version
            versionFile.outputFile = new File(project.buildDir, 'version.txt')
        }

        @Mutate
        void createVersionFileTask(final ModelMap<Task> tasks, final VersionFile versionFile) {
            tasks.create('generateVersionFile', VersionFileTask) { task ->
                task.version = versionFile.version
                task.outputFile = versionFile.outputFile
            }
        }

    }
}

@TupleConstructor
class ProjectWrapper {
    final Project project
}

In our build file we apply the plugin and do not use the model configuration:

// File: build.gradle
apply plugin: mrhaki.gradle.VersionFilePlugin

When we run the model task we see the setVersionFileDefaults method is used to set VersionFile properties:

$ gradle -q model
...
+ versionFile
      | Type:           mrhaki.gradle.VersionFile
      | Creator:        VersionFilePlugin.VersionTaskRules#versionFile(VersionFile)
      | Rules:
         ⤷ VersionFilePlugin.VersionTaskRules#setVersionFileDefaults(VersionFile, ExtensionContainer)
    + outputFile
          | Type:       java.io.File
          | Value:      /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/gradle/versionrule/build/version.txt
          | Creator:    VersionFilePlugin.VersionTaskRules#versionFile(VersionFile)
    + version
          | Type:       java.lang.String
          | Value:      2.0.1
          | Creator:    VersionFilePlugin.VersionTaskRules#versionFile(VersionFile)
...
$

Written with Gradle 3.2.

Gradle Goodness Notebook Updated

Gradle Goodness Notebook has been updated. If you have bought the book you can download the latest version for free. The following blog posts have been added:

  • Running Groovy Scripts Like From Groovy Command Line
  • Specify Spock As Test Framework At Initialization
  • Build Script Using Java Syntax
  • Create Objects Using DSL With Domain Object Containers
  • Using Nested Domain Object Containers
  • Inter-Project Artifact Dependencies
  • Lazy Task Properties
  • Methods Generated For Setting Task Properties
  • Adding Custom Extension To Tasks
  • Add Spring Facet To IntelliJ IDEA Module
  • Configure IntelliJ IDEA To Use Gradle As Testrunner
  • Enable Compiler Annotation Processing For IntelliJ IDEA
  • Set VCS For IntelliJ IDEA In Build File
  • Source Sets As IntelliJ IDEA Modules
  • Get Property Value With findProperty
  • Running All Tests From One Package
  • Check The Gradle Daemon Status
  • Lazy Project Property Evaluation
  • Change Gradle Wrapper Script Name
  • Specify Wrapper Version and Distribution Type From Command Line
  • Use Command Line Options With Custom Tasks

November 18, 2016

Grails Goodness Notebook Is Updated

Grails Goodness Notebook has been updated with the latest blog posts. If you have purchased the book before you can download the latest version of the book for free.

  • Saving Server Port In A File
  • Creating A Runnable Distribution
  • Change Version For Dependency Defined By BOM
  • Use Random Server Port In Integration Tests
  • Running Tests Continuously
  • Add Git Commit Information To Info Endpoint
  • Adding Custom Info To Info Endpoint
  • Add Banner To Grails 3.1 Application
  • Creating A Fully Executable Jar
  • Pass JSON Configuration Via Command Line

Groovy Goodness Notebook Is Updated

Groovy Goodness Notebook which contains the Groovy blog posts in an organised form is updated with the following blog posts. If you have purchased the book before you get the update for free.

  • Using Tuples
  • Creating Files And Directories With Nice DSL Using FileTreeBuilder
  • Make Class Cloneable With @AutoClone
  • IntelliJ IDEA Formatting Of Closure Chains
  • Represent Map As String
  • Turn A Map Or List As String To Map Or List
  • Customise Log AST Annotations

November 16, 2016

Gradle Goodness: Validate Model In Rule Based Model Configuration

Rule based model configuration gives Gradle more knowledge about the objects and their dependencies. This information can be used by Gradle to optimise the build process. We define rules on how we want Gradle to create objects and how we want to mutate objects in a class that extends RuleSource. We can also add rules to validate objects available in the Gradle model space. We use the @Validate annotation on methods that have validation logic. The first argument of the method is of the type of the object we want to validate. This type must be managed by Gradle.

In the following example we use the sample from a previous post. In this sample we have a VersionFile class that is managed by Gradle. The class has a version and outputFile property. The version must be set and must start with a v. The outputFile property is also required.

// File: buildSrc/src/main/groovy/mrhaki/gradle/VersionFileTaskRules.groovy
package mrhaki.gradle

import org.gradle.api.GradleException
import org.gradle.api.Task
import org.gradle.model.Model
import org.gradle.model.ModelMap
import org.gradle.model.Mutate
import org.gradle.model.RuleSource
import org.gradle.model.Validate

class VersionFileTaskRules extends RuleSource {

    @Model
    void versionFile(final VersionFile versionFile) {}

    /**
     * Version property of {@link VersionFile} must have a value and the value
     * must start with a 'v'.
     * 
     * @param versionFile Gradle managed {@link VersionFile} object we want to validate
     */
    @Validate
    void validateVersionFileVersion(final VersionFile versionFile) {
        def message = """\
            Property VersionFile.version is not set. Set a value in the model configuration.
            
            Example:
            -------
            model {
                versionFile {
                    version = 'v1.0.0'
                }
            }
            """.stripIndent()
        checkAssert(message) {
            assert versionFile.version
        }
        
        message = """\
            Property VersionFile.version should start with 'v'. Set a value starting with 'v' in the model configuration.
            
            Example:
            -------
            model {
                versionFile {
                    version = 'v${versionFile.version}'
                }
            }
            """.stripIndent()
        checkAssert(message) {
            assert versionFile.version.startsWith('v')
        }
    }

    /**
     * Outputfile property of {@link VersionFile} must have a value.
     *
     * @param versionFile Gradle managed {@link VersionFile} object we want to validate
     */
    @Validate
    void validateVersionFileOutputFile(final VersionFile versionFile) {
        def message = """\
            Property VersionFile.outputFile is not set. Set a value in the model configuration.

            Example:
            -------
            model {
                versionFile {
                    outputFile = project.file("\${buildDir}/version.txt")
                }
            }
            """.stripIndent()
        checkAssert(message) {
            assert versionFile.outputFile
        }
    }

    /**
     * Run assert statement in assertion Closure. If the assertion fails
     * we catch the exception. We use the message with the error appended with an user message
     * and throw a {@link GradleException}. 
     * 
     * @param message User message to be appended to assertion error message
     * @param assertion Assert statement(s) to run
     */
    private void checkAssert(final String message, final Closure assertion) {
        try {
            // Run Closure with assert statement(s).
            assertion()
        } catch (AssertionError assertionError) {
            // Use Groovy power assert output from the assertionError
            // exception and append user message.
            final exceptionMessage = new StringBuilder(assertionError.message)
            exceptionMessage << System.properties['line.separator'] << System.properties['line.separator']
            exceptionMessage << message
            
            // Throw exception so Gradle knows the validation fails.
            throw new GradleException(exceptionMessage, assertionError)
        }
    }
    
    @Mutate
    void createVersionFileTask(final ModelMap<Task> tasks, final VersionFile versionFile) {
        tasks.create('generateVersionFile', VersionFileTask) { task ->
            task.version = versionFile.version
            task.outputFile = versionFile.outputFile
        }
    }

}

Let's use the following build file and apply the rules to the project:

// File: build.gradle
apply plugin: mrhaki.gradle.VersionFileTaskRules

model {
}

From the command line we run the model task to check the Gradle model space:

$ gradle -q model

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring root project 'versionrule'.
> Exception thrown while executing model rule: VersionFileTaskRules#validateVersionFileOutputFile(VersionFile)
   >                 assert versionFile.outputFile
            |           |
            |           null
            VersionFile 'versionFile'

     Property VersionFile.outputFile is not set. Set a value in the model configuration.

     Example:
     -------
     model {
         versionFile {
             outputFile = project.file("${buildDir}/version.txt")
         }
     }



* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
$

Notice the validation rules are evaluated in alphabetical order of the methods names that have the @Validate annotation.

Let's fix this and set also the version property in our build file:

// File: build.gradle
apply plugin: mrhaki.gradle.VersionFileTaskRules

model {
    versionFile {
        version = '1.0.3.RELEASE'
        outputFile = project.file("${buildDir}/version.txt")
    }
}

We rerun the model task and in the output we see the version is invalid, because it doesn't start with a v:

$ gradle -q model

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring root project 'versionrule'.
> Exception thrown while executing model rule: VersionFileTaskRules#validateVersionFileVersion(VersionFile)
   > assert versionFile.version.startsWith('v')
            |           |       |
            |           |       false
            |           1.0.3.RELEASE
            VersionFile 'versionFile'

     Property VersionFile.version should start with 'v'. Set a value starting with 'v' in the model configuration.

     Example:
     -------
     model {
         versionFile {
             version = 'v1.0.3.RELEASE'
         }
     }


* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
$

Let's make our validation pass with the following build script:

// File: build.gradle
apply plugin: mrhaki.gradle.VersionFileTaskRules

model {
    versionFile {
        version = 'v1.0.3.RELEASE'
        outputFile = project.file("${buildDir}/version.txt")
    }
}

And in the output of the model we see the properties are set with our values:

$ gradle -q model
...
+ versionFile
      | Type:           mrhaki.gradle.VersionFile
      | Creator:        VersionFileTaskRules#versionFile(VersionFile)
      | Rules:
         ⤷ versionFile { ... } @ build.gradle line 10, column 5
         ⤷ VersionFileTaskRules#validateVersionFileOutputFile(VersionFile)
         ⤷ VersionFileTaskRules#validateVersionFileVersion(VersionFile)
    + outputFile
          | Type:       java.io.File
          | Value:      /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/gradle/versionrule/build/version.txt
          | Creator:    VersionFileTaskRules#versionFile(VersionFile)
    + version
          | Type:       java.lang.String
          | Value:      v1.0.3.RELEASE
          | Creator:    VersionFileTaskRules#versionFile(VersionFile)
...
$

Written with Gradle 3.2.

November 15, 2016

Gradle Goodness: Replacing << Operator For Tasks

Gradle 3.2 deprecates the << operator to add actions to a task. The << operator maps to the leftShift method of a task. This operator confuses a lot people that are new to Gradle. Because without the operator we are configuring a task instead of adding actions. I can tell from experience the mistake is easily made. If we use the << in our build script with Gradle 3.2 we get a warning on the console. The warning message already mentions a solution: use the doLast method to add actions.

In the following example build script we define the task deprecatedSample using the << operator. The other task newSample uses the doLast method to add an action:

// Since Gradle 3.2 the << (leftShift) operator
// is deprecated. The operator can confuse
// people, because without the operator
// we would configure the deprecatedSample task,
// instead of adding the action statement:
// println 'Sample task'.
task deprecatedSample << {
    println 'Sample task'
}

// To have no confusion we should use
// the doLast method of a task to add
// the action statement:
// println 'Sample task'.
task newSample {
    doLast {
        println 'Sample task'
    }
}

When we run the deprecatedSample task we see in the output the warning that the leftShift method has been deprecated:

$ gradle deprecatedSample
The Task.leftShift(Closure) method has been deprecated and is scheduled to be removed in Gradle 5.0. Please use Task.doLast(Action) instead.
        at build_dq65b0mbv52w2ikhya3h9ru8d.run(/Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/gradle/leftShift/build.gradle:7)
:deprecatedSample
Sample task

BUILD SUCCESSFUL

Total time: 0.793 secs
$

We still have time to fix our build scripts, because in Gradle 5 the leftShift method will be removed.

Written with Gradle 3.2.

Gradle Goodness: Set Default Values With Rule Based Model Configuration

When we use Rule based model configuration in our Gradle project we can give Gradle rules on how to manage objects from the model space. These rules are defined in a class that extends RuleSource. When we want to set some default values for properties of a model object (in Gradle terms this is a subject) we can use the @Defaults annotation. Rules annotated with @Defaults are invoked right after the object is created and before any other methods that can mutate the state of the object.

The method, to set the default values, must have the type of the object as first parameter. Other parameters are considered input parameters and can be used to set a default value based on other model objects.

In a previous post we worked with a managed object VersionFile. Let's add a default value for the version property. We want the version default value to be the unspecified:

// File: buildSrc/src/main/groovy/mrhaki/gradle/VersionFileTaskRules.groovy
package mrhaki.gradle

import org.gradle.api.Task
import org.gradle.model.Defaults
import org.gradle.model.Model
import org.gradle.model.ModelMap
import org.gradle.model.Mutate
import org.gradle.model.Path
import org.gradle.model.RuleSource

class VersionFileTaskRules extends RuleSource {

    @Model
    void versionFile(final VersionFile versionFile) { }

    /**
     * Method to set default values for {@link VersionFile} object
     * created by the {@link #versionFile} method.
     * 
     * @param versionFile First argument is the type we want to set default values for
     */
    @Defaults
    void defaultsVersionFile(
            final VersionFile versionFile) {

        // Set default value for version property to.
        versionFile.version = 'unspecified'
    }

    @Mutate
    void createVersionFileTask(final ModelMap<Task> tasks, final VersionFile versionFile) {
        tasks.create('generateVersionFile', VersionFileTask) { task ->
            task.version = versionFile.version
            task.outputFile = versionFile.outputFile
        }
    }
    
}

When we run the model task we can see Gradle knows the defaultsVersionFile methods was used to change the state of the VersionFile instance:

$ gradle -q model 
...
+ versionFile
      | Type:           mrhaki.gradle.VersionFile
      | Creator:        VersionFileTaskRules#versionFile(VersionFile)
      | Rules:
         ⤷ VersionFileTaskRules#defaultsVersionFile(VersionFile)
    + outputFile
          | Type:       java.io.File
          | Value:      /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/gradle/versionrule/build/version.txt
          | Creator:    VersionFileTaskRules#versionFile(VersionFile)
    + version
          | Type:       java.lang.String
          | Value:      null
          | Creator:    VersionFileTaskRules#versionFile(VersionFile)
...
$

Written with Gradle 3.2.

November 14, 2016

Gradle Goodness: Show Hidden Model Objects

We use the model task to see which objects are available in the Gradle model space. The model space is managed by Rule based model configuration. Objects can be defined as hidden by the object author(s). By default a hidden object is not shown in the model report. We must use the task option --showHidden to show also the hidden objects in the model report.

$ gradle -q model --showHidden

------------------------------------------------------------
Root project
------------------------------------------------------------

+ buildDir
      | Type:           java.io.File
      | Value:          /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/gradle/versionrule/build
      | Creator:        Project.<init>.buildDir()
+ extensionContainer
      | Type:           org.gradle.api.plugins.ExtensionContainer
      | Creator:        Project.<init>.extensionContainer()
+ fileOperations
      | Type:           org.gradle.api.internal.file.FileOperations
      | Creator:        DefaultProject.BasicServicesRules#fileOperations(ServiceRegistry)
+ instantiator
      | Type:           org.gradle.internal.reflect.Instantiator
      | Creator:        DefaultProject.BasicServicesRules#instantiator(ServiceRegistry)
+ nodeInitializerRegistry
      | Type:           org.gradle.model.internal.core.NodeInitializerRegistry
      | Creator:        DefaultProject.BasicServicesRules#nodeInitializerRegistry(ModelSchemaStore, StructBindingsStore)
+ projectIdentifier
      | Type:           org.gradle.api.internal.project.ProjectIdentifier
      | Value:          root project 'versionrule'
      | Creator:        Project.<init>.projectIdentifier()
+ proxyFactory
      | Type:           org.gradle.model.internal.manage.instance.ManagedProxyFactory
      | Creator:        DefaultProject.BasicServicesRules#proxyFactory(ServiceRegistry)
+ schemaStore
      | Type:           org.gradle.model.internal.manage.schema.ModelSchemaStore
      | Creator:        DefaultProject.BasicServicesRules#schemaStore(ServiceRegistry)
+ serviceRegistry
      | Type:           org.gradle.internal.service.ServiceRegistry
      | Value:          ProjectScopeServices
      | Creator:        Project.<init>.serviceRegistry()
+ sourceDirectorySetFactory
      | Type:           org.gradle.api.internal.file.SourceDirectorySetFactory
      | Creator:        DefaultProject.BasicServicesRules#sourceDirectorySetFactory(ServiceRegistry)
+ structBindingsStore
      | Type:           org.gradle.model.internal.manage.binding.StructBindingsStore
      | Creator:        DefaultProject.BasicServicesRules#structBindingsStore(ServiceRegistry)
+ taskFactory
      | Type:           org.gradle.api.internal.project.taskfactory.ITaskFactory
      | Creator:        DefaultProject.BasicServicesRules#taskFactory(ServiceRegistry)
+ tasks
      | Type:           org.gradle.model.ModelMap<org.gradle.api.Task>
      | Creator:        Project.<init>.tasks()
      | Rules:
         ⤷ VersionFileTaskRules#createVersionFileTask(ModelMap<Task>, VersionFile)
    + buildEnvironment
          | Type:       org.gradle.api.tasks.diagnostics.BuildEnvironmentReportTask
          | Value:      task ':buildEnvironment'
          | Creator:    tasks.addPlaceholderAction(buildEnvironment)
          | Rules:
             ⤷ copyToTaskContainer
    + components
          | Type:       org.gradle.api.reporting.components.ComponentReport
          | Value:      task ':components'
          | Creator:    tasks.addPlaceholderAction(components)
          | Rules:
             ⤷ copyToTaskContainer
    + dependencies
          | Type:       org.gradle.api.tasks.diagnostics.DependencyReportTask
          | Value:      task ':dependencies'
          | Creator:    tasks.addPlaceholderAction(dependencies)
          | Rules:
             ⤷ copyToTaskContainer
    + dependencyInsight
          | Type:       org.gradle.api.tasks.diagnostics.DependencyInsightReportTask
          | Value:      task ':dependencyInsight'
          | Creator:    tasks.addPlaceholderAction(dependencyInsight)
          | Rules:
             ⤷ HelpTasksPlugin.Rules#addDefaultDependenciesReportConfiguration(DependencyInsightReportTask, ServiceRegistry)
             ⤷ copyToTaskContainer
    + dependentComponents
          | Type:       org.gradle.api.reporting.dependents.DependentComponentsReport
          | Value:      task ':dependentComponents'
          | Creator:    tasks.addPlaceholderAction(dependentComponents)
          | Rules:
             ⤷ copyToTaskContainer
    + generateVersionFile
          | Type:       mrhaki.gradle.VersionFileTask
          | Value:      task ':generateVersionFile'
          | Creator:    VersionFileTaskRules#createVersionFileTask(ModelMap<Task>, VersionFile) > create(generateVersionFile)
          | Rules:
             ⤷ copyToTaskContainer
    + help
          | Type:       org.gradle.configuration.Help
          | Value:      task ':help'
          | Creator:    tasks.addPlaceholderAction(help)
          | Rules:
             ⤷ copyToTaskContainer
    + init
          | Type:       org.gradle.buildinit.tasks.InitBuild
          | Value:      task ':init'
          | Creator:    tasks.addPlaceholderAction(init)
          | Rules:
             ⤷ copyToTaskContainer
    + model
          | Type:       org.gradle.api.reporting.model.ModelReport
          | Value:      task ':model'
          | Creator:    tasks.addPlaceholderAction(model)
          | Rules:
             ⤷ copyToTaskContainer
    + projects
          | Type:       org.gradle.api.tasks.diagnostics.ProjectReportTask
          | Value:      task ':projects'
          | Creator:    tasks.addPlaceholderAction(projects)
          | Rules:
             ⤷ copyToTaskContainer
    + properties
          | Type:       org.gradle.api.tasks.diagnostics.PropertyReportTask
          | Value:      task ':properties'
          | Creator:    tasks.addPlaceholderAction(properties)
          | Rules:
             ⤷ copyToTaskContainer
    + tasks
          | Type:       org.gradle.api.tasks.diagnostics.TaskReportTask
          | Value:      task ':tasks'
          | Creator:    tasks.addPlaceholderAction(tasks)
          | Rules:
             ⤷ copyToTaskContainer
    + wrapper
          | Type:       org.gradle.api.tasks.wrapper.Wrapper
          | Value:      task ':wrapper'
          | Creator:    tasks.addPlaceholderAction(wrapper)
          | Rules:
             ⤷ copyToTaskContainer
+ typeConverter
      | Type:           org.gradle.internal.typeconversion.TypeConverter
      | Creator:        DefaultProject.BasicServicesRules#typeConverter(ServiceRegistry)
+ versionFile
      | Type:           mrhaki.gradle.VersionFile
      | Creator:        VersionFileTaskRules#versionFile(VersionFile)
      | Rules:
         ⤷ versionFile { ... } @ build.gradle line 8, column 5
    + outputFile
          | Type:       java.io.File
          | Value:      /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/gradle/versionrule/build/version.file
          | Creator:    VersionFileTaskRules#versionFile(VersionFile)
    + version
          | Type:       java.lang.String
          | Value:      1.0.1.RELEASE
          | Creator:    VersionFileTaskRules#versionFile(VersionFile)
$

Written with Gradle 3.2.

Gradle Goodness: Get Model Report In Short Format

The Gradle model task shows the objects in the model space of Gradle. The output shows the object hierarchy. By default a full report is shown, with a lot of information. We can customize the output format with the --format task argument. The default value is full, but we can also use the value short. With the value short a lot less information is shown.

Let's see the output of the model task for a sample project:

$ gradle -q model
------------------------------------------------------------
Root project
------------------------------------------------------------

+ tasks
      | Type:           org.gradle.model.ModelMap<org.gradle.api.Task>
      | Creator:        Project.<init>.tasks()
      | Rules:
         ⤷ VersionFileTaskRules#createVersionFileTask(ModelMap<Task>, VersionFile)
    + buildEnvironment
          | Type:       org.gradle.api.tasks.diagnostics.BuildEnvironmentReportTask
          | Value:      task ':buildEnvironment'
          | Creator:    tasks.addPlaceholderAction(buildEnvironment)
          | Rules:
             ⤷ copyToTaskContainer
    + components
          | Type:       org.gradle.api.reporting.components.ComponentReport
          | Value:      task ':components'
          | Creator:    tasks.addPlaceholderAction(components)
          | Rules:
             ⤷ copyToTaskContainer
    + dependencies
          | Type:       org.gradle.api.tasks.diagnostics.DependencyReportTask
          | Value:      task ':dependencies'
          | Creator:    tasks.addPlaceholderAction(dependencies)
          | Rules:
             ⤷ copyToTaskContainer
    + dependencyInsight
          | Type:       org.gradle.api.tasks.diagnostics.DependencyInsightReportTask
          | Value:      task ':dependencyInsight'
          | Creator:    tasks.addPlaceholderAction(dependencyInsight)
          | Rules:
             ⤷ HelpTasksPlugin.Rules#addDefaultDependenciesReportConfiguration(DependencyInsightReportTask, ServiceRegistry)
             ⤷ copyToTaskContainer
    + dependentComponents
          | Type:       org.gradle.api.reporting.dependents.DependentComponentsReport
          | Value:      task ':dependentComponents'
          | Creator:    tasks.addPlaceholderAction(dependentComponents)
          | Rules:
             ⤷ copyToTaskContainer
    + generateVersionFile
          | Type:       mrhaki.gradle.VersionFileTask
          | Value:      task ':generateVersionFile'
          | Creator:    VersionFileTaskRules#createVersionFileTask(ModelMap<Task>, VersionFile) > create(generateVersionFile)
          | Rules:
             ⤷ copyToTaskContainer
    + help
          | Type:       org.gradle.configuration.Help
          | Value:      task ':help'
          | Creator:    tasks.addPlaceholderAction(help)
          | Rules:
             ⤷ copyToTaskContainer
    + init
          | Type:       org.gradle.buildinit.tasks.InitBuild
          | Value:      task ':init'
          | Creator:    tasks.addPlaceholderAction(init)
          | Rules:
             ⤷ copyToTaskContainer
    + model
          | Type:       org.gradle.api.reporting.model.ModelReport
          | Value:      task ':model'
          | Creator:    tasks.addPlaceholderAction(model)
          | Rules:
             ⤷ copyToTaskContainer
    + projects
          | Type:       org.gradle.api.tasks.diagnostics.ProjectReportTask
          | Value:      task ':projects'
          | Creator:    tasks.addPlaceholderAction(projects)
          | Rules:
             ⤷ copyToTaskContainer
    + properties
          | Type:       org.gradle.api.tasks.diagnostics.PropertyReportTask
          | Value:      task ':properties'
          | Creator:    tasks.addPlaceholderAction(properties)
          | Rules:
             ⤷ copyToTaskContainer
    + tasks
          | Type:       org.gradle.api.tasks.diagnostics.TaskReportTask
          | Value:      task ':tasks'
          | Creator:    tasks.addPlaceholderAction(tasks)
          | Rules:
             ⤷ copyToTaskContainer
    + wrapper
          | Type:       org.gradle.api.tasks.wrapper.Wrapper
          | Value:      task ':wrapper'
          | Creator:    tasks.addPlaceholderAction(wrapper)
          | Rules:
             ⤷ copyToTaskContainer
+ versionFile
      | Type:           mrhaki.gradle.VersionFile
      | Creator:        VersionFileTaskRules#versionFile(VersionFile)
      | Rules:
         ⤷ versionFile { ... } @ build.gradle line 8, column 5
    + outputFile
          | Type:       java.io.File
          | Value:      /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/gradle/versionrule/build/version.file
          | Creator:    VersionFileTaskRules#versionFile(VersionFile)
    + version
          | Type:       java.lang.String
          | Value:      1.0.1.RELEASE
          | Creator:    VersionFileTaskRules#versionFile(VersionFile)
$

Now we use the short format:

$ gradle -q model --format=short
------------------------------------------------------------
Root project
------------------------------------------------------------

+ tasks
    | buildEnvironment = task ':buildEnvironment'
    | components = task ':components'
    | dependencies = task ':dependencies'
    | dependencyInsight = task ':dependencyInsight'
    | dependentComponents = task ':dependentComponents'
    | generateVersionFile = task ':generateVersionFile'
    | help = task ':help'
    | init = task ':init'
    | model = task ':model'
    | projects = task ':projects'
    | properties = task ':properties'
    | tasks = task ':tasks'
    | wrapper = task ':wrapper'
+ versionFile
    | outputFile = /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/gradle/versionrule/build/version.file
    | version = 1.0.1.RELEASE
$

Written with Gradle 3.2.