You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have figured out a solution to the classloader issue with @Grab. See my answer for this StackOverflow question.
To summarize, because the GrapeIvy.groovy implementation of Grape does not allow classloaders other than groovy.lang.GroovyClassLoader or org.codehaus.groovy.tools.RootLoader, if blows up with a big exception:
Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
at groovy.grape.GrapeIvy.chooseClassLoader(GrapeIvy.groovy:180)
at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:247)
at groovy.grape.Grape.grab(Grape.java:167)
However, I found that it was possible by using the metaclass to override the GrapeIvy.chooseClassLoader with an implementation that allows any classloader:
GrapeIvy.metaClass.chooseClassLoader = { Mapargs->def loader = args.classLoader
if (loader?.class ==null) {
loader = (args.refObject?.class
?:ReflectionUtils.getCallingClass(args.calleeDepth?:1)
)?.classLoader
while (loader && loader?.class ==null) {
loader = loader.parent
}
if (loader?.class ==null) {
thrownewRuntimeException("No suitable ClassLoader found for grab")
}
}
return loader
}
This just needs to run somewhere before the script gets loaded somehow and the @Grab gets executed.
If you are using Junit (or JenkinsPipelineUnit), you probably would want to put this code in a method annotated with @BeforeClass.
If you are using jenkins-spock like I am (instead of JenkinsPipelineUnit), you will need to create a custom Spock global extension (you can't simply use setupSpec because the super class' setupSpec gets executed first and it scans the classpath and loads the scripts, executing the @Grab)
So in my project, added a file test/unit/resources/META-INF/services/org.spockframework.runtime.extension.IGlobalExtension that has a single line, the fully qualified name of the custom global extension class:
org.cvp.extension.GroovyGrapeExtension
Then I have a class called GroovyGrapeExtension that extends org.spockframework.runtime.extension.AbstractGlobalExtension and implements the start() method:
packageorg.cvp.extensionimportgroovy.grape.GrapeIvyimportorg.codehaus.groovy.reflection.ReflectionUtilsimportorg.spockframework.runtime.extension.AbstractGlobalExtension/** * This is necessary because by default, {@link GrapeIvy} will not allow @Grab to load classes using anything but the * classloader implementations {@link groovy.lang.GroovyClassLoader} or {@link org.codehaus.groovy.tools.RootLoader}. * However, the classloader used by Gradle when running unit tests is different, so @Grab will fail with an error * unless it is overridden. To make this more difficult, the offending method * {@link GrapeIvy#isValidTargetClassLoaderClass} is private, and due to a bug in Groovy * (https://issues.apache.org/jira/browse/GROOVY-7368), private methods cannot be overridden using the meta class. * * I would have overridden this behavior by adding a setupSpec() method to {@link org.cvp.pipeline.MessageUtilsSpec} * however {@link com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification#setupSpec} gets called first * and that is where the script is loaded into the classpath (and the @Grab executed). * * This extension needed to be written because the extension gets executed before any setupSpec() methods are run. * This extension is also global, so it gets run before the initialization of every Spock instance.*/classGroovyGrapeExtensionextendsAbstractGlobalExtension {
/** * The {@link org.spockframework.runtime.extension.IGlobalExtension#start} gets executed at the startup of Spock, * before any setupSpec() functions are executed.*/@Overridevoidstart() {
GrapeIvy.metaClass.chooseClassLoader = { Mapargs->def loader = args.classLoader
if (loader?.class ==null) {
loader = (args.refObject?.class
?:ReflectionUtils.getCallingClass(args.calleeDepth?:1)
)?.classLoader
while (loader && loader?.class ==null) {
loader = loader.parent
}
if (loader?.class ==null) {
thrownewRuntimeException("No suitable ClassLoader found for grab")
}
}
return loader
}
}
}
So far it is working in my project, and I haven't really had time to explore if something like this could be integrated into the Gradle plugin itself so people don't need to add the boilerplate code to their repository.
The text was updated successfully, but these errors were encountered:
I have figured out a solution to the classloader issue with
@Grab
. See my answer for this StackOverflow question.To summarize, because the
GrapeIvy.groovy
implementation of Grape does not allow classloaders other thangroovy.lang.GroovyClassLoader
ororg.codehaus.groovy.tools.RootLoader
, if blows up with a big exception:However, I found that it was possible by using the metaclass to override the
GrapeIvy.chooseClassLoader
with an implementation that allows any classloader:This just needs to run somewhere before the script gets loaded somehow and the
@Grab
gets executed.If you are using Junit (or JenkinsPipelineUnit), you probably would want to put this code in a method annotated with
@BeforeClass
.If you are using jenkins-spock like I am (instead of JenkinsPipelineUnit), you will need to create a custom Spock global extension (you can't simply use
setupSpec
because the super class'setupSpec
gets executed first and it scans the classpath and loads the scripts, executing the@Grab
)So in my project, added a file
test/unit/resources/META-INF/services/org.spockframework.runtime.extension.IGlobalExtension
that has a single line, the fully qualified name of the custom global extension class:Then I have a class called
GroovyGrapeExtension
that extendsorg.spockframework.runtime.extension.AbstractGlobalExtension
and implements thestart()
method:So far it is working in my project, and I haven't really had time to explore if something like this could be integrated into the Gradle plugin itself so people don't need to add the boilerplate code to their repository.
The text was updated successfully, but these errors were encountered: