Building a WAR with Groovy and javaee-web-api reference APIs fails with java.lang.ClassFormatError (works with Java source)


(blackbeltdev) #1

I’m using the following build file running on 1.0.m7

apply plugin: 'groovy'
apply plugin: 'war'
apply plugin: 'eclipse'
apply plugin: 'idea'
    sourceCompatibility = 1.6
  dm = [
 // groovy
 'groovy-all': 'org.codehaus.groovy:groovy-all:1.8.5',
    // JavaEE
 'javaee-web-api': 'javax:javaee-web-api:6.0'
]
  dependencies {
 groovy dm['groovy-all']
 providedCompile (dm['javaee-web-api'])
}
package test.servlet;
  import java.io.IOException;
import java.io.PrintWriter;
  import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
  @WebServlet("/hello")
public class HelloWorld extends HttpServlet {
   private static final long serialVersionUID = 1L;
   @Override
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/plain");
  PrintWriter writer = response.getWriter();
  try {
   writer.println("Hello World");
  } finally {
     writer.close();
  }
 }
}

Results in build failures if the source file is Groovy. If it is Java it builds fine.

$ gradle clean build
:clean
:compileJava
:compileGroovy
[ant:groovyc] >>> a serious error occurred: javax/servlet/GenericServlet : Missing Code attribute
[ant:groovyc] >>> stacktrace:
[ant:groovyc] java.lang.ClassFormatError: javax/servlet/GenericServlet : Missing Code attribute
[ant:groovyc]
 at java.lang.ClassLoader.defineClass1(Native Method)
[ant:groovyc]
 at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
[ant:groovyc]
 at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
[ant:groovyc]
 at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
[ant:groovyc]
 at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
[ant:groovyc]
 at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
[ant:groovyc]
 at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
[ant:groovyc]
 at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
[ant:groovyc]
 at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
[ant:groovyc]
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)

I understand that the JavaEE files in the dependencies are only useful for compiling but their is a difference in behavior when compiling the source code as a Java file vs. a Groovy file.

It’s worth noting that the Maven ‘groovy-eclipse-compiler’ works fine using the ‘javaee-web-api’ dependency so it must be a problem not inherent to Groovy itself.

p.s. changing the dependencies to a real impl is fine as a work around. I just wanted to bring this up due to differences in compile behavior could cause other weird problems, e.g.

‘javaee-web-api’: ‘org.glassfish.extras:glassfish-embedded-all:3.1’


(Peter Niederwieser) #2

To determine whether this is a Groovy or Gradle problem, we’d have to know if the same problem exists for an Ant build using the ‘groovyc’ Ant task (with fork=true). Could you give this a spin? (The Groovy Eclipse compiler is an entirely different beast.)


(blackbeltdev) #3

Yep it fails with groovyc. javac works fine using the reference EE jar file.

<?xml version="1.0" encoding="UTF-8"?>
<project name="GroovyBugExample" default="build" basedir=".">
   <property environment="env"/>
   <property name="build" location="build" />
 <property name="mainClassesDirectory" location="build/classes/main" />
 <property name="javaSourceDirectory" location="src/main/java" />
 <property name="groovySourceDirectory" location="src/main/groovy" />
   <path id="groovyPath">
  <fileset dir="${env.GROOVY_HOME}" includes="embeddable/groovy-all-*.jar,lib/ivy*.jar"/>
 </path>
   <path id="compilePath">
  <path refid="groovyPath"/>
  <pathelement location="lib/javaee-api-6.0.jar" />
 </path>
   <path id="testPath">
  <path refid="compilePath"/>
 </path>
   <target name="init">
  <taskdef name="groovy"
          classname="org.codehaus.groovy.ant.Groovy"
         classpathref="groovyPath"/>
  <taskdef name="groovyc"
         classname="org.codehaus.groovy.ant.Groovyc"
         classpathref="groovyPath"/>
 </target>
   <target name="compile" depends="init">
        <mkdir dir="${mainClassesDirectory}"/>
      <javac srcdir="${javaSourceDirectory}" destdir="${mainClassesDirectory}" listfiles="true" includeAntRuntime="false" fork="true" source="1.6" target="1.6" debug="on">
   <classpath>
    <path refid="compilePath" />
   </classpath>
  </javac>
    <groovyc srcdir="${groovySourceDirectory}" destdir="${mainClassesDirectory}" listfiles="true" includeAntRuntime="false" fork="true">
   <classpath>
    <path refid="compilePath" />
   </classpath>
   <javac source="1.6" target="1.6" debug="on" />
  </groovyc>
     </target>
   <target name="test" depends="compile">
 </target>
    <target name="assemble" depends="test">
 </target>
   <target name="build" depends="clean, assemble">
 </target>
    <target name="clean">
     <delete dir="${build}" verbose="true"/>
   </target>
</project>
package com.acme.test.servlet
  import java.io.IOException
  import javax.servlet.ServletException
import javax.servlet.annotation.WebServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
  @WebServlet(urlPatterns = "/groovy/*")
class GroovyServlet {
    @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  super.doGet(req, resp);
 }
   }
package com.acme.test.servlet;
  import java.io.IOException;
  import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
  @SuppressWarnings("serial")
@WebServlet(urlPatterns = "/java/*")
public class JavaServlet extends HttpServlet {
   @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  super.doGet(req, resp);
 }
}
compile:
    [mkdir] Created dir: X:\workspaces\test\krasmussen\GroovyBug\build\classes\main
    [javac] Compiling 1 source file to X:\workspaces\test\krasmussen\GroovyBug\build\classes\main
    [javac] X:\workspaces\test\krasmussen\GroovyBug\src\main\java\com\acme\test\servlet\JavaServlet.java
  [groovyc] Compiling 1 source file to X:\workspaces\test\krasmussen\GroovyBug\build\classes\main
  [groovyc] X:\workspaces\test\krasmussen\GroovyBug\src\main\groovy\com\acme\test\servlet\GroovyServlet.groovy
  [groovyc] >>> a serious error occurred: javax/servlet/ServletException : Missing Code attribute
  [groovyc] >>> stacktrace:
  [groovyc] java.lang.ClassFormatError: javax/servlet/ServletException : Missing Code attribute
  [groovyc]
   at java.lang.ClassLoader.defineClass1(Native Method)
  [groovyc]
   at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
  [groovyc]
   at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
  [groovyc]
   at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
  [groovyc]
   at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
  [groovyc]
   at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
  [groovyc]
   at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
  [groovyc]
   at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
  [groovyc]
   at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
  [groovyc]
   at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
  [groovyc]
   at java.lang.ClassLoader.loadClass(ClassLoader.java:295)
  [groovyc]
   at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:696)
  [groovyc]
   at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:564)
  [groovyc]
   at org.codehaus.groovy.control.ResolveVisitor.resolveToClass(ResolveVisitor.java:709)
  [groovyc]
   at org.codehaus.groovy.control.ResolveVisitor.resolve(ResolveVisitor.java:275)
  [groovyc]
   at org.codehaus.groovy.control.ResolveVisitor.visitClass(ResolveVisitor.java:1241)
  [groovyc]
   at org.codehaus.groovy.control.ResolveVisitor.startResolving(ResolveVisitor.java:148)
  [groovyc]
   at org.codehaus.groovy.control.CompilationUnit$9.call(CompilationUnit.java:605)
  [groovyc]
   at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:843)
  [groovyc]
   at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:548)
  [groovyc]
   at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:497)
  [groovyc]
   at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:480)
  [groovyc]
   at org.codehaus.groovy.tools.FileSystemCompiler.compile(FileSystemCompiler.java:60)
  [groovyc]
   at org.codehaus.groovy.tools.FileSystemCompiler.doCompilation(FileSystemCompiler.java:216)
  [groovyc]
   at org.codehaus.groovy.tools.FileSystemCompiler.commandLineCompile(FileSystemCompiler.java:149)
  [groovyc]
   at org.codehaus.groovy.tools.FileSystemCompiler.commandLineCompileWithErrorHandling(FileSystemCompiler.j
ava:179)
  [groovyc]
   at org.codehaus.groovy.ant.FileSystemCompilerFacade.main(FileSystemCompilerFacade.java:27)
  BUILD FAILED
X:\workspaces\test\krasmussen\GroovyBug\build.xml:42: Forked groovyc returned error code: 1

(blackbeltdev) #4

I can create a JIRA ticket if you like so I can attach the missing lib file. It’s kind of ironic that I cannot compile a groovy source file using a groovy build tool :frowning:

Maybe you would make the eclipse-groovy-compiler plugin available to Gradle rather than groovyc?


(blackbeltdev) #5

BTW the same problem occurs with building EJBs using the reference JavaEE libs.


(Peter Niederwieser) #6

If you get the same issue with the groovyc Ant task, then I recommend to take this to the Groovy list. It would be nice to also support the standalone Eclipse Groovy compiler, but it’s nothing planned for the near future. Contributions are welcome.


(blackbeltdev) #7

It looks like Adam Murdoch already created an incomplete plugin to use the groovy-eclipse-compiler.

https://github.com/adammurdoch/gradle-groovy-eclipse-compiler

Although I understand that this isn’t strictly a Gradle issue IMHO it reflects badly as the" build expert company" not to address the problem considering this is a Groovy problem which Gradle is built upon.


(Peter Niederwieser) #8

The Groovy folks are in a better position to address problems with the Groovy compiler. It’s as simple as that. (By the way, Gradle isn’t built upon Groovy, and it isn’t a Groovy build system.)


(blackbeltdev) #9

BTW sorry didn’t mean to sound unappreciative above (hit the reply too fast). As a developer I value Gradle very much. I’m just bummed that I cannot use Gradle to build simple Java EE servlet with Groovy.


(Peter Niederwieser) #10

I think you are running into a known problem with the Groovy compiler, namely that it loads classes on the compile class path into the JVM as if it wanted to execute them. But again, the Groovy folks are in a better position to assess the situation.


(blackbeltdev) #11

I agree. It seems like the groovyc task is attempting to introspect the class in a way that the eclipse-groovy-compiler does not. I just feel that a bug request coming from a build company has a lot more weight/credibility than some random developer on the internet.

Sometimes companies have to support software that they are not directly responsible. If I found a bug in the Oracle javac compiler I wouldn’t expect Gradleware to fix it but I would expect them to find a work around for it and work with Oracle to try to resolve it as a “build expert” company. You are in the business of building software. This is an obstacle from completing that mission.

For example, when I reported a deadlock in the groovy eclipse compiler some developers from SpringSource dedicated to groovy support fixed it even though they don’t “own” it. When I reported a bug in Glassfish that was actually an Apache Felix bug an Oracle guy was assigned to fix it.

I’ve met both Hans Dockter and Ken Sipe in person and I think they would be surprised by this statement “Gradle isn’t built upon Groovy”. I mean the build scripts themselves are Groovy !

In the meantime I will log a bug with the groovy folks and see what happens. I’m sure there are not too many JavaEE groovy developers out there :wink:

I campaigned hard to get Gradleware a foot in the door at my previous company and I think its an awesome tool.


(Peter Niederwieser) #12

The workaround for this issue is trivial and was mentioned by yourself. Asking Gradleware to rearchitect the Groovy compiler, on their own time, is unrealistic. We do weigh in on such issues, especially when a Gradleware customer is affected (and uses the appropriate support channel). We hope to support the Eclipse Groovy compiler at some point, but so far there hasn’t been a lot of demand for it.

In this particular case, I’ve personally (in my role as a Groovy committer) tried to convince the Groovy leads to stop using reflection in the compiler for a long time. It might actually help if they hear it from someone else.

SpringSource does own the Groovy Eclipse compiler. It’s a fork of the Groovy compiler and their own creation.

All Gradle foundations are written in Java. All APIs are written in a way so that they can be used from any language and don’t depend on Groovy. Of course we do make good use of Groovy, most importantly for our build language. But that’s really just a thin (though wide) layer above the rest.


(blackbeltdev) #13

I will take this up with the Groovy team. Thanks for your help.


(blackbeltdev) #14

FYI…

http://jira.codehaus.org/browse/GROOVY-5320