gwtproject / gwt

GWT Open Source Project
http://www.gwtproject.org
1.52k stars 376 forks source link

The com.google.gwt:gwt-dev jar contains duplicate classes from the org.eclipse.jdt.core:ecl jar #5289

Closed dankurka closed 9 years ago

dankurka commented 9 years ago

Originally reported on Google Code with ID 5290

Found in GWT Release (e.g. 1.5.3, 1.6 RC): 2.0.4
Encountered on OS / Browser (e.g. WinXP, IE6-7, FF3): any

Detailed description (please be as specific as possible):

The gwt-dev jar shouldn't repackage the jdt jar, as the user library might use a newer
version of the jdt jar (for their server side code).
For example: drools-guvnor uses drools-compiler which uses jdt in it's server code,
but wants to compile it's gwt code with gwt-dev, which fails if jdt is in the classpath.

Workaround if you have one:
1) Either don't use any GWT based project or don't use any JDT based project (such
as drools) in your project.
2) Use the same version of the JDT jar (impossible as drools uses 3.5 and gwt-dev only
works with 3.4).

Links :
It causes this issue: http://code.google.com/p/google-web-toolkit/issues/detail?id=4479

Reported by gds.geoffrey.de.smet on 2010-09-20 13:17:46

dankurka commented 9 years ago
This is similar to Sun JRE 1.4 (or 1.5?) having org.apache.xerces klasses in it's classpath.
It was impossible to use a newer version of xerces. In Sun JRE 1.6 they subnamespaced
them to com.sun.org.apache.xerces.

Solution:
Proposition 1: Do not package jdt(ecj) inside gwt-dev.jar, just place the jdt(ecj)
jar next to it, so we can exclude it.
Proposition 2: If you do insist on packaging (=duplicating) the jdt(ecj) classes in
the gwt-dev.jar, then subnamespace them (com.google.org.eclipse.jdt).

Reported by gds.geoffrey.de.smet on 2010-10-01 09:37:46

dankurka commented 9 years ago
I had the same problem using JBoss 6 (JBossAS [6.0.0.20100721-M4 "Neo"]) to host my
GWT Application.

In the folder "jboss6\server\default\deploy\jbossweb.sar" there is a jar named "jasper-jdt.jar",
wich includes the packages org.eclipse.jdt.

I am using eclipse-jee-helios-SR1-win32 and GWT Plugin 2.0.4.

The error happens when the GWT development mode starts and the GWT application is opened
on a browser.

To solve the problem, I edited the Run Configuration of my GWT Development Mode, and
I changed the classpath to make sure it was not include the JBoss JAR jasper-jdt.jar.

Reported by keller.diego on 2010-10-01 14:55:17


dankurka commented 9 years ago
That fixes it for Eclipse, but there is no way to make it work for the gwt-maven-plugin,
see: http://jira.codehaus.org/browse/MGWT-219

Reported by gds.geoffrey.de.smet on 2010-10-03 11:21:31

dankurka commented 9 years ago
Actually it's more of a workaround for eclipse.

This is probably the real solution:
The GWT compiler must make a distinction between the compiler's classpath and the compilation
classpath.

Reported by gds.geoffrey.de.smet on 2010-10-29 08:19:06

dankurka commented 9 years ago
I just wanted to report that this bug still exists in gwt 2.1.0,
apparently no one found it important enough to fix this.

We are using the gwt-maven-plugin version 2.1.0 and get a NoSuchFieldError.

To fix this. we will have to make our own version of the eclipse jdt libraries and
rename them...

I hope someone finds the time to fix this issue.

Reported by samuel.kogler on 2010-11-28 12:41:14

dankurka commented 9 years ago
Given that your own use of JDT is necessarily on the server-side, and that GWT DevMode
should load webapp server-side classes (<war>/WEB-INF/{lib/*.jar,classes}) from a specific
classloader, wouldn't the fix/workaround be to exclude your server-side dependencies
from the DevMode classpath?

And for gwt-maven-plugin, as suggested by Geoffrey (even though that's not what he
meant), it should allow using the plugin dependencies rather than (or in addition to)
the project dependencies.
If you have to depend on com.google.gwt:gwt-dev (because you have a linker or generator),
then you can, as a workaround, put it in its own Maven module, using a <scope>provided</scope>
for the com.google.gwt:gwt-dev dependency.

Reported by t.broyer on 2010-11-28 14:29:56

dankurka commented 9 years ago
> be to exclude your server-side dependencies from the DevMode classpath?

Yes, that workaround/hack could work, but the maven plugin does not support that hack
(and they made it clear they will not support it because they believe gwt should fix
the classpath correctly).

The true fix is that the GWT compiler should make a clear separation between it the
compiler classpath and the complication classpath, just like the JDK compiler.

The provided scope won't work, your dependencies (including jdt) are still given to
the GWT plugin.

Reported by gds.geoffrey.de.smet on 2010-11-28 14:50:39

dankurka commented 9 years ago
Clearly, I'm disappointed by gwt-maven-plugin. If you have both client-side and server-side
code in the same Maven project, gwt:run *has* to run with a classpath containing only
client-side code. Maybe the real fix would be to segregate client-side code to another
source folder than src/main/java but that's not what gwt-maven-plugin developers advocate
either.
I was taking about using DevMode from within Eclipse though, not from gwt:run.

Having the GWT compiler to isolate everything (including generators and linkers! because
that's where conflicts could happen if you –correctly IMO– don't include server-side
code in the classpath) into its own classpath would probably be difficult (would it
even be possible?), and not worth it if only 0.001% of users actually need it.

As for the provided scope, I think you didn't understand what I was talking about.
1. myapp-rebind -> contains generator and/or linkers, depends on gwt-dev with provided
scope.
2. myapp -> contains client code, depends on myapp-rebind (whose classes are referenced
in *.gwt.xml files). gwt-dev won't be inherited (because of scope=provided) so it won't
conflict with your code. And it will effectively be provided when needed, i.e. gwt:run
or gwt:compile.
I haven't tried it but I cannot foresee any reason why it wouldn't work.

Reported by t.broyer on 2010-11-29 10:41:25

dankurka commented 9 years ago
"gwt:run *has* to run with a classpath containing only client-side code."
That's impossible, unless you split up the client and server side into 2 different
maven projects (which is also recommended). The problem for that is, that it doesn't
work due to the wierd directory structure and habbits of GWT (and therefore the GWT
IDE plugins) :/

Anyway, I am just the middleman who detected a problem here. I'd suggest to talk to
the maven guys directly:
  http://jira.codehaus.org/browse/MGWT-219

Reported by gds.geoffrey.de.smet on 2010-11-29 14:04:34

dankurka commented 9 years ago
To clarify: "splitting up the client and server side into 2 different maven projects"
doesn't work because of the *.gwt.xml module files. In which directory do you put them?

Reported by gds.geoffrey.de.smet on 2010-11-29 14:14:32

dankurka commented 9 years ago
Maybe that's a case for a special kind of packaging, and gwt:run would only use those
dependencies with type=gwtlib (and not type=jar, unless specified as module dependencies),
similar to how maven-war-plugin does for overlays (automatically picks up type=war
dependencies, and accepts type=zip ones if explicitly given).
Artifacts with packaging=gwtlib would be jars with embedded sources, and a special
specifier=shared,type=jar artifact could be compiled as well (defaulting to excluding
**/client/** classes, or using additional source directories: src/main/java for server
code, src/main/gwt-client for client-only code and src/main/gwt-shared for shared code;
and not bundling sources; as a side note, *.gwt.xml files are of course sources, not
resources).

About splitting code between client and server modules, that's precisely what I'm doing:
myapp-shared for shared classes (includes GWT-RPC interfaces), myapp-client for client-only
code (depends on myapp-shared, and myapp-shared:sources), myapp-server for server-only
code (depends on myapp-shared and implements GWT-RPC as RemoteServiceServlet-s), and
finally a myapp module with packaging=war, that depends on myapp-server and myapp-client.
The *.gwt.xml of course live in the myapp-client module (server-side code don't need
them), and in myapp-shared src/main/java (they are source files, used to compile the
GWT app; not resources, that would unnecessarily be included in JARs that get deployed
on the server)

That being said, I'm relatively new to maven (5 months) so I might very well be wrong
in how I expect it to work (but definitely the gwt-maven-plugin guys are wrong in how
they expect GWT to work).

Reported by t.broyer on 2010-11-29 14:50:13

dankurka commented 9 years ago
@t.broyer That splitting code between clients and server modules sounds like a good
idea.
How do you make that structure work for the hosted mode plugin in eclipse/intellij?

Reported by gds.geoffrey.de.smet on 2011-02-14 12:16:16

dankurka commented 9 years ago
We're launching the server through Eclipse's WTP (Jetty WTP actually) with M2Eclipse
WTP, and then launching the DevMode in -noserver mode.
Jetty WTP (or is it M2Eclipse WTP? or just Eclipse WTP as a whole?) is not really reliable
though, so I'm investigating using the jetty-maven-plugin (which is a bit tricky as
I don't want to do a full build and/or install the artifacts each time I make a change
in one of the dependencies; I've read that extraClasspath and scanTarget could help
[1], looks a bit hackish but if it works better than WTP...)

[1] http://stackoverflow.com/questions/3636493/multi-module-maven-project-and-jettyrun

Reported by t.broyer on 2011-02-14 14:00:20

dankurka commented 9 years ago
I'm using GWT-2.2.0 with NetBeans 7.0 and Tomcat 7.0.11. I have a unit test extending
GWTTestCase. When I run this unit test I am getting the following exception:

warningThreshold
java.lang.NoSuchFieldError: warningThreshold
    at com.google.gwt.dev.javac.JdtCompiler.getCompilerOptions(JdtCompiler.java:340)
    at com.google.gwt.dev.javac.JdtCompiler$CompilerImpl.<init>(JdtCompiler.java:174)
    at com.google.gwt.dev.javac.JdtCompiler.doCompile(JdtCompiler.java:616)
    at com.google.gwt.dev.javac.CompilationStateBuilder$CompileMoreLater.compile(CompilationStateBuilder.java:193)
    at com.google.gwt.dev.javac.CompilationStateBuilder.doBuildFrom(CompilationStateBuilder.java:390)
    at com.google.gwt.dev.javac.CompilationStateBuilder.buildFrom(CompilationStateBuilder.java:275)
    at com.google.gwt.dev.cfg.ModuleDef.getCompilationState(ModuleDef.java:325)
    at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1322)
    at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1289)
    at com.google.gwt.junit.JUnitShell.runTest(JUnitShell.java:631)
    at com.google.gwt.junit.client.GWTTestCase.runTest(GWTTestCase.java:441)
    at com.google.gwt.junit.client.GWTTestCase.run(GWTTestCase.java:296)

Under `Properties > Libraries > Run Tests` I have a variable pointing to my Tomcat
lib directory such that the required jar files are on my CP for enabling use of embedded
Tomcat.

Moving this variable to the end of my CP enables the GWTTestCase unit test to work..
but then embedded Tomcat does not work.

Is this the same issue?

WulfgarPro

Reported by wulfgar.pro on 2011-06-14 06:51:23

dankurka commented 9 years ago
Re: #14, yes, it looks like the same issue. If you can isolate which Tomcat jar contains
JDT, perhaps you can exclude it. If it turns out to be essential, you're probably out
of luck with embedded Tomcat.

Reported by drfibonacci@google.com on 2011-06-20 13:56:44

dankurka commented 9 years ago
Agreed that it's not ideal for GWT to bundle JDT; however, the dependencies run so deep
that it would be impractical to decouple given the small number of users affected by
this and the availability of workarounds in most cases.

Reported by drfibonacci@google.com on 2011-06-20 13:58:54

dankurka commented 9 years ago
Not agreed that there are decent workarounds for this. Our project build in your favorite
IDE does not work out of the box because of this, as they have to order the dependencies
in their IDE (Eclipse/IntelliJ/NetBeans) correctly just to override it. And our project
is open source, so we got lots of people trying to build it - which fail and complain
(with good reason).

There are many projects that use JDT. Any project that uses drools or jbpm for example.

Reported by gds.geoffrey.de.smet on 2011-06-20 18:59:42

dankurka commented 9 years ago
Any such project using JDT don't do it in client (obviously), generator or linker code.
If they want to do so in generator (or linker) code, it's their responsibility to use
their own classloaders. Projects can use whatever they like on the server side, it's
not GWT's concern how they manage their classpath re. compiling.

It's a classpath issue. It's a "Java-versionning" issue.

If the issue is Maven, then blame Maven.
But I believe the "Maven way" is to separate client and server code in separate projects
(just like you would have one project with your ActionScript code compiling to SWF,
and another project making use of the compiled SWF).

If the issue is the IDE, then blame the IDE.
Eclipse is known to be limited to a single "build path" when, for instance, Maven handles
several scopes: compile, runtime, test. M2Eclipse handles this for you with its specialized
launcher types; but for other launchers and/or other build systems, or no particular
build system, you have to manage your classpath by hand in your launchers.

I'm a Maven user, and I (learned to) like it. I'm an Eclipse user, and I like it too.
But please don't blame GWT when the issue is versionning in Java (something OSGi tries
to solve, and maybe Java 8 will have built-in), your IDE, or your Maven project layout
or plugin/dependengy configuration.

...or provide patches so that GWT uses an internal classloader, à la OSGi, to isolate
its internals (JDT, Tomcat, Jetty).

There's only one thing that I'd improve in GWT's Maven "packaging": extract the generator/linker
framework into its own JAR so you don't have to depend on the whole gwt-dev (a bit
similar to gwt-user vs. gwt-servlet: different artifacts for different "scopes"). And
that would also (probably) solve your issues.

Reported by t.broyer on 2011-06-20 20:29:48

dankurka commented 9 years ago
Actually it's bad. You can not delete gwt-dev.jar in gwt development mode so you can
not use drools in you application while developing web application. Why can't at least
gwt move to next version of jdt? Is it very difficult too?

Reported by seqira on 2011-06-21 05:01:23

dankurka commented 9 years ago
@t.broyer: any OSGi based solution is doomed to fail because gwt-dev doesn't just put
the jdt jar in the classpath, instead it unzips the jdt jar and puts those classes
in the gwt-dev jar without namespacing them.

That's breaking very basic Java conventions: don't mix classes of other jars in your
jar.

If it were to namespace those classes, for example similar to how sun uses org.apache.xerces.*
embedded in the JDK 6+ as com.sun.org.apache.xerces.* instead, there would be not problem
either.

Reported by gds.geoffrey.de.smet on 2011-06-21 07:14:33

dankurka commented 9 years ago
How is it different from "you need version X of that library, it'll break with newer
versions because of internal changes"? That's "DLL hell" the Java way.

If you have a generator/linker that needs JDT in another version, then have it use
a OSGi-like approach, loading it in a child classloader.

If you want to contribute a patch to GWT to make it use an OSGi-like approach, then
also make it load classes from a "bundled JAR" instead of adding the JDT classes right
into gwt-dev.jar.

Reported by t.broyer on 2011-06-21 09:01:23

dankurka commented 9 years ago
@t.broyer This problem is not OSGi specific, but happens to maven, ANT, gradle and builder
users alike.

OSGi has a MANIFEST file per jar, not per class file.
Most people don't use OSGi (too complex). Many do use maven, which also has the limitation
of only being able to exclude per jar to the classpath, not per class. Same goes for
ANT, gradle, buildr, ivy.

PS: Maven, gradle, buildr, ivy, ruby-gems take away 80% of the "Jar hell" (= "DLL hell")
issues away. Problem is, you need to learn what it does to be able to fix those other
20%.

"Patches welcome" is a good point since you're not prioritizing this. However, I doubt
building GWT will be as easy as how I patched the maven-gwt-plugin: "git clone ...;
mvn clean install". Splitting up compiler from the compilation classpath will probably
be a fundamental design flaw, not something which is fixable in a day or 2. Correct
me if I am wrong.

Anyway, please keep this issue open. 13 (unbiased as far as I know) stars is not nothing.

Reported by gds.geoffrey.de.smet on 2011-06-21 09:23:23

dankurka commented 9 years ago
Reopening to be counted with other open issues involving un-bundling jars.

Reported by drfibonacci@google.com on 2011-06-21 16:44:44

dankurka commented 9 years ago
Thank you for keeping this open, I like the proposed solutions in Comment 1.  In our
case it is also the conflict with drools that is causing us pain, but any third party
lib that requires jdt would have the same problem.

Reported by dale.wyttenbach on 2011-10-26 14:10:06

dankurka commented 9 years ago
For people building with maven, the answer to this issue lies here : http://stackoverflow.com/questions/793054/maven-classpath-order-issues

In your pom.xml, declare your dependency on gwt-dev before declaring dependency on
any project using jdt, and set the "gwtSdkFirstInClasspath" gwt-maven-plugin option
to "true". This way, you will be able run both your hosted mode and your GWTTestCase
tests without "java.lang.NoSuchFieldError: warningThreshold" popping up.

Reported by rotoudjimaye.theo on 2011-11-04 10:20:57

dankurka commented 9 years ago
@rotoudji That does not work: your normal tests that use drools with jdt will fail...
(NoSuchMethodError because the shaded jdt classes are used)

Reported by gds.geoffrey.de.smet on 2011-11-04 10:29:11

dankurka commented 9 years ago
@gds In my setup, the business logic, with its drools tests etc. gets developed in a
different maven module. It then gets imported into the gwt project, with the dependency
on gwt-dev declared before the dependency on the the project with drools.

Reported by rotoudjimaye.theo on 2011-11-04 10:49:46

dankurka commented 9 years ago
I tried a simple trick and I was able to get past the ecj dependency include issue.

I have one "service" project that uses drools and another "gwt-ui" project (among others)
- all maven-built.

gwt-ui project has service code that *depends* on service project (hence drools / ecj).

I included the following dependency (ecj version 3.7 which is in tomcat7/lib already)
with scope=provided to my service (drools) project and the compile / deploy problems
went away.

        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>${ecj.version}</version>
            <scope>provided</scope>
        </dependency>

Please give this a try and see if solves the ecj dependency problem.

Thanks

Reported by tracklead on 2012-03-06 12:19:57

dankurka commented 9 years ago

Reported by t.broyer on 2012-10-10 15:47:53

dankurka commented 9 years ago
Issue 6485 has been merged into this issue.

Reported by t.broyer on 2013-04-10 10:15:17