Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aop:scoped-proxy may fail with LinkageError: loader attempted duplicate class definition for name [SPR-11398] #16025

Closed
spring-issuemaster opened this issue Feb 6, 2014 · 12 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

commented Feb 6, 2014

Piotr Findeisen opened SPR-11398 and commented

When using:

  • cglib 2.2 directly
  • spring 3.2.5 (which wraps cglib 3.0) and spring's <aop:scoped-proxy/> feature

it possible to run into

java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader):
   attempted  duplicate class definition for name: "$java/lang/Object$$FastClassByCGLIB$$3f697993"

Full stacktrace:

org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
	at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237)
	at org.springframework.cglib.reflect.FastClass$Generator.create(FastClass.java:64)
	at org.springframework.cglib.proxy.MethodProxy.helper(MethodProxy.java:121)
	at org.springframework.cglib.proxy.MethodProxy.init(MethodProxy.java:74)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:202)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
	at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:132)
	at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:120)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
	at com.MyBean$$EnhancerByCGLIB$$f1fad224.toString(<generated>)
	at com.MyTest.testBeanWithAopScopedProxy(SpringAndCglibTest.java:122)
....
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384)
	at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)
	... 36 more
Caused by: java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "$java/lang/Object$$FastClassByCGLIB$$3f697993"
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
	... 41 more
Attempt at analysis

As far as I can understand, this seems to be caused by class naming conflict between CGLIB 2.2 and Spring-repackaged CGLIB.

CGLIB's DefaultNamingPolicy solves name collisions using sort of "already used set", but obviously there are two such sets -- one CGLIB's and another Spring-CGLIB's -- so name collisions cannot be avoided and class generation fails.

Notes

This is not only about probabilistic name clash. There seems to be nothing preventing Spring-CGLIB unconsciously re-using classes generated by non-Spring-CGLIB, which may be "programmed" with different behavior than desired.

Proposed solution

Spring is already using non-default prefix ("org.springframework.cglib.empty.Object", see DefaultNamingPolicy.getClassName), but this is not enough, since prefix is not used if provided explicitly.

  • Spring-repackaged CGLIB could have DefaultNamingPolicy.getTag() function changed to return something else than "ByCGLIB"
  • OR DefaultNamingPolicy could be called with different 'source' -- see initialization of org.springframework.cglib.reflect.FastClass.SOURCE

Affects: 3.2.5, 4.0 GA

Issue Links:

  • #14875 Calling to Proxy upon abstract class ends with java.lang.LinkageError: duplicate class definition
  • #15337 Spring 3.2.2 AOP has increased memory usage
  • #16404 MemorySafeUndeclaredThrowableStrategy class not found

Backported to: 3.2.8

0 votes, 5 watchers

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 6, 2014

Piotr Findeisen commented

Spring wraps cglib since #14303, I believe.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 6, 2014

Juergen Hoeller commented

See #14875 which is basically referring to the same problem: Our general recommendation to either not use double-proxying with CGLIB, or to use Spring's repackaged CGLIB version yourself.

That said, your suggestion for a different NamingPolicy sounds worthwhile. We'll be exploring that path for 4.0.2, and potentially also for a backport to 3.2.8.

Juergen

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 6, 2014

Juergen Hoeller commented

I've addressed this for 4.0.2, to be pushed to master soon. We're using a SpringNamingPolicy with a tag "BySpringCGLIB" now.

However, I'm afraid this is too high risk a change for 3.2.8 which is a pure maintenance release at the end of the 3.2.x line.

Is there any reason not to upgrade to Spring Framework 4.0.2 for you? I'll let you know once it's available in a snapshot...

Juergen

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 6, 2014

Piotr Findeisen commented

Hi Juergen,

Thanks for quick response. Here are my answers to various topics mentiooned.

Using Spring-packaged CGLIB
Using Spring-CGLIB is not an easy option for two reasons:

  • there are no sources I can use during development (#15094), which would make it a nightmare
  • CGLIB is used not only by my own code, I cannot change it

Regarding SPR-10242 and double-proxying.
I am not sure I understand. I am using CGLIB for enhancing data classes and Spring is using CGLIB for enhancing beans -- I don't think there is a single instance wrapped in a proxy twice, using different CGLIBs.

Regarding upgrade to Spring 4.
I really, really do value Spring for high quality -- I mean it -- but I don't expect it to be bug-free. I have just finished upgrading to Spring 3.2 and I think you're the one who fixed the issues I found on the way. Since I have somewhat fixed schedule of deliveries, I cannot withhold the current one to jump onto Spring 4 now. This is not an option for me now.

Since this bug seems to be introduced in Spring 3.2 I personally would consider fixing this in 3.2.x line, unless 3.2.x is no longer maintained. What problem are you expecting from applying this fix in 3.2?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 6, 2014

Juergen Hoeller commented

Well, it's subtle effects that I'm afraid of, such as some code checking for CGLIB class names based on hardcoded assumptions. Admittedly, it was also some test failures that I started to see but those seem to have gone away now after cleaning the change set.

Point taken about this having been introduced in 3.2, and it technically being a regression from 3.1. So just to be clear, you're seeing this when the same class gets proxied for different purposes in the same system, once by Spring and once by some other code?

Juergen

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 6, 2014

Juergen Hoeller commented

I've committed this to master now, so it'll show up in the next 4.0.2 snapshot in about half an hour. See http://projects.spring.io/spring-framework/ for Maven coordinates, in case you're up for a smoke test even against 4.0.2 there.

My plan is to backport it to 3.2.8 as well now, if you promise to test the following 3.2.8 snapshot as hard as possible :-) We intend to co-release 4.0.2 and 3.2.8 towards the end of next week, so we still have about a week to go.

Juergen

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 6, 2014

Piotr Findeisen commented

Hi,

Thank you!
I will pick it up, yes. One jenkins build is consistently failing because of this. When do you expect the 3.2.8 snapshot to contain your changes?

Btw. I see you removed "$java/lang/Object$$FastClassByCGLIB$$3f697993" from the issue summary. The 3f697993 part looks like random, but actually it's not -- if I remmeber it's hex(hash("$java.lang.Object")) or something like that.
All together, this is a perfect search keyword -- top google results returns me http://proxool.sourceforge.net/changelog.html where they say they have applied naming policy (yeah, the proposed solution wasn't mine)

So just to be clear, you're seeing this when the same class gets proxied for different purposes in the same system, once by Spring and once by some other code?

It's not enough to proxy same class twice. I'm rather a cglib newbie, but looking at AbstractClassGenerator.create, if attemptLoad == true, cglib tries to load the class from ClassLoader before actually creating it. The code is synchronized on some cglib lock, but not on a ClassLoader (uff, that would be a killer), so it's open to fail only in concurrent situation, if another thread does similar operation in "another cglib" with the same parameters.

Obviously, this is not deterministic. I have a test reproducing with about 0.7 probability, but it is still using quite a few classes. Worse, one of those concurring threads is the finalizer thread. Having said that, I don't have an answer to your question.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 6, 2014

Juergen Hoeller commented

I just removed it because the title seemed a bit long and arbitrary, indeed... Do you always see the exception for java.lang.Object there, or sometimes for other classes as well?

As for 3.2.8, I'm in the process of backporting this along with some other changes, so it'll be available in an hour or two. I'll drop a note here once a corresponding snapshot is published.

Juergen

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 6, 2014

Piotr Findeisen commented

So far only Object. I don't expect other classes since CGLIB'd classes and Spring-CGLIB'd classes have Object as the common ancestor.

(This reasoning is based on a 100% false assumption that I know what "$java/lang/Object$$FastClassByCGLIB$$3f697993" class is created for.)

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 6, 2014

Juergen Hoeller commented

This is available in the latest 3.2.8 snapshot now...

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 7, 2014

Piotr Findeisen commented

Just to let you know: I am on 3.2.8 snapshot already and saw no failure so far.
Great thanks!

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Feb 7, 2014

Juergen Hoeller commented

Great to hear - thanks for letting us know!

Juergen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.