Loading...

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 8086.

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.

Gails 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