Ear task can't add generated application.xml to library output

To summarize, I am dealing with a project that produces .ear archives, so I’m trying to use the Ear plugin. The problem is that the application.xml contains tokens for replacement, for example: ${app.path}. Note the presence of these tokens causes the unfiltered version of application.xml to be malformed XML. This means that I cannot use the ear plugin’s bult-in filter() to do replacement on application.xml - Ear task refuses to deal with an application.xml that is badly-formed xml, and quits before filtering it.

I have already written a plugin that produces a string-replacement closure for replacing tokens with matching project properties, for use in filter() calls in various Gradle tasks. This is project.replacementFilterClosure, seen in following code. I’ve also added a task to the build.gradle in question that performs a filtered copy:

task filterMetadata(type: Copy) {

from(‘src/main/application’)

into(‘src/main/application/META-INF’)

include(’*.xml’)

filter(project.replacementFilterClosure)

outputs.upToDateWhen { false } // task can never be up-to-date i.e. it always runs.

}

createMetaInfDir = dir(‘src/main/application/META-INF’)

filterMetadata.dependsOn(createMetaInfDir)

application.xml and another metadata file, jboss-app.xml, are both located in src/main/application. The idea is that the dir task creates src/main/application/META-INF, and the two filtered .xml files are copied into it.

That part of the process happens OK. Things go wrong during the second stage, ear creation. Here is the code:

ear.dependsOn(filterMetadata)

ear {

appDirName(‘src/main/application’)

filter(project.replacementFilterClosure)

}

Please note: The filter added to the ear task successfully filters some resources other than application.xml as mentioned earlier. application.xml in META-INF is already filtered by filterMetadata.

The first time I try running ‘gradle build’ after removing the generated META-INF directory and the two filtered files it contains, the following exception occurs:

Execution failed for task ‘:champion:champion-portal-ear-b:ear’.

Cause: Could not add application.xml to ZIP ‘/home/HQ/idurkan/champion-repo/champion-root/champion/champion-portal-ear-b/build/libs/champion-portal-ear-b-3.7-SNAPSHOT.ear’.

The second time I run gradle build, the .ear is created in build/libs/ without any issues. This seems like some kind of bug. Does anyone have suggestions on how to do the filtering in a different way that will dodge the problem?

For more knowledgeable Gradle users, here’s the ultimate cause of the exception:

Caused by: org.gradle.api.GradleException: Could not add application.xml to ZIP '/home/HQ/idurkan/champion-repo/champion-root/champion/champion-portal-ear-b/build/libs/champion-portal-ear-b-3.7-SNAPSHOT.ear'.
 at org.gradle.api.internal.file.archive.ZipCopySpecVisitor.visitFile(ZipCopySpecVisitor.java:70)
 at org.gradle.api.internal.file.copy.NormalizingCopySpecVisitor.visitFile(NormalizingCopySpecVisitor.java:70)
 at org.gradle.api.internal.file.copy.MappingCopySpecVisitor.visitFile(MappingCopySpecVisitor.java:50)
 at org.gradle.api.internal.file.AbstractFileTree$FilteredFileTree$1.visitFile(AbstractFileTree.java:145)
 at org.gradle.api.internal.file.collections.MapFileTree$Visit.visit(MapFileTree.java:103)
 at org.gradle.api.internal.file.collections.MapFileTree.visit(MapFileTree.java:70)
 at org.gradle.api.internal.file.collections.FileTreeAdapter.visit(FileTreeAdapter.java:96)
 at org.gradle.api.internal.file.AbstractFileTree$FilteredFileTree.visit(AbstractFileTree.java:136)
 at org.gradle.api.internal.file.CompositeFileTree.visit(CompositeFileTree.java:54)
 at org.gradle.api.internal.file.copy.CopyActionImpl.execute(CopyActionImpl.java:63)
 at org.gradle.api.tasks.AbstractCopyTask.copy(AbstractCopyTask.java:40)
 at org.gradle.api.internal.BeanDynamicObject.invokeMethod(BeanDynamicObject.java:158)
 at org.gradle.api.internal.CompositeDynamicObject.invokeMethod(CompositeDynamicObject.java:93)
 at org.gradle.plugins.ear.Ear_Decorated.invokeMethod(Unknown Source)
 at org.gradle.util.ReflectionUtil.invoke(ReflectionUtil.groovy:23)
 at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$2.execute(AnnotationProcessingTaskFactory.java:129)
 at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$2.execute(AnnotationProcessingTaskFactory.java:127)
 at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:63)
 ... 54 more
Caused by: java.lang.UnsupportedOperationException
 at org.gradle.api.internal.file.collections.MapFileTree$FileVisitDetailsImpl.open(MapFileTree.java:155)
 at org.gradle.api.internal.file.copy.MappingCopySpecVisitor$FileVisitDetailsImpl.open(MappingCopySpecVisitor.java:101)
 at org.gradle.api.internal.file.AbstractFileTreeElement.copyTo(AbstractFileTreeElement.java:43)
 at org.gradle.api.internal.file.copy.MappingCopySpecVisitor$FileVisitDetailsImpl.copyTo(MappingCopySpecVisitor.java:109)
 at org.gradle.api.internal.file.archive.ZipCopySpecVisitor.visitFile(ZipCopySpecVisitor.java:67)
 ... 71 more

For posterity here is a workaround we came up with. Here’s a simplified snapshot of the workaround code in a build.gradle:

// ear metadata contains {$ } tokens needing replacement

def filteredMetadataDir = ‘build/metadata-temp’

def applicationXmlPath = ‘’

task filterMetadata(type:Copy) {

from(applicationXmlPath)

into(filteredMetadataDir)

include(’*.xml’)

filter(project.replacementFilterClosure)

outputs.upToDateWhen { false } // task always runs in builds that depend on it

}

ear.dependsOn(filterMetadata)

ear {

from (filteredMetadataDir) {

into(‘META-INF/’)

include(’*.xml’)

}

}

  1. Copy-filter the application.xml and other metadata to a directory other than src/main/application/META-INF. 2. In the ear task use a from-closure to manually add the application.xml and other metadata into the output earfile’s META-INF directory.