Classloader issue running Hosted mode from Eclipse with Maven managed dependencies

58 views
Skip to first unread message

nicolas de loof

unread,
Feb 25, 2009, 11:24:13 AM2/25/09
to Google-Web-Tool...@googlegroups.com
Hi

I'm testing the Eclipse lauch configuration generated by the gwt-maven-plugin for gwt 1.6.
My webapp project has many dependencies managed as an Eclipse classpath-container (thanks to m2eclipse plugin).
Running the lauch file I get a classpath issue :

16:57:29,331 ERROR [log.invoke0](?) failed com.google.gwt.dev.shell.jetty.JettyLauncher$WebAppContextWithReload@1e2105f{/,D:\projets\bios\bios-relation-client\bios-rc-webapp\target\bios-rc-webapp-1.0.0-SNAPSHOT}
javax.xml.parsers.FactoryConfigurationError: Provider org.apache.xerces.jaxp.SAXParserFactoryImpl not found
at javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:113)
at org.mortbay.xml.XmlParser.setValidating(XmlParser.java:92)
at org.mortbay.xml.XmlParser.<init>(XmlParser.java:84)
at org.mortbay.jetty.webapp.TagLibConfiguration.configureWebApp(TagLibConfiguration.java:199)
at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1217)
at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:513)
at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:448)
at com.google.gwt.dev.shell.jetty.JettyLauncher$WebAppContextWithReload.doStart(JettyLauncher.java:236)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
at org.mortbay.jetty.Server.doStart(Server.java:222)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
at com.google.gwt.dev.shell.jetty.JettyLauncher.start(JettyLauncher.java:286)
at com.google.gwt.dev.HostedMode.doStartUpServer(HostedMode.java:367)


It seems to be yet another classloader issue, any suggestion ???



Scott Blum

unread,
Feb 25, 2009, 11:46:49 AM2/25/09
to Google-Web-Tool...@googlegroups.com
I've seen this before.  The workaround is to put a copy of Xerces into your WEB-INF/lib.

What's weird is that on my system, the default SAX parser factory is in the com.sun.* namespace, which lives in the bootstrap ClassLoader and works fine.  I wonder why your default SAX parser factory is pointing at the raw org.apache version, which lives in gwt-dev and isn't on the bootstrap loader?

We could always put an explicit pass-through in WebAppContextWithReload to allow this package.  Can you file an issue and write back on this thread with a link to it?

nicolas de loof

unread,
Feb 25, 2009, 11:52:35 AM2/25/09
to Google-Web-Tool...@googlegroups.com
As I said my application has many other libs, including xerces (and spring, hibernate, aspectJ ...) so there is MANY reason for me to have another SAX parser in my classpath.

The issue here is that in my case I can manually put the parser in web-inf/lib, but the maven plugin is expected to automagically configure the project, and has no simple way to detect what is the SAX parser used when running the project.

Another option (that works) is to force use of the default SAX parser using -Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl JVM option, but this has the side effect to require the SUN JVM. What about Jrockit fans ?

Nicolas

Scott Blum

unread,
Feb 25, 2009, 12:04:51 PM2/25/09
to Google-Web-Tool...@googlegroups.com, Toby Reyelts
I take your point.

In the general case, server side dependencies need to be copied into WEB-INF/lib.  Otherwise, the server code won't be able to access it.  I can see how an XML parser might be a special case where the servlet container could be reasonably expected to provide it.

Toby, do you have any thoughts on this?  I think you're familiar with the pattern we're using of isolating Jetty from the system classpath to enforce the rule that server deps have to go into WEB-INF/lib (as opposed to just being on the system classpath).  How are things like XML parsers generally treated?  Should we special-case this?

Thanks,
Scott

Toby Reyelts

unread,
Feb 25, 2009, 12:21:15 PM2/25/09
to Scott Blum, Google-Web-Tool...@googlegroups.com

I'm not sure I have all the context here, but as a guiding principle, Jetty should be able to parse the XML files of web applications (web.xml, tag libraries, etc...) regardless of whatever XML libraries are or are not bundled with the webapp. This means:

1) Jetty should never use XML libraries from the webapp
2) Jetty should always be able to find an XML parser in the JDK

I'm not sure exactly what's going on in this situation to cause that to not be true, but it should be fixed.

Sami Jaber

unread,
Feb 25, 2009, 12:21:54 PM2/25/09
to Google-Web-Tool...@googlegroups.com, Toby Reyelts
This is a very important issue. To resolve it, we had to code a custom servlet container launcher, this way : 

    /**
     * Ensures that only Jetty and other server classes can be loaded into the
     * {@link WebAppClassLoader}. This forces the user to put any necessary
     * dependencies into WEB-INF/lib.
     */

    private final ClassLoader parentClassLoader = new ClassLoader(null) {
      private final ClassLoader delegateTo = Thread.currentThread().getContextClassLoader(); 

      @Override
      protected Class<?> findClass(String name) throws ClassNotFoundException {
        if (webAppClassLoader != null
            && (webAppClassLoader.isServerPath(name) || webAppClassLoader.isSystemPath(name))) {
          return delegateTo.loadClass(name);
        }
        throw new ClassNotFoundException();
      }
  };

Works with -server MyCustomJettyLauncher

This is not very smooth, but that works.
Scott, maven is widely used by many folks out there, constraining ressources to be loaded from WEB-INF/lib doesn't make sense in Dev mode. Especially when you have many classes. The hosted mode should be able to use the classes passed from the command line by the Eclipse Maven plugin (kind of -cp %maven_classes%).

Sami 

nicolas de loof

unread,
Feb 26, 2009, 5:59:48 AM2/26/09
to Google-Web-Tool...@googlegroups.com, Toby Reyelts
I've found an (ugly ?) workaround by adding xercesImpl in the bootstrap classpath and setting the javax.xml.parsers.SAXParserFactory system property.

With this config the plugin can assume the SAX parser to be used in hosted mode - not very pleasant as the user can't configure it's own, but fixes the hosted mode lauch issue.


Another question about configuring jetty : it expects a web.xml to define the RPC servlets. Many of us will use some more complex server side setup, for example including Spring and Hibernate, DataSources configuration, ... etc

Running such "complex" server side components with hosted mode is easy with noserver, as we can setup the adequate servlet container. But with hosted mode jetty I fall into classloaders issues (jetty expect my libs to be in WEB-INF/lib, but they are set in eclipse launch classpath)

The simpliest fix could be to have a dedicated web.xml file for jetty and hosted mode using mock-servlet RPC implementations. This is possible with 1.5 as the module <servlet> element is
not used after compilation into a webapp. 

Any suggestion of a best-practice ? Any way to configure jetty to use an alternate web.xml ?

Cheers,
Nicolas

nicolas de loof

unread,
Feb 26, 2009, 8:38:13 AM2/26/09
to Google-Web-Tool...@googlegroups.com, Toby Reyelts
To make things a little clearer, here is how I understand the way classloader are built by Hosted mode :

bootClasspath <gwt-dev>
   |_ HostedMode
         |_ Hosted Browser ....
         |
         |_ Jetty Embedded
              |_ WebApplicationClassLoader <WEB-INF/lib/*>

How does Jetty access project (service-side classes) ?

The "maven" way to run Hosted Mode would be :


bootClasspath 
   |_ Maven laucher <UrlClassLoader : full project classpath (sources, classes and libs)>
           |_ HostedMode
                 |_ Hosted Browser ....
                 |
                 |_ Jetty Embedded
                      |_ WebApplicationClassLoader <WEB-INF/lib/ is empty>

As the Maven Laucher can setup any argument / system variable that may be required, is there any way to configure the way Ebedded Jetty search for web application libs & classes ?

Another option would be for the plugin to "prepare" the WEB-INF/lib with project libs - code doing this allready exists - but with more impact on Hosted mode launch speed.

Cheers,
Nicolas

nicolas de loof

unread,
Feb 26, 2009, 10:55:55 AM2/26/09
to Google-Web-Tool...@googlegroups.com, Toby Reyelts
I tested the "create exploded webapp structure in maven plugin before creating lauch configurations" strategy.

I can launch the hosted browser but this has serious productivity cost : The /lib folder is not supposed to change often, so this is not an issue to poulate it from maven dependencies. 
The issue is with the (server-side) classes that I had to copy to /WEB-INF/classes. This must be done before every restart of the Hosted Browser when any Java file has been changed to sync sources and classes executed inside Jetty. As maven (or Eclipse + m2eclipse) compile to target/classes I'd prefer to avoid such costy setup.

Nicolas

nicolas de loof

unread,
Feb 26, 2009, 11:09:20 AM2/26/09
to Google-Web-Tool...@googlegroups.com, Toby Reyelts
You may be interested to see how jetty guys integrate with maven :

They use a classPathFiles List created by the maven "jetty:run" goal to setup the jetty webappClassLoader. Maybe some comparable setup could be integrated into hosted mode ?

Cheers,
Nicolas

nicolas de loof

unread,
Feb 26, 2009, 11:26:57 AM2/26/09
to Google-Web-Tool...@googlegroups.com, Toby Reyelts
I finally found an acceptable solution by :
  • using a test webapp structure in src/test/webapp so that I can register simplified (mock) GWT-RPC servlet implementations and not require my full webapp to test in hosted mode (can still use noserver for that)
  • creating the exploded WAR structure for jetty WEB-INF/lib when the plugin generates the launch configurations
  • configuring my maven project to output compiled classes in jetty WEB-INF/classes (the plugin checks this configuration has been set)
Please let me know if you find a simplier way to integrate maven with Jetty ;-)

John Tamplin

unread,
Feb 26, 2009, 11:42:02 AM2/26/09
to Google-Web-Tool...@googlegroups.com, Toby Reyelts
On Thu, Feb 26, 2009 at 5:59 AM, nicolas de loof <nicolas...@gmail.com> wrote:
Another question about configuring jetty : it expects a web.xml to define the RPC servlets. Many of us will use some more complex server side setup, for example including Spring and Hibernate, DataSources configuration, ... etc

Running such "complex" server side components with hosted mode is easy with noserver, as we can setup the adequate servlet container. But with hosted mode jetty I fall into classloaders issues (jetty expect my libs to be in WEB-INF/lib, but they are set in eclipse launch classpath)

The intention is that if you need more complex configurations, you setup your server yourself and run with -noserver.  While it may or may not be feasible to support your desired behavior with the built-in jetty, there are going to be many unique configurations out there that will not work with it so -noserver is the escape valve needed for those.

--
John A. Tamplin
Software Engineer (GWT), Google

nicolas de loof

unread,
Feb 26, 2009, 11:53:36 AM2/26/09
to Google-Web-Tool...@googlegroups.com, Toby Reyelts
I fully agree about the noserver option when some server-side setup is required. I myself consider the hosted-mode server only for UI testing with mock gwt-RPC servlets.

The issue here with maven integration is that even the simpliest server setup (only RPC RemoteServlet with no dependency on any other lib) requires to put project classes and gwt-* libs in jetty WEB-INF, that is not the best option for maven as it requires many files to get copied. Take a look at the jetty6 MavenConfiguration to see that jetty guys found an alternate way by building the webapp classpath dynamically from maven dependencies.

Cheers,
Nicolas

Scott Blum

unread,
Feb 26, 2009, 12:04:42 PM2/26/09
to Google-Web-Tool...@googlegroups.com, Toby Reyelts
On Thu, Feb 26, 2009 at 11:53 AM, nicolas de loof <nicolas...@gmail.com> wrote:
The issue here with maven integration is that even the simpliest server setup (only RPC RemoteServlet with no dependency on any other lib) requires to put project classes and gwt-* libs in jetty WEB-INF, that is not the best option for maven as it requires many files to get copied.

This is where we made a bit of a tough-love decision to conform to the Servlet spec by essentially requiring server side code to go into WEB-INF/lib and /classes.  Output locations of .class files are generally quite configurable, for example in Eclipse projects we just literally set the output folder for source compilation to be WEB-INF/classes, and we add classpath entries to the Eclipse project that target WEB-INF/lib directly.

While this may cause some friction with some build tools and configurations, the benefits of standardizing on the Servlet spec and exploded war structure seem to far outweigh the costs.  It puts everyone on the same page in terms of tooling, documentation, assumptions, etc.  Down the road it will enable features like generation of server-side bytecode.  RPC for example could be much more efficient on the server if we generated bytecode instead of using reflection so heavily.

That being said, I do want to address specific pain points like this XML validation problem.  I repro'd this locally and you can trigger the problem in a straight up hosted mode launch merely by putting Xerces on the classpath.  This seems pretty bad. :)

Reply all
Reply to author
Forward
0 new messages