Extend war task

I get a

Caused by: org.gradle.api.internal.MissingMethodException: Could not find method file() for arguments [/web.xml] on task ‘:bentallProdWar’.

error with the below code, but I can’t see why. Whats wrong?

Thank you!

Mark

class CustomWar extends War {
  def env = '';
      CustomWar(){
    super()
    //dependsOn 'checkArtifacts'
    //exclude the local web.xml from the build
//
  exclude '**/web.xml'
//
  //copy properties files to classes
//
  from (env){
//
    include '*.properties'
//
    into '/WEB-INF/classes'
//
  }
    // copies a file to WEB-INF/web.xml
    webXml = file(env + '/web.xml')
  }
  }

There is no ‘file’ method on ‘War’. It’s ‘project.file’.

OK, great works like a charm. However, I don’t understand the context switch.

ie. why can’t I use the ‘standard’ calls when I extend an existing class. Would you point me to the documentation on this so I can educate myself?

Thank you,

Mark

Mark - in normal Gradle scripting land (.gradle files), you’re interacting with the Project API: http://gradle.org/docs/current/dsl/org.gradle.api.Project.html

When you’re developing plugins in Groovy (or Java), you’re interacting with things that you specifically pull into your file, in this case… you’re in the domain of the War class: http://gradle.org/docs/current/dsl/org.gradle.api.tasks.bundling.War.html

In other words, the ‘standard calls’ you speak of are only standard to you because you’re normally working in the ‘Project’ scope, not the lower level plugin (or Groovy) scope.

OK, now I’m getting an internal error. I’m thinking that what I am doing may be overkill, and that there is probably a better way. What I’m trying to do is create multiple war’s, one for each environment that we are deploying to. Each war is tweaked in a consistent way - each environment has its own properties files, xml config files, etc.

Anyway, first off the code and internal error:

class CustomWar extends War {
  def env = '';
      CustomWar(){
    super()
    //dependsOn 'checkArtifacts'
    //exclude the local web.xml from the build
    exclude '**/web.xml'
    //copy properties files to classes
    from (env){
      include '*.properties'
      into '/WEB-INF/classes'
    }
    // copies a file to WEB-INF/web.xml
    webXml = project.file(env + '/web.xml')
  }
  }

Error message:

:compileJava Note: Some input files use or override a deprecated API. Note: Recompile with -Xlint:deprecation for details. :processResources UP-TO-DATE :classes :bentallProdWar

FAILURE: Build aborted because of an internal error.

  • What went wrong: Build aborted because of an unexpected internal error. Please file an issue at: http://forums.gradle.org.

  • Try: Run with --debug option to get additional debug info.

  • Exception is: java.lang.IllegalArgumentException: Neither path nor baseDir may be null or empty string. path=’’ basedir=‘C:\eclipseProjects\sustainability’

at org.gradle.api.internal.file.BaseDirFileResolver.doResolve(BaseDirFileResolver.java:67)

at org.gradle.api.internal.file.AbstractFileResolver.resolve(AbstractFileResolver.java:57)

at org.gradle.api.internal.file.AbstractFileResolver.resolve(AbstractFileResolver.java:53)

at org.gradle.api.internal.file.collections.DefaultFileCollectionResolveContext$FileTreeConverter.convertInto(DefaultFileCollectionResolveContext.java:200)

Regardless of the above, I believe I found the solution to my problem, from Rene:

http://www.mail-archive.com/user@gradle.codehaus.org/msg06259.html

This looks like exactly what I need:

["kung", "foo", "bar", ].each{env ->
        task "${env}War"(type:War){
            baseName = "${env}"
            from "target/${env}-tmp"
        }
}

For me, this is quite alien. Very much so. However, I am starting to see how this is the ‘gradle way’ and it makes sense now that I’m starting to understand more about how gradle works. I only wish this example was somewhere in the docs, would have saved me quite a bit of time…

Thanks for the help!

Mark

The ‘CustomWar’ task fails because it has no top-level ‘into’. Anyway, extending the ‘War’ class is not the way to go here. You just need to write a build script or plugin that adds a few War tasks and configures them to your liking.

The second snippet is just creating tasks in a loop. The syntax may look alien, but you could do the same with a more Java-like syntax. Also, the ‘baseName’ line can be simplified to ‘baseName = env’.

OK, thanks. I’m trying to do exactly that:

task doBuild (dependsOn: 'checkArtifacts') {
  buildDirs = project.files('env')
  buildDirs.collect {
    println it
  }
      FileTree tree = fileTree(dir: 'env').visit{
    if(it.isDirectory()){
      println it
      buildDirs.add files(it.name)
    }
  }

but the result is:

C:\eclipseProjects\sustainability>gradle doBuild
Dynamic properties are deprecated: http://gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html
Deprecated dynamic property: "vaadinDir" on "root project 'sustainability'", value: "WebContent/VAADIN".
Deprecated dynamic property: "gwtBuildDir" on "root project 'sustainability'", value: "WebContent/VAADIN/widg...".
Deprecated dynamic property: "widgetsetClass" on "root project 'sustainability'", value: "com.refineddata.sustai...".
Deprecated dynamic property: "buildDirs" on "task ':doBuild'", value: "file collection".
C:\eclipseProjects\sustainability\env
file 'C:\eclipseProjects\sustainability\env\bentallProd'
  FAILURE: Build failed with an exception.
  * Where:
Build file 'C:\eclipseProjects\sustainability\build.gradle' line: 134
  * What went wrong:
A problem occurred evaluating root project 'sustainability'.
> File collection does not allow modification.

but when I look at the docs, it says that it should be mutable!

http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/ConfigurableFileCollection.html

help!

Mark

Did you also read the fine print?

@throws UnsupportedOperationException When this collection does not allow modification.

Apparently, ‘project.files’ returns an immutable file collection.

Yes, I did read the fine print, but was just pointing out the docs are incorrect. Should I create a separate issue for this?

I think that I’m close to something that works, but having trouble with the end of it. I want to build a custom war for every folder in my ‘env’ folder.

ie. in my env folder, I have: bentallProd bentallUat local sandbox

and in each folder I have some config files I want applied to each war. I ended up just keeping track of each folder name in a List instead of a FileCollection due to the previous issues.

I’ve created the below, but I’m not seeing the 4 wars that I expect to see in the output, and I’ve had real trouble finding a similar example, which worries me, but anyway, here it is:

task doBuild (dependsOn: 'checkArtifacts') << {
  List<String> filelist = new ArrayList<String>()
  //buildDirs = project.files('env')
      FileTree tree = fileTree(dir: 'env').visit{
    if(it.isDirectory()){
      //buildDirs.add files(it.name)
      filelist.add(it.name);
    }
  }
    //println buildDirs.files
  //println filelist
  filelist.each{ dirName ->
    println 'adding war task: ' + "${dirName}War"
    task "${dirName}War"(type:War) {
      println 'now building war: ' + dirName
      def env = '';
      //exclude the local web.xml from the build
      exclude '**/web.xml'
      //copy properties files to classes
      from (env){
        include '*.properties'
        into '/WEB-INF/classes'
      }
      // copies a file to WEB-INF/web.xml
      webXml = file(env + '/web.xml')
      baseName = dirName
    }
    println 'adding war task dependency: ' + "${dirName}War"
    war.dependsOn "${dirName}War"
  }
}
  war.dependsOn doBuild

When I build it works, but I only get one war file, can you help me get over the hump here please?

Output:

C:\eclipseProjects\sustainability>gradle war -rerun-tasks
Dynamic properties are deprecated: http://gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html
Deprecated dynamic property: "vaadinDir" on "root project 'sustainability'", value: "WebContent/VAADIN".
Deprecated dynamic property: "gwtBuildDir" on "root project 'sustainability'", value: "WebContent/VAADIN/widg...".
Deprecated dynamic property: "widgetsetClass" on "root project 'sustainability'", value: "com.refineddata.sustai...".
:compileJava
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
:processResources UP-TO-DATE
:classes
:checkArtifacts
:doBuild
adding war task: bentallProdWar
now building war: bentallProd
adding war task dependency: bentallProdWar
adding war task: bentallUatWar
now building war: bentallUat
adding war task dependency: bentallUatWar
adding war task: localWar
now building war: local
adding war task dependency: localWar
adding war task: sandboxWar
now building war: sandbox
adding war task dependency: sandboxWar
:war
  BUILD SUCCESSFUL
  Total time: 14.612 secs

The docs are correct. ‘add’ is an optional operation. The method that you are probably looking for is ‘from’. Only the latter is added by ‘ConfigurableFileCollection’.

Yes, that’s what I’m referring to! At:

http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/ConfigurableFileCollection.html

Its states:

"A ConfigurableFileCollection is a mutable FileCollection.

You can obtain an instance of ConfigurableFileCollection by calling Project.files(Object…)"

One of those lines needs to be fixed, as I did exactly that (called ‘Project.files(Object…)’) and got an immutable class org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection

I would really appreciate help with my almost done build. If I can get it going I’ll commit to adding it to the gradle cookbook as there are no examples there that do what I’m trying to accomplish and I think its a fairly common use case.

Thank you,

Mark

As I said, it isn’t immutable. You just have to use the ‘from’ method. However, typically you don’t mutate file collections; instead you use methods like ‘+’ and ‘-’ to create new file collections out of existing ones.

The code that generates the tasks needs to run in the configuration phase, not the execution phase. It needs to be outside a task. In other words, get rid of the ‘doBuild’ stuff.

re: config phase - ah, I keep wanting to make methods to do stuff, I’ll give that a try, thank you!

re: immutable - don’t you think that its a teensy bit confusing to have a mutable FileCollection that doesn’t support the ‘add(Object)’ method?

re: immutable - don’t you think that its a teensy bit confusing to have a mutable FileCollection that doesn’t support the ‘add(Object)’ method?

A FileCollection is not a java.util.Collection.

Yes, I realize that. Please allow me to re-phrase, and if that doesn’t convince you, I rest my case! :slight_smile:

re: immutable - don’t you think that its a teensy bit confusing to have a mutable FileCollection that doesn’t support the inherited (from its parent, FileCollection) optional ‘add(Object)’ method?

It is confusing, a teensy bit. I wonder if ‘ConfigurableFileCollection’ could just override ‘add’ and delegate to ‘from’.