How can I add a folder to /WEB-INF in a war from a custom War task in a plugin?


(Daniel Mijares) #1

Hi,

I am trying to add folders to the ‘WEB-INF’ of a WAR archive that is defined in a web services plugin I am trying to wrap up.

The plugin basically just goes in, finds the wsdl file, and then resolves all the schema the wsdl depends on and packages them in the war under two separate base folders.

  • ‘WEB-INF/wsdl’ * ‘WEB-INF/schema’

Writing a few plugins in the past I was basically chaining separate tasks together. So my tasks dependency looks like:

getWsdlName << resolveWsdlDependencies << wsdlWar

the ‘resolveWsdlDependencies’ task populate the Extension object with a list of objects that contain the Strings to pass as input to the ‘from’, ‘to’, and ‘include’ arguments to the CopySpec builder.

While the wsdlWar task doesn’t fail, it just doesn’t populate anything in the ‘WEB-INF’ that a regular war wouldn’t do. I know the other tasks are working fine because I have tested them.

The war looks normal:

class WsdlWarTask extends War {
   WsdlWarTask() {
     super()
            }
      @TaskAction
  def void addDocumentsToWar() {
    project.extensions.wsdl.resolved.each { resolvee ->
      log.info("from : {}, into : {}, include: {}", resolvee.from, resolvee.into, resolvee.include)
      webInf {
    from resolvee.from
   into
resolvee.into
   include resolvee.include
      }
    }
  }
}

I have also tried to get the CopySpec defined in ‘War.groovy’, but no dice.

resolved.each { resolvee ->
log.info("from : {}, into : {}, include: {}", resolvee.from, resolvee.into, resolvee.include)
  getWebInf().from (resolvee.from)
{
   into
resolvee.into
   include resolvee.include
  }
}

What DOES work though, and it has a major drawback, is putting the code above, in the constructor. The drawback is that I can’t depend on my other tasks to do the work in the constructor, (I have only been able to chain tasks that have an ‘@TaskAction’ annotation on a method, as it seems that is when the task dependencies are sorted out).

WsdlWarTask() {
  super()
           def wsdlName = nameConvention.findWsdlFileName(project.name)
 def wsdlFile = new File(project.extensions.wsdl.wsdlDirectory, wsdlName + ".wsdl")
 dependencyResolver.wsdlFile = wsdlFile
 def resolveList = dependencyResolver.resolveWSDLDependencies()
   def resolved = resolveList.collect { resolvePaths.resolveRelativePathsToWar(project.rootDir, it) }
 resolved.each { resolvee ->
  log.info("from : {}, into : {}, include: {}", resolvee.from, resolvee.into, resolvee.include)
  getWebInf().from (resolvee.from)
{
   into
resolvee.into
   include resolvee.include
  }
 }
}

With this I get my expected war output

drwxr-xr-x

0 20-Jan-2013 21:19:26 WEB-INF/wsdl/

-rw-r–r--

2049 19-Jan-2013 20:56:44 WEB-INF/wsdl/HelloWorldEpisodeBindingService.wsdl

drwxr-xr-x

0 20-Jan-2013 21:19:26 WEB-INF/schema/

drwxr-xr-x

0 20-Jan-2013 21:19:26 WEB-INF/schema/HelloWorld/

-rw-r–r--

581 19-Jan-2013 20:56:44 WEB-INF/schema/HelloWorld/HelloWorld.xsd

There are two things that are a little off. Any subproject that applies this plugin goes through this whole resolution process before anything is even run. Like even the debug output of ‘gradle tasks’ shows all subprojects doing all the processing in the ‘WsdlWarTask()’ constructor, just to show what the tasks are. If I’m not mistaken it even does this on a clean too. (putting the code where I originally wanted it, doesn’t do this however)

I also lose the flexibility of extensions with this method. It only works with the default extension settings (for me, the default wsdl folder is ‘wsdl’). If for some instance, say the wsdl folder was actualy ‘WSDL’ the plugin would crash at ‘apply’ time because it is trying to find the file at wsdl and process it’s contents with XmlSlurper and it can’t find it so it crashes and outputs

TaskInstantiationException: Could not create task of type ‘WsdlWarTask’

and the extension mechanism of

wsdl {
   wsdlDirectory = file(new File(project.rootDir, "WSDL"))
}

doesn’t work, because it doesn’t get called as early as the things in the constructor. Or so I think.

I would really like to extend the war with the @TaskAction annotated method creating and populating the folders inside ‘WEB-INF’. Am I doing something wrong, or is there a better way?


(Daniel Mijares) #2

I got some where playing with conventionMapping and Input annotations.

A separate task populates the Extension object with a List called ‘resolved’

war.conventionMapping.resolvedWarPaths = { project.wsdl.resolved }

It is a list of objects ‘WsdlWarRelativePathResolver’

class WsdlWarRelativePathResolver {
   @Input
  String from
  @Input
  String into
  @Input
  String include
}

I Nest the list as an input to the War task

class WsdlWarTask extends War {
   @Nested
  List resolvedWarPaths
      WsdlWarTask() {
     super()
              getResolvedWarPaths().each { resolved ->
      log.info("from : {}, into : {}, include: {}", resolved.from, resolved.into, resolved.include)
      getWebInf().from (resolved.from)
{
 into
resolved.into
 include resolved.include
      }
    }
  }
}

The task doesn’t complain but it doesn’t populate anything inside the web-inf that the instance fields of the ‘WarRelativePathResolver’ object have as data. :confused:

Just to make sure that that input is being passed in, I wrote a do nothing TaskAction method

@TaskAction
  def void addSchemas() {
    getResolvedWarPaths().each { resolved ->
      log.info("@TaskAction from : {}, into : {}, include: {}", resolved.from, resolved.into, resolved.include)
    }
  }

And lo and behold, I got this in the log statements running with ‘–debug’. :frowning:

[WsdlWarTask] @TaskAction from : /Users/djmijares/dev/gradle/gradle-plugins/gradle-wsdl/examples/wsdl, into : wsdl, include: HelloWorldDualEpisodeBindingService.wsdl [WsdlWarTask] @TaskAction from : /Users/djmijares/dev/gradle/gradle-plugins/gradle-wsdl/examples/schema/Import, into : schema/Import, include: HelloWorldImport.xsd [WsdlWarTask] @TaskAction from : /Users/djmijares/dev/gradle/gradle-plugins/gradle-wsdl/examples/schema/Import, into : schema/Import, include: TestImport.xsd

I figured I would see the same log in the constructor (minus @TaskAction text), but it isn’t in the debug output.

I should have * WEB-INF

  • wsdl
  • wsdl file
  • schema/Import
  • two schema files.

Any help?