DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
11 Monitoring and Observability Tools for 2023
Learn more
  1. DZone
  2. Coding
  3. Languages
  4. Gradle Plugin Conventions: Groovy Magic Explained

Gradle Plugin Conventions: Groovy Magic Explained

Hamlet D'Arcy user avatar by
Hamlet D'Arcy
·
Apr. 01, 10 · Interview
Like (1)
Save
Tweet
Share
10.44K Views

Join the DZone community and get the full member experience.

Join For Free

Hackergarten last Friday was a big success. About 10 of us (not all Canooies!) stayed up late and created an Announce plugin for Gradle. In the 0.9 release you will be able to announce build events to Twitter, Snarl, and the Ubuntu notification service (sorry, no Growl yet, it is coming). It was fun, educational, and energizing, and you should sign up for the Google Group and come to the next event.

While writing the plugin, we were briefly delayed by a technical detail. The user guide explains how to pass simple parameters to a plugin, but does not explain how to nest parameters within a closure. For instance, in the announce plugin you specify the Twitter username and password within an announce block:

announce {
username 'your-username'
password 'your-password'
}
It turns out to be easy (just not documented, but we will fix that soon). Here is the full gradle build that shows you how to do it, with a detailed explanation of how it works below. As an example, we'll extend the Gradle documentation greeter custom plugin. This whole thing is runnable from the command line with "gradle hello":
apply plugin: GreetingPlugin

greet {
message = 'Hi from Gradle'
}

class GreetingPlugin implements Plugin<Project> {
def void apply(Project project) {

project.convention.plugins.greeting = new GreetingPluginConvention()
project.task('hello') << {
println project.convention.plugins.greeting.message
}
}
}

class GreetingPluginConvention {
String message

def greet(Closure closure) {
closure.delegate = this
closure()
}
}
Starting at the top... there is a property declaration called "message" within a "greet" block. This is a much nicer configuration option than just using build-global variables, and your plugin should do something like this. It is important to understand what this block actually is and how it works. In Groovy, parentheses on method calls are optional and curly braces declare a closure. So this greet block is really an invocation of a method with type "Object greet(Closure)". To read and capture this message variable, you must somewhere declare and wire in this "Object greet(Closure)" method within your plugin.

Moving on to the Plugin declaration. Plugin configuration is captured in something Gradle calls a "Convention" object. It is just a plain old Groovy object, and there is no Gradle magic... all the magic happending is standard Groovy. Let's examine the apply() method closely:
def void apply(Project project) {

project.convention.plugins.greeting = new GreetingPluginConvention()
project.task('hello') &lt;&lt; {
println project.convention.plugins.greeting.message
}
}
During the configuration phase (before any targets are run) you will be given a Project object, and that Project object has a Convention object that holds onto a Map of all the project conventions. Three pieces of Groovy magic: Getters can be accessed with property notation, Map entries can be accessed with dot notation, and Map.put can be called with the = sign. So this line:
project.convention.plugins.greeting = new GreetingPluginConvention()
Is the same as the Java equivalent:
project.getConvention().getPlugins().put("greeting", new GreetingPluginConvention());
During the build, Gradle is going to execute the build script. Any time an unknown property is assigned or an undeclared method is invoked, Gradle will look at this plugins Map and use the respondTo method to check if any of the Plugin conventions accepts that variable. If it does, then your convention object gets invoked and that variable is set. Gradle takes care of translating an unknown method call of "Object greet(Closure)" into a call to your convention object. All that you need to do is make sure the greet(Closure) method is defined properly:
class GreetingPluginConvention {
String message

def greet(Closure closure) {
closure.delegate = this
closure()
}
}

Now it is up to you to dispatch the parameters declared within the closure into fields on your convention object. If you just try to execute the closure then you will receive a nice PropertyMissingException because the "message" field is not declared anywhere. You get around this by defining a "message" field on your object and then setting "this" to the closure's delegate. Groovy method dispatch is flexible (complex?). Closures have a delegate object that will act as a sort of "this" reference when the closure executes. So the message field was out of scope when the closure was created, but adding a delegate makes the message field from the convention object in scope. When the closure assigns message a value, it will be the message on your Convention object. Check out my old blog post Fun with Closures for a more in depth explanation.

Now your convention object has the message String, hooray! You can do something interesting with it, like printing it out to the console:

println project.convention.plugins.greeting.message

Or maybe you had something more interesting in mind for your cool plugin. Let us hope.

By the way, you can just as easily declare a settings variable. This is an equivilent implementation of apply:

def settings = new GreetingPluginConvention()
project.convention.plugins.greeting = settings
project.task('hello') << {
println settings.message
}

And now there is just one last piece of Groovy magic left unexplained:

apply plugin: GreetingPlugin

This too is plain old Groovy. Method parenthesis are optional. Method parameters can be passed in as name value pairs. And Class literals can be referenced without the .class extension. The apply plugin statement is really just a plain old method call in disguise, and it is an invocation of apply(Map). The apply statement can be written in Java as:

Map map = new HashMap();
map.put("plugin", GreetingPlugin.class);
apply(map);
 

Groovy magic is good. Thanks for taking some time to understand it. Hopefully this example makes it to the Gradle user guide in the next few days.

From http://hamletdarcy.blogspot.com/2010/03/gradle-plugin-conventions-groovy-magic.html

Gradle Groovy (programming language) Object (computer science)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Spinnaker vs. Argo CD: Best Tools for Continuous Delivery
  • Keep Your Application Secrets Secret
  • Unlock the Power of Terragrunt’s Hierarchy
  • Journey to Event Driven, Part 1: Why Event-First Programming Changes Everything

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: