EarPluginConvention and build.gradle script DeploymentDescriptor are not the same?

Hello,

I am trying to build a convinience plugin based on top of the gradles ear plugin. For simplicity reasons, lets say I’m only trying to set initializeInOrder=true.

What I expected was to be able to do that:

public class MyEarPlugin {
    @Override
    public void apply(final Project project) {
        pluginManager.apply(EarPlugin.class);
        project.getConvention().getPlugin(EarPluginConvention.class).getDeploymentDescriptor().setInitializeInOrder(true);
    }
}

But this works only as long as I do not add a deploymentDescriptor configuration to my build.gradle file:

apply plugin: 'MyEarPlugin'
ear {
    deploymentDescriptor {
    	securityRole {
            roleName = 'User'
            description = 'the ordinary guy'
        }
    }
}

As soon as I do that the configuration from my plugin is gone.
It took me very long to recognize that the convention from my plugin is another object instance than the one from the script. So I’ve written this code:

apply plugin: 'MyEarPlugin'
ear {
    deploymentDescriptor {

        org.my.EarUtil.configure(delegate)

    	securityRole {
            roleName = 'User'
            description = 'the ordinary guy'
        }
    }
}
public class EarUtil {
    public static void configure(DeploymentDescriptor descriptor){
        // FIXME: why is that so?
        final EarPluginConvention earPluginConvention = project.getConvention().getPlugin(EarPluginConvention.class);
        Ear task = (Ear) project.getTasksByName("ear",false).iterator().next();
        DeploymentDescriptor taskDeploymentDescriptor = task.getDeploymentDescriptor();
        //just some debugging
        System.out.println("Script descriptor: " + deploymentDescriptor);
        System.out.println("Task descriptor: " + taskDeploymentDescriptor);
        System.out.println("Convention descriptor: " + earPluginConvention.getDeploymentDescriptor());
        // now it works
        descriptor.setInitializeInOrder(true);
        // feels like a very dirty hack
        earPluginConvention.setDeploymentDescriptor(deploymentDescriptor);
    }
}

The hacky part is nessesary for other parts of my real plugin to be able to read the users configurations later in the configuration phase (actually I need them in the eclipse.wtp.component.file.whenMerged(action)).

Here’s the output of the System.outs:

Script descriptor: org.gradle.plugins.ear.descriptor.internal.DefaultDeploymentDescriptor_Decorated@5e68108a
Task descriptor: org.gradle.plugins.ear.descriptor.internal.DefaultDeploymentDescriptor_Decorated@17f9b169
Convention descriptor: org.gradle.plugins.ear.descriptor.internal.DefaultDeploymentDescriptor_Decorated@17f9b169

But I don’t understand why this is even a thing and why the convention and task have another deploymentDescriptor instance than the build.gradle script.
Could anyone point me into the right direction? I’ve found nothing in the plugin development guide and I’m not sure if I missunderstood some core concepts of the gradle configuration lifecycle.

kind regards
Daniel

I agree that this is confusing.

What’s going on is that the deploymentDescriptor from the EarPluginConvention is the “default” deploymentDescriptor an Ear task uses if no other configuration has been provided.

As soon as you configure the deploymentDescriptor for the task, this creates a task only deploymentDescriptor so it can be configured separately from the default. We should probably copy all of the properties from the convention at that point in time.

In MyEarPlugin, I think just doing earTask.setDeploymentDescriptor(earPluginConvention.getDeploymentDescriptor()) will be enough to force there to be one instance of the deploymentDescriptor (the one from the convention). The downside to this is if you have more than one Ear task, you need to think if you want one configuration or not.

Alternatively, you could also ignore the deploymentDescriptor in EarPluginConvention and just configure the deploymentDescriptor in the Ear task directly (with the Closure/Action method), that will create a new instance for the Ear task before the build script does.

In Gradle 7.1, we’ve deprecated EarPluginConvention because conventions and convention mapping (what’s causing this) has strange behavior like this.

Thank you for the hint, that fixed my initial problem and after all we never thought of multiple Ear tasks in one project, so the convention is kind of useless for us.