Bug 44041 - WebappClassLoader duplicate class definition under high Load if hasExternalRepositories is true
Summary: WebappClassLoader duplicate class definition under high Load if hasExternalRe...
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 6
Classification: Unclassified
Component: Catalina (show other bugs)
Version: 6.0.26
Hardware: All All
: P2 critical (vote)
Target Milestone: default
Assignee: Tomcat Developers Mailing List
URL: http://www.ubik-ingenierie.com
Keywords:
Depends on:
Blocks:
 
Reported: 2007-12-07 13:55 UTC by Philippe Mouawad
Modified: 2010-05-06 15:21 UTC (History)
0 users



Attachments
Sample Project that contains the Servlet and a Test case (8.95 KB, application/x-zip-compressed)
2007-12-07 14:02 UTC, Philippe Mouawad
Details
The dependent project that will be added by DevLoader as an externalrepository (2.16 KB, application/x-zip-compressed)
2007-12-07 14:04 UTC, Philippe Mouawad
Details
Tomcat Sysdeo DevLoader Last version (5.91 KB, text/x-java)
2007-12-07 14:05 UTC, Philippe Mouawad
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Philippe Mouawad 2007-12-07 13:55:58 UTC
I am using Tomcat Sysdeo Plugin combined with their DevLoader.
This class extends Tomcat WebappClassLoader to add external repositories through:
WebappClassLoader#addRepository

Under high load this exception occurs if a Servlet or JSP calls:
			Class clazz =
Thread.currentThread().getContextClassLoader().loadClass("<Class Name>");


java.lang.LinkageError: duplicate class definition:
com/ubikingenierie/bug/ClassLoadedDynamically
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
	at
org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:890)
	at
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1329)
	at
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1208)
	at com.ubikingenierie.servlet.BasicServlet.service(BasicServlet.java:27)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
	at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:269)
	at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
	at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
	at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:174)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:544)
	at
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:151)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:874)
	at
org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
	at
org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
	at
org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
	at
org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:689)
	at java.lang.Thread.run(Thread.java:595)


Philippe.
http://www.ubik-ingenierie.com
Comment 1 Philippe Mouawad 2007-12-07 14:02:42 UTC
Created attachment 21244 [details]
Sample Project that contains the Servlet and a Test case

This project uses Tomcat Sysdeo DevLoader.
TO use it, import the project DependentProject (next attachment) in Eclipse.
TO use it, import the project BugTomcat5 in Eclipse.
Define classpath variable LIBRARIES that points to a folder containing:
-commons-codec-1.3.jar
-commons-httpclient-3.1.jar
-commons-logging-1.0.4.jar
-junit-3.8.jar
-junitperf-1.8.jar
-log4j-1.2.15.jar
Reference the config\server-j2ee14.xml as Tomcat config file
Start tomcat
Run com.ubikingenierie.client.MultiThreadedTestClientSuite
Comment 2 Philippe Mouawad 2007-12-07 14:04:05 UTC
Created attachment 21245 [details]
The dependent project that will be added by DevLoader as an externalrepository

See previous patch.
You just have to import in in the same Eclipse workspace as a project called
DependentProject
Comment 3 Philippe Mouawad 2007-12-07 14:05:18 UTC
Created attachment 21246 [details]
Tomcat Sysdeo DevLoader Last version

You can get this from:
http://www.eclipsetotale.com/tomcatPlugin.html
Comment 4 Philippe Mouawad 2007-12-07 14:07:23 UTC
I just forgot one thing:
You have to unpack DevLoader.zip in TOMCAT5_HOME/server/classes to make the
DevLoader available to Tomcat.
See:
http://www.eclipsetotale.com/tomcatPlugin.html
And inside plugin see DevLoader.zip
Comment 5 Philippe Mouawad 2007-12-07 14:22:40 UTC
Their is a solution, add synchronized to loadClass(String name, boolean resolve)
line 1238, but it is maybe an important contention point for you:
public synchronized Class loadClass(String name, boolean resolve)

Although note that java.lang.ClassLoader has a synchronized method:
    protected synchronized Class<?> loadClass(String name, boolean resolve)

Philippe.
Comment 6 Mark Thomas 2007-12-10 14:28:21 UTC
Thanks for the test case. I was able to reproduce this. A sync is required to
fix it but a much smaller one than proposed.

I have fixed trunk and proposed the fix for backport to 6.0.x and 5.5.x
Comment 7 Mark Thomas 2008-01-06 15:19:16 UTC
Fixed in 5.5.x and will be included in 5.5.26 onwards.
Fixed in 6.0.x and will be included in 6.0.16 onwards.
Comment 8 Alfonso Gonzalez del Riego 2009-02-18 12:03:38 UTC
Are you sure it is OK to synchronize around the call to URLClassLoader.findClass?

If you have a page with 2 frames, each loading a JSP at the same time:

Thread T1 tries to load my.class.Foo
Thread T2 tries to load my.class.Foo

T1 calls WebappClassLoader.loadClass
T2 calls WebappClassLoader.loadClass

WebappClassLoader determines it has not loaded the class for both threads (since there is no synchronization at this point).

Both T1 and T2 get to the synchronized block in findClass.
They get synchronized, but doing the wrong thing, and a LinkageError gets thrown.

Don't you think that WebappClassLoader.loadClass(String, boolean) ought to be synchronized just like it is in java.lang.ClassLoader?

I am actually running into this problem with Tomcat 5.5.27 (on a 4 core machine):

Caused by: java.lang.LinkageError: loader (instance of  org/apache/catalina/loader/WebappClassLoader): attempted  duplicate class definition for name: "org/apache/struts/taglib/tiles/PutTag"
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
    at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
    at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:904)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1353)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1232)
    at org.apache.jasper.servlet.JasperLoader.loadClass(JasperLoader.java:125)
    at org.apache.jasper.servlet.JasperLoader.loadClass(JasperLoader.java:63)
    at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320) 
.... etc ....
Comment 9 Richard George 2009-05-12 08:58:56 UTC
(In reply to comment #8)

Hi,

We also experienced this issue while using the Sysdeo plugin with DevLoader. It was resolved (for us) by synchronizing WebappClassLoader.loadClass.

Cheers.

> Are you sure it is OK to synchronize around the call to
> URLClassLoader.findClass?
> 
> If you have a page with 2 frames, each loading a JSP at the same time:
> 
> Thread T1 tries to load my.class.Foo
> Thread T2 tries to load my.class.Foo
> 
> T1 calls WebappClassLoader.loadClass
> T2 calls WebappClassLoader.loadClass
> 
> WebappClassLoader determines it has not loaded the class for both threads
> (since there is no synchronization at this point).
> 
> Both T1 and T2 get to the synchronized block in findClass.
> They get synchronized, but doing the wrong thing, and a LinkageError gets
> thrown.
> 
> Don't you think that WebappClassLoader.loadClass(String, boolean) ought to be
> synchronized just like it is in java.lang.ClassLoader?
> 
> I am actually running into this problem with Tomcat 5.5.27 (on a 4 core
> machine):
> 
> Caused by: java.lang.LinkageError: loader (instance of 
> org/apache/catalina/loader/WebappClassLoader): attempted  duplicate class
> definition for name: "org/apache/struts/taglib/tiles/PutTag"
>     at java.lang.ClassLoader.defineClass1(Native Method)
>     at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
>     at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
>     at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
>     at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
>     at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
>     at java.security.AccessController.doPrivileged(Native Method)
>     at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
>     at
> org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:904)
>     at
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1353)
>     at
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1232)
>     at org.apache.jasper.servlet.JasperLoader.loadClass(JasperLoader.java:125)
>     at org.apache.jasper.servlet.JasperLoader.loadClass(JasperLoader.java:63)
>     at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320) 
> .... etc ....
Comment 10 Richard George 2009-05-12 09:07:56 UTC
Just to add to that, we are using Tomcat 6.0.18, with Sysdeo 3.2.1. I realise there it may be a little heavy-handed synchronizing the whole method, but since this is only a patch intended for use in dev I don't see it as too much of an issue.

Cheers.
Comment 11 Mark Thomas 2009-08-17 15:23:18 UTC
Point taken re syncs and WebappClassLoader.loadClass(String, boolean)
I have proposed a patch for 6.0.x and 5.5.x that adds that sync.
Comment 12 Mark Thomas 2009-11-18 04:26:14 UTC
This has been fixed in 6.0.x (with a slightly different patch) and will be included in 6.0.21 onwards.
Comment 13 Mark Thomas 2010-03-24 14:09:56 UTC
Further testing has identified issues under some circumstances. The final solution is being worked on for 7.0.x and when that is finalised it will be back-ported to 6.0.x and 5.5.x
Comment 14 Mark Thomas 2010-03-25 20:36:49 UTC
Updated patch that uses synchronized(this) applied to 7.0.x and proposed for 5.5.x and 6.0.x
Comment 15 Konstantin Kolinko 2010-04-20 11:20:17 UTC
The issue was fixed in 5.5.x in r935947 and will be in 5.5.30 onwards.

The patch for 6.0.x mentioned in Comment 13 is tracked as bug 48903. As of now, it has been proposed for 6.0.x, but has not been applied yet.
Comment 16 Mark Thomas 2010-04-20 13:15:13 UTC
Moving to 6 since this is fixed in 5.
Comment 17 Rainer Jung 2010-05-06 15:21:03 UTC
Fixed in TC 6 by r941868. Will be part of 6.0.27.