How can i do convention mappings from java without depending on an internal api?


(Jelmer Kuperus) #1

I am developing a plugin using java, not groovy and i want to make use of conventions for tasks.

So inially I made my tasks extend from ConventionTask but then i became aware that Gradle has the notion of a public and private api and ConventionTask and ConventionMapping are part of the private api and as such should not be depended upon by third party plugins. What more it seems that many classes (like DefaultTask) now automatically implement IConventionAware through the of groovy magic

How can i do convention mappings from java without depending on an internal api ?


(Peter Niederwieser) #2

Convention mapping isn’t currently part of the public API, neither in Groovy nor in Java. Some alternatives are to use callbacks like ‘gradle.projectsEvaluated’ or ‘task.doFirst’.


(Jelmer Kuperus) #3

I am not sure how projectsEvaluated or task.doFirst can be used as a replacement for convention mapping.

Convention mappings are used to define sensible defaults, but still allow a consumer to override these defaults.

I tried adding a task to my build.gradle file like so :

task testje(type: com.bla.Task) {
 somevalue = "test"
}

Then in my plugin wrote a block like so :

project.getGradle().addBuildListener(new BuildAdapter() {
 @Override
 public void projectsEvaluated(Gradle gradle) {

It seems that at the point projectsEvaluated is called , that somevalue is already set to test, so i would overwrite it if i would apply my defaults from within this callback


(Peter Niederwieser) #4

Of course you’ll have to check whether the value has been set. For example, the task could initialize it to null, and the callback could set it to a default if it is still null.

Defaults that don’t need to be evaluated lazily can (and should) be set directly at task creation time, either by the task itself or by the plugin that adds the task.

I’m not saying that using callbacks like ‘gradle.projectsEvaluated’ is a perfect replacement for convention mapping. I’m saying it’s the best you can currently do if you want to stick to public APIs. Before using either of these techniques, make sure that the property at hand really needs to be configured lazily.


(Jelmer Kuperus) #5

Yes, in my case yes the defaults need to be lazily evaluated

But i see what you mean, so the idiom is : in your projectsEvaluated callback check if the value is the tasks default value (most often null), if it is then apply your convention and otherwise leave it untouched

I can see cases where this would lead to some ugly code but in my case i think this will work out fine. Thanks for your help.


(sander) #6

Hi guys,

so a year has passed… is it getting any better? I’ve been pulling my hair with Gradle 1.6 for the whole day about how to get rid of conventionMapping closures to turn one Gradle plugin that has 95% Java code and 5% of Groovy code into a completely Java-only plugin, but it currently looks like an impossible task. Yes i need lazy evaluation of some properties… where do i need to evaluate them then? Can I just put them into the beginning of my @TaskAction method (probably strongly against your lifecycles philosophy), or is there some other negative impact i would get by doing that?

Other thing I really don’t understand, probably becaues i’m stupid or don’t know Groovy very well…

private configure(Project project) {
    project.getPlugins().withType(WarPlugin) {
       foo();
    }
  }

If there is no WarPlugin in the plugins list at the moment when configure() executes, but it gets added to the list later, somehow foo() still executes. How on earth is this possible… isn’t the closure with foo() in it executed immediately when i call withType()? Too much magic for me :frowning:


(Peter Niederwieser) #7

We haven’t yet found an appropriate replacement for convention mapping but keep on trying. Note that Java code can use convention mapping as well - it’s only more verbose.

To answer your question, ‘withType(Foo) { … }’ is shorthand for ‘withType(Foo).all { … }’, and the whole point of having an ‘all’ method (besides Groov’s ‘each’ method) on Gradle’s ‘DomainObjectCollection’'s is to also “catch” elements subsequently added to the collection. It’s yet another tool to remain independent of evaluation order, and again you can also use it from Java.


(sander) #8

Thanks a lot for quick and good answer! Would it be too much to ask how could i use conventionsMappings from java? Call it with reflection API somehow…? Lets say i currently have the Groovy code:

generateRebelTask.conventionMapping.warSourceDirectory = {
        RebelPluginExtension rebelExtension = (RebelPluginExtension) project.getExtenions().getByName(REBEL_EXTENSION_NAME);
        rebelExtension.getWarSourceDirectory() ? project.file(rebelExtension.getWarSourceDirectory()) : project.webAppDir;
      }

(Peter Niederwieser) #9

Have a look at, say, JavaPlugin.java in the Gradle codebase.


(sander) #10

Works like a charm : )) Thanks a lot for the most responsive support and keep up the good work … gradle rocks! :slight_smile:


(sander) #11

The java syntax works, but one still has to a) compile it with a groovy compiler (you have the JavaPlugin.java also under src/main/groovy) b) call the getConventionMapping() method with reflection , is that right?

After switching to Java compiler, it still went red, as this method is not there, you probably add it with some groovy magic. Should I take the words of the original starter of this topic as truth – that conventionMappings are internal and should not be depended upon by 3rd party plugin writers?

Maybe i’m better off evaluating all my properties in the beginnig of my @TaskAction method still… i’m quite sure the nature of our plugin (ZeroTurnaround’s JRebel integration) is such that nobody else needs to depend on our plugin in turn, so it seems it shouldn’t bother anybody whatever moment i’m gonna evaluate my stuff(?)


(Luke Daley) #12

The java syntax works, but one still has to

a) compile it with a groovy compiler (you have the JavaPlugin.java also under src/main/groovy)

b) call the getConventionMapping() method with reflection, is that right?

No, you don’t need to do anything special.

After switching to Java compiler, it still went red, as this method is not there, you probably add it with some groovy magic.

The method is added dynamically at runtime. You need to cast the object to IConventionAware.

Should I take the words of the original starter of this topic as truth – that conventionMappings are internal and should not be depended upon by 3rd party plugin writers?

They are internal, so may change in future Gradle versions.

Maybe i’m better off evaluating all my properties in the beginnig of my @TaskAction method still… i’m quite sure the nature of our plugin (ZeroTurnaround’s JRebel integration) is such that nobody else needs to depend on our plugin in turn, so it seems it shouldn’t bother anybody whatever moment i’m gonna evaluate my stuff(?)

The problem with this approach is that it’s too late to influence incremental build or task graph building.

You may be better off using Project.afterEvaluate().


(sander) #13

Thanks a lot for your help :slight_smile: