title: Custom gradle task extending the 'War' task does not see some required classes
body: <p>I've been trying to move to Gradle for some time now. One of the things I have to do is automatically create additional entries in the WAR file based on those that would normally be added. For performance and other reasons I attempted to do this the same way it was done before by automating this inside a custom subclass of the War task. As I have to work around private visibility and final fields this is not as nice as I wanted, but it fails before it starts. The moment I added references to classes from the org.apache.tools.zip package, the build fails at runtime with the following stack trace:</p>
  
  <pre><code>17:44:32.888 [ERROR] [org.gradle.BuildExceptionReporter] FAILURE: Build failed with an exception.
  17:44:32.900 [ERROR] [org.gradle.BuildExceptionReporter]
  17:44:32.907 [ERROR] [org.gradle.BuildExceptionReporter] * Where:
  17:44:32.914 [ERROR] [org.gradle.BuildExceptionReporter] Build file '...\build.gradle' line: 92
  17:44:32.921 [ERROR] [org.gradle.BuildExceptionReporter]
  17:44:32.928 [ERROR] [org.gradle.BuildExceptionReporter] * What went wrong:
  17:44:32.935 [ERROR] [org.gradle.BuildExceptionReporter] A problem occurred evaluating root project '...'.
  17:44:32.942 [ERROR] [org.gradle.BuildExceptionReporter] &gt; Could not generate a proxy class for class ....ProblemWarTask.
  17:44:32.953 [ERROR] [org.gradle.BuildExceptionReporter]
  17:44:32.960 [ERROR] [org.gradle.BuildExceptionReporter] * Exception is:
  17:44:32.969 [ERROR] [org.gradle.BuildExceptionReporter] org.gradle.api.GradleScriptException: A problem occurred evaluating root project '...'.
  17:44:32.976 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:54)
  17:44:32.983 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:127)
  17:44:32.991 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.configuration.BuildScriptProcessor.evaluate(BuildScriptProcessor.java:38)
  17:44:32.998 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.configuration.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:44)
  17:44:33.005 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.project.AbstractProject.evaluate(AbstractProject.java:464)
  17:44:33.013 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.project.AbstractProject.evaluate(AbstractProject.java:77)
  17:44:33.020 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.configuration.DefaultBuildConfigurer$ConfigureProject.execute(DefaultBuildConfigurer.java:38)
  17:44:33.027 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.configuration.DefaultBuildConfigurer$ConfigureProject.execute(DefaultBuildConfigurer.java:36)
  17:44:33.034 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.project.AbstractProject.configure(AbstractProject.java:440)
  17:44:33.041 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.project.AbstractProject.allprojects(AbstractProject.java:435)
  17:44:33.048 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.configuration.DefaultBuildConfigurer.configure(DefaultBuildConfigurer.java:32)
  17:44:33.055 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:142)
  17:44:33.062 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:113)
  17:44:33.069 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:81)
  17:44:33.077 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:38)
  17:44:33.086 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.exec.InProcessGradleLauncherActionExecuter.execute(InProcessGradleLauncherActionExecuter.java:39)
  17:44:33.095 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.exec.InProcessGradleLauncherActionExecuter.execute(InProcessGradleLauncherActionExecuter.java:25)
  17:44:33.103 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:50)
  17:44:33.112 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.Actions$RunnableActionAdapter.execute(Actions.java:171)
  17:44:33.121 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:201)
  17:44:33.130 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:174)
  17:44:33.139 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:170)
  17:44:33.148 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:139)
  17:44:33.157 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
  17:44:33.166 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
  17:44:33.175 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.Main.doAction(Main.java:48)
  17:44:33.182 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
  17:44:33.190 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.Main.main(Main.java:39)
  17:44:33.197 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:50)
  17:44:33.206 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:32)
  17:44:33.216 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.launcher.GradleMain.main(GradleMain.java:26)
  17:44:33.227 [ERROR] [org.gradle.BuildExceptionReporter] Caused by: org.gradle.api.GradleException: Could not generate a proxy class for class ....ProblemWarTask.
  17:44:33.238 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.AbstractClassGenerator.generate(AbstractClassGenerator.java:201)
  17:44:33.249 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.project.taskfactory.TaskFactory.createTaskObject(TaskFactory.java:105)
  17:44:33.258 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.project.taskfactory.TaskFactory.createTask(TaskFactory.java:70)
  17:44:33.266 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory.createTask(AnnotationProcessingTaskFactory.java:93)
  17:44:33.276 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.project.taskfactory.DependencyAutoWireTaskFactory.createTask(DependencyAutoWireTaskFactory.java:39)
  17:44:33.285 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.tasks.DefaultTaskContainer.add(DefaultTaskContainer.java:56)
  17:44:33.294 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.project.AbstractProject.task(AbstractProject.java:921)
  17:44:33.301 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:216)
  17:44:33.311 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.BeanDynamicObject.invokeMethod(BeanDynamicObject.java:122)
  17:44:33.319 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.CompositeDynamicObject.invokeMethod(CompositeDynamicObject.java:147)
  17:44:33.328 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.groovy.scripts.BasicScript.methodMissing(BasicScript.java:83)
  17:44:33.335 [ERROR] [org.gradle.BuildExceptionReporter]        at build_77lv568h8hqtuhsa63froq7bcr.run(...\build.gradle:92)
  17:44:33.343 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:52)
  17:44:33.352 [ERROR] [org.gradle.BuildExceptionReporter]        ... 30 more
  17:44:33.362 [ERROR] [org.gradle.BuildExceptionReporter] Caused by: java.lang.NoClassDefFoundError: Lorg/apache/tools/zip/ZipOutputStream;
  17:44:33.370 [ERROR] [org.gradle.BuildExceptionReporter]        at org.gradle.api.internal.AbstractClassGenerator.generate(AbstractClassGenerator.java:100)
  17:44:33.379 [ERROR] [org.gradle.BuildExceptionReporter]        ... 42 more
  17:44:33.387 [ERROR] [org.gradle.BuildExceptionReporter] Caused by: java.lang.ClassNotFoundException: org.apache.tools.zip.ZipOutputStream
  17:44:33.395 [ERROR] [org.gradle.BuildExceptionReporter]        ... 43 more
  17:44:33.403 [ERROR] [org.gradle.BuildExceptionReporter]
  17:44:33.411 [LIFECYCLE] [org.gradle.BuildResultLogger]
  17:44:33.419 [LIFECYCLE] [org.gradle.BuildResultLogger] BUILD FAILED
  </code></pre>
  
  <p>This is still mostly learning about Gradle. My biggest issue right now is actually not understanding why this happens and what am I missing in general (i.e. if/when I write other tasks). How come this passes the compilation stage just fine (buildSrc.jar is created just fine) but fails at runtime?</p>
  
  <p>Here's the slightly obfuscated source code of this (you'll see that it is mostly a copy of Gradle's own code - namely the Zip task):</p>
  
  <pre><code>import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.lang.reflect.Constructor;
  
  import org.apache.tools.zip.UnixStat;
  import org.apache.tools.zip.ZipEntry;
  import org.apache.tools.zip.ZipOutputStream;
  
  import org.gradle.api.file.FileVisitDetails;
  import org.gradle.api.GradleException;
  import org.gradle.api.internal.file.archive.ZipCopyAction;
  import org.gradle.api.internal.file.archive.ZipCopySpecVisitor;
  import org.gradle.api.internal.file.copy.CopyAction;
  import org.gradle.api.internal.file.copy.CopyActionImpl;
  import org.gradle.api.internal.file.copy.CopySpecImpl;
  import org.gradle.api.internal.file.copy.CopySpecVisitor;
  import org.gradle.api.internal.file.copy.MappingCopySpecVisitor;
  import org.gradle.api.internal.file.copy.NormalizingCopySpecVisitor;
  import org.gradle.api.internal.file.copy.ReadableCopySpec;
  import org.gradle.api.internal.file.copy.ZipCompressor;
  import org.gradle.api.internal.file.copy.ZipDeflatedCompressor;
  import org.gradle.api.internal.file.FileResolver;
  import org.gradle.api.tasks.bundling.War;
  import org.gradle.api.tasks.bundling.Zip;
  import org.gradle.api.UncheckedIOException;
  import org.gradle.internal.nativeplatform.filesystem.FileSystems;
  
  class ProblemWarTask extends War {
  
    private ZipCopyActionImpl copyAction = null;
  
    public ProblemWarTask() {}
  
    protected synchronized Zip.ZipCopyActionImpl getCopyAction() {
      if (copyAction == null) {
        copyAction = new ProblemWarCopyActionImpl(getServices().get(FileResolver.class));
      }
      return copyAction;
    }
  
    protected class ProblemWarCopyActionImpl extends Zip.ZipCopyActionImpl {
      private final CopySpecVisitor visitor;
  
      public ProblemWarCopyActionImpl(FileResolver fileResolver) {
        super((FileResolver) fileResolver);
        visitor = new MappingCopySpecVisitor(new NormalizingCopySpecVisitor(new ProblemWarCopySpecVisitor()), FileSystems.getDefault());
      }
  
      public void execute() {
        visitor.startVisit(this);
        for (ReadableCopySpec spec : getRootSpec().getAllSpecs()) {
          visitor.visitSpec(spec);
          spec.getSource().visit(visitor);
        }
       visitor.endVisit();
      }
  
      public boolean getDidWork() {
          return visitor.getDidWork();
      }
    }
  
    protected class ProblemWarCopySpecVisitor extends ZipCopySpecVisitor {
      private ZipOutputStream zipOutStr;
      private File zipFile;
  
      public void startVisit(CopyAction action) {
        ZipCopyAction archiveAction = (ZipCopyAction) action;
        zipFile = archiveAction.getArchivePath();
        try {
          zipOutStr = archiveAction.getCompressor().createArchiveOutputStream(zipFile);
        } catch (Exception e) {
          throw new GradleException(String.format("Could not create ZIP '%s'.", zipFile), e);
        }
      }
  
      public void endVisit() {
        try {
          zipOutStr.close();
        } catch (IOException e) {
          throw new UncheckedIOException(e);
        } finally {
          zipOutStr = null;
        }
      }
  
      public void visitFile(FileVisitDetails fileDetails) {
        try {
          ZipEntry archiveEntry = new ZipEntry(fileDetails.getRelativePath().getPathString());
          archiveEntry.setTime(fileDetails.getLastModified());
          archiveEntry.setUnixMode(UnixStat.FILE_FLAG | fileDetails.getMode());
          zipOutStr.putNextEntry(archiveEntry);
          fileDetails.copyTo(zipOutStr);
          zipOutStr.closeEntry();
        } catch (Exception e) {
          throw new GradleException(String.format("Could not add %s to ZIP '%s'.", fileDetails, zipFile), e);
        }
  
        // Now do a little more - add stuff
  
        try {
          if (...) { 
            byte[] buffer;
            ...
            ZipEntry archiveEntry = new ZipEntry(secondPath);
            archiveEntry.setTime(fileDetails.getLastModified());
            archiveEntry.setUnixMode(UnixStat.FILE_FLAG | fileDetails.getMode());
            zipOutStr.putNextEntry(archiveEntry);
            zipOutStr.write(buffer, 0, buffer.length);
            zipOutStr.closeEntry();
          }
        } catch (Exception e) {
          throw new GradleException(String.format("Could not add secondary form of %s to ZIP '%s'.", fileDetails, zipFile), e);
        }
      }
  
      public void visitDir(FileVisitDetails dirDetails) {
        try {
          // Trailing slash in name indicates that entry is a directory
          ZipEntry archiveEntry = new ZipEntry(dirDetails.getRelativePath().getPathString() + '/');
          archiveEntry.setTime(dirDetails.getLastModified());
          archiveEntry.setUnixMode(UnixStat.DIR_FLAG | dirDetails.getMode());
          zipOutStr.putNextEntry(archiveEntry);
          zipOutStr.closeEntry();
        } catch (Exception e) {
          throw new GradleException(String.format("Could not add %s to ZIP '%s'.", dirDetails, zipFile), e);
        }
      }
    }
  }
  </code></pre>
  
  <p>Presently the build script itself is not doing anything special with it:</p>
  
  <pre><code>task war(type: ....ProblemWarTask) {
    ...
  }
  </code></pre>
  
  <p>The body is is the same as I used for the 'War' task and even for initial experimenting with this one (ProblemWarTask). Exception started happening when I added references to the package noted. What bothers me is how can a 'War' task run just fine when it has the same set of runtime dependencies as this one in the end.</p>
  
  <p>This is with Gradle 1.5. I also tried adding the following to build.gradle but did not help  (same error):</p>
  
  <pre><code>buildscript {
    dependencies {
      classpath files(".../gradle/lib/ant-1.8.4.jar")
    }
  }
  </code></pre>
  
  <p><em>Edit</em> - tried with Gradle 1.6, no change. Posted in Gradle forums too:</p>
  
  <p><a href="http://forums.gradle.org/gradle/topics/custom_gradle_task_extending_the_war_task_does_not_see_some_required_classes?rfm=1" rel="nofollow">http://forums.gradle.org/gradle/topics/custom_gradle_task_extending_the_war_task_does_not_see_some_required_classes?rfm=1</a></p>
  
  <p><em>Edit 2</em> - isolated sample files can be found at:</p>
  
  <p><a href="http://wikisend.com/download/241430/sample.zip" rel="nofollow">http://wikisend.com/download/241430/sample.zip</a></p>