Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Gradle Goodness: Validate Model In Rule Based Model Configuration

DZone's Guide to

Gradle Goodness: Validate Model In Rule Based Model Configuration

Gradle allows for rule based configuration for builds of JVM projects in Java, Groovy, Scala and more.

· Java Zone
Free Resource

Learn how to troubleshoot and diagnose some of the most common performance issues in Java today. Brought to you in partnership with AppDynamics.

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)
...
$


Understand the needs and benefits around implementing the right monitoring solution for a growing containerized market. Brought to you in partnership with AppDynamics.

Topics:
gradle ,rule based model configuration ,java ,validation

Published at DZone with permission of Hubert Klein Ikkink, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}