
It's no secret that one of the most common properties ever set on a Maven build is -DskipTests
which as the name implies, instructs Maven to skip running tests. As a matter of fact that property is read by the maven-surefire-plugin
to figure out if it should run tests or not. As it turns out there's a separate property named skip that instructs Maven to skip compiling and running tests. If you're curious and peek at the documentation you may notice that the field/property names for skipTest
match but for skip
you'll find a property named maven.test.skip
. As it happens, both properties are exposed to the outside world using the @Parameter
annotation, as shown next
@Parameter( property = "skipTests", defaultValue = "false" ) protected boolean skipTests; @Parameter( property = "maven.test.skip", defaultValue = "false" ) protected boolean skip;
While the name of the field may be used inside the plugin's <configuration>
block to configure its behavior, the value of the property attribute can be used as either a project property (defined inside a <properties>
block) or a System property defined in the command prompt with -D
for example. Let's follow up with the echo-maven-plugin, whose EchoMojo class defines the following field
@Parameter(property = "echo.message") private String message;
The echo plugin can be configured in a pom.xml file like so
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.acme</groupId> <artifactId>sample</artifactId> <version>0.0.0-SNAPSHOT</version> <build> <plugins> <plugin> <groupId>com.github.ekryd.echo-maven-plugin</groupId> <artifactId>echo-maven-plugin</artifactId> <version>1.2.0</version> <inherited>false</inherited> <executions> <execution> <id>echo</id> <goals> <goal>echo</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
The custom echo
goal can be invoked by passing an additional argument in the command prompt
Great, we have successfully proved that the plugin can read a configured property from the command prompt. We can also define the input for the echo
goal as a project property, as shown by the next pom.xml file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.acme</groupId> <artifactId>sample</artifactId> <version>0.0.0-SNAPSHOT</version> <properties> <echo.message>Hello from pom.xml</echo.message> </properties> <build> <plugins> <plugin> <groupId>com.github.ekryd.echo-maven-plugin</groupId> <artifactId>echo-maven-plugin</artifactId> <version>1.2.0</version> <inherited>false</inherited> <executions> <execution> <id>echo</id> <goals> <goal>echo</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
As you can appreciate the value of the echo.message
property is defined inside a <properties>
block. The build can now be invoked like this
We can even override the value from the command prompt, proving that System properties win over project properties
Now, let's see what happens when a explicit value is defined in the plugin's configuration, like it's shown by the following file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.acme</groupId> <artifactId>sample</artifactId> <version>0.0.0-SNAPSHOT</version> <properties> <echo.message>Hello from pom.xml</echo.message> </properties> <build> <plugins> <plugin> <groupId>com.github.ekryd.echo-maven-plugin</groupId> <artifactId>echo-maven-plugin</artifactId> <version>1.2.0</version> <inherited>false</inherited> <executions> <execution> <id>echo</id> <goals> <goal>echo</goal> </goals> <configuration> <message>hello</message> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
Let's attempt overriding the value using a project property
Hmm no luck. What if we switch to a System property instead? Let's see
No luck either. As a matter of fact there's an ongoing thread at the Maven mailing list regarding this specific scenario. For some the property should be overridable, for others the behavior shown here is how the feature should work.
Switching to Gradle now. Some major releases ago (might been around Gradle 3) a feature was added that lets task authors accept values that may have been defined on the command prompt, effectively letting consumers define values on the go instead of inside the build file. This feature is the @Option
annotation. Say we find an equivalent echo plugin for Gradle, it could define a message property in the following manner
Property<String> message = project.objects.property(String).convention('') @Option(option='echo-message', description = 'The message to write') void setMessage(String message) { getMessage().set(message) } @Input Property<String> getMessage() { this.message }
Once the plugins is applied in a build file, as shown next, we can invoke the echo task using the echo-message
option.
plugins { id 'org.kordamp.gradle.echo' version '0.40.0' }
So far so good. Gradle let's you define values for task properties either explicitly in the build file or passing an explicit flag in the command prompt. However it does not let you supply values via System properties or project properties. Also, the naming conventions that govern option names exclude characters that are not alphanumeric nor '-' (dash/hyphen) or '_' (underscore), thus a '.' (dot/period) can't be used as a separator. For this reason the Kordamp Gradle Plugin suite provides an API to solve these issues. Following an idiomatic pattern of pairing a Property
(write access) with a Provider
(read access) plugin authors now have more choices at their disposal. The EchoTask can be written as
import groovy.transform.CompileStatic import org.gradle.api.DefaultTask import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.options.Option import org.kordamp.gradle.property.SimpleStringState import org.kordamp.gradle.property.StringState @CompileStatic class EchoTask extends DefaultTask { private final StringState message EchoTask() { message = SimpleStringState.of(this, 'echo.message', '') } @Option(option='echo-message', description = 'The message to write') void setMessage(String message) { getMessage().set(message) } @Internal Property<String> getMessage() { message.property } @Input @Optional Provider<String> getResolvedMessage() { message.provider } @TaskAction void echo() { println getResolvedMessage().get() } }
With this code in place we can apply the echo plugin and pass parameters on the command line using either System or project properties
plugins { id 'org.kordamp.gradle.echo' version '0.40.0' } project.ext.set('echo.message', 'Hello from build.gradle')
Note that the last build file had to use a different syntax to set the project property because of the '.' (dot/period) in the property's name. Overriding a project property with a System property, like it's done in Maven, it's also possible as shown next
However overriding an explicit value defined directly in the task's configuration proves to be elusive, like it is in Maven
plugins { id 'org.kordamp.gradle.echo' version '0.41.0-SNAPSHOT' } echo { message = 'Hello from task' }
Bummer. Or is it? The APIs exposed by Kordamp assume that external configuration can occur if no explicit value is set, but this behavior can be changed with yet another System property that may be set on the command prompt, the local gradle.properties
file, the user's global gradle.properties
file, even an init script.
A few more additional things not mentioned before
- Environment variables may also be used as value sources, not just System and project properties.
- The resolution order between Environment, System, and Project can be changed.
- Property names can be set with their owning Task or Project path as prefix, such that an Echo task set on the root project with name
shout
will check forshout.echo.message
beforeecho.message
.
There you have it. Even though Gradle offers some support for overriding task properties, the Kordamp APIs give you more options to customize task behavior.
Keep on Coding!