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
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
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
Created attachment 21246 [details] Tomcat Sysdeo DevLoader Last version You can get this from: http://www.eclipsetotale.com/tomcatPlugin.html
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
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.
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
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.
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 ....
(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 ....
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.
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.
This has been fixed in 6.0.x (with a slightly different patch) and will be included in 6.0.21 onwards.
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
Updated patch that uses synchronized(this) applied to 7.0.x and proposed for 5.5.x and 6.0.x
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.
Moving to 6 since this is fixed in 5.
Fixed in TC 6 by r941868. Will be part of 6.0.27.