[ 
https://issues.apache.org/jira/browse/GROOVY-12458?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18130121#comment-18130121
 ] 

ASF GitHub Bot commented on GROOVY-12458:
-----------------------------------------

paulk-asert opened a new pull request, #3004:
URL: https://github.com/apache/groovy/pull/3004

   …ls at runtime with groovy-grape-maven ("No suitable ClassLoader found for 
grab")




> @Grab in a class defined by a non-GroovyClassLoader fails at runtime with 
> groovy-grape-maven ("No suitable ClassLoader found for grab")
> ---------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12458
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12458
>             Project: Groovy
>          Issue Type: Bug
>          Components: Grape
>    Affects Versions: 6.0.0
>            Reporter: Jex Jexler
>            Priority: Major
>         Attachments: GrabClassLoaderRepro.groovy
>
>
> Since Groovy 6, the static initializer generated for @Grab passes the class's 
> own class loader to Grape.grab() 
> (GrabAnnotationTransformation.callGrabAsStaticInitIfNeeded: classLoader: 
> <Class>.getClassLoader()).
> If that class was defined by a class loader that is not a GroovyClassLoader 
> (or RootLoader), but has one as an ancestor, both engines reject the passed 
> loader in chooseClassLoader() without considering its parents, and fall back 
> to the stack walk via calleeDepth, which (as the comment in 
> callGrabAsStaticInitIfNeeded notes) overshoots from a static initializer:
> * GrapeMaven.chooseClassLoader() then throws "RuntimeException: No suitable 
> ClassLoader found for grab".
> * GrapeIvy.chooseClassLoader() usually recovers via its fallbacks (scanning 
> frames at calleeDepth +/- 2, which finds a class whose class loader has a 
> valid ancestor), i.e. more by chance than by design.
> Hosts that define compiled script classes with their own class loaders below 
> a GroovyClassLoader are affected. We hit this with *Grengine* 
> (https://grengine.ch/), where @Grab works with groovy-grape-ivy, but fails 
> with groovy-grape-maven. Grengine now works around it in its GrapeEngine 
> wrapper by replacing the passed class loader with its closest valid ancestor:
> https://github.com/alainstalder/grengine/commit/bd0dd9845440f627e282c4b474f1ec111b6f9a5d
> (code: 
> https://github.com/alainstalder/grengine/blob/bd0dd9845440f627e282c4b474f1ec111b6f9a5d/src/main/java/ch/grengine/code/groovy/DefaultGroovyCompiler.java#L673-L699)
> *Standalone reproducer (attached, GrabClassLoaderRepro.groovy):* compiles a 
> script with @Grab using a GroovyClassLoader, defines the resulting class with 
> a plain ClassLoader subclass whose parent is that GroovyClassLoader, and 
> instantiates it, which runs the static initializer:
> * with groovy-6.0.0 + groovy-grape-ivy (and its dependencies) on the 
> classpath: OK
> * with groovy-6.0.0 + groovy-grape-maven (and its dependencies) on the 
> classpath:
>   ExceptionInInitializerError, caused by RuntimeException: No suitable 
> ClassLoader found for grab
>   at groovy.grape.maven.GrapeMaven.chooseClassLoader(GrapeMaven.groovy:785)
> Run e.g. with: java -cp <classpath> groovy.ui.GroovyMain 
> GrabClassLoaderRepro.groovy
> *Suggested fix:* in chooseClassLoader() (in both engines, or at least in 
> GrapeMaven), if args.classLoader is set but not a valid target, walk up its 
> parents to the first valid one before falling back to the stack walk, e.g.:
>  
> {code:java}
>     ClassLoader loader = (ClassLoader) args.classLoader
>     while (loader && !isValidTargetClassLoader(loader)) {
>         loader = loader.getParent()
>     }
>     if (!isValidTargetClassLoader(loader)) {
>         // existing stack walk (and fallbacks)
>     }{code}
>  



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to