[
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)