Extend custom war task and setting values for use in constructor

I would like to extend the war task and have the consumer set properties on the custom task, AND the values have to be available in the constructor. Please see below for an example.

class RdsWarTask extends War {
   private String directoryName
   RdsWarTask(){
  super()
                //always prints null
                println "directoryName=" + directoryName
                ...
         }
          @TaskAction
        def doTask() {
                println "directoryName2=" + directoryName
        }

Consumer code

task bentallProd(type: com.refineddata.RdsWarTask){
  directoryName = 'env/bentallProd'
}

This is what I get:

>gradle bentallProd
directoryName = null
directoryName2 = 'env/bentallProd'

The problem stems from code like the following needing to be in the constructor:

from (dirName){
      exclude 'web.xml.properties'
      include '*.properties'
      into '/WEB-INF/classes'
    }

If the code above is in the constructor, war is built perfectly if directoryName is hardcoded. But not when I try and specify directoryName from the consumer (because its null). However, if I move the code to the @TaskAction method, then the dirName is set from the consumer, but the war will not include the properties files I need inside. So, I’m kinda darned if I do, darned if I don’t…

Any help appreciated! :slight_smile:

Mark

I would like to extend the war task and have the consumer set properties on the custom task, AND the values have to be available in the constructor.

That’s impossible (as in regular OO programming). You are probably attacking the problem from the wrong angle.

Well, I’m actually more focused on the end result rather than the constructor, I was just trying to be clear with the problem. Let me put this another way:

  1. is it feasible to extend the built in war task and override its attributes? If so, is there an example of such?

  2. The following code will only work correctly in the constructor, why is that? Is there a way around

from (dirName){
      exclude 'web.xml.properties'
      include '*.properties'
      into '/WEB-INF/classes'
    }

Thank you,

Mark

ad 1. It’s uncommon because most built-in task types are not designed for inheritance. Typically it’s better to write a plugin (which, in the simplest case, is just another build script) or, if necessary, a replacement for the War task.

ad 2. Such code needs to be evaluated in the configuration phase rather than in the execution phase of the build. The main challenge here is that any access to user-configurable values like ‘dirName’ needs to be deferred until the end of the configuration phase. (Until then, the values might still change.) A simple way to do so is to wrap the code in ‘project.gradle.projectsEvaluated {}’. A more sophisticated way is convention mapping; search the forum for more on this.

Actually, in this particular case there is a better solution: ‘from({ dirName }) { … }’ This is an example of an API which accepts a code block (i.e. closure) to allow you to defer evaluation of an expression. It might be better though to copy the properties files as part of the ‘processResources’ task than the ‘War’ task, one advantage being that it makes the files visible to tests.

OK great Peter, this is exactly what I was looking for, thank you. I need that projectsEvaluated{} for another issue I was facing and I can then be done my initial work with our new Gradle build, and also was successful using the from closure.

I will attempt to solicit some specific advice about better ways of doing things (if any) once I’m done with the initial version. There is only so much that I can actually do at one time when moving over from Ant, but now that I create a war task that is similar in behavior to what we currently have, it’ll allow me to quickly move forward.

btw - I was totally read to re-write the war task, but projectsEvaluated is working and solves my current needs, so glad I don’t have to spend that time now.