Thomas Sundberg

March 22, 2015

A Gradle plugin written in Java

Filed under: Automation, Gradle, Java, Tools — Tags: , , , , — Thomas Sundberg @ 15:28

This post has been migrated to

Gradle is a build automation system. You write your build script in Groovy. This is different compared to other build system such as Ant or Maven. They both use xml. Using Groovy instead of xml gives you a lot of benefits. You have an entire programming language at your disposal. This mean that you can easily customize the build behaviour.

If you, however, want to be able to do the same thing in many projects, it may be a good idea to write a plugin that you can refer to from other projects. I will show you, step by step, how to implement a Hello World Gradle plugin.

Setup the Gradle infrastructure

Create a directory and create a file called build.gradle in it. Add the content below to this new file.


group = 'se.thinkcode.gradle' version = '1.0.0-SNAPSHOT' apply plugin: 'groovy' apply plugin: 'maven' sourceCompatibility = 1.7 dependencies { compile gradleApi() testCompile 'junit:junit:4.12' } repositories { mavenCentral() mavenLocal() }

This build script (almost) defines the project.

I start with defining a group and version that should be used when the plugin is referred to later.

This is then followed by applying two plugins, the Groovy and maven plugins. The Groovy plugin will allow me to write Groovy code. The Maven plugin will give me access to an install task as well as the Java stack.

I want to tie this project to Java 1.7, so I specify the source code compatibility to be 1.7.

The next section defines the dependencies for this project. I have two dependencies in this example but it is obviously possible to have many more if you need them.

The magic is contained in the gradleApi() dependency. It will give us the things needed to create a Gradle plugin.

The second dependency is the well known testing framework junit. I use it to test the plugin.

The last thing I do in this example is to define that I want access to the Maven repository as well as my local Maven repository. I use The Central Repository to get access to JUnit. I use the local Maven repository to make this plugin available on my computer for a test project I will show you below.

The last part I want to have control over is the name of the project. It is called artifact id in Maven. It would have been nice to be able to define it in build.gradle, but that is not possible. Instead, the name will default to the directory where this project lives. If I want, I can define it to something else by setting the property in settings.gradle as I do below.

settings.gradle = 'demoPlugin'

This is the infrastructure needed.

Create the Plugin

The plugin is defined in a class called DemoPlugin. My example looks like this:


package se.thinkcode; import org.gradle.api.Plugin; import org.gradle.api.Project; public class DemoPlugin implements Plugin<Project> { @Override public void apply(Project project) { project.getExtensions().create("demoSetting", DemoPluginExtension.class); project.getTasks().create("demo", DemoTask.class); } }

A Gradle plugin is an extension to Gradle. The extension is defined in the apply method. It is defined with a name, demoSetting, and a class DemoPluginExtension that will hold default values. The default values will be used if the plugin user doesn’t change any values in the configuration section demoSetting in a build script.

A task is also defined. It is called demo and the executing class is defined as DemoTask.

This is all we need to create a new extension and a new task. The magic that makes this a Gradle plugin is the existence of a property file in META-INF/gradle-plugins. The name of the property file will be the name of the plugin to apply in your build script as I will show later.

Let’s create a file called It will connect the name se.thinkcode.demo.plugin to the implementation se.thinkcode.DemoPlugin. This will allow you to apply the plugin se.thinkcode.demo.plugin later.



Now it’s time to take a look at the implementation of the two classes used in the DemoPlugin above.

Defining the default value holder

The extension class holds the default values that will be used, if the user of the plugin doesn’t change any settings. It look like this:


package se.thinkcode; public class DemoPluginExtension { private String message = "Default Greeting from Gradle"; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }

It is a plain POJO and very uninteresting in many senses. It is, however, interesting to know that we have now left Gradle land and we are in Java POJO land.

Implementing an actual Task

What about the task then, is it anything special? Our implementation looks like this:


package se.thinkcode; import org.gradle.api.DefaultTask; import org.gradle.api.tasks.TaskAction; public class DemoTask extends DefaultTask { @TaskAction public void greet() { DemoPluginExtension extension = getProject().getExtensions().findByType(DemoPluginExtension.class); if (extension == null) { extension = new DemoPluginExtension(); } String message = extension.getMessage(); HelloWorld helloWorld = new HelloWorld(message); System.out.println(helloWorld.greet()); } }

It inherits a DefaultTask and implements a method with an annotation that defines that this is the method that will be executed when the user calls the task demo. This is the method that actually does something. In our case, it looks for an extension and delegates to a HelloWorld class that will create a message that the user will be greeted with. If the plugin user hasn’t added any extension in build.gradle, an instance of the default extension will be created and the default values will be used.

The HelloWorld class could obviously have been skipped in this small case, but I wanted to show you how you can connect your own implementation into a Gradle plugin.

What about testing?

The plugin testing is done using two unit tests written in Groovy. My custom model, HelloWorld, is done using unit tests written in Java.

Testing the plugin can be done like this:


package se.thinkcode import org.junit.Test import org.gradle.testfixtures.ProjectBuilder import org.gradle.api.Project import static org.junit.Assert.* class DemoPluginTest { @Test public void demo_plugin_should_add_task_to_project() { Project project = ProjectBuilder.builder().build() project.getPlugins().apply 'se.thinkcode.demo.plugin' assertTrue(project.tasks.demo instanceof DemoTask) } }

I don’t do much more than verifying that applying the plugin se.thinkcode.demo.plugin gives me access to a task called demo that is implemented in an instance of DemoTask.

Testing the DemoTask can be done as below::


package se.thinkcode import org.junit.Test import org.gradle.testfixtures.ProjectBuilder import org.gradle.api.Project import static org.junit.Assert.* class DemoTaskTest { @Test public void should_be_able_to_add_task_to_project() { Project project = ProjectBuilder.builder().build() def task = project.task('demo', type: DemoTask) assertTrue(task instanceof DemoTask) } }

I verify that I am able to create a task called demo using a class DemoTask and that I get a task of the type DemoTask back.

These two tests verify the wiring of the plugin. The last thing to do is to verify the actual behaviour. It is done in a regular Java unit test that verifies HelloWorld.


package se.thinkcode; import org.junit.Test; import static; import static org.junit.Assert.assertThat; public class HelloWorldTest { @Test public void greet_the_user() { String greeting = "Hello World!"; HelloWorld helloWorld = new HelloWorld(greeting); String actualGreeting = helloWorld.greet(); assertThat(actualGreeting, is(greeting)); } }

A unit test that verifies that it is possible to return a value given in the constructor. We might have been able to cope without it, but now you know how to incorporate a unit test in this setup.

The files used to build this Gradle plugin

These are all the files I have used to create this plugin:

|-- build.gradle
|-- settings.gradle
`-- src
    |-- main
    |   |-- java
    |   |   `-- se
    |   |       `-- thinkcode
    |   |           |--
    |   |           |--
    |   |           |--
    |   |           `--
    |   `-- resources
    |       `-- META-INF
    |           `-- gradle-plugins
    |               `--
    `-- test
        |-- groovy
        |   `-- se
        |       `-- thinkcode
        |           |-- DemoPluginTest.groovy
        |           `-- DemoTaskTest.groovy
        `-- java
            `-- se
                `-- thinkcode

How do you build it?

With all this implemented, how do you actually go about and build the plugin? The answer is, execute

gradle build install

from a command prompt or similar. This will build the project, execute all the tests and finally install it in the local Maven repository.

Next step is to actually use the plugin.

How do you use it?

The last interesting piece is to use the new plugin. It is done from a Gradle project. An example is this:


apply plugin: 'se.thinkcode.demo.plugin' demoSetting { message = "Hi from an extension" } buildscript { repositories { mavenLocal() } dependencies { classpath 'se.thinkcode.gradle:demoPlugin:1.0.0-SNAPSHOT' } }

There are three important parts here.

Firstly, I apply the plugin. That is making sure that Gradle is aware of something called se.thinkcode.demo.plugin.

Secondly I define my own extension, that is changing the message the user will be greeted with. This will override the default values. If you skip this section, the default values will be used instead.

Thirdly I get hold of the dependency that actually implement the plugin. I define that this build script should use the mavenLocal() repository. This is where the plugin was installed when I executed install earlier. I also define the name and version of the dependency that contains the implementation, classpath 'se.thinkcode.gradle:demoPlugin:1.0.0-SNAPSHOT'.

We recognise the group, name and version from earlier.

Executing gradle tasks show us that there is now a task defined that is called demo.

Executing gradle demo should result in something like this:

Hi from an extension


Remove the demoSetting extension from the consumer project and re-run gradle demo. The result is expected to be similar to this:

Default Greeting from Gradle


That it folks, this is one way to build you own Gradle plugin.


I would like to thank Malin Ekholm, Johan helmfrid, Adrian Bolboaca and Alexandru Bolboaca for proof reading.



  1. Hi Thomas, thanks for the article🙂 Could you please fix the first word? Instead of “Gralde” use “Gradle”.

    Comment by Igor Popov — April 27, 2015 @ 18:33

  2. Have you been in touch with the Gradle developers to have this tutorial added to the product wiki.
    I found the gradle plugin docs quite lacking in detail and variety.
    This would be a valuable addition to the product docs.
    Well done and thanks

    Comment by John Lonergan — June 29, 2015 @ 19:01

    • Hi!

      You are welcome!

      I haven’t been in contact with the Gradle developers. Do you have an url to the wiki where it could be added?


      Comment by Thomas Sundberg — June 29, 2015 @ 22:16

  3. For completeness, can you post the content of please?

    Comment by Martin d'Anjou — December 15, 2015 @ 14:17

    • Hi!

      This would be tha source for the

      package se.thinkcode;
      public class HelloWorld {
          private String greeting;
          public HelloWorld(String greeting) {
              this.greeting = greeting;
          public String greet() {
              return greeting;


      Comment by Thomas Sundberg — December 15, 2015 @ 22:46

  4. Hi Thomas,

    Thanks for posting this. There is a typo on this line.

    def task = project.task(‘demoype: DemoTask)

    should be

    def task = project.task(‘demo’, type: DemoTask)


    Comment by Erwin — April 26, 2016 @ 16:37

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at

%d bloggers like this: