4

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:

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] > 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

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?

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):

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);
      }
    }
  }
}

Presently the build script itself is not doing anything special with it:

task war(type: ....ProblemWarTask) {
  ...
}

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.

This is with Gradle 1.5. I also tried adding the following to build.gradle but did not help (same error):

buildscript {
  dependencies {
    classpath files(".../gradle/lib/ant-1.8.4.jar")
  }
}

Edit - tried with Gradle 1.6, no change. Posted in Gradle forums too:

http://forums.gradle.org/gradle/topics/custom_gradle_task_extending_the_war_task_does_not_see_some_required_classes?rfm=1

Edit 2 - isolated sample files can be found at:

http://wikisend.com/download/241430/sample.zip

Learner
  • 1,215
  • 1
  • 11
  • 26
  • I am pretty sure this means that Gradle is using task specific classloaders, but how does one control that? Also, how can I tell it to use its own ant.jar (because that is where the problem classes are)? – Learner May 06 '13 at 23:04

1 Answers1

3

Task types like War aren't designed to be subclassed by users. Instead of subclassing and using lots of internals, it's often better (and easier) to write a plugin (which in the simplest case is just another build script applied with apply from: "path/to/script.gradle") that preconfigures tasks using their public API. For example:

tasks.withType(War) {
    eachFile { FileCopyDetails details -> ... }
}

If absolutely necessary, such configuration can also go into the constructor of a subclass.

That said, I'm not sure why you are hitting a class loader problem with an Ant class. If you can submit an issue with a self-contained reproducible example at http://forums.gradle.org, we'll have a look.

Peter Niederwieser
  • 121,412
  • 21
  • 324
  • 259
  • Thanks. Posted it there too: http://forums.gradle.org/gradle/topics/custom_gradle_task_extending_the_war_task_does_not_see_some_required_classes?rfm=1 – Learner May 07 '13 at 17:01