piotrb86
piotrb86

Reputation: 187

gwt + jetty + spring + log4j ERROR: "DOMConfigurator object is not assignable to a Configurator"

This problem was mentioned in several sources around the web but I was unable to solve it with solutions provided there.

PROBLEM: The following error is thrown from log4j when issuing mvn gwt:run:

[ERROR] log4j:ERROR A "org.apache.log4j.xml.DOMConfigurator" object is not assignable to a "org.apache.log4j.spi.Configurator" variable.
[ERROR] log4j:ERROR The class "org.apache.log4j.spi.Configurator" was loaded by
[ERROR] log4j:ERROR [sun.misc.Launcher$AppClassLoader@23137792] whereas object of type
[ERROR] log4j:ERROR "org.apache.log4j.xml.DOMConfigurator" was loaded by [WebAppClassLoader=Demo@3d1665ac].
[ERROR] log4j:ERROR Could not instantiate configurator [org.apache.log4j.xml.DOMConfigurator].

DESCRIPTION of my project: I use default jetty server provided with gwt and run it on exploded war.

<gwt.version>2.6.1</gwt.version>
<spring.version>3.2.6.RELEASE</spring.version>
<log4j.version>1.2.17</log4j.version>
<slf4j.version>1.7.5</slf4j.version>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>${log4j.version}</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- Normally this jar would be listed in dependencies but in my case causes log4j ERROR. -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>${slf4j.version}</version>
    <scope>runtime</scope>
</dependency>

What's more, I exclude commons-logging from spring and other projects that depend on it.

(not satisfying) SOLUTION: Logging works fine only when log4j, slf4j-api, slf4j-log4j12 and jcl-over-slf4j jars are put in my WEB-INF/lib directory but only when jcl-over-slf4 is not in the project's classpath (i.e. I comment out the last mentioned dependency).

When jcl-overl-slf4j is included in maven dependencies (see above) it is not only added to target's lib directory but also included in project's classpath. It causes the error. This jar is necessary to be put in lib but the error disappears only when it is not included in the classpath. maven-dependency-plugin is used to work this problem around by copying it into lib directory and skipping maven dependency.

This solution is obviously just a workaround as all four jars - log4j, slf4j-api, slf4j-log4j12, jcl-over-slf4j - are mentioned in many standard examples of gwt and spring projects.

Could you explain why is it behaving that way and how could I solve this with normal inclusion of jcl-over-slf4j in maven dependencies?

Upvotes: 4

Views: 7745

Answers (1)

Thomas Broyer
Thomas Broyer

Reputation: 64551

Jetty treats org.apache.commons.logging as a system class, i.e. it loads it from the system class loader (i.e. the classpath) in priority over the webapp's WEB-INF/lib. In your case, org.apache.commons.logging is provided by jcl-over-slf4j. So, code in your webapp calls Commons Logging which is then loaded from the system class loader, and it probably initializes SLF4J using the class's class loader (as opposed to the current thread class loader), which thus uses code from slf4j-log4j12 and log4j from the system class loader. Later on, code in the webapp calls into Log4j (possibly through SLF4J) to initialize the logging configuration, and it then uses the JARs from the webapp's WEB-INF/lib (as expected). When it comes to put everything together, then comes the issue, with classes loaded from different class loaders.

Now to solve the issue: it's not straightforward.

Simply put, classloading in DevMode is a mess (see https://docs.google.com/document/d/1bWfafaPA0m0Z1Swodnx7m3QTv31OdqFkE7aeadN_2BU/edit?usp=sharing where I tried to document it).

To solve your issue, you'd have to either use your own ServletContainerLauncher in DevMode with your own classloading rules, or more simply run your webapp in another servlet container (e.g. mvn jetty:run or mvn tomcat7:run, or whatever). Then you'd run DevMode in -noserver mode.

It's a slightly more complex setup, but it has the big advantage of being exactly the one you'll need for SuperDevMode; and SuperDevMode will replace DevMode this year (DevMode is dead already in Firefox, and in Chrome on Linux –basically, it's dead on Linux–, and support will be removed from Chrome on other platforms later this year, leaving only one working platform: Internet Explorer on Windows).

Upvotes: 5

Related Questions